@@ -96,79 +96,82 @@ protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
96
96
}
97
97
}
98
98
99
- if ( _configuration == null && Options . ConfigurationManager != null )
100
- {
101
- _configuration = await Options . ConfigurationManager . GetConfigurationAsync ( Context . RequestAborted ) ;
102
- }
103
-
104
- var validationParameters = Options . TokenValidationParameters . Clone ( ) ;
105
- if ( _configuration != null )
106
- {
107
- var issuers = new [ ] { _configuration . Issuer } ;
108
- validationParameters . ValidIssuers = validationParameters . ValidIssuers ? . Concat ( issuers ) ?? issuers ;
109
-
110
- validationParameters . IssuerSigningKeys = validationParameters . IssuerSigningKeys ? . Concat ( _configuration . SigningKeys )
111
- ?? _configuration . SigningKeys ;
112
- }
113
-
99
+ var tvp = await SetupTokenValidationParameters ( ) ;
114
100
List < Exception > ? validationFailures = null ;
115
101
SecurityToken ? validatedToken = null ;
116
- foreach ( var validator in Options . SecurityTokenValidators )
102
+ ClaimsPrincipal ? principal = null ;
103
+
104
+ if ( Options . UseTokenHandlers )
117
105
{
118
- if ( validator . CanReadToken ( token ) )
106
+ foreach ( var tokenHandler in Options . TokenHandlers )
119
107
{
120
- ClaimsPrincipal principal ;
121
- try
108
+ var tokenValidationResult = await tokenHandler . ValidateTokenAsync ( token , tvp ) ;
109
+ if ( tokenValidationResult . IsValid )
122
110
{
123
- principal = validator . ValidateToken ( token , validationParameters , out validatedToken ) ;
111
+ principal = new ClaimsPrincipal ( tokenValidationResult . ClaimsIdentity ) ;
112
+ validatedToken = tokenValidationResult . SecurityToken ;
113
+ break ;
124
114
}
125
- catch ( Exception ex )
115
+ else
126
116
{
127
- Logger . TokenValidationFailed ( ex ) ;
128
-
129
- // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event.
130
- if ( Options . RefreshOnIssuerKeyNotFound && Options . ConfigurationManager != null
131
- && ex is SecurityTokenSignatureKeyNotFoundException )
117
+ // TODO - what to log if there is no exception.
118
+ if ( tokenValidationResult . Exception != null )
132
119
{
133
- Options . ConfigurationManager . RequestRefresh ( ) ;
120
+ validationFailures ??= new List < Exception > ( 1 ) ;
121
+ RecordTokenValidationError ( tokenValidationResult . Exception , validationFailures ) ;
134
122
}
135
-
136
- if ( validationFailures == null )
123
+ }
124
+ }
125
+ }
126
+ else
127
+ {
128
+ foreach ( var validator in Options . SecurityTokenValidators )
129
+ {
130
+ if ( validator . CanReadToken ( token ) )
131
+ {
132
+ try
133
+ {
134
+ principal = validator . ValidateToken ( token , tvp , out validatedToken ) ;
135
+ }
136
+ catch ( Exception ex )
137
137
{
138
- validationFailures = new List < Exception > ( 1 ) ;
138
+ validationFailures ??= new List < Exception > ( 1 ) ;
139
+ RecordTokenValidationError ( ex , validationFailures ) ;
140
+ continue ;
139
141
}
140
- validationFailures . Add ( ex ) ;
141
- continue ;
142
142
}
143
+ }
144
+ }
143
145
144
- Logger . TokenValidationSucceeded ( ) ;
146
+ if ( principal != null && validatedToken != null )
147
+ {
148
+ Logger . TokenValidationSucceeded ( ) ;
145
149
146
- var tokenValidatedContext = new TokenValidatedContext ( Context , Scheme , Options )
147
- {
148
- Principal = principal ,
149
- SecurityToken = validatedToken
150
- } ;
150
+ var tokenValidatedContext = new TokenValidatedContext ( Context , Scheme , Options )
151
+ {
152
+ Principal = principal
153
+ } ;
151
154
152
- tokenValidatedContext . Properties . ExpiresUtc = GetSafeDateTime ( validatedToken . ValidTo ) ;
153
- tokenValidatedContext . Properties . IssuedUtc = GetSafeDateTime ( validatedToken . ValidFrom ) ;
155
+ tokenValidatedContext . SecurityToken = validatedToken ;
156
+ tokenValidatedContext . Properties . ExpiresUtc = GetSafeDateTime ( validatedToken . ValidTo ) ;
157
+ tokenValidatedContext . Properties . IssuedUtc = GetSafeDateTime ( validatedToken . ValidFrom ) ;
154
158
155
- await Events . TokenValidated ( tokenValidatedContext ) ;
156
- if ( tokenValidatedContext . Result != null )
157
- {
158
- return tokenValidatedContext . Result ;
159
- }
159
+ await Events . TokenValidated ( tokenValidatedContext ) ;
160
+ if ( tokenValidatedContext . Result != null )
161
+ {
162
+ return tokenValidatedContext . Result ;
163
+ }
160
164
161
- if ( Options . SaveToken )
165
+ if ( Options . SaveToken )
166
+ {
167
+ tokenValidatedContext . Properties . StoreTokens ( new [ ]
162
168
{
163
- tokenValidatedContext . Properties . StoreTokens ( new [ ]
164
- {
165
- new AuthenticationToken { Name = "access_token" , Value = token }
166
- } ) ;
167
- }
168
-
169
- tokenValidatedContext . Success ( ) ;
170
- return tokenValidatedContext . Result ! ;
169
+ new AuthenticationToken { Name = "access_token" , Value = token }
170
+ } ) ;
171
171
}
172
+
173
+ tokenValidatedContext . Success ( ) ;
174
+ return tokenValidatedContext . Result ! ;
172
175
}
173
176
174
177
if ( validationFailures != null )
@@ -187,6 +190,11 @@ protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
187
190
return AuthenticateResult . Fail ( authenticationFailedContext . Exception ) ;
188
191
}
189
192
193
+ if ( Options . UseTokenHandlers )
194
+ {
195
+ return AuthenticateResults . TokenHandlerUnableToValidate ;
196
+ }
197
+
190
198
return AuthenticateResults . ValidatorNotFound ;
191
199
}
192
200
catch ( Exception ex )
@@ -208,6 +216,62 @@ protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
208
216
}
209
217
}
210
218
219
+ // TODO - this method could be shared across OIDC, WsFed, JwtBearer to ensure consistency
220
+ private void RecordTokenValidationError ( Exception exception , List < Exception > exceptions )
221
+ {
222
+ if ( exception != null )
223
+ {
224
+ Logger . TokenValidationFailed ( exception ) ;
225
+ exceptions . Add ( exception ) ;
226
+ }
227
+
228
+ // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event.
229
+ // Refreshing on SecurityTokenSignatureKeyNotFound may be redundant if Last-Known-Good is enabled, it won't do much harm, most likely will be a nop.
230
+
231
+ if ( Options . RefreshOnIssuerKeyNotFound && Options . ConfigurationManager != null
232
+ && exception is SecurityTokenSignatureKeyNotFoundException )
233
+ {
234
+ Options . ConfigurationManager . RequestRefresh ( ) ;
235
+ }
236
+ }
237
+
238
+ // TODO - this method could be shared across OIDC, WsFed, JwtBearer to ensure consistency
239
+ private async Task < TokenValidationParameters > SetupTokenValidationParameters ( )
240
+ {
241
+ // Clone to avoid cross request race conditions for updated configurations.
242
+ var tokenValidationParameters = Options . TokenValidationParameters . Clone ( ) ;
243
+
244
+ if ( Options . ConfigurationManager is BaseConfigurationManager baseConfigurationManager )
245
+ {
246
+ // TODO - we need to add a parameter to TokenValidationParameters for the CancellationToken.
247
+ tokenValidationParameters . ConfigurationManager = baseConfigurationManager ;
248
+ }
249
+ else
250
+ {
251
+ if ( Options . ConfigurationManager != null )
252
+ {
253
+
254
+ // TODO - understand existing code:
255
+ // ConfigurationManager has a refresh interval of 12 hours by default and will only make an https call every 12 hours.
256
+ // Not sure where it would ever get updated before
257
+
258
+ // if (_configuration == null && Options.ConfigurationManager != null)
259
+ // {
260
+ // _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
261
+ // }
262
+
263
+ // it is safe to just call GetConfigurationAsync
264
+ _configuration = await Options . ConfigurationManager . GetConfigurationAsync ( Context . RequestAborted ) ;
265
+
266
+ var issuers = new [ ] { _configuration . Issuer } ;
267
+ tokenValidationParameters . ValidIssuers = ( tokenValidationParameters . ValidIssuers == null ? issuers : tokenValidationParameters . ValidIssuers . Concat ( issuers ) ) ;
268
+ tokenValidationParameters . IssuerSigningKeys = ( tokenValidationParameters . IssuerSigningKeys == null ? _configuration . SigningKeys : tokenValidationParameters . IssuerSigningKeys . Concat ( _configuration . SigningKeys ) ) ;
269
+ }
270
+ }
271
+
272
+ return tokenValidationParameters ;
273
+ }
274
+
211
275
private static DateTime ? GetSafeDateTime ( DateTime dateTime )
212
276
{
213
277
// Assigning DateTime.MinValue or default(DateTime) to a DateTimeOffset when in a UTC+X timezone will throw
0 commit comments