I know normally Cognito itself is an authorization server actor in Oauth2 flow.
But as per my custom requirements I want to use spring authorization server with Cognito, basically:
- A client comes to oauth2/authorize endpoint with its client ID
- Redirects to login page and spring will know which client is trying a user to login.
- Client receives an auth code from spring after successful login (via
Cognito, so auth code from spring, but login should be via cognito) - Then sends a POST request to token endpoint of Spring to receive access/ID/refresh tokens from Cognito.
- All app clients will be taken from Cognito user pool.
I saw the JDBC example but can’t really get it work with Cognito, any help would be appreciated.
2
Answers
I find in that regard the presentation "Want to authenticate using Amazon Cognito? Then use Spring Security!", from Ryosuke Uchitate enlightening, regarding the integration of Amazon Cognito with Spring.
You can find his implementation at
b1a9id/spring-security-with-cognito
(2018)The general idea would be to provide a
AbstractUserDetailsAuthenticationProvider
, using aCognitoService
, withAutoWired
annotation:For the Cognito service, you would need to add the AWS Java SDK for Cognito Identity Provider to your project’s dependencies. If you’re using Maven, add the following to your
pom.xml
:The service itself would require the appropriate AWS credentials, region, and Amazon Cognito configuration (client ID, client secret):
You then could configure your Spring Security to use the custom authentication provider and set up the Authorization Server.
BUT: as documented here:
That means the more modern implementation would use a
SecurityFilterChain
bean:You need to pass the original parameter, like
.authenticationProvider(customCognitoAuthenticationProvider)
to the newHttpSecurity http
, as illustrated in "WebSecurityConfigurerAdapter
Deprecated in Spring Boot" or in "Spring Security – How to FixWebSecurityConfigurerAdapter Deprecated
".Then you configure your Spring Authorization Server (
OAuth2AuthorizationServerConfiguration
) with your custom token store (using Amazon Cognito’s access/ID/refresh tokens).That means implementing a custom
TokenStore
, which should be designed to handle the storage and retrieval of OAuth2 tokens (access tokens, ID tokens, and refresh tokens).In your case: tokens issued by Amazon Cognito.
An in-memory one (not suitable for production) would start with:
As noted in "TokenStore in Spring Security 5.x after removal of Spring Security OAuth 2.x", you need to check if this is compatible with the latests from Spring Security 5.X/6.0+.
From what I understand (but cannot durectly test), using your steps:
/oauth2/authorize
endpoint with its client ID:In
SecurityConfig.java
:As noted above, this should be done within a
SecurityFilterChain
if you are using Spring Security 5.X/6.x.In
application.yml
:In
SecurityConfig.java
:That redirection endpoint configured in the SecurityConfig would be responsible for handling the callback from Amazon Cognito after a successful login. The auth code is actually received from Amazon Cognito, not from Spring.
Meaning:
/cognito/callback
) along with an authorization code as a query parameter./cognito/callback
endpoint and extracts the authorization code from the query parameter.access/ID/refresh
tokens from Cognito:In
CognitoOAuth2LoginSuccessHandler.java
:In
CognitoOAuth2UserService.java
:Regarding
OAuth2AuthorizationService
: there should be no need for a direct replacement ofOAuth2AuthorizationService
.Spring Security’s built-in support for OAuth2 should take care of managing the authorization code flow, including sending the authorization code to Amazon Cognito’s token endpoint and exchanging it for access, ID, and refresh tokens.
The
CognitoOAuth2UserService
is responsible for loading user details from Amazon Cognito, while theCognitoOAuth2LoginSuccessHandler
handles the successful authentication and provides access to the tokens.Thought I’d add some notes here on the OAuth architecture to aim for, and how I think of it, since your question had a couple of points that don’t quite seem right. I can’t help you on Spring AS specifics though.
ROLES
The client implements a code flow at the AS. The AS runs another code flow to the IDP. Chaining these systems together is very standard and should require only configuration, with zero code changes in the client.
REGISTRATION
TOKENS ISSUED
The client always receives tokens from the AS and not the IDP. The AS issues tokens that protect your business data. It enables you to issue whatever scopes and claims you need to lock down tokens.
UPSTREAM TOKENS
Clients and APIs should not usually need to deal with tokens from the IDP. There is sometimes an exception, eg also use AWS tokens to access the user’s AWS resources.
If that is your requirement, aim to use embedded tokens. This means Spring AS issues IDP tokens as custom claims to AS tokens. This enables your APIs to continue to authorize correctly, while also being able to access AWS resources when needed.