diff --git a/src/Clerk/BackendAPI/Helpers/AuthenticateRequest.cs b/src/Clerk/BackendAPI/Helpers/AuthenticateRequest.cs index 10352bb5..2881a875 100644 --- a/src/Clerk/BackendAPI/Helpers/AuthenticateRequest.cs +++ b/src/Clerk/BackendAPI/Helpers/AuthenticateRequest.cs @@ -2,6 +2,7 @@ using System.Net; using System.Net.Http; using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; namespace Clerk.BackendAPI.Helpers.Jwks; @@ -60,6 +61,54 @@ public static async Task AuthenticateRequestAsync( } } + /// + /// Checks if the ASP.NET Core HTTP request is authenticated. + /// First the session token is retrieved from either the __session cookie + /// or the HTTP Authorization header. + /// Then the session token is verified: networklessly if the options.jwtKey + /// is provided, otherwise by fetching the JWKS from Clerk's Backend API. + /// + /// The ASP.NET Core HTTP request + /// The request authentication options + /// The request state + /// WARNING: AuthenticateRequestAsync is applicable in the context of Backend APIs only. + public static async Task AuthenticateRequestAsync( + HttpRequest request, + AuthenticateRequestOptions options) + { + var sessionToken = GetSessionToken(request); + if (sessionToken == null) return RequestState.SignedOut(AuthErrorReason.SESSION_TOKEN_MISSING); + + VerifyTokenOptions verifyTokenOptions; + + if (options.JwtKey != null) + verifyTokenOptions = new VerifyTokenOptions( + jwtKey: options.JwtKey, + audiences: options.Audiences, + authorizedParties: options.AuthorizedParties, + clockSkewInMs: options.ClockSkewInMs + ); + else if (options.SecretKey != null) + verifyTokenOptions = new VerifyTokenOptions( + options.SecretKey, + audiences: options.Audiences, + authorizedParties: options.AuthorizedParties, + clockSkewInMs: options.ClockSkewInMs + ); + else + return RequestState.SignedOut(AuthErrorReason.SECRET_KEY_MISSING); + + try + { + var claims = await VerifyToken.VerifyTokenAsync(sessionToken, verifyTokenOptions); + return RequestState.SignedIn(sessionToken, claims); + } + catch (TokenVerificationException e) + { + return RequestState.SignedOut(e.Reason); + } + } + /// /// Retrieve token from __session cookie or Authorization header. /// @@ -90,4 +139,30 @@ public static async Task AuthenticateRequestAsync( return null; } -} \ No newline at end of file + + /// + /// Retrieve token from __session cookie or Authorization header. + /// + /// The ASP.NET Core HTTP request + /// The session token, if present + private static string? GetSessionToken(HttpRequest request) + { + // Check for Authorization header + if (request.Headers.TryGetValue("Authorization", out var authValues)) + { + var authHeader = authValues.ToString(); + if (!string.IsNullOrEmpty(authHeader) && authHeader.StartsWith("Bearer ")) + { + return authHeader.Substring("Bearer ".Length).Trim(); + } + } + + // Check for __session cookie + if (request.Cookies.TryGetValue(SESSION_COOKIE_NAME, out var sessionCookie)) + { + return sessionCookie; + } + + return null; + } +}