本文档汇总了使用 user.blym.top OAuth服务时的常见问题及解决方案。
可能原因:
解决方案:
// 检查redirect_uri是否完全匹配
$registeredUris = json_decode($client['redirect_uris'], true);
if (!in_array($redirectUri, $registeredUris)) {
throw new Exception('redirect_uri不匹配');
}
确保:
可能原因:
解决方案:
# 验证客户端凭据
curl -X POST https://user.blym.top/oauth/token.php \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
如果返回 invalid_client,请检查:
可能原因:
解决方案:
授权码只能使用一次,请确保:
可能原因:
解决方案:
// 正确的state处理流程
// 1. 生成并保存state
const state = crypto.randomUUID();
sessionStorage.setItem('oauth_state', state);
// 2. 构建授权URL
const authUrl = `https://user.blym.top/oauth/authorize.php?` +
`client_id=${clientId}&` +
`redirect_uri=${encodeURIComponent(redirectUri)}&` +
`response_type=code&` +
`state=${state}`;
// 3. 在回调中验证
const returnedState = new URLSearchParams(location.search).get('state');
const savedState = sessionStorage.getItem('oauth_state');
if (returnedState !== savedState) {
console.error('State验证失败,可能存在CSRF攻击');
// 不要继续处理
}
解决方案:
使用刷新令牌获取新的访问令牌:
<?php
function refreshAccessToken($refreshToken, $clientId, $clientSecret) {
$ch = curl_init('https://user.blym.top/oauth/token.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken,
'client_id' => $clientId,
'client_secret' => $clientSecret
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// 使用示例
if (tokenExpired()) {
$newTokens = refreshAccessToken(
$_SESSION['refresh_token'],
CLIENT_ID,
CLIENT_SECRET
);
$_SESSION['access_token'] = $newTokens['access_token'];
$_SESSION['refresh_token'] = $newTokens['refresh_token'];
}
?>
解决方案:
刷新令牌过期后,需要用户重新授权:
if (error.error === 'invalid_grant') {
// 刷新令牌过期,重新授权
window.location.href = getAuthorizationUrl();
}
管理员撤销:
访问后台 https://user.blym.top/admin/oauth_server.php,在令牌管理中撤销。
用户撤销:
用户可在个人中心解除第三方应用授权。
可能原因:
解决方案:
// 正确的请求方式
$ch = curl_init('https://user.blym.top/api/userinfo.php');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $accessToken // 注意Bearer后有空格
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 401) {
// 令牌无效,尝试刷新
$newToken = refreshAccessToken();
// 重试请求
}
可能原因: 请求的作用域(scope)不包含该字段。
解决方案:
| 字段 | 所需作用域 |
|---|---|
| sub | openid |
| name, preferred_username | profile |
| email, email_verified |
确保授权时请求了正确的作用域:
scope=openid profile email
解决方案:
始终使用state参数:
// 生成state
$state = bin2hex(random_bytes(16));
$_SESSION['oauth_state'] = $state;
// 验证state
if ($_GET['state'] !== $_SESSION['oauth_state']) {
die('CSRF验证失败');
}
推荐方式:
| 环境 | 存储方式 |
|---|---|
| 服务器端 | 数据库(加密)或安全会话 |
| 浏览器 | HttpOnly Cookie 或 Session Storage |
| 移动端 | 系统密钥库(Keychain/Keystore) |
不推荐:
完整示例:
// 1. 生成code_verifier
function generateCodeVerifier() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return base64UrlEncode(array);
}
// 2. 生成code_challenge
async function generateCodeChallenge(verifier) {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const digest = await crypto.subtle.digest('SHA-256', data);
return base64UrlEncode(new Uint8Array(digest));
}
// 3. 授权请求
const verifier = generateCodeVerifier();
const challenge = await generateCodeChallenge(verifier);
sessionStorage.setItem('code_verifier', verifier);
const authUrl = `https://user.blym.top/oauth/authorize.php?` +
`client_id=${clientId}&` +
`redirect_uri=${redirectUri}&` +
`response_type=code&` +
`code_challenge=${challenge}&` +
`code_challenge_method=S256&` +
`state=${state}`;
// 4. 令牌请求(包含code_verifier)
const tokenResponse = await fetch('https://user.blym.top/oauth/token.php', {
method: 'POST',
body: new URLSearchParams({
grant_type: 'authorization_code',
code: authCode,
redirect_uri: redirectUri,
client_id: clientId,
code_verifier: verifier // 使用之前保存的verifier
})
});
管理员操作:
管理员操作:
管理员操作:
⚠️ 重置后旧密钥立即失效,需要更新所有使用该客户端的应用配置。
如果以上解决方案未能解决您的问题,请: