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,44 @@ 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 ) {
72
63
return null ;
73
64
}
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
65
+ if (!(context .getPrincipal ().getPrincipal () instanceof Jwt )) {
79
66
return null ;
80
67
}
81
-
82
- OAuth2JwtBearerGrantRequest jwtBearerGrantRequest = new OAuth2JwtBearerGrantRequest (clientRegistration , jwt );
83
- OAuth2AccessTokenResponse tokenResponse = this .accessTokenResponseClient
84
- .getTokenResponse (jwtBearerGrantRequest );
85
-
68
+ Jwt jwt = (Jwt ) context .getPrincipal ().getPrincipal ();
69
+ // As per spec, in section 4.1 Using Assertions as Authorization Grants
70
+ // https://tools.ietf.org/html/rfc7521#section-4.1
71
+ //
72
+ // An assertion used in this context is generally a short-lived
73
+ // representation of the authorization grant, and authorization servers
74
+ // SHOULD NOT issue access tokens with a lifetime that exceeds the
75
+ // validity period of the assertion by a significant period. In
76
+ // practice, that will usually mean that refresh tokens are not issued
77
+ // in response to assertion grant requests, and access tokens will be
78
+ // issued with a reasonably short lifetime. Clients can refresh an
79
+ // expired access token by requesting a new one using the same
80
+ // assertion, if it is still valid, or with a new assertion.
81
+ JwtBearerGrantRequest jwtBearerGrantRequest = new JwtBearerGrantRequest (clientRegistration , jwt );
82
+ OAuth2AccessTokenResponse tokenResponse = getTokenResponse (clientRegistration , jwtBearerGrantRequest );
86
83
return new OAuth2AuthorizedClient (clientRegistration , context .getPrincipal ().getName (),
87
84
tokenResponse .getAccessToken ());
88
85
}
89
86
90
- private boolean hasTokenExpired (AbstractOAuth2Token token ) {
91
- return this .clock .instant ().isAfter (token .getExpiresAt ().minus (this .clockSkew ));
87
+ private OAuth2AccessTokenResponse getTokenResponse (ClientRegistration clientRegistration ,
88
+ JwtBearerGrantRequest jwtBearerGrantRequest ) {
89
+ try {
90
+ return this .accessTokenResponseClient .getTokenResponse (jwtBearerGrantRequest );
91
+ }
92
+ catch (OAuth2AuthorizationException ex ) {
93
+ throw new ClientAuthorizationException (ex .getError (), clientRegistration .getRegistrationId (), ex );
94
+ }
92
95
}
93
96
94
97
/**
@@ -98,32 +101,9 @@ private boolean hasTokenExpired(AbstractOAuth2Token token) {
98
101
* credential at the Token Endpoint for the {@code jwt-bearer} grant
99
102
*/
100
103
public void setAccessTokenResponseClient (
101
- OAuth2AccessTokenResponseClient <OAuth2JwtBearerGrantRequest > accessTokenResponseClient ) {
104
+ OAuth2AccessTokenResponseClient <JwtBearerGrantRequest > accessTokenResponseClient ) {
102
105
Assert .notNull (accessTokenResponseClient , "accessTokenResponseClient cannot be null" );
103
106
this .accessTokenResponseClient = accessTokenResponseClient ;
104
107
}
105
108
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
109
}
0 commit comments