16
16
17
17
package org .springframework .security .oauth2 .client ;
18
18
19
- import java .time .Clock ;
20
- import java .time .Duration ;
21
- import java .time .Instant ;
22
-
23
19
import org .springframework .lang .Nullable ;
24
20
import org .springframework .security .oauth2 .client .endpoint .DefaultJwtBearerTokenResponseClient ;
21
+ import org .springframework .security .oauth2 .client .endpoint .JwtBearerGrantRequest ;
25
22
import org .springframework .security .oauth2 .client .endpoint .OAuth2AccessTokenResponseClient ;
26
- import org .springframework .security .oauth2 .client .endpoint .OAuth2JwtBearerGrantRequest ;
27
23
import org .springframework .security .oauth2 .client .registration .ClientRegistration ;
28
- import org .springframework .security .oauth2 .core .AbstractOAuth2Token ;
24
+ import org .springframework .security .oauth2 .core .AuthorizationGrantType ;
25
+ import org .springframework .security .oauth2 .core .OAuth2AuthorizationException ;
29
26
import org .springframework .security .oauth2 .core .endpoint .OAuth2AccessTokenResponse ;
30
27
import org .springframework .security .oauth2 .jwt .Jwt ;
31
28
import org .springframework .util .Assert ;
32
29
33
30
/**
34
31
* An implementation of an {@link OAuth2AuthorizedClientProvider} for the
35
- * {@link OAuth2JwtBearerGrantRequest#JWT_BEARER_GRANT_TYPE jwt-bearer} grant.
32
+ * {@link AuthorizationGrantType#JWT_BEARER jwt-bearer} grant.
36
33
*
37
34
* @author Joe Grandja
38
35
* @since 5.5
41
38
*/
42
39
public final class JwtBearerOAuth2AuthorizedClientProvider implements OAuth2AuthorizedClientProvider {
43
40
44
- private OAuth2AccessTokenResponseClient <OAuth2JwtBearerGrantRequest > accessTokenResponseClient = new DefaultJwtBearerTokenResponseClient ();
45
-
46
- private Duration clockSkew = Duration .ofSeconds (60 );
47
-
48
- private Clock clock = Clock .systemUTC ();
41
+ private OAuth2AccessTokenResponseClient <JwtBearerGrantRequest > accessTokenResponseClient = new DefaultJwtBearerTokenResponseClient ();
49
42
50
43
/**
51
44
* Attempt to authorize the {@link OAuth2AuthorizationContext#getClientRegistration()
52
45
* client} in the provided {@code context}. Returns {@code null} if authorization is
53
46
* not supported, e.g. the client's
54
47
* {@link ClientRegistration#getAuthorizationGrantType() authorization grant type} is
55
- * not {@link OAuth2JwtBearerGrantRequest#JWT_BEARER_GRANT_TYPE jwt-bearer}.
48
+ * not {@link AuthorizationGrantType#JWT_BEARER jwt-bearer}.
56
49
* @param context the context that holds authorization-specific state for the client
57
50
* @return the {@link OAuth2AuthorizedClient} or {@code null} if authorization is not
58
51
* supported
@@ -61,34 +54,45 @@ public final class JwtBearerOAuth2AuthorizedClientProvider implements OAuth2Auth
61
54
@ Nullable
62
55
public OAuth2AuthorizedClient authorize (OAuth2AuthorizationContext context ) {
63
56
Assert .notNull (context , "context cannot be null" );
64
-
65
57
ClientRegistration clientRegistration = context .getClientRegistration ();
66
- if (!OAuth2JwtBearerGrantRequest . JWT_BEARER_GRANT_TYPE .equals (clientRegistration .getAuthorizationGrantType ())) {
58
+ if (!AuthorizationGrantType . JWT_BEARER .equals (clientRegistration .getAuthorizationGrantType ())) {
67
59
return null ;
68
60
}
69
-
70
- Jwt jwt = context . getAttribute ( OAuth2AuthorizationContext . JWT_ATTRIBUTE_NAME );
71
- if ( jwt == null ) {
61
+ OAuth2AuthorizedClient authorizedClient = context . getAuthorizedClient ();
62
+ if ( authorizedClient != null ) {
63
+ // Client is already authorized
72
64
return null ;
73
65
}
74
-
75
- OAuth2AuthorizedClient authorizedClient = context .getAuthorizedClient ();
76
- if (authorizedClient != null && !hasTokenExpired (authorizedClient .getAccessToken ())) {
77
- // If client is already authorized but access token is NOT expired than no
78
- // need for re-authorization
66
+ if (!(context .getPrincipal ().getPrincipal () instanceof Jwt )) {
79
67
return null ;
80
68
}
81
-
82
- OAuth2JwtBearerGrantRequest jwtBearerGrantRequest = new OAuth2JwtBearerGrantRequest (clientRegistration , jwt );
83
- OAuth2AccessTokenResponse tokenResponse = this .accessTokenResponseClient
84
- .getTokenResponse (jwtBearerGrantRequest );
85
-
69
+ Jwt jwt = (Jwt ) context .getPrincipal ().getPrincipal ();
70
+ // As per spec, in section 4.1 Using Assertions as Authorization Grants
71
+ // https://tools.ietf.org/html/rfc7521#section-4.1
72
+ //
73
+ // An assertion used in this context is generally a short-lived
74
+ // representation of the authorization grant, and authorization servers
75
+ // SHOULD NOT issue access tokens with a lifetime that exceeds the
76
+ // validity period of the assertion by a significant period. In
77
+ // practice, that will usually mean that refresh tokens are not issued
78
+ // in response to assertion grant requests, and access tokens will be
79
+ // issued with a reasonably short lifetime. Clients can refresh an
80
+ // expired access token by requesting a new one using the same
81
+ // assertion, if it is still valid, or with a new assertion.
82
+ JwtBearerGrantRequest jwtBearerGrantRequest = new JwtBearerGrantRequest (clientRegistration , jwt );
83
+ OAuth2AccessTokenResponse tokenResponse = getTokenResponse (clientRegistration , jwtBearerGrantRequest );
86
84
return new OAuth2AuthorizedClient (clientRegistration , context .getPrincipal ().getName (),
87
85
tokenResponse .getAccessToken ());
88
86
}
89
87
90
- private boolean hasTokenExpired (AbstractOAuth2Token token ) {
91
- return this .clock .instant ().isAfter (token .getExpiresAt ().minus (this .clockSkew ));
88
+ private OAuth2AccessTokenResponse getTokenResponse (ClientRegistration clientRegistration ,
89
+ JwtBearerGrantRequest jwtBearerGrantRequest ) {
90
+ try {
91
+ return this .accessTokenResponseClient .getTokenResponse (jwtBearerGrantRequest );
92
+ }
93
+ catch (OAuth2AuthorizationException ex ) {
94
+ throw new ClientAuthorizationException (ex .getError (), clientRegistration .getRegistrationId (), ex );
95
+ }
92
96
}
93
97
94
98
/**
@@ -98,32 +102,9 @@ private boolean hasTokenExpired(AbstractOAuth2Token token) {
98
102
* credential at the Token Endpoint for the {@code jwt-bearer} grant
99
103
*/
100
104
public void setAccessTokenResponseClient (
101
- OAuth2AccessTokenResponseClient <OAuth2JwtBearerGrantRequest > accessTokenResponseClient ) {
105
+ OAuth2AccessTokenResponseClient <JwtBearerGrantRequest > accessTokenResponseClient ) {
102
106
Assert .notNull (accessTokenResponseClient , "accessTokenResponseClient cannot be null" );
103
107
this .accessTokenResponseClient = accessTokenResponseClient ;
104
108
}
105
109
106
- /**
107
- * Sets the maximum acceptable clock skew, which is used when checking the
108
- * {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is
109
- * 60 seconds. An access token is considered expired if it's before
110
- * {@code Instant.now(this.clock) - clockSkew}.
111
- * @param clockSkew the maximum acceptable clock skew
112
- */
113
- public void setClockSkew (Duration clockSkew ) {
114
- Assert .notNull (clockSkew , "clockSkew cannot be null" );
115
- Assert .isTrue (clockSkew .getSeconds () >= 0 , "clockSkew must be >= 0" );
116
- this .clockSkew = clockSkew ;
117
- }
118
-
119
- /**
120
- * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access
121
- * token expiry.
122
- * @param clock the clock
123
- */
124
- public void setClock (Clock clock ) {
125
- Assert .notNull (clock , "clock cannot be null" );
126
- this .clock = clock ;
127
- }
128
-
129
110
}
0 commit comments