Problem:
I have a Keycloak realm with a mixed user base.
Some users are native (native users), some are provided trough a custom User Storage SPI implementation (federated users).
When I perform an OIDC login, Keycloak asks to approve 4 scopes (test-client, profile, email, roles).
The request is performed with scope openid. Keycloak transforms it to these four scopes.
test-client is the client name. There is no actual client scope with this name showing in the admin UI.
Using a native user everything works fine. I approve and the login completes successfully.
Having a look at the user consents confirms that everything is ok.
Using a federated user the login fails with the following message.
[invalid_scope] Client no longer has requested consent from user
Looking at the users consent we see that the test-client scope is not being added.
Trying to exchange the code for a token, produces a fitting error in the Keycloak log
11:06:31,964 WARN [org.keycloak.events] (default task-10) type=CODE_TO_TOKEN_ERROR, realmId=torment, clientId=test-client, userId=f:ff4c66e5-2a6f-465c-8418-200648a49973:dfb_user, ipAddress=127.0.0.1, error=not_allowed, grant_type=authorization_code, code_id=418dfa66-b4c8-4481-b46d-ceac97e65b39, client_auth_method=client-secret
All further login tries will ask to approve only the missing test-client scope, but it will never be added to the consent.
Question:
Why does this happen?
How do I make it work?
It seems like you are not passing scopes list as query parameters in the URL constructed to initiate Keycloak authentication. Please check your authentication URL.
Set<ClientScopeModel> clientScopes = TokenManager.getRequestedClientScopes(scopeParam, client);
if (!TokenManager.verifyConsentStillAvailable(session, user, client, clientScopes)) {
event.error(Errors.NOT_ALLOWED);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_SCOPE, "Client no longer has requested consent from user", Response.Status.BAD_REQUEST);
}
Your error is coming from here.
If you take a look at TokenManager.getRequestedClientScopes (line number 523), it checks whether user still has granted consents to all requested client scopes.
So, I think you can try to add all the scopes in request URL. For example, http://keycloak_url/auth/realms/.......?....scope=openid%20test_client%20profile.....
I hope it helps.
Additionally, you can enable this setting on Keycloak Client (Scopes tab).
Related
I am new using Azure Graph Rest API Java using this repo.
My aim is to list all of the users in the AAD tenant
So far I was only able to get to this:
List<String> scopes= Arrays.asList("https://graph.microsoft.com/User.Read.All");
AzureProfile profile = new AzureProfile(tenantId, subscriptionId, AzureEnvironment.AZURE);
final ClientSecretCredential credential = new ClientSecretCredentialBuilder()
.clientId(clientId)
.clientSecret(clientSecret)
.tenantId(tenantId)
//.httpClient(client)
.authorityHost(profile.getEnvironment().getActiveDirectoryEndpoint())
.build();
TokenCredentialAuthProvider tokenCredentialAuthProvider = new TokenCredentialAuthProvider(scopes, credential);
GraphServiceClient<Request> graphClient =
GraphServiceClient
.builder()
.authenticationProvider(tokenCredentialAuthProvider)
.buildClient();
UserCollectionPage users = graphClient.users()
.buildRequest()
.get();
for(User user: users.getCurrentPage()){
System.out.println(user.displayName);
System.out.println(user.id);
System.out.println(user.userPrincipalName);
}
However, I run into this error instead:
Caused by: java.io.IOException:
java.util.concurrent.ExecutionException:
com.microsoft.aad.msal4j.MsalServiceException:
AADSTS1002012: The
provided value for scope https://graph.microsoft.com/User.Read.All
openid profile offline_access is not valid. Client credential flows
must have a scope value with /.default suffixed to the resource
identifier (application ID URI).
It seems the Scope that I have used is wrong/insufficient, but I am not too sure what should I use the scope with. Any idea?
It is written in the documentation that:
Client credentials requests in your client service must include
scope={resource}/.default. Here, {resource} is the web API that your
app intends to call, and wishes to obtain an access token for. Issuing
a client credentials request by using individual application
permissions (roles) is not supported. All the app roles (application
permissions) that have been granted for that web API are included in
the returned access token.
The Client Credential flow is best suited for situations where you have a Deamon App that will have to authenticate and get access to some kind of a resource through a Non-Interactive way, which in sequence means that the permissions for this Deamon App have been configured and consented from a step done prior to the auth request.
The /.default scope can be translated as the request of the Background App that runs unattended, to get the bulk of the permissions that it has been configured with and access the resource that it asks.
In plain english, the use of the above scope in the Client Credentials flow is a convention that has to be implemented always when this flow is chosen :P.
I tried to reproduce the same in my environment via Postman and got below results:
I registered one Azure AD application and added API permissions like below:
When I tried to generate access token with same scope as you via Postman using client credentials flow, I got same error as below:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
grant_type:client_credentials
client_id: <appID>
client_secret: <secret_value>
scope: https://graph.microsoft.com/User.Read.All openid profile offline_access
Response:
To resolve the above error, you must change your scope to https://graph.microsoft.com/.default if you are using client credentials flow.
After changing the scope, I'm able to generate access token successfully like below:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
grant_type:client_credentials
client_id: <appID>
client_secret: <secret_value>
scope: https://graph.microsoft.com/.default
Response:
When I used the above token to call below Graph query, I got the list of users with display name, id and user principal name successfully like below:
GET https://graph.microsoft.com/v1.0/users?$select=displayName,id,userPrincipalName
Response:
In your case, change scope value in your code like below:
List<String> scopes= Arrays.asList("https://graph.microsoft.com/.default");
I'm using Xero-Java and I'm trying to push invoices to Xero. The scopes I requested in the Ouath2 authorization were:
openid
email
profile
offline_access
accounting.settings
accounting.transactions
accounting.contacts
accounting.journals.read
accounting.reports.read
accounting.attachments
Invoking the AccountingApi.updateInvoice() method throws the error:
com.xero.api.XeroApiException: Unauthorized - check your scopes and confirm access to this resource
I was under the impression that the scope accounting.transactions would allow pushing Invoices to Xero. Where is my problem and how do I fix it?
Thanks.
Looking at some logs, it seems that your access token has expired. Access tokens only live for 30 minutes.
As you've used offline_access, you can acquire a new access token for the same user by using the refresh token provided during user authorisation with a refresh request as outlined in the Readme of the SDKs github repo.
You'll probably want to check token expiration prior to each call to the Xero API.
I am trying out get the access token from the super user so that I can the same to create new users in key cloak, I have deployed keycloak in wildfly and when I try to do the get call, I am getting Invalid user credentials as response,
How to know the actual credentials?
And when I try to update the password from the console, I getting the error message like below.
Since I am new to this and din't find enough information from internet also, any kind of help will be appreciated .
Updated:
Now i am getting new error description as Parameter client_assertion_type is missing like below. What should be client_assertion_type here ?
This keycloak help page describes the most likely reason for the second error:
Q: When logging in, I get an error: *Parameter client_assertion_type is missing [invalid_client].
A: This error means your client is configured with Signed JWT token credentials, which means you have to use the --keystore parameter when logging in.
Alternatively you can disable using JWT tokens for the client in Keycloak.
For your information, the client_assertion_type would probably be urn:ietf:params:oauth:client-assertion-type:jwt-bearer. But then you'd get another error because the client_assertion is missing.
If ccp-portal is a confidential client using client authentication with signed JWT then the Keycloak doc states that
During authentication, the client generates a JWT token and signs it
with its private key and sends it to Keycloak in the particular
backchannel request (for example, code-to-token request) in the
client_assertion parameter.
I guess it's not possible to generate a JWT with PostMan.
This is meant for backchannel client-keycloak communication, not for
user authentication.
Solutions
You can use the admin-cli as client_id instead of your ccp-portal client. The admin-cli should be in the list of clients configured for your ccp realm. You can see that from the Keycloak interface.
Another option is allow direct access grants in ccp-portal client config.
Finally you could use ccp-portal client in your application configured with one of the Keycloak client adapters, instead of POSTMan.
As subrob sugrobych mentionned, parameters should be passed as form-data.
first of all, when you are posting data to keycloak over a rest client, you need to input parameters as form paramaters, and not as query parameters. This is why you are getting this strange error of not providing parameter grant_type, when you obviously are providing it. Same is valid for accessing keycloak api via code.
Next thing you need to think about are roles for your superuser. You can assign realm roles and client roles. There is a client named 'realm-management' which contains roles which would normally count as "system roles". You will need to use them. When you are getting HTTP code 403, it means, that probably your user is missing a role from this client.
I have a web application that I deploy using JBoss 5.2. In order for a user to use the application, he/she must authenticate with an LDAP server (using simple authentication) with a username and password. This is all done through setting up the login-config.xml for JBoss and providing a <login-module> with our implementation.
The problem comes in here: After having logged in, I have a scenario that requires the user to provide a username & password when a particular action is performed (which I will also authenticate with the LDAP server). I want to be able to reuse the same mechanism that I use for authenticating the user into the web application.
My form to log in to the application posts to j_security_check so in accordance with this, I was trying to send a request to j_security_check but JBOSS returns a 404. From reading around a bit, I've gathered j_security_check cannot be accessed by any arbitrary request and must be in response to a challenged request to a secured resource.
So then, how can I authenticate the second set of credentials the user has provided with the same LDAP server?
EDIT:
To clarify, the question is how to send the user's credential inputs to the LDAP server for authentication. Grabbing the input from the user, etc. is all done. All that is left is to take this input and send it to the LDAP server and get the response (which is where I am stuck).
If it helps to mention, the login to the web application uses a custom class that extends UsernamePasswordLoginModule.
So, after lots of research, I ended up finding a solution for JBoss environments (which is what I'm using).
Once you capture the user's credentials, you send them to your server via a POST/GET and your server can perform the following to use whatever authentication policy you have configured (in login-config.xml) to verify the credentials:
WebAuthentication webAuthentication = new WebAuthentication();
boolean success = webAuthentication.login(username, password);
To expand on this, I was also able to check the user's role/group via the HttpServletRequest (which is passed into my server-side handler):
boolean userIsInRole = servletRequest.isUserInRole("nameOfGroup")
The spring security documentation explains it
Wanted to add another answer for JBoss 6.2+, where WebAuthentication no longer exists.
I've used the creation of a LoginContext to achieve the same result:
String SECURITY_DOMAIN_NAME = "ssd"; // the security domain's name from standalone.xml
String username = "user";
String password = "password";
LoginContext lc = null;
try {
lc = new LoginContext(SECURITY_DOMAIN_NAME, new UsernamePasswordHandler(username, password.toCharArray()));
lc.login();
// successful login
} catch (LoginException loginException) {
// failed login
}
And the use uf lc.getSubject().getPrincipals() to verify roles.
I am using an URL like the following:
https://www.google.com/accounts/OAuthAuthorizeToken?
oauth_token=ab3cd9j4ks73hf7g&oauth_callback=http%3A%2F%2Fwww.example.com
This gets redirected to:
http://www.youtube.com/oauth_authorize_token?oauth_callback=http%3A%2F%2Fwww.google.com&oauth_token=1%2FyT-RZ-5PAMCp43Wt0RAGpNUAbMKAHxq1MG_RiX3Cmtk
After the user clicks allow access, YouTube directs the page to:
http://www.youtube.com/t/oauth_token_authorized
Why is the callback not working?
Their API seems to agree with what I am doing.
In sum, you're specifying the oauth_callback too late in the process. Instead, provide it at the very beginning. For Google's provider, that's when you're calling OAuthGetRequestToken.
This Google documentation seems to get it right. Strange. I also found other documentation that explains the same thing: "The [Service Provider] MUST associate the callback URL sent in Step 1 with the request token it issues." So, you have to provide the callback URL when getting the "unauthorized" request token ("unauthorized" because the user hasn't given their permission yet). After you've done this, you can forward the user to the Authorization URL, where the user (aka resource owner) grants access explicitly for your application.
Here's a page that gives related information about OAuth 2.0.