How to use confidential client in keycloak? - keycloak

I have OpenID public-type client that is linked to user. I can login into that user and get grops in access in token.
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
     headers.put("grant_type", List.of("password"));
     headers.put("client_id", List.of("client-id"));
     headers.put("username", List.of("username"));
     headers.put("password", List.of("password"));
     headers.put("scope", List.of("openid"));
     String reponse = new RestTemplate()
.postForObject("https://domain/auth/realms/r/protocol/openid-connect/token", headers, String.class);
But I get token in the same way for 'confidential' type client linked to user. Getting error 401.
So, how can I authenticate for confidential type?

You need to send also client secret for the confidential client -client_secret parameter. So in your case:
headers.put("client_secret", List.of("client_secret"));
Note: You can find client secret value, in the Keycloak client configuration Credentials section - it depends on used Client Authenticator, so it can be even more complicated.

You're missing to send the client_secret in your request.
Confidential clients are required to provide a client secret when they exchange the temporary codes for tokens. Public clients are not required to provide this client secret.
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.put("grant_type", List.of("password"));
headers.put("client_id", List.of("client-id"));
headers.put("client_secret", List.of("client-secret"));
headers.put("username", List.of("username"));
headers.put("password", List.of("password"));
headers.put("scope", List.of("openid"));
String reponse = new RestTemplate()
.postForObject("https://domain/auth/realms/r/protocol/openid-connect/token", headers, String.class);

Related

How do I load a "user" in a micronaut backend when JWT is provided

I have a Micronaut microservice that handles authentication via JsonWebTokens (JWT) from this guide.
Now I'd like to extend this code. The users in my app have some extra attributes such as email, adress, teamId etc. I have all users in the database.
How do I know in the backend controller method which user corresponds to the JWT that is sent by the client?
The guide contains this example code for the Micronaut REST controller:
#Secured(SecurityRule.IS_AUTHENTICATED)
#Controller
public class HomeController {
#Produces(MediaType.TEXT_PLAIN)
#Get
public String index(Principal principal) {
return principal.getName();
}
}
I know that I can get the name of the principal, ie. the username from the HttpRequest. But how do I get my additional attributes?
(Maybe I misunderstand JWT a bit???)
Are these JWT "claims" ?
Do I need to load the corresponding user by username from my DB table?
How can I verify that the sent username is actually valid?
edit Describing my usecase in more detail:
Security requirements of my use case
Do not expose valid information to the client
Validate everything the client (a mobile app) sends via REST
Authentication Flow
default oauth2 flow with JWTs:
Precondition: User is already registerd. Username, hash(password) and furhter attributes (email, adress, teamId, ..) are known on the backend.
Client POSTs username and password to /login endpoint
Client receives JWT in return, signed with server secret
On every future request the client sends this JWT as bearer in the Http header.
Backend validates JWT <==== this is what I want to know how to do this in Micronaut.
Questions
How to validate that the JWT is valid?
How to and where in which Java class should I fetch additional information for that user (the additional attributes). What ID should I use to fetch this information. The "sub" or "name" from the decoded JWT?
How do I load a “user” in a micronaut backend when JWT is provided?
I am reading this as you plan to load some kind of User object your database and access it in the controller.
If this is the case you need to hook into the place where Authentication instance is created to read the "sub" (username) of the token and then load it from the database.
How to extend authentication attributes with more details ?
By default for JWT authentication is created using JwtAuthenticationFactory and going more concrete default implementation is DefaultJwtAuthenticationFactory. If you plan to load more claims this could be done by replacing it and creating extended JWTClaimsSet or your own implementation of Authentication interface.
How do I access jwt claims ?
You need to check SecurityService -> getAuthentication() ->getAttributes(), it returns a map of security attributes which represent your token serialised as a map.
How to validate that the JWT is valid?
There is a basic validation rules checking the token is not expired and properly signed, all the rest validations especially for custom claims and validating agains a third parties sources have to be done on your own.
If you plan to validate your custom claims, I have already open source a project in this scope, please have a look.
https://github.com/traycho/micronaut-security-attributes
How to extend existing token with extra claims during its issuing ?
It is required to create your own claims generator extending JWTClaimsSetGenerator
#Singleton
#Replaces(JWTClaimsSetGenerator)
class CustomJWTClaimsSetGenerator extends JWTClaimsSetGenerator {
CustomJWTClaimsSetGenerator(TokenConfiguration tokenConfiguration, #Nullable JwtIdGenerator jwtIdGenerator, #Nullable ClaimsAudienceProvider claimsAudienceProvider) {
super(tokenConfiguration, jwtIdGenerator, claimsAudienceProvider)
}
protected void populateWithUserDetails(JWTClaimsSet.Builder builder, UserDetails userDetails) {
super.populateWithUserDetails(builder, userDetails)
// You your custom claims here
builder.claim('email', userDetails.getAttributes().get("email"));
}
}
How do I access jwt claims ?
If you want to access them from the rest handler just add io.micronaut.security.authentication.Authentication as an additional parameter in the handling method. Example
#Get("/{fooId}")
#Secured(SecurityRule.IS_AUTHENTICATED)
public HttpResponse<Foo> getFoo(long fooId, Authentication authentication) {
...
}
I found a solution. The UserDetails.attributes are serialized into the JWT. And they can easily be set in my CustomAuthenticationProviderclass:
#Singleton
#Slf4j
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Publisher<AuthenticationResponse> authenticate(
#Nullable HttpRequest<?> httpRequest,
AuthenticationRequest<?, ?> authenticationRequest)
{
// ... autenticate the request here ...
// eg. via BasicAuth or Oauth 2.0 OneTimeToken
// then if valid:
return Flowable.create(emitter -> {
UserDetails userDetails = new UserDetails("sherlock", Collections.emptyList(), "sherlock#micronaut.example");
// These attributes will be serialized as custom claims in the JWT
Map attrs = CollectionUtils.mapOf("email", email, "teamId", teamId)
userDetails.setAttributes(attrs);
emitter.onNext(userDetails);
emitter.onComplete();
}, BackpressureStrategy.ERROR);
}
}
And some more pitfalls when validating the JWT in the backend
A JWT in Micronaut MUST contain a "sub" claim. The JWT spec does not require this, but Micronaut does. The value of the "sub" claim will become the username of the created UserDetails object.
If you want to load addition attributes into these UserDetails when the JWT is validated in the backend, then you can do this by implementing a TokenValidator. But (another pitfal) then you must set its ORDER to a value larger than micronaut's JwtTokenValidator. Your order must be > 0 otherwise your TokenValidator will not be called at all.

Generate access token using JWT

I've been given access to an okta token endpoint. I would like to use this service to request a token. I was given a url, client id, client secret, scope and grant type. I can use postman to make a POST call to the url (/v1/token) and pass the above info (client id, client secret, scope and grant type) and I get an access token back.
I can easily make this call in java with RestTemplate or equivalent, but I would like to use an API that would manage the token for me.
I've found JJWT. All the examples I see out there show me how to create a JWT using JJWT. What I would like to do is to get my access token, but I'm not sure how to do that. I mean i get that JJWT is an API to create JWT, but then how can I use the JWT to get my access token?
Any help/clarification/direction is much appreciated.
We using JWT with the node.js, to create new Token jwt.sign(data, key) takes at least to an argument, the fist must be some credential like userId, email..., the second will be key to verify later. to verify the token is it valid we use jwt.verify(), the first argument is token (where the jwt.sing() give you) and the second is the key (where you provide when creating);
example:
Creating JWT token:
var jwt = require('jsonwebtoken');
cosnt token = jwt.sign({ email: 'test#test.com', userId: '993333' }, 'secretkey');
verifying Token:
try {
const decodedToken = jwt.verify(token, 'secretkey');
}
catch(err) {
throw new Error(err)
}
// once verified
conosole.log(decodedToken)
I found this post how to create and verify token using java, thanks!

Gsuite Directory API 403 Error on API call or Grant Type Error on JWT Generation

Using Python and creating my own JWT using HTTP/Rest methodology, I simply can't get delegation to work.
On one hand, google JWT troubleshoot documentation says that ISS needs to be the same as the SUB (the service account).
However, on the server to server oauth2 documentation, it says that to impersonate an account, the sub needs to be the account I am attempting to impersonate in the claim.
Needless to say, despite enabling domain-wide delegation, adding the correct scopes, etc, I get nothing back but 403 when attempting to access the user domain utilizing the requests library in python with the following example:
> requests.get("https://www.googleapis.com/admin/directory/v1/users/useremail#/
> google.org",headers={'Authorization':f' Bearer {oauth2tokenhere}'})
Here is an example of my claim:
> claim = { "iss": 'serviceaccountemail',
> 'sub': 'impersonatedaccountemail',
> 'scope': 'https://www.googleapis.com/auth/admin.directory.user.readonly',
> 'exp': ((datetime.datetime.today() + datetime.timedelta(minutes=60)).timestamp()),
> 'iat': ((datetime.datetime.today()).timestamp()),
> 'aud': "https://oauth2.googleapis.com/token"
> }
The above claim will generate a generalized grant error (cute, but not helpful).
If I change the claim and ensure that the sub and the iss are the same, the oauth2token generates, but I get a 403 error when attempting to hit the API.
Here is the server to server oauth2 documentation stating the sub should be the
account the service account is attempting to impersonate.
https://developers.google.com/identity/protocols/OAuth2ServiceAccount
Here is the troubleshooting article outlining the ISS/Sub being the same (although cloud article is the closest relevant topic I could find)
https://cloud.google.com/endpoints/docs/openapi/troubleshoot-jwt
EDIT:
I am utilizing the service account information from the downloaded .json file that is downloaded when creating the service account file.
import json as j
import datetime
import jwt
import requests
#creates the claim, 'secret' (from the private key), and the kid, from the service account file, and returns these values in a tuple.
#the tuple can then be used to make dependable positional argument entries to the parameters of the createJWT function.
def create_claim_from_json(self,objpath,scope=["https://www.googleapis.com/auth/admin.directory.user.readonly" "https://www.googleapis.com/auth/admin.directory.user"]):
with open(f'{objpath}','r') as jobj:
data = j.load(jobj)
claim = {
"iss": str(data['client_id']),
"sub": str(data['client_id']),
"scope": str(scope),
"exp": ((datetime.datetime.today() + datetime.timedelta(minutes=59)).timestamp()),
"iat": ((datetime.datetime.today()).timestamp()),
"aud": "https://oauth2.googleapis.com/token"
}
private_key = data['private_key']
kid = {"kid": f"{data['private_key_id']}"}
return claim, private_key, kid
#assembles the JWT using the claim, secret (Private key from the Service account file), the kid value, and the documented RS256 alg.
#returns the completed JWT object back to be used to send to the oauth2 endpoint
#the JWT will be used in the function call retrieve_oauth2token.
def createJWT(self, claim, secret, kid, alg='RS256'):
encoded_jwt = (jwt.encode(claim, secret, alg, kid)).decode('UTF-8')
return encoded_jwt
#Using the JWT created in memory, sends the JWT to the googleapi oauth2 uri and returns a token
def retrieve_oauth2token(self, jwt):
oauth2 = requests.post(f'https://oauth2.googleapis.com/token?grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant- type%3Ajwt-bearer&assertion={jwt}')
oauth2=oauth2.json()
return oauth2 #['access_token'], oauth2['token_type']
The documentation has a clear overview, did you follow the steps as described in the addendum? I am missing some parts of your code. But you did not mention using a service account (json) key. And the documentation also show that you have to use the (delegated) service account as both iss and sub. Furthermore, you need to use a kid. This is how it is done:
payload = {
'iss': '123456-compute#developer.gserviceaccount.com',
'sub': '123456-compute#developer.gserviceaccount.com',
'aud': 'https://firestore.googleapis.com/',
'iat': time.time(),
'exp': iat + 3600
}
additional_headers = {'kid': PRIVATE_KEY_ID_FROM_JSON}
signed_jwt = jwt.encode(payload, PRIVATE_KEY_FROM_JSON, headers=additional_headers, algorithm='RS256')
url = "URL OF THE API TO CALL"
header = {'Authorization': f'Bearer {signed_jwt}'}
resp = requests.get(url, headers=header)
Note: you can find PRIVATE_KEY_FROM_JSON in the private_key_id field of your service account JSON credentials file.

Auth 0 configuration audience

I just found out that I have a problem with auth0 and it relates to the auth0 configuration audience. So when I explicitly write the audience, the JWT verification failed with error The provided Algorithm doesn't match the one defined in the JWT's Header. When I don't write the audience, everything will work fine, except now everytime the token expire and user click on login link it skip the login process and immediately logged in with the previous credential. I don't want this to happen, I want user to still authenticate themselves again after token expire, just like when I write the audience.
So what is audience and why does it affect the behaviour like this?
And How can I fix it to get the behaviour I wanted?
Below is the configuration of the Auth0
auth0 = new auth0.WebAuth({
clientID: environment.auth0ClientId,
domain: environment.auth0Domain,
responseType: 'token id_token',
//Below is the audience I'm talking about
audience: '${constants.MY_APP}/userinfo',
redirectUri: `${constants.ORIGIN_URL}/auth`,
scope: 'openid email'
});
I need to know how I can make the JWT to be verified correctly as well as make the login behaviour correctly when the JWT expire.
Auth0 can issue two types of tokens: opaque and JWT.
When you specify the audience parameter, you will receive a JWT token. JWTs differ from opaque tokens in that they are self-contained and therefore you verify them directly in your application.
In this case, the JWT you have received is signed with an algorithm different to that which you've defined in your verification logic. You can decode the JWT using https://jwt.io and you can see which algorithm it was signed with in the alg attribute of the header.
You can also find out the signing algorithm your API uses in the Auth0 dashboard. Go APIs, click your API, click the Settings tab and then scroll to Token Setting. You will see it listed as the Signing Algorithm.
Judging by the error message, you are using the java-jwt library, in which case you will need change the signing algorithm accordingly per the steps outlined here: https://github.com/auth0/java-jwt#verify-a-token
For HS256:
try {
Algorithm algorithm = Algorithm.HMAC256("secret");
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("auth0")
.build(); //Reusable verifier instance
DecodedJWT jwt = verifier.verify(token);
} catch (JWTVerificationException exception){
//Invalid signature/claims
}
Where secret is your API's Signing Secret.
For RS256, it's a little more involved. You first need to decode the token to retrieve the kid (key ID) from the header:
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE";
try {
DecodedJWT jwt = JWT.decode(token);
} catch (JWTDecodeException exception){
//Invalid token
}
You then need to construct a JwkProvider using the jwks-rsa-java library:
JwkProvider provider = new UrlJwkProvider("https://your-domain.auth0.com/");
Jwk jwk = provider.get(jwt.getKeyId());
Finally, you can use the public key retrieved from the JWKS and use it to verify the token:
RSAPublicKey publicKey = (RSAPublicKey) jwk.getPublicKey();
try {
Algorithm algorithm = Algorithm.RSA256(publicKey, null);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("auth0")
.build(); //Reusable verifier instance
DecodedJWT jwt = verifier.verify(token);
} catch (JWTVerificationException exception) {
//Invalid signature/claims
}
Keep in mind that it's preferred to use RS256 over HS256 for the reasons outlined here: https://auth0.com/docs/apis#signing-algorithms
You may also find this article useful for detailed information on verifying tokens: https://auth0.com/docs/api-auth/tutorials/verify-access-token

Using OAuth with SforceServiceLocator

I'm looking at some code which is using the Salesforce SOAP API to create a session and access data:
SoapBindingStub binding = (SoapBindingStub) new SforceServiceLocator().getSoap();
String username;
String password;
[...]
LoginResult result = binding.login(username, password);
binding._setProperty(SoapBindingStub.ENDPOINT_ADDRESS_PROPERTY,result.getServerUrl());
SessionHeader sh = new SessionHeader();
sh.setSessionId(result.getSessionId());
binding.setHeader(new SforceServiceLocator().getServiceName().getNamespaceURI(), "SessionHeader", sh);
Given that I've got an OAuth access token and endpoint, is there a way to adapt this to work correctly without a username/password?
After a lot of trial and error -- the answer appears to be the following
Use the OAuth Access token as the sessionID
The ENDPOINT_ADDRESS_PROPERTY is the Endpoint URL and a SOAP API URL, eg: https://na15.salesforce.com/services/Soap/u/21.0