Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Context - Spring + OAuth2

oauth2Login() method:
This method configures OAuth 2.0 login in Spring Security, allowing users to authenticate with third-party providers like Amazon Cognito, Google, Facebook, GitHub, etc. It implements OAuth 2.0 flow where users are redirected to an external provider for authentication. It uses the authorization code flow to obtain tokens for the client. Session management is done by storing the authentication in the session (stateful), though it can be configured to work with stateless setups using tokens.

Securing the ID Token

  • Ensure the receiving service (profile service) verifies the audience (aud) claim of the ID token to confirm it was intended for profile service.

  • ID tokens have expiration times (exp claim). Ensure the profile service checks that the token hasn't expired before accepting it.

  • Verify the ID token’s signature using the public keys from Cognito’s JWKS endpoint to ensure it hasn't been tampered with.

Current (Keycloak) Auth Flow

Drawio
lbox
mVer2
zoom1
simple0
zoominComment10
inCommentcustContentId04453236739
pageId4450910534
custContentIdlbox44532367391
diagramDisplayNameUntitled Diagram-1732880583246.drawio
1contentVer1
revision1
baseUrlhttps://hee-tis.atlassian.net/wiki
diagramNameUntitled Diagram-1732880583246.drawio
pCenter0
width1061
links
tbstyle
height610.5

Notes: Current implementation is specific to either Keycloak or Cognito flow, not necessarily generic to auth providers

Proposed

...

TIS OAuth2 Flow - Stateful

To implement a more generic OAuth2 flow, the filter chain in the web security config will be changed to use the Spring Security oauth2Login provider as below:

Code Block
    httpSecurity.csrf()
        .and()
        .authorizeRequests()
        .anyRequest().authenticated()
        .and()
        .oauth2Login()
        .successHandler(successHandler);

In order to create a reusable approach, a shared success handler can be created or adapted from an existing class to achieve the following:

  • Retrieve the appropriate claim from the token to be matched with the username in the profile service

  • Verify the user exists, and retrieve the user’s roles and principles for authorization

  • Set the calling service’s security context with the retrieved authorities

A high level idea of this can be seen below:

image-20241213-111227.pngImage Added

And an example implementation referring to real repositories/classes:

Drawio
mVer2
zoom1
simple0
inComment0
custContentId4450975911
pageId4450910534
lbox1
diagramDisplayNameUntitled Diagram-1732882607371.drawio
contentVer1
revision5
baseUrlhttps://hee-tis.atlassian.net/wiki
diagramNameUntitled Diagram-1732882607371.drawio
pCenter0
width1061.0000000000002
links
tbstyle
height656.5000000000003

A more specific example idea of a real implementation can be split into two parts:

Shared Components

  1. SharedAuthencticationSuccessHandler (implements AuthenticationSuccessHandler) (needs new name)

    • Instantiated with following dependencies/params:

      • JwtUtil(another shared component, see below) - for decoding token to retrieve user details

      • UserDetailsService - for retrieving user details

    • Does the following in its onAuthenticationSuccessmethod:

      • Decodes token to find username (email) using JwtUtil

      • Fetches user details from UserDetailsService

      • Extracts granted authorities from UserInfo SecurityContext

      • Authorization can now be performed on controllers etc within the calling service

  2. JwtUtil class (again, needs a better name)

    • Instantiated with the following dependencies/params:

      • jwkSetUri String configured as an application property

      • jwtIssuer String configured as an application property

      • usernameClaimString configured as an application property (this is because we use email as the username)

      • creates a NimbusJwtDecoder Bean from jwkSetUri and jwtIssuer

    • Does the following:

      • Method to retrieve the username from the token

    • UserDetailsService - Already exists, fetches user infor by user name

Service-Specific Configuration

Given the above, each service receiving a token (e.g. profile) would need to be configured with the following application properties:

  • jwkSetUri

  • jwtIssuer

  • usernameClaim

  • & any provider-specific (e.g. cognito) configuration required by spring framework to perform the authentication step in oauth2Login()

and would need to instantiate the following beans:

  1. SharedAuthencticationSuccessHandler (injected into successHandler)

  2. JwtUtil (injected into the above)

In theory, this implementation should support any OAuth2 Provider with only configuration changes

Tickets

  • One Ticket to implement the new Shared Components (enough of a vertical slice?)

  • One Ticket per service to switch over configuration (possibly with different subtasks depending on authorization/redirect requirements of each service)

Tech Sharing 12/12/24

View file
nameOauthCognito_2.pptx