AuthScape

Docs

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 Login
2. Frontend generates code_verifier and code_challenge
3. Redirect to /connect/authorize with code_challenge
4. User authenticates at IDP
5. IDP redirects back with authorization code
6. Frontend exchanges code + code_verifier for tokens
7. Store tokens and redirect to app

PKCE Implementation

The authService handles PKCE automatically:

javascript
// authService.js internal implementation
function 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 later
sessionStorage.setItem('pkce_verifier', codeVerifier);
// Build authorization URL
const 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 tokens
Cookies.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 ticket
var 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 claims
identity.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

  1. Prevents code interception - Even if authorization code is intercepted, it's useless without the verifier
  2. No client secret needed - Safe for public clients (SPAs, mobile apps)
  3. Mitigates CSRF - State parameter prevents cross-site request forgery

Next Steps

  • Client Credentials
  • Claims Structure