2
2
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3
3
4
4
using System ;
5
+ using System . Linq ;
5
6
using System . Security . Claims ;
6
7
using System . Threading . Tasks ;
7
8
using Microsoft . AspNetCore . Http ;
@@ -62,7 +63,7 @@ public virtual async Task<AuthenticateResult> AuthenticateAsync(HttpContext cont
62
63
var handler = await Handlers . GetHandlerAsync ( context , scheme ) ;
63
64
if ( handler == null )
64
65
{
65
- throw new InvalidOperationException ( $ "No authentication handler is configured to authenticate for the scheme: { scheme } " ) ;
66
+ throw await CreateMissingHandlerException ( scheme ) ;
66
67
}
67
68
68
69
var result = await handler . AuthenticateAsync ( ) ;
@@ -96,7 +97,7 @@ public virtual async Task ChallengeAsync(HttpContext context, string scheme, Aut
96
97
var handler = await Handlers . GetHandlerAsync ( context , scheme ) ;
97
98
if ( handler == null )
98
99
{
99
- throw new InvalidOperationException ( $ "No authentication handler is configured to handle the scheme: { scheme } " ) ;
100
+ throw await CreateMissingHandlerException ( scheme ) ;
100
101
}
101
102
102
103
await handler . ChallengeAsync ( properties ) ;
@@ -124,7 +125,7 @@ public virtual async Task ForbidAsync(HttpContext context, string scheme, Authen
124
125
var handler = await Handlers . GetHandlerAsync ( context , scheme ) ;
125
126
if ( handler == null )
126
127
{
127
- throw new InvalidOperationException ( $ "No authentication handler is configured to handle the scheme: { scheme } " ) ;
128
+ throw await CreateMissingHandlerException ( scheme ) ;
128
129
}
129
130
130
131
await handler . ForbidAsync ( properties ) ;
@@ -155,13 +156,19 @@ public virtual async Task SignInAsync(HttpContext context, string scheme, Claims
155
156
}
156
157
}
157
158
158
- var handler = await Handlers . GetHandlerAsync ( context , scheme ) as IAuthenticationSignInHandler ;
159
+ var handler = await Handlers . GetHandlerAsync ( context , scheme ) ;
159
160
if ( handler == null )
160
161
{
161
- throw new InvalidOperationException ( $ "No IAuthenticationSignInHandler is configured to handle sign in for the scheme: { scheme } ") ;
162
+ throw await CreateMissingSignInHandlerException ( scheme ) ;
163
+ }
164
+
165
+ var signInHandler = handler as IAuthenticationSignInHandler ;
166
+ if ( signInHandler == null )
167
+ {
168
+ throw await CreateMismatchedSignInHandlerException ( scheme , handler ) ;
162
169
}
163
170
164
- await handler . SignInAsync ( principal , properties ) ;
171
+ await signInHandler . SignInAsync ( principal , properties ) ;
165
172
}
166
173
167
174
/// <summary>
@@ -183,13 +190,114 @@ public virtual async Task SignOutAsync(HttpContext context, string scheme, Authe
183
190
}
184
191
}
185
192
186
- var handler = await Handlers . GetHandlerAsync ( context , scheme ) as IAuthenticationSignOutHandler ;
193
+ var handler = await Handlers . GetHandlerAsync ( context , scheme ) ;
187
194
if ( handler == null )
188
195
{
189
- throw new InvalidOperationException ( $ "No IAuthenticationSignOutHandler is configured to handle sign out for the scheme: { scheme } ") ;
196
+ throw await CreateMissingSignOutHandlerException ( scheme ) ;
197
+ }
198
+
199
+ var signOutHandler = handler as IAuthenticationSignOutHandler ;
200
+ if ( signOutHandler == null )
201
+ {
202
+ throw await CreateMismatchedSignOutHandlerException ( scheme , handler ) ;
203
+ }
204
+
205
+ await signOutHandler . SignOutAsync ( properties ) ;
206
+ }
207
+
208
+ private async Task < Exception > CreateMissingHandlerException ( string scheme )
209
+ {
210
+ var schemes = string . Join ( ", " , ( await Schemes . GetAllSchemesAsync ( ) ) . Select ( sch => sch . Name ) ) ;
211
+
212
+ var footer = $ " Did you forget to call AddAuthentication().Add[SomeAuthHandler](\" { scheme } \" ,...)?";
213
+
214
+ if ( string . IsNullOrEmpty ( schemes ) )
215
+ {
216
+ return new InvalidOperationException (
217
+ $ "No authentication handlers are registered." + footer ) ;
218
+ }
219
+
220
+ return new InvalidOperationException (
221
+ $ "No authentication handler is registered for the scheme '{ scheme } '. The registered schemes are: { schemes } ." + footer ) ;
222
+ }
223
+
224
+ private async Task < string > GetAllSignInSchemeNames ( )
225
+ {
226
+ return string . Join ( ", " , ( await Schemes . GetAllSchemesAsync ( ) )
227
+ . Where ( sch => typeof ( IAuthenticationSignInHandler ) . IsAssignableFrom ( sch . HandlerType ) )
228
+ . Select ( sch => sch . Name ) ) ;
229
+ }
230
+
231
+ private async Task < Exception > CreateMissingSignInHandlerException ( string scheme )
232
+ {
233
+ var schemes = await GetAllSignInSchemeNames ( ) ;
234
+
235
+ // CookieAuth is the only implementation of sign-in.
236
+ var footer = $ " Did you forget to call AddAuthentication().AddCookies(\" { scheme } \" ,...)?";
237
+
238
+ if ( string . IsNullOrEmpty ( schemes ) )
239
+ {
240
+ return new InvalidOperationException (
241
+ $ "No sign-in authentication handlers are registered." + footer ) ;
242
+ }
243
+
244
+ return new InvalidOperationException (
245
+ $ "No sign-in authentication handler is registered for the scheme '{ scheme } '. The registered sign-in schemes are: { schemes } ." + footer ) ;
246
+ }
247
+
248
+ private async Task < Exception > CreateMismatchedSignInHandlerException ( string scheme , IAuthenticationHandler handler )
249
+ {
250
+ var schemes = await GetAllSignInSchemeNames ( ) ;
251
+
252
+ var mismatchError = $ "The authentication handler registered for scheme '{ scheme } ' is '{ handler . GetType ( ) . Name } ' which cannot be used for SignInAsync. ";
253
+
254
+ if ( string . IsNullOrEmpty ( schemes ) )
255
+ {
256
+ // CookieAuth is the only implementation of sign-in.
257
+ return new InvalidOperationException ( mismatchError
258
+ + $ "Did you intended to call AddAuthentication().AddCookies(\" Cookies\" ) and SignInAsync(\" Cookies\" ,...)?") ;
259
+ }
260
+
261
+ return new InvalidOperationException ( mismatchError + $ "The registered sign-in schemes are: { schemes } .") ;
262
+ }
263
+
264
+ private async Task < string > GetAllSignOutSchemeNames ( )
265
+ {
266
+ return string . Join ( ", " , ( await Schemes . GetAllSchemesAsync ( ) )
267
+ . Where ( sch => typeof ( IAuthenticationSignOutHandler ) . IsAssignableFrom ( sch . HandlerType ) )
268
+ . Select ( sch => sch . Name ) ) ;
269
+ }
270
+
271
+ private async Task < Exception > CreateMissingSignOutHandlerException ( string scheme )
272
+ {
273
+ var schemes = await GetAllSignOutSchemeNames ( ) ;
274
+
275
+ var footer = $ " Did you forget to call AddAuthentication().AddCookies(\" { scheme } \" ,...)?";
276
+
277
+ if ( string . IsNullOrEmpty ( schemes ) )
278
+ {
279
+ // CookieAuth is the most common implementation of sign-out, but OpenIdConnect and WsFederation also support it.
280
+ return new InvalidOperationException ( $ "No sign-out authentication handlers are registered." + footer ) ;
281
+ }
282
+
283
+ return new InvalidOperationException (
284
+ $ "No sign-out authentication handler is registered for the scheme '{ scheme } '. The registered sign-out schemes are: { schemes } ." + footer ) ;
285
+ }
286
+
287
+ private async Task < Exception > CreateMismatchedSignOutHandlerException ( string scheme , IAuthenticationHandler handler )
288
+ {
289
+ var schemes = await GetAllSignOutSchemeNames ( ) ;
290
+
291
+ var mismatchError = $ "The authentication handler registered for scheme '{ scheme } ' is '{ handler . GetType ( ) . Name } ' which cannot be used for { nameof ( SignOutAsync ) } . ";
292
+
293
+ if ( string . IsNullOrEmpty ( schemes ) )
294
+ {
295
+ // CookieAuth is the most common implementation of sign-out, but OpenIdConnect and WsFederation also support it.
296
+ return new InvalidOperationException ( mismatchError
297
+ + $ "Did you intended to call AddAuthentication().AddCookies(\" Cookies\" ) and { nameof ( SignOutAsync ) } (\" Cookies\" ,...)?") ;
190
298
}
191
299
192
- await handler . SignOutAsync ( properties ) ;
300
+ return new InvalidOperationException ( mismatchError + $ "The registered sign-out schemes are: { schemes } ." ) ;
193
301
}
194
302
}
195
303
}
0 commit comments