Programatic username/password access with KeyCloak using external IDP brokering - java

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

Related

Can anyone tell me the difference between basic authentication and JWT token?

So in our project I am responsible for the UI part which is in Angular 8 and the backend is developed using JAVA. Both will be deployed in the same windows server.
Currently we are using basic authentication for authenticating the user and accessing different REST URL's. I am new to this so I have not much idea about the JWT tokens.
So can anyone explain what is the difference between using basic authentication and JWT tokens and do we require JWT token when both backend and frontend are deployed in the same server?
sample code for user verification in current project using basic auth...
LoginAuthorisation(logindata: LoginData) {
let authrizationdata = logindata.inputEmail+":"+logindata.inputPassword;
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
Authorization: 'Basic ' + btoa(authrizationdata),
}),
observe: "response" as const,
withCredentials: true
};
const body = { "message": "Test Data"}
console.log(httpOptions);
return this.http.post<any>('http://*****************/home',body,httpOptions);
}
This is a really broad question but here goes.
What is basic authentication?
Usually what we mean with BASIC Authentication is HTTP's basic auth which is: In the context of an HTTP transaction, basic access authentication is a method for an HTTP user agent (e.g. a web browser) to provide a user name and password when making a request. In basic HTTP authentication, a request contains a header field in the form of Authorization: Basic , where credentials is the Base64 encoding of ID and password joined by a single colon (Wikipedia)
Bottom line: a client sends their credentials over to a server for validation.
What is JWT-based Auth?
That, however, is not a great idea. You generally don't want to send your credentials. Rather you want to send a token that represents your rights / entitlements to talk to the aerver. This is where JWT comes in. JWT stands for JSON Web Token. Your identity server generates a token that certifies the user identity, and sends it to the client. The client will send the token back to the server for every subsequent request, so the server knows the request comes from a particular identity.
Now it helps to understand JWT kind of comes from OAuth 2.0 and OpenID Connect which are authorization / authentication protocols built to avoid the "password anti-pattern" i.e. having to share your creds.
Have a look at sites like oauth.tools or jwt.io for more info.
JWT-based auth is becoming the de facto standard for API authentication rather than username-password.
Resources
https://jwt.io
https://oauth.tools
https://medium.com/#maison.moa/using-jwt-json-web-tokens-to-authorize-users-and-protect-api-routes-3e04a1453c3e

keycloak offline token with Java

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
}

How to get Microsoft graph access token from java spring

I tried to get graph api token from postman UI and was able to get planner data.
How to achieve same in java spring
I am not able to get access token for Microsoft graph api using java spring. I am able to get access token using postman.
I need to access planner API from one of the web application. As per Microsoft documentation I configured a app in azure active directory and got client key, secret key etc.
I also configured required permission to get groups and users.
Very first time I used below from POSTMAN
https://login.microsoftonline.com//oauth2/token with below data
client_id : <client_id from configured app>
client_secret : <client secret from configured app>
grant_type : client_credentials
resource : https://graph.microsoft.com
I got token, and I was able to get groups from https://graph.microsoft.com/v1.0/groups/
But same token was not valid for getting plans of group.
With lot of digging, I came to know that token accessed with client_credentials is not applicable to get data from planner API. So, next I used below details to get access token from UI of postman.
Grant Type : authorization_code
Callback URL : https://www.getpostman.com/oauth2/callback
Auth URL : https://login.microsoftonline.com/<tenant_id>/oauth2/authorize?resource=https://graph.microsoft.com
Access Token URL : https://login.microsoftonline.com/<tenant_id>/oauth2/v2.0/token
client_id : <client_id from configured app>
client_secret : <client secret from configured app>
I got the Microsoft login screen, and after successful login, I got token.
I could call planner API using this access token.
Now my question is how can I get this same token using java spring.
Also, my web app will be having background service running in scheduler calling graph API daily.
I do not want manual intervention here, but as told earlier, graph API will ask to login.
How to achieve above requirement.
private String getAuth() {
ConfidentialClientApplication app = null;
IClientCredential credential = ClientCredentialFactory.create(Appsecret);
try {
app = ConfidentialClientApplication.builder(MicrsoftAppId, credential).authority("https://login.microsoftonline.com/"+tenantId+"/").build();
}catch(MalformedURLException e) {
System.out.println(e.getMessage());
}
ClientCredentialParameters parameters = ClientCredentialParameters.builder(Collections.singleton("https://graph.microsoft.com/.default")).build();
CompletableFuture<IAuthenticationResult> future = app.acquireToken(parameters);
try {
IAuthenticationResult result = future.get();
return result.accessToken();
}catch(ExecutionException e){
System.out.println(e.getMessage());
}catch(InterruptedException e) {
System.out.println(e.getMessage());
}
return null;
}
Here you go! This code is made for application permission (so not delegated). It only requires your client Id and secret to operate. You will need the microsoft graph jar for it to work (and the many jars supporting it).

How to generate JWT Token using Facebook credentials in Spring boot

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

AccessTokens returned for Active Directory are different between sample projects

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.

Categories

Resources