Authorization Code Flow
Implement OAuth 2.0 Authorization Code Flow with PKCE in AuthScape.
AuthScape uses the Authorization Code Flow with PKCE (Proof Key for Code Exchange) for secure user authentication.
Flow Overview
text
1. User clicks Login2. Frontend generates code_verifier and code_challenge3. Redirect to /connect/authorize with code_challenge4. User authenticates at IDP5. IDP redirects back with authorization code6. Frontend exchanges code + code_verifier for tokens7. Store tokens and redirect to app
PKCE Implementation
The authService handles PKCE automatically:
javascript
// authService.js internal implementationfunction generateCodeVerifier() {const array = new Uint8Array(32);crypto.getRandomValues(array);return base64UrlEncode(array);}async function generateCodeChallenge(verifier) {const encoder = new TextEncoder();const data = encoder.encode(verifier);const hash = await crypto.subtle.digest('SHA-256', data);return base64UrlEncode(new Uint8Array(hash));}
Authorization Request
javascript
const codeVerifier = generateCodeVerifier();const codeChallenge = await generateCodeChallenge(codeVerifier);// Store verifier for latersessionStorage.setItem('pkce_verifier', codeVerifier);// Build authorization URLconst params = new URLSearchParams({response_type: 'code',client_id: 'web-app',redirect_uri: 'https://localhost:3000/callback',scope: 'openid email profile api1 offline_access',code_challenge: codeChallenge,code_challenge_method: 'S256',state: generateState()});window.location.href = `https://localhost:5001/connect/authorize?${params}`;
Token Exchange
After redirect back with authorization code:
javascript
async function exchangeCodeForTokens(code) {const codeVerifier = sessionStorage.getItem('pkce_verifier');const response = await fetch('https://localhost:5001/connect/token', {method: 'POST',headers: {'Content-Type': 'application/x-www-form-urlencoded'},body: new URLSearchParams({grant_type: 'authorization_code',code: code,redirect_uri: 'https://localhost:3000/callback',client_id: 'web-app',code_verifier: codeVerifier})});const tokens = await response.json();// Store tokensCookies.set('access_token', tokens.access_token, { secure: true });Cookies.set('refresh_token', tokens.refresh_token, { secure: true });return tokens;}
Backend Authorization Endpoint
csharp
[HttpGet("~/connect/authorize")][HttpPost("~/connect/authorize")]public async Task<IActionResult> Authorize(){var request = HttpContext.GetOpenIddictServerRequest();// Get or create authentication ticketvar result = await HttpContext.AuthenticateAsync();if (!result.Succeeded){return Challenge(authenticationSchemes: IdentityConstants.ApplicationScheme,properties: new AuthenticationProperties{RedirectUri = Request.PathBase + Request.Path + QueryString.Create(Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList())});}var user = await _userManager.GetUserAsync(result.Principal);var identity = new ClaimsIdentity(TokenValidationParameters.DefaultAuthenticationType,Claims.Name,Claims.Role);// Add claimsidentity.AddClaim(new Claim(Claims.Subject, user.Id.ToString()));identity.AddClaim(new Claim(Claims.Email, user.Email));identity.AddClaim(new Claim(Claims.Name, user.UserName));identity.SetDestinations(GetDestinations);return SignIn(new ClaimsPrincipal(identity),OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);}
Security Benefits of PKCE
- Prevents code interception - Even if authorization code is intercepted, it's useless without the verifier
- No client secret needed - Safe for public clients (SPAs, mobile apps)
- Mitigates CSRF - State parameter prevents cross-site request forgery