<!DOCTYPE html>
<html>
<head>
<title>OAuth Demo</title>
</head>
<body>
<button id="login">Login with Authority</button>
<div id="user"></div>
<script>
const CLIENT_ID = 'your_client_id';
const REDIRECT_URI = 'http://localhost:3000/callback';
const AUTHORITY_URL = 'http://localhost:4000';
// Check if this is a callback
if (window.location.search.includes('code=')) {
handleCallback();
}
document.getElementById('login').onclick = login;
async function login() {
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);
const state = generateCodeVerifier();
sessionStorage.setItem('code_verifier', codeVerifier);
sessionStorage.setItem('state', state);
const params = new URLSearchParams({
response_type: 'code',
client_id: CLIENT_ID,
redirect_uri: REDIRECT_URI,
scope: 'openid profile email',
state: state,
code_challenge: codeChallenge,
code_challenge_method: 'S256'
});
window.location.href = `${AUTHORITY_URL}/authorize?${params}`;
}
async function handleCallback() {
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const state = params.get('state');
if (state !== sessionStorage.getItem('state')) {
alert('Invalid state');
return;
}
const codeVerifier = sessionStorage.getItem('code_verifier');
const tokenResponse = await fetch(`${AUTHORITY_URL}/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
redirect_uri: REDIRECT_URI,
client_id: CLIENT_ID,
code_verifier: codeVerifier
})
});
const tokens = await tokenResponse.json();
const userResponse = await fetch(`${AUTHORITY_URL}/userinfo`, {
headers: { 'Authorization': `Bearer ${tokens.access_token}` }
});
const user = await userResponse.json();
document.getElementById('user').textContent = `Welcome, ${user.name}!`;
}
function generateCodeVerifier() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return base64URLEncode(array);
}
async function generateCodeChallenge(verifier) {
const data = new TextEncoder().encode(verifier);
const hash = await crypto.subtle.digest('SHA-256', data);
return base64URLEncode(new Uint8Array(hash));
}
function base64URLEncode(buffer) {
return btoa(String.fromCharCode(...buffer))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}
</script>
</body>
</html>