Spring Boot SAML and AWS Cognito - spring-boot

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

Related

How to configure spring boot to validate JWT token with call instropection endpoint of authorization server

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

Configuring spring-boot-starter-oauth2-client to authenticate with Azure AD

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'.

Missing required UserInfo Uri in UserInfoEndpoint for Client Registration

I am facing with below error while accessing to api within OAuth2.0 credentials.
The examples on Internet containing user-info-uri credential, but I have only authorization-uri and token-uri. How can I successfully connect without user-info-uri ?
Authorization screen is successfully opened, but after that the error is shown:
[missing_user_info_uri] Missing required UserInfo Uri in UserInfoEndpoint for Client Registration: aa-client-1
My application.yml like this :
spring:
security:
oauth2:
client:
registration:
aa-client-1:
client-id: XXXX-XXXXX-XXXX-XXX
client-secret: XXXX-XXXXX-XXXX-XXX
provider: A
scope: A:scope
authorization-grant-type: authorization_code
redirect-uri: http://localhost:8080/login
provider:
A:
authorization-uri: https://someurls/authorize
token-uri: https://someurl/token
I want to login the API without user-info-uri.
Unfortunately, you must provide an user-info-uri if your grant type is authorization_code.
See GitHub discussion and Spring Security Source Code.
Perhaps this workaround may also applicable to your context.

How to adapt spring security to work with Ali Cloud's OAuth2 endpoint

I am trying to get spring-oauth2 to work with Ali Cloud's OAuth endpoint. I have the following in my application.yml.
security:
oauth2:
client:
registration:
alicloud:
clientId: foo
clientSecret: bar
redirectUriTemplate: "{baseUrl}/login/oauth2/code/{registrationId}"
authorizationGrantType: authorization_code
scope:
- openid
- profile
provider:
alicloud:
authorizationUri: https://signin.aliyun.com/oauth2/v1/auth
tokenUri: https://oauth.aliyun.com/v1/token
jwkSetUri: https://oauth.aliyun.com/v1/keys
Things seem to get to the point where the code needs to be exchanged for an access_token. However, DefaultAuthorizationCodeTokenResponseClient.getTokenResponse seems to package the request with HTTP Basic authentication. This works for Google and I imagine most of the rest of the big providers. But Ali Cloud's endpoint requires the client_id and client_secret in the POST body. The converter that seems to create this request is OAuth2AuthorizationCodeGrantRequestEntityConverter. It doesn't seem easy to override it.
What can I do to overcome this?

Spring Oauth2 ResourceOwnerPassword flow for Single Sign on

I have an oauth Authserver app,a web app(enabled oauth sso),an android app(needs to access rest resources in the web app)
I am trying to implement spring-oauth2 taking example of oauth-vanilla(latest one with no jwt) spring boot sample project.The project has the following client configuration
security.oauth2.client.clientId: acme
security.oauth2.client.clientSecret: acmesecret
security.oauth2.client.authorized-grant-types: authorization_code,refresh_token,password
security.oauth2.client.scope: openid
so the logic flow goes like(correct me if am wrong) user attempts access to the ui app -->zuul proxied redirect(with client details) to authentication server-->login with credentials (protected /authorize url)-->authorize the scopes-->return with the token/authorization code.
How to eliminate the user authorization step(for android app purpose i want resource owner apporach).I changed my client configuration like this to follow
clients.inMemory().withClient("****").secret("*****").authorities("ROLE_USER")
.authorizedGrantTypes("password", "refresh_token").scopes("read", "write");
but I am getting error from authserver app (DefaultRedirectResolver.java)
"A redirect_uri can only be used by implicit or authorization_code
grant types."
if i have my security.oauth properties a below in my web-ui app
security:
oauth2:
client:
accessTokenUri: http://localhost:9097/uaa/oauth/token
userAuthorizationUri: http://localhost:9097/uaa/oauth/authorize
clientId: ****
clientSecret: ****
resource:
userInfoUri: http://localhost:9097/uaa/user
for single sign on ,can resource owner password approach be used or not ? if so what should i change as part of configuration?
"A redirect_uri can only be used by implicit or authorization_code
grant types."
I'm not sure about the "redirect_uri" but I don't think you can't get single sign on with the password flow. SSO is achieved by creating a session on the authorization server so that a client (same or different) won't have to authenticate again if the authorization server already has an authenticated session. The password flow doesn't create a session on the authentication server...it only gets tokens.
You must specify authorization_code or implicit grant types on .authorizedGrantTypes(), for they are also redirect types.
clients.inMemory().withClient("****").secret("*****").authorities("ROLE_USER")
.authorizedGrantTypes("authorization_code", "password", "refresh_token").scopes("read", "write");
or
clients.inMemory().withClient("****").secret("*****").authorities("ROLE_USER")
.authorizedGrantTypes("implicit", "password", "refresh_token").scopes("read", "write");
Source:
DefautlRedirectResolver

Resources