Spring Security 7 Adds OAuth2 Authorization Server

The Spring Authorization Server is now integrated into Spring Security 7, maintaining the same Maven coordinates. Upgrading from version 1.5.x simply requires a version update, not a change in artifacts. The standalone project was previously published under org.springframework.security:spring-security-oauth2-authorization-server, and the merged module in Spring Security 7 retains the same groupId and artifactId. The version has shifted from the 1.5.x line to the Spring Security version line (7.0.x, currently 7.1.x).
If you encounter information suggesting a rename of a spring-authorization-server artifact, note that such an artifact never existed. However, if you use the Boot starter, a rename is necessary. In Boot 4, the starter now includes a security- prefix, making spring-boot-starter-oauth2-authorization-server deprecated in favor of spring-boot-starter-security-oauth2-authorization-server, with both available up to version 4.1.1. Beyond this, the migration involves a version update and the same package relocations, including the transition to Jackson that all Spring Security 7 users must adopt. This is not a coordinate swap.
Baseline Changes and Migration Steps
Before exploring new features, Spring Security 7 introduces several baseline changes, mostly unrelated to OAuth: the removal of and() from the HttpSecurity builder, the replacement of Jackson 2 with Jackson 3, and updates to some JSON Web Token (JWT) decoder defaults. Spring’s migration guide and its OAuth 2.0-specific page detail these changes, with steps 2 through 4 in the playbook providing concrete instructions. Two critical points are not explicitly mentioned in the guide, as they are specific to authorization servers rather than general Spring Security applications.
First, the typ header change does not weaken validation. Instead, it automates what was previously done manually in version 6.5: disabling Nimbus’s typ check with validateTypes(false) to allow registration of JwtTypeValidator.jwt(). In version 7.0, this process is automated. The key concern is not whether tokens will validate, but that JwtTypeValidator.jwt() requires the typ to be JWT or absent. Tokens with a different typ, such as the RFC 9068 at+jwt access-token type, will fail validation unless a custom validator is registered.
Second, if OAuth2Authorization objects are persisted to a database using Spring’s Jackson modules, the registration must be moved from Jackson 2 SecurityJackson2Modules to Jackson 3 SecurityJacksonModules in the new org.springframework.security.jackson package. Failure to do so will result in deserialization errors for previously persisted authorization state. Spring automatically handles JWK thumbprint binding via cnf.jkt and refresh token management for public clients, leaving two decisions to the identity provider: the nonce policy and whether to replace the default in-memory, single-node jti replay cache with a distributed one for clustered environments.
Handling DPoP Proof Replay Checks
RFC 9449 §11.1 mandates rejecting proofs with a previously seen jti within an acceptable time window. In Spring Security 7.0.x and 7.1.0, this check was hard-coded using a fixed, thousand-entry in-memory cache within DPoPProofJwtDecoderFactory, with no option for customization. Since Spring Security 6.5.12 / 7.1.1, this has been replaced by the pluggable DPoPProofReplayValidator, which defaults to an InMemoryCache with one hundred thousand entries, capped per key. The Javadoc for this default implementation clearly states it is only suitable for single-node setups and recommends using distributed caches like Redis or Hazelcast in clustered environments.
The nonce challenge, while recommended (SHOULD) in the RFC, is not mandatory (MUST), which explains its absence in the current implementation. If your threat model requires it, you must implement the entire mechanism yourself: issuing nonces, handling challenge responses, storing issued nonces, and validating claims against them. Most in-house identity providers (IdPs) use a form of “service-account swap,” where Service A uses a secondary set of credentials or a side channel to obtain a token for Service B, often relying on lenient audience checks.
Token Exchange Grant Standardization
RFC 8693 standardizes this process with a token exchange grant. RFC 8693 §1.1 distinguishes between impersonation and delegation. Impersonation issues a token where the subject and actor are the same, while delegation preserves both identities, with the issued token including the subject and an act (actor) claim. The choice between these is determined by a single parameter.
Enabling the Grant
The OAuth2TokenExchangeAuthenticationProvider is automatically registered when the authorization server starts, so no additional provider bean is needed. The provider validates the request structure and ensures the calling client is authorized to use this grant type. You must enable the grant for specific clients in the configuration. The RegisteredClient entity contains this configuration, listing allowed grant types, including token exchange. Without this entry, the server returns an unsupported_grant_type error. The client must also have the necessary scopes to request the token. The grant handles request body parameters.
It extracts the subject_token, audience, and optional actor_token, then constructs a new OAuth2Token. The token includes the requested scope and audience, as well as the subject claim from the original subject_token. If an actor_token is present, the provider adds an act claim identifying the acting party, using the actor_token to build this claim. This enables tracing the delegation chain. The resulting token is a standard OAuth 2.0 access token, suitable for calling downstream resources.
Combining DPoP and Token Exchange
DPoP and Token Exchange features complement each other. Clients often use DPoP to secure their initial token, which can then be exchanged for a more restricted token with a limited audience, preventing token leakage to other services. The DPoP proof binds the exchange request, ensuring it originates from a trusted source. The security of this combined approach depends on the exchange policy. You must write a customizer for the token to correctly set the audience and scope, preventing clients from requesting excessive permissions.
