I have one requirement that have to develop Spring boot Rest API for login using Facebook credentials and have to generate JWT token for this credentials and validate every request.
Thanks.
When the user logs in using facebook credentials, the user will be authenticated by the facebook and will issue the token that can be used for authorisation. My point is that once you authenticate the user on facebook, you get the credential. Instead of wrapping it into the JWT, store it somewhere with id and wrap that ID in the JWT. On each request, you will receive the JWT token and while authorising, you can get id from the JWT and get it from the persistence or store and authorise the user.
Let say you have the facebook tokens after login, then you have to store it somewhere using some id
String fbTokenId = storeFBToken(fbToken);. Using this token you can generate JWT token like:
Algorithm algorithm = Algorithm.HMAC256(key);
String token = JWT.create().withIssuer(author)
.withClaim("fbTokenId", fbTokenId)
.withSubject("user")
.sign(algorithm);
When you authorise the request, you will have the JWT token. Now you have to get verify the token and read the fbTokenId to verify it:
Algorithm algorithm = Algorithm.HMAC256(key);
JWTVerifier verifier = JWT.require(algorithm).withIssuer(author)
.build();
DecodedJWT jwt = verifier.verify(token);
String fbTokenId = jwt.getClaim("fbTokenId").as(String.class);
String fbToken = getFBTokenFromStore(fbTokenId);
Before you jump into this, make sure you understand stateless authentication and authorisation concepts especially with tokens.
Find a sample authentication application here
Related
I am using authentication of users in Java 8 against Keycloak, with the Keycloak adapter API for Java.
In this case, the class KeycloakBuilder (keycloak-admin-client-6.0.0.jar) builds a Keycloak instance to perform authentication operations.
how can I request an offline token rather than a normal Bearer token using this API?
Have not found parameter or way to request it. I need tokens with 1 month expiration time, which cannot get unless change the "SSO Session Max" field, but I donĀ“t want this to affect other Clients or users in the same Realm / client.
I am not sure if there are any specialties with the Keycloak Java adapter but I already implemented this with other clients. On the Authorization server side, you need to add a role offline_access to the users, which are allowed to request an offline session (this can be done explicitly or as a default role mapping). On the client side, you have to add another scope offline_access to the auth request. This can also be done by default (see default scopes). Please refer to the official Keycloak documentation about Offline Sessions for further details.
I post a possible solution using keycloak-authz-client library instead.
As stated by #Philipp , it is also necessary that the user you log in with has the role offline_access.
public String login(String username, String password) {
String authServerUrl = "http://localhost:18080/auth"; // Your keycloak auth entpoint
String realm = "realm"; // Realm
String clientId = "client"; // Client
Map<String, Object> clientCredentials = new LinkedHashMap<String, Object>();
clientCredentials.put("secret", "clientSecret"); // Client secret (Access Type: Confidential)
Configuration configuration = new Configuration(
authServerUrl,
realm,
clientId,
clientCredentials,
null
);
AuthzClient authzClient = AuthzClient.create(configuration);
AuthorizationRequest request = new AuthorizationRequest();
request.setScope("offline_access");
AuthorizationResponse response = authzClient.authorization(username, password).authorize(request);
return response.getRefreshToken(); // response.getToken() returns the bearer token
}
In Spring, to verify the user credentials, 'authorization' header is used. Usually, user name and password are encoded using some algorithm(commonly base 64) and passed in this header. But, validating user name and password comes under authentication, does n't it? It means, this 'authorization' header is not appropriate. It should have been called 'authentication' header.
Read https://howtodoinjava.com/spring-boot2/security-rest-basic-auth-example/
With OAuth the Authorization header is used to send a JSON Web Token (jwt).
This token is the result of a authentication process and holds user's data like username, the issuer of the token and roles like admin. It should never contain sensitive data like password. Furthermore the token is signed by the issuer with a rsa key.
Spring uses this token for authorization. The signature is verified, so no one can claim roles by creating a fake token.
The roles eg can than be uses grant/deny access to certain methods.
This article shows an example application that might help you understand the concept:
https://www.baeldung.com/rest-api-spring-oauth2-angular
I'm trying to use Keycloak Admin REST API SDK.
Then, I want to use refresh token to generate access token.
I know that the following code code can generate access token by refresh token.
val token = Keycloak.getInstance(serverUrl,
realmName,
userName,
password,
clientId,
clientSecret)
.tokenManager()
.refreshToken()
But this code can only work on TokenManager.
I want to use any refresh token like following code by admin user.
// I hope to call token endpoint -> /realms/{realm}/protocol/openid-connect/token?grant_type=refresh_token...
val accessToken = tokenManager.refresh(String refreshToken);
Could any SDK Class refresh token as I want?
I've been experimenting with Azure Active Directory access for Java using two sample projects:
1) https://github.com/AzureAD/azure-activedirectory-library-for-java which builds a stand-alone war using OAuth tokens for security, and
2) https://github.com/Microsoft/azure-spring-boot/tree/master/azure-spring-boot-samples/azure-active-directory-spring-boot-backend-sample for spring-boot embeded containers
I've come across quite a difference in the way the APIs can be used, that I can't understand.
In both cases, I get an OAuth token for AD by logging in with my Azure credentials.
In the Http response, I get an authorizationCode of the form:
AQABAAIAAAD.....
Then using the following URL as an authContext:
https://login.microsoftonline.com/{tenantId}
I get a AuthenticationResult by making the following call:
Future<AuthenticationResult> future = authContext.acquireTokenByAuthorizationCode(authorizationCode, redirectUri, credential, null);
in the Adal4j project (1), the AuthenticationResult's AccessToken is of the form:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6I...
Which I can use as a Bearer token in an HTTP call to retrieve the user's profile picture via https://graph.windows.net/myorganization/me/thumbnailPhoto?api-version=1.6
whereas in the SpringBoot AD example, the AccessToken returned from exactly the same call is of the form:
AQABAAAAAADXzZ3ifr-GRbDT....
and If I use that in exactly the same way to try to retrieve the user's profile pic, I get a 401 Unauthorized response
What's the reason for the difference in the form and use of these AccessTokens?
What's the reason for the difference in the form and use of these AccessTokens?
I assume that you got the access token is authorization_code not the bearer token.
As Rohit Saigal mentioned that you could use JWT.IO or JWT.MS to check that.
If we want to get the access token for Azure AD graph we could use the follow code to do that.
public String index(Model model, OAuth2AuthenticationToken authentication) {
...
DefaultOidcUser user = (DefaultOidcUser)authentication.getPrincipal();
String accessToken = user.getIdToken().getTokenValue();
...
}
Then we could use the access token to access the Azure AD graph api if you have assiged corrosponding permission.
I'm using Identity Brokering feature and external IDP. So, user logs in into external IDP UI, then KeyCloak broker client receives JWT token from external IDP and KeyCloak provides JWT with which we access the resources. I've set up Default Identitiy Provider feature, so external IDP login screen is displayed to the user on login. That means that users and their passwords are stored on external IDP.
The problem occurs when I need to log in using "Direct Access Grant" (Resource Owner Password grant) programatically in tests. As password is not stored on KeyCloak, I always get 401 Unauthorized error from KeyCloak on login. When I tried to change user password it started to work, so the problem is that user password is not provisioned on KeyCloak and using "Direct Access Grant" KeyCloak doesn't invoke external IDP on programatic login.
I use the following code to obtain access token, but get 401 error everytime I pass valid username/password.
org.keycloak.authorization.client.util.HttpResponseException: Unexpected response from server: 401 / Unauthorized
Direct access grant is enabled for that client.
public static String login(final Configuration configuration) {
final AuthzClient authzClient = AuthzClient.create(configuration);
final AccessTokenResponse accessTokenResponse = authzClient.obtainAccessToken(USERNAME, PASSWORD);
return accessTokenResponse.getToken();
}
Is there any way it can be fixed? For example to call identity broker on "Direct Access Grant", so that KeyCloak provides us it's valid token?
The problem was that KeyCloak has no information about passwords from initial identity provider. They have a token exchange feature which should be used for programmatic token exchange.
External Token to Interanal Token Exchange should be used to achieve it.
Here is an example code in Python which does it (just place correct values in placeholders):
def login():
idp_access_token = idp_login()
return keycloak_token_exchange(idp_access_token)
def idp_login():
login_data = {
"client_id": <IDP-CLIENT-ID>,
"client_secret": <IDP-CLIENT-SECRET>,
"grant_type": <IDP-PASSWORD-GRANT-TYPE>,
"username": <USERNAME>,
"password": <PASSWORD>,
"scope": "openid",
"realm": "Username-Password-Authentication"
}
login_headers = {
"Content-Type": "application/json"
}
token_response = requests.post(<IDP-URL>, headers=login_headers, data=json.dumps(login_data))
return parse_response(token_response)['access_token']
def keycloak_token_exchange(idp_access_token):
token_exchange_url = <KEYCLOAK-SERVER-URL> + '/realms/master/protocol/openid-connect/token'
data = {
'grant_type': 'urn:ietf:params:oauth:grant-type:token-exchange',
'subject_token': idp_access_token,
'subject_issuer': <IDP-PROVIDER-ALIAS>,
'subject_token_type': 'urn:ietf:params:oauth:token-type:access_token',
'audience': <KEYCLOAK-CLIENT-ID>
}
response = requests.post(token_exchange_url, data=data,
auth=(<KEYCLOAK-CLIENT-ID>, <KEYCLOAK-CLIENT-SECRET>))
logger.info(response)
return parse_response(response)['access_token']
This example was very useful for me, i only want to add more info about the KEYCLOAK-CLIENT used for the token-exchange (for me authorization_client). I have KEYCLOAK as a broker for IDP ADFS.
First you need to enable the token-exchange feature adding 2 parameters in your keycloak startup command line (depending of how you do it)
-Dkeycloak.profile=preview
-Dkeycloak.profile.feature.token_exchange=enabled
The IDP (for me ADFS) tab Permissions with the token-exchange permission will be available.
Add a policy to the "token-exchange" provider permission, to the client KEYCLOAK-CLIENT
Add this previous policy to the "token-exchange" client permission
With POSTMAN you can test the authentication flow:
External IDP ADFS Login Username/password
Token Exchange