How can i decode JWT using Keycloak - spring-boot

I am using Keycloak with Spring-Boot in my application. My browser client request keycloak to generate JWT and then sends this JWT to my ZUUL server which validates JWT using keycloak-spring adapter and then i have written a pre-filter to decodes JWT payload and extract username.
I am using com.auth0.java-jwt library to decode JWT like in below snippet
DecodedJWT dJWT=JWT.decode(header);
String username=dJWT.getClaim("preferred_username").asString();
I was wondering if there is anyway i can do this without using external library. I want to use keycloak library to decode JWT explicitly. How can i achieve this?

You have to include keycloak's core library into your dependencies.
Gradle: compileOnly 'org.keycloak:keycloak-core:3.4.2.Final'
Then use the org.keycloak.TokenVerifier to parse the token.
Example:
try
{
// deprecated: AccessToken token = RSATokenVerifier.create(tokenString).getToken();
AccessToken token = TokenVerifier.create(tokenString, AccessToken.class).getToken();
System.out.printf("iss = %s%n", token.getIssuer());
System.out.printf("sub = %s%n", token.getSubject());
System.out.printf("typ = %s%n", token.getType());
}
catch (VerificationException e)
{
// some error handling
}
You can also activate various verifications on the RSATokenVerifier and in particular the signature validation by setting the public key:
RSATokenVerifier.create(tokenString).checkActive(true).publicKey(key).verify().getToken()

As i am using keycloak to authenticate jwt, it decodes jwt and puts details into SecurityContextHolder and I just pulled the details from there it self. here is the code.
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
if (authentication.getPrincipal() instanceof KeycloakPrincipal) {
KeycloakPrincipal<KeycloakSecurityContext> kp = (KeycloakPrincipal<KeycloakSecurityContext>) authentication.getPrincipal();
// retrieving username here
String username = kp.getKeycloakSecurityContext().getToken().getPreferredUsername();
}
}
this solved it for me.

Related

How to change an additional information of jwt access token

I'm working with Spring Security and use JWT as access token , When a client sends the access token to server I must change an additional information (metadata) of this token and return a new one.
How can I achieve that ?
i try with this code but not working
String authorization = Context.getHeader("Authorization");
if (authorization != null) {
String tokenValue = authorization.replace("Bearer", "").trim();
OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
accessToken.getAdditionalInformation().put("activeProfileId", defaultProfileId);
return accessToken.getValue();
}
return null;
You should get your metadata ("claims") from the token, then add them to a new JWT builder that will return a new token. The new JWT must be entered in HttpResponse to forward it to the client. Instead, the client will have to implement an interceptor to retrieve it in a comfortable and transparent way.
You've to get all Additional Information as HashMap and place them in OAuth2Authentication. stackoverflow.com/a/19057480/11951081
In ajax should be:
https://api.jquery.com/category/ajax/global-ajax-event-handlers/
$.ajaxSetup({
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', <Jwt>)
},
success:function(event,jqXHR,ajaxOptions,data ){
console.log(ajaxOptions.getResponseHeader('Authorization'))
}
})

How to get email address from authorization code OAuth2

When User Sign In Gmail account via Oauth2 protocol and finish it, my server get authorization code and I make exchange this code for refresh token and access token, everything works as planned but I need to get email address too. I mean if user logged in as helloworld#gmail.com, somehow with authorization code I would like to know this address, may I somehow to know it?
This is endpoint where I exchange authorization code on access token and refresh token:
public OAuth2AccessToken oauth(String authorizationCode) {
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
resource.setUserAuthorizationUri(userAuthorizationUri);
resource.setAccessTokenUri(accessTokenUri);
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
resource.setPreEstablishedRedirectUri(redirectUrl);
resource.setScope(scopes);
resource.setUseCurrentUri(false);
AccessTokenRequest request = new DefaultAccessTokenRequest();
request.setPreservedState(new Object());
request.setAuthorizationCode(authorizationCode);
AuthorizationCodeAccessTokenProvider provider = new AuthorizationCodeAccessTokenProvider();
OAuth2AccessToken accessToken = provider.obtainAccessToken(resource, request);
return accessToken;
}
I don't have WebSecurityConfigurerAdapter for OAuth2
If the user's email address is not already provided in the id_token part of the oauth2 response, you can use the Gmail API Users.getProfile operation, using the special value "me" as the userId to refer to the authenticated user.
See: https://developers.google.com/gmail/api/v1/reference/users/getProfile
That should give you a response like:
{
"emailAddress": -string-,
"messagesTotal": -integer-,
"threadsTotal": -integer-,
"historyId": -unsigned long-
}

Asp.net Core Client with Identityserver 3 missing claims

Given:
A JavaScript app that authenticates with oidc over identityerver v3
A Asp.net Core Webapi that authenticates with the given bearer token to identiyserver
The Javascript clients makes calls with the access token itself to the api.
Problem:
The Authentication suceeds but the restored principal is missing some custom claim like "username" and "familyName". I can see that the oidc client in the javascript client has these informations
some claims like "idp" is set in both Javascript and Api Client. But bot are not handled explicitly.
The main difference is that idp is part of the access_token which the username is not.
the configuration of the api is :
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
var authority = config["identity:authority:url"];
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
LegacyAudienceValidation = true,
Authority = authority,
RequireHttpsMetadata = false,
EnableCaching = false,
ApiName = "MyApp.Read",
});
Any hint what i'm missing ? ( I assume it is some kind of profile read in the api?)
Workaround
I extend the configuration with JwtBearerEvents and make a manual read with userclient when the token was authenticated like this
JwtBearerEvents = new JwtBearerEvents
{
OnTokenValidated = async context =>
{
string header = context.Request.Headers["Authorization"];
string accessToken = header.Substring(6);
var result = await userInfoClient.GetAsync(accessToken);
but is this the intended way? Are extended / profile claims meant to be returned only by manually querying them?

Enable authenticator manually

Currently my client authenticates request only on case of 401 response:
this.client.authenticator(new okhttp3.Authenticator() {
public Request authenticate(Route route, Response response) throws IOException {
String credentials = authenticator.getCredentials();
if (credentials.equals(response.request().header("Authorization"))) {
throw new TraversonException(401, "Unauthorized", response.request().url().toString());
} else {
defaultHeader("Authorization", credentials);
Request.Builder newRequest = response.request().newBuilder()
.headers(Headers.of(defaultHeaders));
return newRequest.build();
}
});
But I'd like to change this behavior and be able to call it either manually or auto per first call? Is it possible somehow?
If the authentication is predictably required and not related to a proxy, then the solution is to implement an Interceptor instead of Authenticator.
OkHttpClient.Builder clientBuilder = ...;
clientBuilder.networkInterceptors().add(0, myInterceptor);
client = clientBuilder.build();
Example Interceptor https://github.com/yschimke/oksocial/blob/48e0ca53b85e608443eab614829cb0361c79aa47/src/main/java/com/baulsupp/oksocial/uber/UberAuthInterceptor.java
n.b. There is discussion around possible support for this usecase in https://github.com/square/okhttp/pull/2458. One issue with current Authenticator API is that it assumes a Response from the failed (401) request.

google ExchangeCodeForTokenAsync invalid_grant in webapi

i have implemented GoogleAuthorizationCodeFlow scenario from google api client dotnet and tutorial to get token from what my client sent to server as a code. but when i call flow.ExchangeCodeForTokenAsync , I get the following error :
{"Error:\"invalid_grant\", Description:\"\", Uri:\"\""}
I read google authorization invalid_grant and gusclass oauth 2 using google dotnet api client libraries but they didn't help me and. I think it must be very simple but I don't know why it doesn't work.
For client side , I have used Satellizer and this is my server Codes:
public bool PostExchangeAccessToken(GoogleClientAccessCode code)
{
string[] SCOPES = { "email" };
IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets()
{
ClientSecret = "******",
ClientId = "********.apps.googleusercontent.com"
},
Scopes = SCOPES
});
try
{
TokenResponse token;
token = flow.ExchangeCodeForTokenAsync("*****#gmail.com", Newtonsoft.Json.JsonConvert.SerializeObject(code), "https://localhost:44301/",
CancellationToken.None).Result;
}
catch (Exception ex)
{
throw ex;
}
return true;
}
what is the problem?
On Github I found that I must use the Token from the client and use
GoogleAuthorizationCodeFlow.Initializer()
to create my UserCredential object.
You can check your google developer console settings.(Authorized redirect URIs)
Credentials => OAuth 2.0 client IDs => Your Application Settings => Authorized redirect URIs
You must add url. ("https://localhost:44301/")
My code :
flow.ExchangeCodeForTokenAsync("me", authCode, redirectUri, CancellationToken.None).Result;
Authorized redirect URIs
For use with requests from a web server. This is the path in your application that users are redirected to after they have authenticated with Google. The path will be appended with the authorization code for access. Must have a protocol. Cannot contain URL fragments or relative paths. Cannot be a public IP address.

Resources