This question already has answers here:
Iss claim not valid Keycloak
(2 answers)
Closed last month.
I'm using a Spring Boot application as a resource server, and it needs to validate the JWTs. But I'm always getting the following error on a request:
Bearer error="invalid_token", error_description="An error occurred while attempting to decode the Jwt: The iss claim is not valid", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1"
The issurer URI is set correct in the application.properties by the following line:
spring.security.oauth2.resourceserver.jwt.issuer-uri=<IssuerUri>
But I found out, that my JWT doesn't contain a claim which contains the issuer URI. Is there any way to override the validation for the issuer URI in Spring Boot?
I already tried to find a config in the WebSecurityConfigurerAdapter configurations to toggle the validation off. But I haven't found anything.
I think I found the configuration I was looking for. You can override the issuer uri from the JWT in the WebSecurityConfigurerAdapter
#Override
protected void configure(HttpSecurity http) throws Exception {
http.oauth2ResourceServer().jwt().jwkSetUri("<IssuerUri>");
}
If you are using Keycloak as Auth Server, this error can sometimes happen on local dev setup, when the Auth Server and Resource Server are running in a docker container, and the token was requested using the access token URL which points to localhost instead of the Auth Server's container name.
If you provide the host name of the access token url as localhost, Keycloak will add the iss claims to the token with the host as localhost, now when the resource server running in a container tries to validate the token against the Auth Server, it tries to contact it's own localhost instead of calling the AuthServer container.
The solution in this case, is to provide the container name as host name while requesting access token.
I explained this in detail in this video - youtu.be/5_EXMJbhLY4?t=2519
Related
I have a problem on a Spring Boot microservice about the token decryption part in spring security. Basically the following problem occurs:
when calling the getSubject() method on #AuthenticationPrincipal Jwt jwt the following error occurs
Caused by: java.lang.IllegalStateException: The Issuer "https://ids-for-spid.aqp.it:443/oauth2/token" provided in the configuration did not match the requested issuer "https://clidens1.aqp.it:9443/oauth2/token"
I guess this is due to the following condition: in the well-known file there is an issuer with a different url base than the one passed to the library (I guess because there is a proxy or gateway), how could I solve the following problem?
Specifically, the url to access the well-known file is the following https://clidens1.aqp.it:9443/oauth2/token/.well-known/openid-configuration
ed and inside there is the following issuer https://ids-for-spid.aqp.it/oauth2/token, which differs from the base url clidens1.aqp.it on which I have the only accessibility.
During JWT validation, token iss claim is checked against the issuer URI in your conf. It must match exactly (even trailing slash is important if any).
For token validation, the Authorization server public key is also required. It is fetched using JWK-set URI, generally found in OIDC configuration, itself generally available from a location deduced from th iss claim value.
If the host declared in iss claim is not reachable from resource-server, have you tried to set JWK-set URI property too? Something like:
# this must be the exact iss claim value
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://clidens1.aqp.it:9443/oauth2/token
# actual value to use is the "jwks_uri" in https://clidens1.aqp.it:9443/oauth2/token/.well-known/openid-configuration
# with hostname and port reachable from resource-server (spring-app this properties file belongs to)
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://ids-for-spid.aqp.it:9443/oauth2/token/protocol/openid-connect/certs
If this hostname in the iss claim is not reachable by anyone, have you tried to configure your authorization-server to change the value of iss claim?
In Keycloak setting hostname configuration property would solve your problem, but you're obviously using another authorisation-server.
I use the keycloak service to login my web app. Use as a backend spring with oauth 2.0 security. When I go to make a request with postman using the bearer token obtained from keycloak it gives me an error 401 and also in the text of the answer next to the www-Authenticate entry it tells me:
Bearer error="invalid_token", error_description="An error occurred while attempting to decode the Jwt: The iss claim is not valid", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1".
How could I solve this problem?
Spring config value must be exactly the same as iss claim value. Even trailing slash, if any, is important.
Do as #BenchVue wrote in comment: open one of access-token JWTs in jwt.io, copy iss claim value and paste it in spring conf.
the OAuth properties in application.properties must be the same as the Keycloak address:
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://keycloak-IP:Port/auth/realms/XXX
I have an application with multiple services. One of them is the auth service that takes care of creating the jwt after checking that the client id (i.e. browser or app) is valid and provided username/password is also valid.
I have another service (gateway) that redirect to other services any requests from the users using the jwt token for authentication.
All this works and has been working for a few years. It is based on Spring Boot using Spring Security (starter v2.1.5). It is soon time to renew our certificate and at the same time move to our new domain. The new certificate has been applied to the Google Cloud Platform load balancer.
The call for health check using the Spring Actuator is responding with the proper "UP" (in json) response. When I try to make the call from same Postman request (with new domain name used) I get the following response with a 401 code:
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
I do have the proper clientid and password/secrete encoded using base64 (hey it worked before and nothing else was changed).
I tried to debug setting #EnableWebSecurity(debug = true), but it generates zero logs from the request.
If I switch back the certificate to old one (and old domain), it works again without any other changes. This has been driving me a bit on the crazy side to say the least. Any help, suggestion, ideas would be appreciated.
It ended up being an error on my part. When I added a A class resources during the copy paste of Load Balancer IP, I put the same IP for both of my services (i.e. one of the copy didn't stick). Ending up in calling security on the gateway service when asking for the auth token.
Thanks for the comment #Boris-Treukhov
First of all I'm using
keycloak-authz-client-3.3.0.Final
spring boot 1.5.8.RELEASE
spring-boot-starter-security
I've been playing with Keycloak spring adapter exploring the examples since we want to adopt it to our project.
I was able to make it run for Roles easily using this tutorial:
https://dzone.com/articles/easily-secure-your-spring-boot-applications-with-k
After that I moved to permissions and that's when it gets trickier (that's also our main goal).
I want to achieve something like described in here (9.1.2):
http://www.keycloak.org/docs/2.4/authorization_services_guide/topics/enforcer/authorization-context.html#
To get permissions you need to setup in Keycloak Authorization, credentials, and then create Resources or Scopes and Policies to be able to create permissions (it took me a while but I got it working). Testing in the Evaluater everything seems fine.
Next step was to get user permissions on the Spring side. In order to do that I had to enable:
keycloak.policy-enforcer-config.enforcement-mode=permissive
The moment I enable this I get everytime this exception
java.lang.RuntimeException: Could not find resource.
at org.keycloak.authorization.client.resource.ProtectedResource.findAll(ProtectedResource.java:88)
at org.keycloak.adapters.authorization.PolicyEnforcer.configureAllPathsForResourceServer...
...
Caused by: org.keycloak.authorization.client.util.HttpResponseException:
Unexpected response from server: 403 / Forbidden
No matter what address I hit in the server.
So I started to investigate what was the root of the problem. Looking at some examples how to manually get the permissions I actually got them in postman with the following request:
http://localhost:8080/auth/realms/${myKeycloakRealm}/authz/entitlement/${MyKeycloakClient}
including the header Authorization : bearer ${accessToken}
response was {"rpt": ${jwt token}} that actually contains the permissions
So knowing this was working it must be something wrong with the Spring adapter. Investigating a bit further on the Keycloak exception I found that that error was occurring the moment the adapter was getting all the resources. For that it was using the following url:
http://localhost:28080/auth/realms/license/authz/protection/resource_set
with a different token in the headers (that I copied when debugging)
So when I tried it in postman I also got a 403 error, but with a json body:
{
"error": "invalid_scope",
"error_description": "Requires uma_protection scope."
}
I've enabled and disabled all uma configuration within keycloak and I can't make it work. Can please someone point me into the right direction?
Update
I've now updated Keycloak adapter to 3.4.0.final and I'm getting the following error in the UI:
Mon Nov 20 10:09:21 GMT 2017
There was an unexpected error (type=Internal Server Error, status=500).
Could not find resource. Server message: {"error":"invalid_scope","error_description":"Requires uma_protection scope."}
(Pretty much the same I was getting in the postman request)
I've also printed all the user roles to make sure the uma_protection role is there, and it is.
Another thing I did was to disable spring security role prefix to make sure it wasn't a mismatch on the role.
Update 2
Was able to resolve the 403 issue (you can see it in the response below).
Still getting problems obtaining KeycloakSecurityContext from the HttpServletRequest
Update 3
Was able to get KeycloakSecurityContext like this:
Principal principal = servletRequest.getUserPrincipal();
KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) principal;
OidcKeycloakAccount auth = token.getAccount();
KeycloakSecurityContext keycloakSecurityContext = auth.getKeycloakSecurityContext();
AuthorizationContext authzContext = keycloakSecurityContext.getAuthorizationContext();
The problem now is that the AuthorizationContext is always null.
I've managed to get it working by adding uma_protection role to the Service Account Roles tab in Keycloak client configuration
More information about it here:
http://www.keycloak.org/docs/2.0/authorization_services_guide/topics/service/protection/whatis-obtain-pat.html
Second part of the solution:
It's mandatory to have the security constrains in place even if they don't mean much to you. Example:
keycloak.securityConstraints[0].authRoles[0] = ROLE1
keycloak.securityConstraints[0].securityCollections[0].name = protected
keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /*
Useful demos:
https://github.com/keycloak/keycloak-quickstarts
This code works for me:
HttpServletRequest request = ...; // obtain javax.servlet.http.HttpServletRequest
Principal userPrincipal = request.getUserPrincipal();
KeycloakPrincipal < KeycloakSecurityContext > keycloakPrincipal =
(KeycloakPrincipal < KeycloakSecurityContext > ) userPrincipal;
KeycloakSecurityContext securityContext =
keycloakPrincipal.getKeycloakSecurityContext();
If you faced "java.lang.RuntimeException: Could not find resource" error and you are using Keycloak docker container, The error can be because of this. I dont have enough information about docker. Hence I download keycloak as a zip file and run it. It works properly.
Have just installed spring security oauth2 in my eclipse IDE. The service am trying to implement will be consumed by second party users through their installed applications hence i chose to use password grant type. As per my understanding of Oauth2 the following request should work for the demo sparklr2 service without the need of me encording the username and password parameters. i.e
POST http://localhost:8080/sparklr2/oauth/token?grant_type=password&client_id=my-trusted-client&scope=trust&username=marissa&password=koala
but i keep getting
<oauth>
<error_description>
Full authentication is required to access this resource
</error_description>
<error>unauthorized</error>
</oauth>
am i missing something in this request or do i need to enable something in the repo
It seems like Spring OAuth2 doesn't support the password grant type for a secret-less OAuth2 client. This might be as per the OAuth2 spec: https://www.rfc-editor.org/rfc/rfc6749#section-4.3.2, although the spec seems to indicate that the client authentication is not always required (that's not very clear to me).
That means that when calling the token endpoint using the password grant type, you need to pass in the client ID and secret (using basic auth), which also mean that you can't use the password grant if the client does not have a secret (you might still be able to use the implicit flow).
In sparklr2, my-trusted-client does not have a secret defined which is why your call fails.
If you want to see the password grant type in action you can try my-trusted-client-with-secret:
curl -u my-trusted-client-with-secret:somesecret "http://localhost:8080/sparklr2/oauth/token?grant_type=password&username=marissa&password=koala"
Although the question is a bit old, I would like to contribute with my findings around this.
It is true that for Spring OAuth you need to specify a client ID in order to access to the token endpoint, but it is not necessary to specify client Secret for password grant type.
Next lines are an example of an Authorization Server client for password grant type without any client Secret. Yout just need to add them in your class that extends AuthorizationServerConfigurerAdapter:
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("clientId")
.authorizedGrantTypes("password")
.authorities("ROLE_CLIENT")
.scopes("read");
}
}
Furthermore, it is indeed possible to avoid the HTTP Basic Authentication in the token endpoint and add our client_id as another request parameter in our POST call.
To achieve this, you just need to add these lines in the same class as before:
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.allowFormAuthenticationForClients();
}
Now we can call the token endpoint by this way, which seems more correct following the examples found in Stormpath webpage
POST http://localhost:8080/sparklr2/oauth/token?grant_type=password&client_id=clientId&scope=read&username=marissa&password=koala