Files
pdnode-account/examples/oauth-client-example.html
2025-07-29 15:36:25 -07:00

274 lines
10 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OAuth客户端示例</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input, textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 14px;
}
button {
background-color: #007bff;
color: white;
padding: 12px 24px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
margin-right: 10px;
}
button:hover {
background-color: #0056b3;
}
button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.result {
margin-top: 20px;
padding: 15px;
border-radius: 5px;
white-space: pre-wrap;
font-family: monospace;
font-size: 12px;
}
.success {
background-color: #d4edda;
border: 1px solid #c3e6cb;
color: #155724;
}
.error {
background-color: #f8d7da;
border: 1px solid #f5c6cb;
color: #721c24;
}
.info {
background-color: #d1ecf1;
border: 1px solid #bee5eb;
color: #0c5460;
}
.step {
margin-bottom: 30px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #f9f9f9;
}
.step h3 {
margin-top: 0;
color: #333;
}
</style>
</head>
<body>
<div class="container">
<h1>OAuth 2.0 客户端示例</h1>
<p>这个页面演示了如何使用我们的OAuth提供商进行身份验证。</p>
<div class="step">
<h3>步骤 1: 配置OAuth客户端</h3>
<div class="form-group">
<label for="clientId">客户端ID:</label>
<input type="text" id="clientId" placeholder="输入你的OAuth客户端ID">
</div>
<div class="form-group">
<label for="clientSecret">客户端密钥:</label>
<input type="text" id="clientSecret" placeholder="输入你的OAuth客户端密钥">
</div>
<div class="form-group">
<label for="redirectUri">重定向URI:</label>
<input type="text" id="redirectUri" value="http://localhost:3001/callback" readonly>
</div>
<button onclick="testOAuthFlow()">开始OAuth流程</button>
</div>
<div class="step">
<h3>步骤 2: 获取授权码</h3>
<p>点击上面的按钮开始OAuth授权流程。你需要先登录到OAuth提供商。</p>
<div id="authResult" class="result" style="display: none;"></div>
</div>
<div class="step">
<h3>步骤 3: 交换访问令牌</h3>
<button onclick="exchangeToken()" id="exchangeBtn" disabled>交换访问令牌</button>
<div id="tokenResult" class="result" style="display: none;"></div>
</div>
<div class="step">
<h3>步骤 4: 获取用户信息</h3>
<button onclick="getUserInfo()" id="userInfoBtn" disabled>获取用户信息</button>
<div id="userInfoResult" class="result" style="display: none;"></div>
</div>
<div class="step">
<h3>步骤 5: 撤销令牌</h3>
<button onclick="revokeToken()" id="revokeBtn" disabled>撤销访问令牌</button>
<div id="revokeResult" class="result" style="display: none;"></div>
</div>
</div>
<script>
let authCode = '';
let accessToken = '';
let refreshToken = '';
async function testOAuthFlow() {
const clientId = document.getElementById('clientId').value;
const redirectUri = document.getElementById('redirectUri').value;
if (!clientId) {
showResult('authResult', '请输入客户端ID', 'error');
return;
}
try {
// 构建授权URL
const authUrl = new URL('http://localhost:3000/api/oauth/authorize');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('client_id', clientId);
authUrl.searchParams.set('redirect_uri', redirectUri);
authUrl.searchParams.set('scope', 'read write');
authUrl.searchParams.set('state', 'test_state_' + Date.now());
showResult('authResult', `正在重定向到授权页面...\n${authUrl.toString()}`, 'info');
// 在实际应用中,这里会重定向到授权页面
// 为了演示,我们模拟一个授权码
authCode = 'demo_auth_code_' + Date.now();
showResult('authResult', `模拟授权成功!\n授权码: ${authCode}`, 'success');
document.getElementById('exchangeBtn').disabled = false;
} catch (error) {
showResult('authResult', `授权失败: ${error.message}`, 'error');
}
}
async function exchangeToken() {
const clientId = document.getElementById('clientId').value;
const clientSecret = document.getElementById('clientSecret').value;
const redirectUri = document.getElementById('redirectUri').value;
if (!clientId || !clientSecret) {
showResult('tokenResult', '请输入客户端ID和密钥', 'error');
return;
}
try {
const response = await fetch('http://localhost:3000/api/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: clientId,
client_secret: clientSecret,
code: authCode,
redirect_uri: redirectUri
})
});
const data = await response.json();
if (data.success) {
accessToken = data.data.access_token;
refreshToken = data.data.refresh_token;
showResult('tokenResult', `令牌交换成功!\n访问令牌: ${accessToken.substring(0, 20)}...\n刷新令牌: ${refreshToken.substring(0, 20)}...`, 'success');
document.getElementById('userInfoBtn').disabled = false;
document.getElementById('revokeBtn').disabled = false;
} else {
showResult('tokenResult', `令牌交换失败: ${data.message}`, 'error');
}
} catch (error) {
showResult('tokenResult', `请求失败: ${error.message}`, 'error');
}
}
async function getUserInfo() {
try {
const response = await fetch('http://localhost:3000/api/oauth/userinfo', {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
const data = await response.json();
if (data.success) {
showResult('userInfoResult', `用户信息获取成功!\n${JSON.stringify(data.data, null, 2)}`, 'success');
} else {
showResult('userInfoResult', `获取用户信息失败: ${data.message}`, 'error');
}
} catch (error) {
showResult('userInfoResult', `请求失败: ${error.message}`, 'error');
}
}
async function revokeToken() {
const clientId = document.getElementById('clientId').value;
const clientSecret = document.getElementById('clientSecret').value;
try {
const response = await fetch('http://localhost:3000/api/oauth/revoke', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
token: accessToken,
client_id: clientId,
client_secret: clientSecret
})
});
const data = await response.json();
if (data.success) {
showResult('revokeResult', '令牌撤销成功!', 'success');
accessToken = '';
refreshToken = '';
document.getElementById('userInfoBtn').disabled = true;
document.getElementById('revokeBtn').disabled = true;
} else {
showResult('revokeResult', `令牌撤销失败: ${data.message}`, 'error');
}
} catch (error) {
showResult('revokeResult', `请求失败: ${error.message}`, 'error');
}
}
function showResult(elementId, message, type) {
const element = document.getElementById(elementId);
element.textContent = message;
element.className = `result ${type}`;
element.style.display = 'block';
}
</script>
</body>
</html>