Add documentation for DPoP support

Closes gh-17072
This commit is contained in:
Joe Grandja 2025-05-07 14:09:23 -04:00
parent 3110f3679a
commit e3c39f02bc
8 changed files with 345 additions and 2 deletions

View File

@ -53,9 +53,14 @@ import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* An {@link AbstractHttpConfigurer} for OAuth 2.0 Demonstrating Proof of Possession
* (DPoP) support.
*
* @author Joe Grandja
* @since 6.5
* @see DPoPAuthenticationProvider
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc9449">RFC 9449
* OAuth 2.0 Demonstrating Proof of Possession (DPoP)</a>
*/
final class DPoPAuthenticationConfigurer<B extends HttpSecurityBuilder<B>>
extends AbstractHttpConfigurer<DPoPAuthenticationConfigurer<B>, B> {

View File

@ -82,6 +82,7 @@
**** xref:servlet/oauth2/resource-server/opaque-token.adoc[Opaque Token]
**** xref:servlet/oauth2/resource-server/multitenancy.adoc[Multitenancy]
**** xref:servlet/oauth2/resource-server/bearer-tokens.adoc[Bearer Tokens]
**** xref:servlet/oauth2/resource-server/dpop-tokens.adoc[DPoP-bound Access Tokens]
** xref:servlet/saml2/index.adoc[SAML2]
*** xref:servlet/saml2/login/index.adoc[SAML2 Log In]
**** xref:servlet/saml2/login/overview.adoc[SAML2 Log In Overview]

View File

@ -0,0 +1,212 @@
[[oauth2-dpop-bound-access-tokens]]
= OAuth 2.0 DPoP-bound Access Tokens
https://datatracker.ietf.org/doc/html/rfc9449[RFC 9449 OAuth 2.0 Demonstrating Proof of Possession (DPoP)] is an application-level mechanism for sender-constraining an access token.
The primary goal of DPoP is to prevent unauthorized or illegitimate clients from using leaked or stolen access tokens, by binding an access token to a public key upon issuance by the authorization server and requiring that the client proves possession of the corresponding private key when using the access token at the resource server.
Access tokens that are sender-constrained via DPoP stand in contrast to the typical bearer token, which can be used by any client in possession of the access token.
DPoP introduces the concept of a https://datatracker.ietf.org/doc/html/rfc9449#name-dpop-proof-jwts[DPoP Proof], which is a JWT created by the client and sent as a header in an HTTP request.
A client uses a DPoP proof to prove the possession of a private key corresponding to a certain public key.
When the client initiates an <<dpop-access-token-request,access token request>>, it attaches a DPoP proof to the request in an HTTP header.
The authorization server binds (sender-constrains) the access token to the public key associated in the DPoP proof.
When the client initiates a <<dpop-protected-resource-request,protected resource request>>, it again attaches a DPoP proof to the request in an HTTP header.
The resource server obtains information about the public key bound to the access token, either directly in the access token (JWT) or via the token introspection endpoint.
The resource server then verifies that the public key bound to the access token matches the public key in the DPoP proof.
It also verifies that the access token hash in the DPoP proof matches the access token in the request.
[[dpop-access-token-request]]
== DPoP Access Token Request
To request an access token that is bound to a public key using DPoP, the client MUST provide a valid DPoP proof in the `DPoP` header when making an access token request to the authorization server token endpoint.
This is applicable for all access token requests regardless of authorization grant type (e.g. `authorization_code`, `refresh_token`, `client_credentials`, etc).
The following HTTP request shows an `authorization_code` access token request with a DPoP proof in the `DPoP` header:
[source,shell]
----
POST /oauth2/token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
DPoP: eyJraWQiOiJyc2EtandrLWtpZCIsInR5cCI6ImRwb3Arand0IiwiYWxnIjoiUlMyNTYiLCJqd2siOnsia3R5IjoiUlNBIiwiZSI6IkFRQUIiLCJraWQiOiJyc2EtandrLWtpZCIsIm4iOiIzRmxxSnI1VFJza0lRSWdkRTNEZDdEOWxib1dkY1RVVDhhLWZKUjdNQXZRbTdYWE5vWWttM3Y3TVFMMU5ZdER2TDJsOENBbmMwV2RTVElOVTZJUnZjNUtxbzJRNGNzTlg5U0hPbUVmem9ST2pRcWFoRWN2ZTFqQlhsdW9DWGRZdVlweDRfMXRmUmdHNmlpNFVoeGg2aUk4cU5NSlFYLWZMZnFoYmZZZnhCUVZSUHl3QmtBYklQNHgxRUFzYkM2RlNObWtoQ3hpTU5xRWd4YUlwWThDMmtKZEpfWklWLVdXNG5vRGR6cEtxSGN3bUI4RnNydW1sVllfRE5WdlVTRElpcGlxOVBiUDRIOTlUWE4xbzc0Nm9SYU5hMDdycTFob0NnTVNTeS04NVNhZ0NveGxteUUtRC1vZjlTc01ZOE9sOXQwcmR6cG9iQnVoeUpfbzVkZnZqS3cifX0.eyJodG0iOiJQT1NUIiwiaHR1IjoiaHR0cHM6Ly9zZXJ2ZXIuZXhhbXBsZS5jb20vb2F1dGgyL3Rva2VuIiwiaWF0IjoxNzQ2ODA2MzA1LCJqdGkiOiI0YjIzNDBkMi1hOTFmLTQwYTUtYmFhOS1kZDRlNWRlYWM4NjcifQ.wq8gJ_G6vpiEinfaY3WhereqCCLoeJOG8tnWBBAzRWx9F1KU5yAAWq-ZVCk_k07-h6DIqz2wgv6y9dVbNpRYwNwDUeik9qLRsC60M8YW7EFVyI3n_NpujLwzZeub_nDYMVnyn4ii0NaZrYHtoGXOlswQfS_-ET-jpC0XWm5nBZsCdUEXjOYtwaACC6Js-pyNwKmSLp5SKIk11jZUR5xIIopaQy521y9qJHhGRwzj8DQGsP7wMZ98UFL0E--1c-hh4rTy8PMeWCqRHdwjj_ry_eTe0DJFcxxYQdeL7-0_0CIO4Ayx5WHEpcUOIzBRoN32RsNpDZc-5slDNj9ku004DA
grant_type=authorization_code\
&client_id=s6BhdRkqt\
&code=SplxlOBeZQQYbYS6WxSbIA\
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb\
&code_verifier=bEaL42izcC-o-xBk0K2vuJ6U-y1p9r_wW2dFWIWgjz-
----
The following shows a representation of the DPoP Proof JWT header and claims:
[source,json]
----
{
"typ": "dpop+jwt",
"alg": "RS256",
"jwk": {
"kty": "RSA",
"e": "AQAB",
"n": "3FlqJr5TRskIQIgdE3Dd7D9lboWdcTUT8a-fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRvc5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4_1tfRgG6ii4Uhxh6iI8qNMJQX-fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2kJdJ_ZIV-WW4noDdzpKqHcwmB8FsrumlVY_DNVvUSDIipiq9PbP4H99TXN1o746oRaNa07rq1hoCgMSSy-85SagCoxlmyE-D-of9SsMY8Ol9t0rdzpobBuhyJ_o5dfvjKw"
}
}
----
[source,json]
----
{
"htm": "POST",
"htu": "https://server.example.com/oauth2/token",
"iat": 1746806305,
"jti": "4b2340d2-a91f-40a5-baa9-dd4e5deac867"
}
----
The following code shows an example of how to generate the DPoP Proof JWT:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
RSAKey rsaKey = ...
JWKSource<SecurityContext> jwkSource = (jwkSelector, securityContext) -> jwkSelector
.select(new JWKSet(rsaKey));
NimbusJwtEncoder jwtEncoder = new NimbusJwtEncoder(jwkSource);
JwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)
.type("dpop+jwt")
.jwk(rsaKey.toPublicJWK().toJSONObject())
.build();
JwtClaimsSet claims = JwtClaimsSet.builder()
.issuedAt(Instant.now())
.claim("htm", "POST")
.claim("htu", "https://server.example.com/oauth2/token")
.id(UUID.randomUUID().toString())
.build();
Jwt dPoPProof = jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));
----
======
After the authorization server successfully validates the DPoP proof, the public key from the DPoP proof will be bound (sender-constrained) to the issued access token.
The following access token response shows the `token_type` parameter as `DPoP` to signal to the client that the access token was bound to its DPoP proof public key:
[source,shell]
----
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"access_token": "Kz~8mXK1EalYznwH-LC-1fBAo.4Ljp~zsPE_NeO.gxU",
"token_type": "DPoP",
"expires_in": 2677
}
----
[[dpop-public-key-confirmation]]
== Public Key Confirmation
Resource servers MUST be able to identify whether an access token is DPoP-bound and verify the binding to the public key of the DPoP proof.
The binding is accomplished by associating the public key with the access token in a way that can be accessed by the resource server, such as embedding the public key hash in the access token directly (JWT) or through token introspection.
When an access token is represented as a JWT, the public key hash is contained in the `jkt` claim under the confirmation method (`cnf`) claim.
The following example shows the claims of a JWT access token containing a `cnf` claim with a `jkt` claim, which is the JWK SHA-256 Thumbprint of the DPoP proof public key:
[source,json]
----
{
"sub":"user@example.com",
"iss":"https://server.example.com",
"nbf":1562262611,
"exp":1562266216,
"cnf":
{
"jkt":"CQMknzRoZ5YUi7vS58jck1q8TmZT8wiIiXrCN1Ny4VU"
}
}
----
[[dpop-protected-resource-request]]
== DPoP Protected Resource Request
Requests to DPoP-protected resources MUST include both a DPoP proof and the DPoP-bound access token.
The DPoP proof MUST include the `ath` claim with a valid hash of the access token.
The resource server will calculate the hash of the received access token and verify that it is the same as the `ath` claim in the DPoP proof.
A DPoP-bound access token is sent using the `Authorization` request header with an authentication scheme of `DPoP`.
The following HTTP request shows a protected resource request with a DPoP-bound access token in the `Authorization` header and the DPoP proof in the `DPoP` header:
[source,shell]
----
GET /resource HTTP/1.1
Host: resource.example.com
Authorization: DPoP Kz~8mXK1EalYznwH-LC-1fBAo.4Ljp~zsPE_NeO.gxU
DPoP: eyJraWQiOiJyc2EtandrLWtpZCIsInR5cCI6ImRwb3Arand0IiwiYWxnIjoiUlMyNTYiLCJqd2siOnsia3R5IjoiUlNBIiwiZSI6IkFRQUIiLCJraWQiOiJyc2EtandrLWtpZCIsIm4iOiIzRmxxSnI1VFJza0lRSWdkRTNEZDdEOWxib1dkY1RVVDhhLWZKUjdNQXZRbTdYWE5vWWttM3Y3TVFMMU5ZdER2TDJsOENBbmMwV2RTVElOVTZJUnZjNUtxbzJRNGNzTlg5U0hPbUVmem9ST2pRcWFoRWN2ZTFqQlhsdW9DWGRZdVlweDRfMXRmUmdHNmlpNFVoeGg2aUk4cU5NSlFYLWZMZnFoYmZZZnhCUVZSUHl3QmtBYklQNHgxRUFzYkM2RlNObWtoQ3hpTU5xRWd4YUlwWThDMmtKZEpfWklWLVdXNG5vRGR6cEtxSGN3bUI4RnNydW1sVllfRE5WdlVTRElpcGlxOVBiUDRIOTlUWE4xbzc0Nm9SYU5hMDdycTFob0NnTVNTeS04NVNhZ0NveGxteUUtRC1vZjlTc01ZOE9sOXQwcmR6cG9iQnVoeUpfbzVkZnZqS3cifX0.eyJodG0iOiJHRVQiLCJodHUiOiJodHRwczovL3Jlc291cmNlLmV4YW1wbGUuY29tL3Jlc291cmNlIiwiYXRoIjoiZlVIeU8ycjJaM0RaNTNFc05yV0JiMHhXWG9hTnk1OUlpS0NBcWtzbVFFbyIsImlhdCI6MTc0NjgwNzEzOCwianRpIjoiM2MyZWU5YmItMDNhYy00MGNmLWI4MTItMDBiZmJhMzQxY2VlIn0.oS6NwjURR6wZemh1ZBNiBjycGeXwnkguLtgiKdCjQSEhFQpEJm04bBa0tdfZgWT17Z2mBgddnNQSkROzUGfssg8rBBldZXOAiduF-whtEGZA-pXXWJilXrwH3Glb6hIOMZOVmIH8fmYCDmqn-sE_DmDIsv57Il2-jdZbgeDcrxADO-6E5gsuNf1jvy7qqHq7INrKX6jRuydti_Re35lecvaAWfTyD7s7tQ_-3x_xLxxPwf_eA6z8OWbc58O2PYoUeO2JKLiOIg6UVZOZzxLEWV42WIKjha_kkoykvsf98W2y8pWOEr65u0VPsn5esw2X3I1eFL_A-XkxstZHRaGXJg
----
The following shows a representation of the DPoP Proof JWT header and claims with the `ath` claim:
[source,json]
----
{
"typ": "dpop+jwt",
"alg": "RS256",
"jwk": {
"kty": "RSA",
"e": "AQAB",
"n": "3FlqJr5TRskIQIgdE3Dd7D9lboWdcTUT8a-fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRvc5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4_1tfRgG6ii4Uhxh6iI8qNMJQX-fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2kJdJ_ZIV-WW4noDdzpKqHcwmB8FsrumlVY_DNVvUSDIipiq9PbP4H99TXN1o746oRaNa07rq1hoCgMSSy-85SagCoxlmyE-D-of9SsMY8Ol9t0rdzpobBuhyJ_o5dfvjKw"
}
}
----
[source,json]
----
{
"htm": "GET",
"htu": "https://resource.example.com/resource",
"ath": "fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo",
"iat": 1746807138,
"jti": "3c2ee9bb-03ac-40cf-b812-00bfba341cee"
}
----
The following code shows an example of how to generate the DPoP Proof JWT:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
RSAKey rsaKey = ...
JWKSource<SecurityContext> jwkSource = (jwkSelector, securityContext) -> jwkSelector
.select(new JWKSet(rsaKey));
NimbusJwtEncoder jwtEncoder = new NimbusJwtEncoder(jwkSource);
String accessToken = ...
JwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)
.type("dpop+jwt")
.jwk(rsaKey.toPublicJWK().toJSONObject())
.build();
JwtClaimsSet claims = JwtClaimsSet.builder()
.issuedAt(Instant.now())
.claim("htm", "GET")
.claim("htu", "https://resource.example.com/resource")
.claim("ath", sha256(accessToken))
.id(UUID.randomUUID().toString())
.build();
Jwt dPoPProof = jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));
----
======

View File

@ -7,6 +7,7 @@ Below are the highlights of the release, or you can view https://github.com/spri
== New Features
* Support for automatic context-propagation with Micrometer (https://github.com/spring-projects/spring-security/issues/16665[gh-16665])
* OAuth 2.0 Demonstrating Proof of Possession (DPoP) (https://github.com/spring-projects/spring-security/pull/16574[gh-16574])
== Breaking Changes

View File

@ -23,6 +23,9 @@ import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.util.Assert;
/**
* A context class that holds a DPoP Proof {@link Jwt} and additional parameters
* associated to an Access Token request or a Protected Resource request.
*
* @author Joe Grandja
* @since 6.5
* @see DPoPProofJwtDecoderFactory
@ -44,28 +47,58 @@ public final class DPoPProofContext {
this.accessToken = accessToken;
}
/**
* Returns the DPoP Proof {@link Jwt}.
* @return the DPoP Proof {@link Jwt}
*/
public String getDPoPProof() {
return this.dPoPProof;
}
/**
* Returns the value of the HTTP method of the request to which the DPoP Proof
* {@link Jwt} is attached.
* @return the value of the HTTP method of the request to which the DPoP Proof
* {@link Jwt} is attached
*/
public String getMethod() {
return this.method;
}
/**
* Returns the value of the HTTP target URI of the request to which the DPoP Proof
* {@link Jwt} is attached, without query and fragment parts.
* @return the value of the HTTP target URI of the request to which the DPoP Proof
* {@link Jwt} is attached
*/
public String getTargetUri() {
return this.targetUri;
}
/**
* Returns the access token if the request is a Protected Resource request.
* @param <T> the type of the access token
* @return the access token if the request is a Protected Resource request or
* {@code null}
*/
@SuppressWarnings("unchecked")
@Nullable
public <T extends OAuth2Token> T getAccessToken() {
return (T) this.accessToken;
}
/**
* Returns a new {@link Builder}, initialized with the DPoP Proof {@link Jwt}.
* @param dPoPProof the DPoP Proof {@link Jwt}
* @return the {@link Builder}
*/
public static Builder withDPoPProof(String dPoPProof) {
return new Builder(dPoPProof);
}
/**
* A builder for {@link DPoPProofContext}.
*/
public static final class Builder {
private String dPoPProof;
@ -81,21 +114,45 @@ public final class DPoPProofContext {
this.dPoPProof = dPoPProof;
}
/**
* Sets the value of the HTTP method of the request to which the DPoP Proof
* {@link Jwt} is attached.
* @param method the value of the HTTP method of the request to which the DPoP
* Proof {@link Jwt} is attached
* @return the {@link Builder}
*/
public Builder method(String method) {
this.method = method;
return this;
}
/**
* Sets the value of the HTTP target URI of the request to which the DPoP Proof
* {@link Jwt} is attached, without query and fragment parts.
* @param targetUri the value of the HTTP target URI of the request to which the
* DPoP Proof {@link Jwt} is attached
* @return the {@link Builder}
*/
public Builder targetUri(String targetUri) {
this.targetUri = targetUri;
return this;
}
/**
* Sets the access token if the request is a Protected Resource request.
* @param accessToken the access token if the request is a Protected Resource
* request
* @return the {@link Builder}
*/
public Builder accessToken(OAuth2Token accessToken) {
this.accessToken = accessToken;
return this;
}
/**
* Builds a new {@link DPoPProofContext}.
* @return a {@link DPoPProofContext}
*/
public DPoPProofContext build() {
validate();
return new DPoPProofContext(this.dPoPProof, this.method, this.targetUri, this.accessToken);

View File

@ -48,17 +48,29 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* A {@link JwtDecoderFactory factory} that provides a {@link JwtDecoder} for the
* specified {@link DPoPProofContext} and is used for authenticating a DPoP Proof
* {@link Jwt}.
*
* @author Joe Grandja
* @since 6.5
* @see JwtDecoderFactory
* @see DPoPProofContext
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc9449">RFC 9449
* OAuth 2.0 Demonstrating Proof of Possession (DPoP)</a>
*/
public final class DPoPProofJwtDecoderFactory implements JwtDecoderFactory<DPoPProofContext> {
/**
* The default {@code OAuth2TokenValidator<Jwt>} factory that validates the
* {@code htm}, {@code htu}, {@code jti} and {@code iat} claims of the DPoP Proof
* {@link Jwt}.
*/
public static final Function<DPoPProofContext, OAuth2TokenValidator<Jwt>> DEFAULT_JWT_VALIDATOR_FACTORY = defaultJwtValidatorFactory();
private static final JOSEObjectTypeVerifier<SecurityContext> DPOP_TYPE_VERIFIER = new DefaultJOSEObjectTypeVerifier<>(
new JOSEObjectType("dpop+jwt"));
public static final Function<DPoPProofContext, OAuth2TokenValidator<Jwt>> DEFAULT_JWT_VALIDATOR_FACTORY = defaultJwtValidatorFactory();
private Function<DPoPProofContext, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = DEFAULT_JWT_VALIDATOR_FACTORY;
@Override
@ -69,6 +81,14 @@ public final class DPoPProofJwtDecoderFactory implements JwtDecoderFactory<DPoPP
return jwtDecoder;
}
/**
* Sets the factory that provides an {@link OAuth2TokenValidator} for the specified
* {@link DPoPProofContext} and is used by the {@link JwtDecoder}. The default
* {@code OAuth2TokenValidator<Jwt>} factory is
* {@link #DEFAULT_JWT_VALIDATOR_FACTORY}.
* @param jwtValidatorFactory the factory that provides an
* {@link OAuth2TokenValidator} for the specified {@link DPoPProofContext}
*/
public void setJwtValidatorFactory(Function<DPoPProofContext, OAuth2TokenValidator<Jwt>> jwtValidatorFactory) {
Assert.notNull(jwtValidatorFactory, "jwtValidatorFactory cannot be null");
this.jwtValidatorFactory = jwtValidatorFactory;

View File

@ -50,10 +50,15 @@ import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* An {@link AuthenticationProvider} implementation that is responsible for authenticating
* a DPoP-bound access token for a protected resource request.
*
* @author Joe Grandja
* @since 6.5
* @see DPoPAuthenticationToken
* @see DPoPProofJwtDecoderFactory
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc9449">RFC 9449
* OAuth 2.0 Demonstrating Proof of Possession (DPoP)</a>
*/
public final class DPoPAuthenticationProvider implements AuthenticationProvider {
@ -61,6 +66,11 @@ public final class DPoPAuthenticationProvider implements AuthenticationProvider
private JwtDecoderFactory<DPoPProofContext> dPoPProofVerifierFactory;
/**
* Constructs a {@code DPoPAuthenticationProvider} using the provided parameters.
* @param tokenAuthenticationManager the {@link AuthenticationManager} used to
* authenticate the DPoP-bound access token
*/
public DPoPAuthenticationProvider(AuthenticationManager tokenAuthenticationManager) {
Assert.notNull(tokenAuthenticationManager, "tokenAuthenticationManager cannot be null");
this.tokenAuthenticationManager = tokenAuthenticationManager;
@ -121,6 +131,13 @@ public final class DPoPAuthenticationProvider implements AuthenticationProvider
return DPoPAuthenticationToken.class.isAssignableFrom(authentication);
}
/**
* Sets the {@link JwtDecoderFactory} that provides a {@link JwtDecoder} for the
* specified {@link DPoPProofContext} and is used for authenticating a DPoP Proof
* {@link Jwt}. The default factory is {@link DPoPProofJwtDecoderFactory}.
* @param dPoPProofVerifierFactory the {@link JwtDecoderFactory} that provides a
* {@link JwtDecoder} for the specified {@link DPoPProofContext}
*/
public void setDPoPProofVerifierFactory(JwtDecoderFactory<DPoPProofContext> dPoPProofVerifierFactory) {
Assert.notNull(dPoPProofVerifierFactory, "dPoPProofVerifierFactory cannot be null");
this.dPoPProofVerifierFactory = dPoPProofVerifierFactory;

View File

@ -20,9 +20,14 @@ import java.io.Serial;
import java.util.Collections;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.util.Assert;
/**
* An {@link Authentication} representing a protected resource request with a DPoP-bound
* access token.
*
* @author Joe Grandja
* @since 6.5
* @see DPoPAuthenticationProvider
@ -40,6 +45,14 @@ public class DPoPAuthenticationToken extends AbstractAuthenticationToken {
private final String resourceUri;
/**
* Constructs a {@code DPoPAuthenticationToken} using the provided parameters.
* @param accessToken the DPoP-bound access token
* @param dPoPProof the DPoP Proof {@link Jwt}
* @param method the value of the HTTP method of the request
* @param resourceUri the value of the HTTP resource URI of the request, without query
* and fragment parts
*/
public DPoPAuthenticationToken(String accessToken, String dPoPProof, String method, String resourceUri) {
super(Collections.emptyList());
Assert.hasText(accessToken, "accessToken cannot be empty");
@ -62,18 +75,35 @@ public class DPoPAuthenticationToken extends AbstractAuthenticationToken {
return getAccessToken();
}
/**
* Returns the DPoP-bound access token.
* @return the DPoP-bound access token
*/
public String getAccessToken() {
return this.accessToken;
}
/**
* Returns the DPoP Proof {@link Jwt}.
* @return the DPoP Proof {@link Jwt}
*/
public String getDPoPProof() {
return this.dPoPProof;
}
/**
* Returns the value of the HTTP method of the request.
* @return the value of the HTTP method of the request
*/
public String getMethod() {
return this.method;
}
/**
* Returns the value of the HTTP resource URI of the request, without query and
* fragment parts.
* @return the value of the HTTP resource URI of the request
*/
public String getResourceUri() {
return this.resourceUri;
}