11using Microsoft . AspNetCore . Authentication . JwtBearer ;
2- using Microsoft . Azure . Functions . Worker . Http ;
32using Microsoft . Azure . Functions . Worker . Middleware ;
43using Microsoft . Extensions . DependencyInjection ;
54using Microsoft . Extensions . Logging ;
65using Microsoft . Extensions . Options ;
76using Microsoft . IdentityModel . Protocols . OpenIdConnect ;
87using Microsoft . IdentityModel . Tokens ;
9- using System ;
10- using System . Collections . Generic ;
11- using System . Linq ;
12- using System . Net . Http . Headers ;
138using System . Net ;
9+ using System . Net . Http . Headers ;
1410using System . Reflection ;
15- using System . Runtime . Loader ;
1611using System . Security . Claims ;
17- using System . Text . RegularExpressions ;
18- using System . Threading ;
19- using System . Threading . Tasks ;
20- using Azure . Core ;
2112
22- namespace Microsoft . Azure . Functions . Worker . Http
13+ namespace Microsoft . Azure . Functions . Worker . Http ;
14+
15+ public class JwtBearerAuthenticationMiddleware : IFunctionsWorkerMiddleware
2316{
24- public class JwtBearerAuthenticationMiddleware : IFunctionsWorkerMiddleware
17+ private OpenIdConnectConfiguration ? _configuration ;
18+
19+ public async Task Invoke ( FunctionContext context , FunctionExecutionDelegate next )
2520 {
26- private OpenIdConnectConfiguration ? _configuration ;
21+ var authentication = GetAuthenticationAttribute ( context ) ;
2722
28- public async Task Invoke ( FunctionContext context , FunctionExecutionDelegate next )
23+ if ( authentication is not null )
2924 {
30- var authentication = GetAuthenticationAttribute ( context ) ;
31-
32- if ( authentication is not null )
25+ var request = context . GetHttpRequestData ( ) ;
26+
27+ if ( request is not null )
3328 {
34- var request = context . GetHttpRequestData ( ) ;
29+ var principal = await AuthenticateAsync ( context , request ) . ConfigureAwait ( false ) ;
3530
36- if ( request is not null )
31+ if ( principal is null )
32+ {
33+ context . SetHttpResponseData ( request . CreateResponse ( HttpStatusCode . Unauthorized ) ) ;
34+ return ;
35+ }
36+ else
3737 {
38- var principal = await AuthenticateAsync ( context , request ) . ConfigureAwait ( false ) ;
38+ context . Items [ nameof ( ClaimsPrincipal ) ] = principal ;
3939
40- if ( principal is null )
40+ if ( ! authentication . IsAuthorized ( principal ) )
4141 {
42- context . SetHttpResponseData ( request . CreateResponse ( HttpStatusCode . Unauthorized ) ) ;
42+ context . SetHttpResponseData ( request . CreateResponse ( HttpStatusCode . Forbidden ) ) ;
4343 return ;
4444 }
45- else
46- {
47- context . Items [ nameof ( ClaimsPrincipal ) ] = principal ;
48-
49- if ( ! authentication . IsAuthorized ( principal ) )
50- {
51- context . SetHttpResponseData ( request . CreateResponse ( HttpStatusCode . Forbidden ) ) ;
52- return ;
53- }
54- }
5545 }
5646 }
57-
58- await next ( context ) . ConfigureAwait ( false ) ;
5947 }
6048
61- private JwtBearerAuthenticationAttribute ? GetAuthenticationAttribute ( FunctionContext context )
62- => context . FunctionDefinition . GetMethod ( ) . GetCustomAttribute < JwtBearerAuthenticationAttribute > ( ) ;
49+ await next ( context ) . ConfigureAwait ( false ) ;
50+ }
51+
52+ private JwtBearerAuthenticationAttribute ? GetAuthenticationAttribute ( FunctionContext context )
53+ => context . FunctionDefinition . GetMethod ( ) . GetCustomAttribute < JwtBearerAuthenticationAttribute > ( ) ;
6354
64- private async Task < ClaimsPrincipal ? > AuthenticateAsync ( FunctionContext context , HttpRequestData request )
55+ private async Task < ClaimsPrincipal ? > AuthenticateAsync ( FunctionContext context , HttpRequestData request )
56+ {
57+ var logger = context . GetLogger < JwtBearerAuthenticationMiddleware > ( ) ;
58+
59+ try
6560 {
66- var logger = context . GetLogger < JwtBearerAuthenticationMiddleware > ( ) ;
61+ var options = context . InstanceServices . GetRequiredService < IOptionsMonitor < JwtBearerOptions > > ( ) . CurrentValue ;
6762
68- try
63+ if ( _configuration is null && options . ConfigurationManager != null )
6964 {
70- var options = context . InstanceServices . GetRequiredService < IOptionsMonitor < JwtBearerOptions > > ( ) . CurrentValue ;
65+ _configuration = await options . ConfigurationManager . GetConfigurationAsync ( CancellationToken . None ) . ConfigureAwait ( false ) ;
66+ }
7167
72- if ( _configuration is null && options . ConfigurationManager != null )
73- {
74- _configuration = await options . ConfigurationManager . GetConfigurationAsync ( CancellationToken . None ) . ConfigureAwait ( false ) ;
75- }
68+ if ( request . Headers . TryGetValues ( "Authorization" , out var values ) )
69+ {
70+ var authorization = values . SingleOrDefault ( ) ;
7671
77- if ( request . Headers . TryGetValues ( "Authorization" , out var values ) )
72+ if ( authorization is not null )
7873 {
79- var authorization = values . SingleOrDefault ( ) ;
74+ var value = AuthenticationHeaderValue . Parse ( authorization ) ;
8075
81- if ( authorization is not null )
76+ if ( string . Equals ( JwtBearerDefaults . AuthenticationScheme , value . Scheme , StringComparison . InvariantCultureIgnoreCase ) )
8277 {
83- var value = AuthenticationHeaderValue . Parse ( authorization ) ;
78+ var token = value . Parameter ;
79+ var validationParameters = options . TokenValidationParameters . Clone ( ) ;
8480
85- if ( string . Equals ( JwtBearerDefaults . AuthenticationScheme , value . Scheme , StringComparison . InvariantCultureIgnoreCase ) )
81+ if ( _configuration is not null )
8682 {
87- var token = value . Parameter ;
88- var validationParameters = options . TokenValidationParameters . Clone ( ) ;
89-
90- if ( _configuration is not null )
91- {
92- var issuers = new [ ] { _configuration . Issuer } ;
93- validationParameters . ValidIssuers = validationParameters . ValidIssuers ? . Concat ( issuers ) ?? issuers ;
83+ var issuers = new [ ] { _configuration . Issuer } ;
84+ validationParameters . ValidIssuers = validationParameters . ValidIssuers ? . Concat ( issuers ) ?? issuers ;
9485
95- validationParameters . IssuerSigningKeys = validationParameters . IssuerSigningKeys ? . Concat ( _configuration . SigningKeys )
96- ?? _configuration . SigningKeys ;
97- }
86+ validationParameters . IssuerSigningKeys = validationParameters . IssuerSigningKeys ? . Concat ( _configuration . SigningKeys )
87+ ?? _configuration . SigningKeys ;
88+ }
9889
99- List < Exception > ? validationFailures = null ;
100- SecurityToken ? validatedToken = null ;
101- foreach ( var validator in options . SecurityTokenValidators )
90+ List < Exception > ? validationFailures = null ;
91+ SecurityToken ? validatedToken = null ;
92+ foreach ( var validator in options . SecurityTokenValidators )
93+ {
94+ if ( validator . CanReadToken ( token ) )
10295 {
103- if ( validator . CanReadToken ( token ) )
96+ ClaimsPrincipal principal ;
97+ try
10498 {
105- ClaimsPrincipal principal ;
106- try
99+ principal = validator . ValidateToken ( token , validationParameters , out validatedToken ) ;
100+ }
101+ catch ( Exception ex )
102+ {
103+ logger . LogInformation ( ex , "Token validation failed: {Message}" , ex . Message ) ;
104+
105+ // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event.
106+ if ( options . RefreshOnIssuerKeyNotFound && options . ConfigurationManager != null
107+ && ex is SecurityTokenSignatureKeyNotFoundException )
107108 {
108- principal = validator . ValidateToken ( token , validationParameters , out validatedToken ) ;
109+ options . ConfigurationManager . RequestRefresh ( ) ;
109110 }
110- catch ( Exception ex )
111+
112+ if ( validationFailures == null )
111113 {
112- logger . LogInformation ( ex , "Token validation failed: {Message}" , ex . Message ) ;
113-
114- // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event.
115- if ( options . RefreshOnIssuerKeyNotFound && options . ConfigurationManager != null
116- && ex is SecurityTokenSignatureKeyNotFoundException )
117- {
118- options . ConfigurationManager . RequestRefresh ( ) ;
119- }
120-
121- if ( validationFailures == null )
122- {
123- validationFailures = new List < Exception > ( 1 ) ;
124- }
125- validationFailures . Add ( ex ) ;
126- continue ;
114+ validationFailures = new List < Exception > ( 1 ) ;
127115 }
116+ validationFailures . Add ( ex ) ;
117+ continue ;
118+ }
128119
129- logger . LogTrace ( "Token validation succeeded for principal: {Identity}" , principal . Identity ? . Name ?? "[null]" ) ;
120+ logger . LogTrace ( "Token validation succeeded for principal: {Identity}" , principal . Identity ? . Name ?? "[null]" ) ;
130121
131- return principal ;
132- }
122+ return principal ;
133123 }
124+ }
134125
135- if ( validationFailures is not null )
136- {
137- var exception = ( validationFailures . Count == 1 ) ? validationFailures [ 0 ] : new AggregateException ( validationFailures ) ;
138- logger . LogTrace ( exception , "Token validation failed: {Message}" , exception . Message ) ;
139- }
140- else
141- {
142- logger . LogTrace ( "No SecurityTokenValidator available for token: {Token}" , token ?? "[null]" ) ;
143- }
126+ if ( validationFailures is not null )
127+ {
128+ var exception = ( validationFailures . Count == 1 ) ? validationFailures [ 0 ] : new AggregateException ( validationFailures ) ;
129+ logger . LogTrace ( exception , "Token validation failed: {Message}" , exception . Message ) ;
130+ }
131+ else
132+ {
133+ logger . LogTrace ( "No SecurityTokenValidator available for token: {Token}" , token ?? "[null]" ) ;
144134 }
145135 }
146136 }
147-
148- return null ;
149- }
150- catch ( Exception ex )
151- {
152- logger . LogError ( ex , "Authentication failed: {Message}" , ex . Message ) ;
153- throw ;
154137 }
138+
139+ return null ;
140+ }
141+ catch ( Exception ex )
142+ {
143+ logger . LogError ( ex , "Authentication failed: {Message}" , ex . Message ) ;
144+ throw ;
155145 }
156146 }
157- }
147+ }
0 commit comments