1+ using System . IdentityModel . Tokens . Jwt ;
2+ using System . Net ;
3+ using System . Security . Claims ;
4+ using Intent . RoslynWeaver . Attributes ;
5+ using Microsoft . AspNetCore . Authorization ;
6+ using Microsoft . AspNetCore . Http ;
7+ using Microsoft . Azure . Functions . Worker ;
8+ using Microsoft . Azure . Functions . Worker . Extensions . Http ;
9+ using Microsoft . Azure . Functions . Worker . Middleware ;
10+ using Microsoft . Extensions . Hosting ;
11+ using Microsoft . Extensions . Logging ;
12+ using Microsoft . Extensions . Primitives ;
13+
14+ [ assembly: DefaultIntentManaged ( Mode . Fully ) ]
15+ [ assembly: IntentTemplate ( "Intent.AzureFunctions.Jwt.JwtClaimsMiddleware" , Version = "1.0" ) ]
16+
17+ namespace AzureFunctions . NET8 . Api
18+ {
19+ public class JwtClaimsMiddleware : IFunctionsWorkerMiddleware
20+ {
21+ private readonly JwtSecurityTokenHandler _tokenHandler ;
22+ private readonly IHttpContextAccessor _httpContextAccessor ;
23+ private readonly ILogger < JwtClaimsMiddleware > _logger ;
24+
25+ public JwtClaimsMiddleware ( JwtSecurityTokenHandler tokenHandler ,
26+ IHttpContextAccessor httpContextAccessor ,
27+ ILogger < JwtClaimsMiddleware > logger )
28+ {
29+ _tokenHandler = tokenHandler ;
30+ _httpContextAccessor = httpContextAccessor ;
31+ _logger = logger ;
32+ }
33+
34+ public async Task Invoke ( FunctionContext context , FunctionExecutionDelegate next )
35+ {
36+ if ( ! IsHttpTrigger ( context ) )
37+ {
38+ await next ( context ) ;
39+ return ;
40+ }
41+ var httpContext = context . GetHttpContext ( ) ;
42+
43+ if ( httpContext == null )
44+ {
45+ await next ( context ) ;
46+ return ;
47+ }
48+ _httpContextAccessor . HttpContext = httpContext ;
49+
50+ if ( TryGetBearerToken ( httpContext . Request , out var tokenValue ) )
51+ {
52+ if ( TryCreatePrincipal ( tokenValue , out var principal ) )
53+ {
54+ AttachPrincipal ( httpContext , tokenValue , principal ) ;
55+ }
56+ else
57+ {
58+ httpContext . Response . StatusCode = ( int ) HttpStatusCode . BadRequest ;
59+ await httpContext . Response . WriteAsync ( @"Invalid authorization token." ) ;
60+ return ;
61+ }
62+ }
63+ await next ( context ) ;
64+ }
65+
66+ private void AttachPrincipal ( HttpContext httpContext , string tokenValue , ClaimsPrincipal principal )
67+ {
68+ httpContext . User = principal ;
69+ httpContext . Items [ "RawToken" ] = tokenValue ;
70+ }
71+
72+ private static bool TryGetBearerToken ( HttpRequest request , out string token )
73+ {
74+ token = string . Empty ;
75+
76+ if ( ! request . Headers . TryGetValue ( @"Authorization" , out StringValues authorizationValues ) )
77+ {
78+ return false ;
79+ }
80+ var authorizationHeader = authorizationValues . FirstOrDefault ( ) ;
81+
82+ if ( string . IsNullOrWhiteSpace ( authorizationHeader ) || ! authorizationHeader . StartsWith ( @"Bearer " , StringComparison . OrdinalIgnoreCase ) )
83+ {
84+ return false ;
85+ }
86+ token = authorizationHeader [ @"Bearer " . Length ..] . Trim ( ) ;
87+ return ! string . IsNullOrEmpty ( token ) ;
88+ }
89+
90+ private static bool IsHttpTrigger ( FunctionContext context )
91+ {
92+ return context . FunctionDefinition . InputBindings . Values . Any ( binding => string . Equals ( binding . Type , @"httpTrigger" , StringComparison . OrdinalIgnoreCase ) ) ;
93+ }
94+
95+ private bool TryCreatePrincipal ( string tokenValue , out ClaimsPrincipal principal )
96+ {
97+ principal = default ! ;
98+
99+ try
100+ {
101+ var token = _tokenHandler . ReadJwtToken ( tokenValue ) ;
102+ var identity = new ClaimsIdentity ( token . Claims , @"Bearer" , ClaimTypes . Name , ClaimTypes . Role ) ;
103+ principal = new ClaimsPrincipal ( identity ) ;
104+ return true ;
105+ }
106+ catch ( ArgumentException ex )
107+ {
108+ _logger . LogWarning ( ex , @"Failed to parse JWT token due to invalid format." ) ;
109+ return false ;
110+ }
111+ }
112+ }
113+
114+ public static class JwtClaimsMiddlewareExtensions
115+ {
116+ public static IFunctionsWorkerApplicationBuilder UseJwtClaimsMiddleware ( this IFunctionsWorkerApplicationBuilder builder )
117+ {
118+ return builder . UseMiddleware < JwtClaimsMiddleware > ( ) ;
119+ }
120+ }
121+ }
0 commit comments