or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

authentication.mdindex.mdkotlin-dsl.mdmethod-security.mdoauth2.mdreactive-web-security.mdsaml2.mdservlet-web-security.md
tile.json

oauth2.mddocs/

OAuth2 Configuration

Comprehensive OAuth2 and OpenID Connect (OIDC) support including OAuth2 client, resource server, and authorization server configuration.

Capabilities

OAuth2 Login

Configure OAuth2/OIDC login for single sign-on.

package org.springframework.security.config.annotation.web.configurers.oauth2.client;

public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
        extends AbstractAuthenticationFilterConfigurer<B, OAuth2LoginConfigurer<B>,
                                                       OAuth2LoginAuthenticationFilter> {

    public OAuth2LoginConfigurer<B> loginPage(String loginPage);

    public OAuth2LoginConfigurer<B> authorizationEndpoint(
        Customizer<AuthorizationEndpointConfig> authorizationEndpointCustomizer
    );

    public OAuth2LoginConfigurer<B> redirectionEndpoint(
        Customizer<RedirectionEndpointConfig> redirectionEndpointCustomizer
    );

    public OAuth2LoginConfigurer<B> tokenEndpoint(
        Customizer<TokenEndpointConfig> tokenEndpointCustomizer
    );

    public OAuth2LoginConfigurer<B> userInfoEndpoint(
        Customizer<UserInfoEndpointConfig> userInfoEndpointCustomizer
    );

    public OAuth2LoginConfigurer<B> clientRegistrationRepository(
        ClientRegistrationRepository clientRegistrationRepository
    );

    public OAuth2LoginConfigurer<B> authorizedClientService(
        OAuth2AuthorizedClientService authorizedClientService
    );

    public OAuth2LoginConfigurer<B> authorizedClientRepository(
        OAuth2AuthorizedClientRepository authorizedClientRepository
    );

    public OAuth2LoginConfigurer<B> loginProcessingUrl(String loginProcessingUrl);

    public OAuth2LoginConfigurer<B> successHandler(
        AuthenticationSuccessHandler successHandler
    );

    public OAuth2LoginConfigurer<B> failureHandler(
        AuthenticationFailureHandler failureHandler
    );

    public OAuth2LoginConfigurer<B> permitAll();
}

Usage Example:

http
    .oauth2Login(oauth2 -> oauth2
        .loginPage("/login")
        .defaultSuccessUrl("/dashboard")
        .userInfoEndpoint(userInfo -> userInfo
            .userService(customOAuth2UserService)
            .oidcUserService(customOidcUserService)
        )
        .tokenEndpoint(token -> token
            .accessTokenResponseClient(customAccessTokenResponseClient)
        )
    );

// application.yml configuration
spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: YOUR_CLIENT_ID
            client-secret: YOUR_CLIENT_SECRET
            scope:
              - openid
              - profile
              - email
        provider:
          google:
            issuer-uri: https://accounts.google.com

OAuth2 Client

Configure OAuth2 client for accessing protected resources.

package org.springframework.security.config.annotation.web.configurers.oauth2.client;

public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>>
        extends AbstractHttpConfigurer<OAuth2ClientConfigurer<B>, B> {

    public OAuth2ClientConfigurer<B> clientRegistrationRepository(
        ClientRegistrationRepository clientRegistrationRepository
    );

    public OAuth2ClientConfigurer<B> authorizedClientRepository(
        OAuth2AuthorizedClientRepository authorizedClientRepository
    );

    public OAuth2ClientConfigurer<B> authorizedClientService(
        OAuth2AuthorizedClientService authorizedClientService
    );

    public OAuth2ClientConfigurer<B> authorizationCodeGrant(
        Customizer<AuthorizationCodeGrantConfigurer> authorizationCodeGrantCustomizer
    );
}

Usage Example:

http
    .oauth2Client(Customizer.withDefaults());

// Use in controller
@RestController
public class ApiController {

    @GetMapping("/api/data")
    public String getData(@RegisteredOAuth2AuthorizedClient("my-client") OAuth2AuthorizedClient authorizedClient) {
        String accessToken = authorizedClient.getAccessToken().getTokenValue();
        // Use access token to call external API
        return restTemplate.exchange(
            "https://api.example.com/data",
            HttpMethod.GET,
            new HttpEntity<>(createHeaders(accessToken)),
            String.class
        ).getBody();
    }
}

OAuth2 Resource Server

Configure OAuth2 resource server with JWT or opaque token validation.

package org.springframework.security.config.annotation.web.configurers.oauth2.server.resource;

public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<H>>
        extends AbstractHttpConfigurer<OAuth2ResourceServerConfigurer<H>, H> {

    public OAuth2ResourceServerConfigurer<H> jwt(
        Customizer<JwtConfigurer> jwtCustomizer
    );

    public OAuth2ResourceServerConfigurer<H> opaqueToken(
        Customizer<OpaqueTokenConfigurer> opaqueTokenCustomizer
    );

    public OAuth2ResourceServerConfigurer<H> authenticationManagerResolver(
        AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver
    );

    public OAuth2ResourceServerConfigurer<H> authenticationEntryPoint(
        AuthenticationEntryPoint entryPoint
    );

    public OAuth2ResourceServerConfigurer<H> bearerTokenResolver(
        BearerTokenResolver bearerTokenResolver
    );

    public OAuth2ResourceServerConfigurer<H> accessDeniedHandler(
        AccessDeniedHandler accessDeniedHandler
    );

    // JWT Configurer
    public final class JwtConfigurer {
        public JwtConfigurer decoder(JwtDecoder jwtDecoder);
        public JwtConfigurer jwkSetUri(String jwkSetUri);
        public JwtConfigurer jwtAuthenticationConverter(Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter);
        public JwtConfigurer jwtAuthenticationConverter(JwtAuthenticationConverter jwtAuthenticationConverter);
    }

    // Opaque Token Configurer
    public final class OpaqueTokenConfigurer {
        public OpaqueTokenConfigurer introspectionUri(String introspectionUri);
        public OpaqueTokenConfigurer introspectionClientCredentials(String clientId, String clientSecret);
        public OpaqueTokenConfigurer introspector(OpaqueTokenIntrospector introspector);
    }
}

Usage Examples:

// JWT Resource Server
http
    .authorizeHttpRequests(authorize -> authorize
        .requestMatchers("/api/public/**").permitAll()
        .requestMatchers("/api/**").hasAuthority("SCOPE_read")
        .anyRequest().authenticated()
    )
    .oauth2ResourceServer(oauth2 -> oauth2
        .jwt(jwt -> jwt
            .jwkSetUri("https://idp.example.com/.well-known/jwks.json")
            .jwtAuthenticationConverter(jwtAuthenticationConverter())
        )
    );

// Using issuer URI (auto-configuration)
http
    .oauth2ResourceServer(oauth2 -> oauth2
        .jwt(Customizer.withDefaults())
    );

// application.yml
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://idp.example.com

// Opaque Token Resource Server
http
    .oauth2ResourceServer(oauth2 -> oauth2
        .opaqueToken(opaque -> opaque
            .introspectionUri("https://idp.example.com/introspect")
            .introspectionClientCredentials("client-id", "client-secret")
        )
    );

// Custom JWT authentication converter
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
    JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
    JwtGrantedAuthoritiesConverter authoritiesConverter = new JwtGrantedAuthoritiesConverter();
    authoritiesConverter.setAuthoritiesClaimName("roles");
    authoritiesConverter.setAuthorityPrefix("ROLE_");
    converter.setJwtGrantedAuthoritiesConverter(authoritiesConverter);
    return converter;
}

// Accessing JWT claims in controller
@GetMapping("/user")
public Map<String, Object> user(@AuthenticationPrincipal Jwt jwt) {
    return Map.of(
        "sub", jwt.getSubject(),
        "email", jwt.getClaim("email"),
        "roles", jwt.getClaim("roles")
    );
}

OAuth2 Authorization Server

Configure OAuth2 authorization server.

package org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;

public final class OAuth2AuthorizationServerConfigurer
        extends AbstractHttpConfigurer<OAuth2AuthorizationServerConfigurer, HttpSecurity> {

    public OAuth2AuthorizationServerConfigurer clientAuthentication(
        Customizer<OAuth2ClientAuthenticationConfigurer> clientAuthenticationCustomizer
    );

    public OAuth2AuthorizationServerConfigurer authorizationEndpoint(
        Customizer<OAuth2AuthorizationEndpointConfigurer> authorizationEndpointCustomizer
    );

    public OAuth2AuthorizationServerConfigurer pushedAuthorizationRequestEndpoint(
        Customizer<OAuth2PushedAuthorizationRequestEndpointConfigurer> parEndpointCustomizer
    );

    public OAuth2AuthorizationServerConfigurer tokenEndpoint(
        Customizer<OAuth2TokenEndpointConfigurer> tokenEndpointCustomizer
    );

    public OAuth2AuthorizationServerConfigurer tokenIntrospectionEndpoint(
        Customizer<OAuth2TokenIntrospectionEndpointConfigurer> tokenIntrospectionEndpointCustomizer
    );

    public OAuth2AuthorizationServerConfigurer tokenRevocationEndpoint(
        Customizer<OAuth2TokenRevocationEndpointConfigurer> tokenRevocationEndpointCustomizer
    );

    public OAuth2AuthorizationServerConfigurer deviceAuthorizationEndpoint(
        Customizer<OAuth2DeviceAuthorizationEndpointConfigurer> deviceAuthorizationEndpointCustomizer
    );

    public OAuth2AuthorizationServerConfigurer deviceVerificationEndpoint(
        Customizer<OAuth2DeviceVerificationEndpointConfigurer> deviceVerificationEndpointCustomizer
    );

    public OAuth2AuthorizationServerConfigurer clientRegistrationEndpoint(
        Customizer<OAuth2ClientRegistrationEndpointConfigurer> clientRegistrationEndpointCustomizer
    );

    public OAuth2AuthorizationServerConfigurer authorizationServerMetadataEndpoint(
        Customizer<OAuth2AuthorizationServerMetadataEndpointConfigurer> authorizationServerMetadataEndpointCustomizer
    );

    public OAuth2AuthorizationServerConfigurer oidc(
        Customizer<OidcConfigurer> oidcCustomizer
    );

    public RequestMatcher getEndpointsMatcher();

    // OIDC Configurer
    public final class OidcConfigurer {
        public OidcConfigurer userInfoEndpoint(
            Customizer<OidcUserInfoEndpointConfigurer> userInfoEndpointCustomizer
        );

        public OidcConfigurer providerConfigurationEndpoint(
            Customizer<OidcProviderConfigurationEndpointConfigurer> providerConfigurationEndpointCustomizer
        );

        public OidcConfigurer clientRegistrationEndpoint(
            Customizer<OidcClientRegistrationEndpointConfigurer> clientRegistrationEndpointCustomizer
        );

        public OidcConfigurer logoutEndpoint(
            Customizer<OidcLogoutEndpointConfigurer> logoutEndpointCustomizer
        );
    }
}

Usage Example:

@Configuration
public class AuthorizationServerConfig {

    @Bean
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfigurer authzServer =
            new OAuth2AuthorizationServerConfigurer();

        http
            .securityMatcher(authzServer.getEndpointsMatcher())
            .with(authzServer, authz -> authz
                .oidc(oidc -> oidc
                    .userInfoEndpoint(userInfo -> userInfo
                        .userInfoMapper(context -> {
                            // Custom user info mapping
                        })
                    )
                )
            )
            .authorizeHttpRequests(authorize -> authorize
                .anyRequest().authenticated()
            );

        return http.build();
    }

    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient client = RegisteredClient.withId(UUID.randomUUID().toString())
            .clientId("client-id")
            .clientSecret("{noop}secret")
            .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
            .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
            .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
            .redirectUri("http://localhost:8080/login/oauth2/code/client")
            .scope(OidcScopes.OPENID)
            .scope(OidcScopes.PROFILE)
            .scope("read")
            .scope("write")
            .clientSettings(ClientSettings.builder()
                .requireAuthorizationConsent(true)
                .requireProofKey(true)
                .build())
            .tokenSettings(TokenSettings.builder()
                .accessTokenTimeToLive(Duration.ofHours(1))
                .refreshTokenTimeToLive(Duration.ofDays(7))
                .reuseRefreshTokens(false)
                .build())
            .build();

        return new InMemoryRegisteredClientRepository(client);
    }

    @Bean
    public OAuth2AuthorizationService authorizationService() {
        return new InMemoryOAuth2AuthorizationService();
    }

    @Bean
    public OAuth2AuthorizationConsentService authorizationConsentService() {
        return new InMemoryOAuth2AuthorizationConsentService();
    }

    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        KeyPair keyPair = generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        RSAKey rsaKey = new RSAKey.Builder(publicKey)
            .privateKey(privateKey)
            .keyID(UUID.randomUUID().toString())
            .build();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return new ImmutableJWKSet<>(jwkSet);
    }

    @Bean
    public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
        return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
    }

    @Bean
    public AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings.builder()
            .issuer("http://localhost:9000")
            .build();
    }
}

OIDC Logout

Configure OpenID Connect logout (back-channel and front-channel).

package org.springframework.security.config.annotation.web.configurers.oauth2.client;

public final class OidcLogoutConfigurer<H extends HttpSecurityBuilder<H>>
        extends AbstractHttpConfigurer<OidcLogoutConfigurer<H>, H> {

    public OidcLogoutConfigurer<H> backChannel(
        Customizer<BackChannelLogoutConfigurer> backChannelCustomizer
    );

    // Back-Channel Logout Configurer
    public final class BackChannelLogoutConfigurer {
        public BackChannelLogoutConfigurer logoutUri(String logoutUri);
        public BackChannelLogoutConfigurer logoutHandler(OidcBackChannelLogoutHandler logoutHandler);
    }
}

Usage Example:

http
    .oauth2Login(Customizer.withDefaults())
    .oidcLogout(oidcLogout -> oidcLogout
        .backChannel(Customizer.withDefaults())
    );

Types

// Client Registration
public final class ClientRegistration {
    public String getRegistrationId();
    public String getClientId();
    public String getClientSecret();
    public ClientAuthenticationMethod getClientAuthenticationMethod();
    public AuthorizationGrantType getAuthorizationGrantType();
    public String getRedirectUri();
    public Set<String> getScopes();
    public ProviderDetails getProviderDetails();
}

// OAuth2 Authorized Client
public class OAuth2AuthorizedClient {
    public ClientRegistration getClientRegistration();
    public String getPrincipalName();
    public OAuth2AccessToken getAccessToken();
    public OAuth2RefreshToken getRefreshToken();
}

// OAuth2 Access Token
public class OAuth2AccessToken {
    public TokenType getTokenType();
    public String getTokenValue();
    public Instant getIssuedAt();
    public Instant getExpiresAt();
    public Set<String> getScopes();
}

// JWT
public interface Jwt extends AbstractOAuth2Token {
    Map<String, Object> getClaims();
    Map<String, Object> getHeaders();
    String getSubject();
    Instant getIssuedAt();
    Instant getExpiresAt();
    <T> T getClaim(String claim);
}