With a Spring Boot client configured in the DMZ and Spring Security OAuth configured using:
issuer-uri: https://authentication_server/auth/realms/my-realm
I get this error from Spring Security:
The Issuer "https://external_url/auth/realms/my-realm" provided in the configuration metadata did not match the requested issuer "https://authentication_server/auth/realms/my-realm
From this post I have learned that I need to specify authorization-uri, token-uri and jwk-set-uri instead of issuer-uri, and then it also works.
authorization-uri: https://external_url/auth/realms/my-realm/protocol/openid-connect/auth
token-uri: https://authentication_server/auth/realms/my-realm/protocol/openid-connect/token
jwk-set-uri: https://authentication_server/auth/realms/my-realm/protocol/openid-connect/certs
(I do not get why Spring Security cannot auto-configure with the same values from the issuer-uri when it works setting the values individually)
Now the problem is that logout stops working. When using issuer-uri the OAuth is auto-configured and end_session_endpoint is fetched from the answer, but when specifying each setting there is no way to specify the end_session_endpoint.
Is this an outstanding issue in Spring Security OAuth, or do I need to configure it differently?
I had to make a work around for this. With little time I started by copying the existing OidcClientInitiatedLogoutSuccessHandler which I already were using in configuring LogoutRedirectUri.
I simply copied the class and changed the implementation of the method endSessionEndpoint() to return the URI which is returned by our OAuth server as end_session_endpoint.
This issue is tracked in spring-security GitHub.
Probable fix will be allowing to add "Additional attributes for ClientRegistration and ProviderDetails".
Related
I have simple resource server application with spring boot, this is yaml file:
server:
port: 8081
servlet:
context-path: /resource-server-jwt
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://localhost:8083/auth/realms/rasool
Now, i want to make change in configuration or code to force spring security to validate JWT token with calling introspection endpoint of authorization server instead of local validation with keys, but i didn't find any way as spring security docs says.
Spring-boot spring.security.oauth2.resourceserver.jwt.* configuration properties are for JWT decoder.
For token introspection, use spring.security.oauth2.resourceserver.opaque-token.* properties instead (token being in whatever format, including JWT). "opaque" means that tokens are considered a "black-box" by resource-server which delegates validataion and attributes retrieval to authorization-server on introspection endpoint:
server:
port: 8081
servlet:
context-path: /resource-server-jwt
spring:
security:
oauth2:
resourceserver:
opaque-token:
introspection-uri: http://localhost:8083/auth/realms/rasool/protocol/openid-connect/token/introspect
client-id: change-me
client-secret: change-me
Introspection uri from .well-known/openid-configuration
If you are using Java configurationn the switch is about the same: replace http.oauth2ResourceServer().jwt()... with http.oauth2ResourceServer().opaqueToken()...
A few notes about declared clients on authorisation-server
Resource-servers introspect token on authorisation-server introspection endpoint using client-credentials flow: for each and every request it process, resource-servers will send a request to authorization-server to get token details. This can have serious performance impact. Are you sure you want to switch to token introspection?
As a consequence, in the properties above, you must configure a client with:
"Access Type" set to confidential
"Service Accounts Enabled" activated
Create one if you don't have yet. You'll get client-secret from "credentials tab" once configuration saved.
Note you should have other (public) clients to identify users (from web / mobile apps or REST client) and query your resource-server on behalf of those users.
From the authorization-server point of view, this means that access-tokens will be issued to a (public) client and introspected by antoher (confidential) client.
Complete working sample here
It does a few things useful for resource-servers:
authorities mapping (choose attributes to parse user authorities from, prefix & case processing)
CORS configuration
stateless-session management
CSRF with Cookie repo
anonymous enabled for a list of configured public routes
401 (unauthorized) instead of 302 (redirect to login) when trying to access protected resources with missing or invalid Authorization
I have a working Cognito app client that utilizes user pool that is wired to use SAML. It accesses Azure AD as IdP. When I click "Launch Hosted UI" it properly redirects me to the login screen and upon authentication attempts to load my callback URL.
Now I want to wire this with a Spring Boot app.
I found this example developed by Joe Grandja that is using spring-security-saml2-service-provider to connect to a simple IdP.
The example is very compelling because all I really need to do is to provide correct configuration that in example provided like this:
spring:
security:
saml2:
relyingparty:
registration:
simplesamlphp:
signing.credentials:
- private-key-location: "classpath:credentials/rp-private.key"
certificate-location: "classpath:credentials/rp-certificate.crt"
identityprovider:
entity-id: https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php
verification.credentials:
- certificate-location: "classpath:credentials/idp-certificate.crt"
sso-url: https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php
However I'm lost at how to map information available to me from Cognito to these settings? For example values in signing.credentials?
Here's the list of settings I get from Cognito (all keys/names are bogus):
Pool Id: us-west-2_1B1AHf00
Pool ARN: arn:aws:cognito-idp:us-west-2:1234567898:userpool/us-west-2_1B1AHf00
Domain: https://blah-foo.auth.us-west-2.amazoncognito.com
App client: blahfoo-client
App client ID: 1b1l1a2f8oo83456c
Callback URL: http://localhost:8080
Login URL: https://blah-foo.auth.us-west-2.amazoncognito.com/login?client_id=1b1l1a2f8oo83456c&response_type=code&scope=email+openid&redirect_uri=http://localhost:8080
I also have a SAML-formatted file I got back from IdP but that is already plugged into Cognito so why would I put anything from it into the app configuration?
I wonder if part of spring-security-saml2-service-provider is to assemble that login URL and if I can get away with less settings that are given in the example?
Any pointers will be greatly appreciated
The signing.credentials section is if your app needs to sign things like an AuthnRequest. They are credentials that you own.
The items under identityprovider are things that Cognito would provide.
For Spring Boot 2.4+, if Cognito supports a SAML metadata endpoint, then you can provide that and Spring Security will discover the rest:
spring:
security:
saml2:
relyingparty:
registration:
simplesamlphp:
identityprovider:
metadata-uri: classpath:cognito/metadata/file/location
Or, for earlier versions, you can use RelyingPartyRegistrations:
#Bean
RelyingPartyRegistrationRepository registrations() {
String location = "classpath:cognito/metadata/file/location";
RelyingPartyRegistration registration =
RelyingPartyRegistrations.fromMetadataLocation(location)
.build();
return new InMemoryRelyingPartyRegistration(registration);
}
That said, the information that you've posted about Cognito's authentication endpoint appears OAuth-based, especially the Login URL. You may instead consider configuring your app for OAuth 2.0 and pointing at Cognito's OAuth endpoint.
I am posting the solution as a separate answer however credit and accepted answer go to #jzheaux
Basically the comment section provides the much needed hint: Even if you are wiring SAML-based Identity provider you will wire up Cognito using OAuth information given to you in the AWS console for User Pool
In my specific case the application.yaml then looks like this:
spring:
security:
oauth2:
client:
registration:
cognito:
client-id: 1ab2cd34efghi5jk6klmno7p8
client-secret: *********
scope: openid
redirect-uri: http://localhost:8080/login/oauth2/code/cognito
clientName: foobar-sandbox
provider:
cognito:
issuerUri: https://cognito-idp.us-west-2.amazonaws.com/us-west-2_abCDeFGHI
user-name-attribute: cognito:username
client-secret is found in General settings -> App clients -> Show Details
I want to add Azure AD as an OAuth2 provider in Spring Boot 2.4. I followed Spring Boot's OAuth2 docs and came up with the following configuration:
spring.security.oauth2.client.provider.azuread.issuer-uri=https://login.microsoftonline.com/<tenant uuid>/v2.0
spring.security.oauth2.client.registration.azuread.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.azuread.client-id=<client uuid>
spring.security.oauth2.client.registration.azuread.client-name=Azure AD
spring.security.oauth2.client.registration.azuread.client-secret=<client secret>
spring.security.oauth2.client.registration.azuread.provider=azuread
spring.security.oauth2.client.registration.azuread.scope=openid
Just for completeness, this is my web security configuration:
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.authorizeRequests(a -> a
.antMatchers("/", "/login", "/error", "/webjars/**").permitAll()
.anyRequest().authenticated()
)
.oauth2Login();
// #formatter:on
}
}
When coming back from entering the credentials on https://login.microsoftonline.com, I get the following error:
[invalid_id_token] An error occurred while attempting to decode the Jwt: Signed JWT rejected: Another algorithm expected, or no matching key(s) found.
The problem originates in DefaultJWTProcessor.java from Nimus-JOSE-JWT.
Looking through the requests in Firefox's network inspector, Spring Boot picks up the right URLs from the Issuer URI. I'm at a loss what's going wrong and appreciate any pointers.
Azure AD has some pretty unintuitive (in my opinion) default behaviour - I think this is what you are experiencing:
YOUR PROBLEM CAUSE (I THINK)
You are using standard OpenID Connect scopes
This causes Azure AD to issue an access token intended for the Graph API
This token fails standards based validation in Custom APIs since it is only designed for Graph APIs to use - it is recognisable by the nonce field in the JWT header
WHAT YOU NEED TO DO
Expose an API scope such as 'default'
Use the full value of this scope in your web client, with a value such as 'api://cb398b43-96e8-48e6-8e8e-b168d5816c0e/default', where the long identifier is that of the API
You will then get a normal OAuth token that Spring can validate - with no nonce field in the JWT header
FURTHER INFO
See steps 3 to 8 of my blog post from a couple of years ago
See the OpenID Connect settings of my web code sample
I had a similar problem and if I remember correctly there was an issue with scope. Don't know whether this is also your issue but in any case following configuration is working for me (note I'm using client credentials not auth code):
spring:
security:
oauth2:
client:
provider:
azure:
token-uri: https://login.microsoftonline.com/${custom.azure.account.tenant-id}/oauth2/token
registration:
azure:
client-id: ${custom.azure.service-principal.client-id}
client-secret: ${custom.azure.service-principal.client-secret}
authorization-grant-type: client_credentials
scope:
- https://graph.microsoft.com/.default
According to your description, it seems that you wanna use azure ad as the an OAuth2 provider in Spring Boot, and I found a doc said that 'Spring Starter for Azure Active Directory (AD) is now integrated with Spring Security 5.0'. It also offered a sample of springboot web app. I also tried it and it did work.
What I think need to note is, the tutorial said the redirect url setted in azure ad is 'http://localhost:8080/login/oauth2/code/', but when my tested, it proved to be 'http://localhost:8080/login/oauth2/code/azure'.
Upon your error, I'm not sure but maybe you can change your configuration of 'scope' to 'openid,profile,offline_access'.
I am trying to configure a Spring Boot app to use OIDC. The server is behind an SSL termination proxy.
Here are the properties I use:
spring:
security:
oauth2:
client:
provider:
oidc:
authorization-uri: https://example.org/oidc/oauth2/authorize
token-uri: https://example.org/oidc/oauth2/access_token
user-info-uri: https://example.org/oidc/oauth2/userinfo
jwk-set-uri: https://example.org/oidc/oauth2/connect/jwk_uri
custom-params: param_name1,param_value1,param_name2,param_value2,nonce,123456789
registration:
oidc:
client-id: myclientid
client-secret: myclientsecret
authorization-grant-type: authorization_code
scope:
- openid
- email
- profile
redirect-uri: https://mydomain/myapp/login/oauth2/code/oidc
Here's where it goes wrong:
1. The OIDC server requires a nonce param to be added to the request URL
I have solved this by using a custom OAuth2AuthorizationRequest that reads the custom-params property and appends those values to the request URL
2. The OidcAuthorizationCodeAuthenticationProvider throws an exception caused by invalid_redirect_uri_parameter
I have tried many approaches to fix this.
I have tried creating a filter that adds the X-Forwarded-Proto to the request (because the proxy doesn't handle that).
The headers are added, I have also added the following properties:
server:
forward-headers-strategy: native
tomcat.protocol-header: x-forwarded-proto
But it doesn't seem to work.
OidcAuthorizationCodeAuthenticationProvider still throws an exception because this condition is false:
!authorizationResponse.getRedirectUri().equals(authorizationRequest.getRedirectUri())
I have debugged the code and the only difference is one being http and the other https.
I have found a VERY hacky solution that I don't like at all, which is another filter that modifies the URL just for that particular URL.
I would prefer a more elegant solution.
3. When using the custom nonce parameter, the OidcAuthorizationCodeAuthenticationProvider throws an exception cause by invalid_nonce
Now I am stuck. I considered writing my own Authentication Provider, but I have no guarantee that mine will be picked up before the OIDC one provided by Spring.
And with the nonce, it's a catch 22:
if I don't use the custom param, I couldn't find a way to make Spring add the nonce to the request
if I use that one, Spring doesn't recognize it when it's part of the JWT and freaks out
Any help would be GREATLY appreciated, as this has been driving me nuts for days if not weeks.
Thank you.
EDIT
The 2 urls that are compared in case 2 come from:
OAuth2AuthorizationRequest
OAuth2AuthorizationResponse
OAuth2AuthorizationRequest is built in the
OAuth2AuthorizationRequestRedirectFilter at the following line:
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestResolver.resolve(request);
The redirect uri is built in DefaultOAuth2AuthorizationRequestResolver.expandRedirectUri() which calls
UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
OAuth2AuthorizationResponse is built in the OAuth2LoginAuthenticationFilter.attemptAuthentication() which also calls
UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
and then
OAuth2AuthorizationResponseUtils.convert(params, redirectUri)
I will double check, but I don't remember UriComponentsBuilder.adaptFromForwardedHeaders(HttpHeaders headers) being called when building these URLs.
And even if that works, that still leaves the nonce issue :(
We stumbled upon the same problem , the problem was mainly because our server was behind a reverse proxy, and it seems the proxy changed the url somehow causing this check to fail
!authorizationResponse.getRedirectUri().equals(authorizationRequest.getRedirectUri())
this line was removed in later versions of spring security , at commit
24500fa3ca23aa23ede86dfcfe02113671d5b8bc
commit at github
which was introduced on Dec 6, 2019 and was in spring security release 5.1.13
so the solution was to upgrade spring boot to at least 2.1.17 for spring boot 2.1.X line of versions.
while the OP said he can't upgrade his libraries, I hope this can help those who can.
We also did the solution mentioned above by Kavithakaran Kanapathippilla and configured our reverse proxy to add X-forwarded-proto http headers , also i believed we configured spring boot application.properties to check for them
spring boot documentation for working behind proxies
I try to implement a simple OAuth2 "Client Authentication with Signed JWT" Demo App using Spring Boot and Keycloak as AuthService.
The idea is:
one secured REST service "The Producer"
offering an endpoint GET /person for all users/principals with the role "read_person"
offering an endpoint POST /person for all users/principals with the role "write_person"
another (unsecured) REST service "The Consumer"
offering an enpoint /api open for everybody
calling internal the "producer" viaFeignclient using an RequestInterceptor to pass the AccessToken (signed JWT / JWS)
I read about the docs:
http://www.keycloak.org/docs/latest/securing_apps/topics/oidc/java/client-authentication.html
saying:
Once the client application is started, it allows to download its public >key in JWKS format using a URL such as http://myhost.com/myapp/k_jwks, >assuming that http://myhost.com/myapp is the base URL of your client >application. This URL can be used by Keycloak (see below).
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 googled a lot to find tutorials/demos or docs about this topic but failed so far.
So here my questions:
How do I implement this "k_jwk" endpoint? Do I simple build a #RestController by myself in "the Producer"? How do I configure Keycloak to get aware of this URL?
How do I implement my "Consumer" to get fresh signed JWT from Keycloak?
Update
Removed irritating PS statement.
You don't need to implement the k_jwk endpoint, this is handled by the adapter. Keycloak will by default look at http:///your.app.com/k_jwk(but if needed you can override that in the console).
Then you need to configure your Spring Boot client, just use the same properties as the keycloak.json but in the application.properties format:
...
keycloak.credentials.jwt.client-keystore-file=classpath:keystore-client.jks
keycloak.credentials.jwt.client-keystore-type=JKS
etc ...
You need a token to call the producerbut as you said the entry point will be an insecured endpoint so you might want to use a Service Account for this.
I hope this will help.
Update
I couldnt solve this issue but learned some things about singned JWT in the mean time:
create a so called "Bearer Token" by creating a Json Structure with all necessary claims (sub, nbf, exp ...) by yourself and sign/certificate it with your JKS/Private Key from Keycloak. There are some nice third party libs beside Keycloak to do this.
To get a real AccessToken (JWE/JWS) from Keycloak: send this static final Bearer Token to Keycloak at /auth/realms/$realm/protocol/openid-connect/token/introspect
with QueryParams:
grant_type=client_credentials&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=$BEARER_TOKEN
Use the received real AccessToken to access the ResourceServer...