Documentation
Integrate PESU Auth into your app.
Overview
PESU Auth implements OAuth 2.0 Authorization Code flow with PKCE. Your app redirects users here to authenticate, receives an authorization code, exchanges it for tokens, then fetches user data.
1. Create a Client
Go to /admin and create an OAuth client. You'll receive a client_id. For public clients (SPAs, mobile apps), no secret is needed — PKCE handles security.
SPAs, mobile apps, CLIs. No secret, PKCE required.
Backend servers. Has secret + PKCE for extra security.
2. Generate PKCE
PKCE prevents authorization code interception attacks. Generate a randomcode_verifier, then create a code_challenge by SHA-256 hashing it.
// Generate code_verifier (random string)
const codeVerifier = crypto.randomUUID() + crypto.randomUUID();
// Generate code_challenge (SHA-256 hash, base64url encoded)
async function generateChallenge(verifier) {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const hash = await crypto.subtle.digest('SHA-256', data);
return btoa(String.fromCharCode(...new Uint8Array(hash)))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
const codeChallenge = await generateChallenge(codeVerifier);
// Store verifier for later (you'll need it for token exchange)
sessionStorage.setItem('code_verifier', codeVerifier);3. Redirect to Authorize
Send users to the authorization endpoint with your client details and PKCE challenge.
const authUrl = new URL('https://your-domain.com/oauth2/authorize');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('client_id', 'YOUR_CLIENT_ID');
authUrl.searchParams.set('redirect_uri', 'https://yourapp.com/callback');
authUrl.searchParams.set('scope', 'profile:basic profile:contact');
authUrl.searchParams.set('state', crypto.randomUUID()); // CSRF protection
authUrl.searchParams.set('code_challenge', codeChallenge);
authUrl.searchParams.set('code_challenge_method', 'S256');
// Redirect user
window.location.href = authUrl.toString();4. Exchange Code for Token
After user authorizes, they're redirected back with a code. Exchange it for tokens using your stored code_verifier.
// Get code from URL params
const code = new URLSearchParams(window.location.search).get('code');
const codeVerifier = sessionStorage.getItem('code_verifier');
const response = await fetch('https://your-domain.com/api/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
redirect_uri: 'https://yourapp.com/callback',
client_id: 'YOUR_CLIENT_ID',
code_verifier: codeVerifier,
}),
});
const tokens = await response.json();
// {
// access_token: "...",
// token_type: "Bearer",
// expires_in: 3600,
// refresh_token: "...",
// scope: "profile:basic profile:contact"
// }5. Fetch User Info
Use the access token to retrieve user data. Only fields the user consented to will be returned.
const response = await fetch('https://your-domain.com/api/v1/user', {
headers: {
'Authorization': `Bearer ${accessToken}`,
},
});
const user = await response.json();
// {
// name: "John Doe",
// prn: "PES1202504001",
// srn: "PES1UG25CS001",
// email: "john@example.com",
// phone: "9876543210"
// }
console.log(`Welcome, ${user.name}!`);Available Scopes
Request only what you need. Users see exactly what you're asking for.
| Scope | Fields |
|---|---|
profile:basic | Read your basic identity (Name, PRN, SRN). |
profile:academic | Read your academic details (Program, Branch, Semester, Section, Campus). |
profile:photo | Read your profile photo. |
profile:contact | Read your contact information (Email, Phone Number). |
Error Handling
| Error | Cause |
|---|---|
invalid_client | Unknown client_id or wrong secret |
invalid_grant | Code expired, already used, or PKCE mismatch |
invalid_token | Access token expired or revoked |
insufficient_scope | User didn't consent to any fields |
access_denied | User clicked "Deny" on consent screen |
Try It
Test the full flow interactively without writing code.
Open OAuth Tester →