v.0.2.0-beta

目前oauth已经可以正常使用
This commit is contained in:
2025-07-29 17:20:26 -07:00
parent 0c481c7a0e
commit 66a901c676
7 changed files with 943 additions and 43 deletions

View File

@ -8,6 +8,39 @@ const { authenticateOAuthToken, requireScope } = require('../middleware/oauth');
const router = express.Router();
// 验证重定向URI的通用函数
const isValidRedirectUri = (storedUris, requestedUri) => {
try {
const requestedUrl = new URL(requestedUri);
for (const storedUri of storedUris) {
const storedUrl = new URL(storedUri);
// 检查协议是否匹配
if (requestedUrl.protocol !== storedUrl.protocol) {
continue;
}
// 获取文件名(路径的最后一部分)
const requestedFilename = requestedUrl.pathname.split('/').pop();
const storedFilename = storedUrl.pathname.split('/').pop();
// 检查文件名是否匹配,忽略路径前缀
if (requestedFilename === storedFilename) {
return true;
}
// 如果文件名不匹配,检查完整路径是否匹配
if (requestedUrl.pathname === storedUrl.pathname) {
return true;
}
}
return false;
} catch (error) {
return false;
}
};
// 验证中间件
const handleValidationErrors = (req, res, next) => {
const errors = validationResult(req);
@ -24,7 +57,7 @@ const handleValidationErrors = (req, res, next) => {
next();
};
// 1. 授权端点 - 用户授权第三方应用
// 1. 授权端点 - 验证参数并返回授权信息
router.get('/authorize', authenticateToken, async (req, res) => {
try {
const {
@ -61,10 +94,14 @@ router.get('/authorize', authenticateToken, async (req, res) => {
}
// 验证重定向URI
if (!client.redirect_uris.includes(redirect_uri)) {
if (!isValidRedirectUri(client.redirect_uris, redirect_uri)) {
return res.status(400).json({
success: false,
message: '无效的重定向URI'
message: '无效的重定向URI',
debug: {
requested: redirect_uri,
allowed: client.redirect_uris
}
});
}
@ -77,7 +114,101 @@ router.get('/authorize', authenticateToken, async (req, res) => {
});
}
// 生成授权码
// 返回授权信息,让前端显示授权页面
const scopes = scope ? scope.split(' ') : ['read', 'write'];
res.json({
success: true,
message: '授权信息获取成功',
data: {
client: {
id: client.client_id,
name: client.client_name,
description: client.description
},
scopes: scopes,
state: state,
redirect_uri: redirect_uri
}
});
} catch (error) {
console.error('授权信息获取失败:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
});
// 2. 用户同意授权端点
router.post('/authorize/consent', authenticateToken, async (req, res) => {
try {
const {
client_id,
redirect_uri,
scope,
state,
approved
} = req.body;
// 验证必需参数
if (!client_id || !redirect_uri || approved === undefined) {
return res.status(400).json({
success: false,
message: '缺少必需参数'
});
}
// 验证客户端
const client = await OAuthClient.findByClientId(client_id);
if (!client) {
return res.status(400).json({
success: false,
message: '无效的客户端ID'
});
}
// 验证重定向URI
if (!isValidRedirectUri(client.redirect_uris, redirect_uri)) {
return res.status(400).json({
success: false,
message: '无效的重定向URI',
debug: {
requested: redirect_uri,
allowed: client.redirect_uris
}
});
}
// 获取用户信息
const user = await User.findByUsername(req.user.username);
if (!user) {
return res.status(401).json({
success: false,
message: '用户不存在'
});
}
if (!approved) {
// 用户拒绝授权
const errorUrl = new URL(redirect_uri);
errorUrl.searchParams.set('error', 'access_denied');
errorUrl.searchParams.set('error_description', '用户拒绝授权');
if (state) {
errorUrl.searchParams.set('state', state);
}
return res.json({
success: true,
message: '用户拒绝授权',
data: {
redirect_url: errorUrl.toString()
}
});
}
// 用户同意授权,生成授权码
const authCode = OAuthToken.generateAuthCode();
const scopes = scope ? scope.split(' ') : ['read', 'write'];
@ -96,8 +227,6 @@ router.get('/authorize', authenticateToken, async (req, res) => {
redirectUrl.searchParams.set('state', state);
}
// 对于API测试直接返回授权码
// 对于生产环境应该重定向到redirectUrl
res.json({
success: true,
message: '授权成功',
@ -109,7 +238,7 @@ router.get('/authorize', authenticateToken, async (req, res) => {
});
} catch (error) {
console.error('授权失败:', error);
console.error('授权处理失败:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
@ -117,7 +246,7 @@ router.get('/authorize', authenticateToken, async (req, res) => {
}
});
// 2. 令牌端点 - 交换授权码获取访问令牌
// 3. 令牌端点 - 交换授权码获取访问令牌
router.post('/token', [
body('grant_type').notEmpty().withMessage('grant_type不能为空'),
body('client_id').notEmpty().withMessage('client_id不能为空'),
@ -273,7 +402,7 @@ router.post('/token', [
}
});
// 3. 撤销令牌端点
// 4. 撤销令牌端点
router.post('/revoke', [
body('token').notEmpty().withMessage('token不能为空'),
body('client_id').notEmpty().withMessage('client_id不能为空'),
@ -312,7 +441,7 @@ router.post('/revoke', [
}
});
// 4. 用户信息端点
// 5. 用户信息端点
router.get('/userinfo', authenticateOAuthToken, requireScope('read'), async (req, res) => {
try {
res.json({
@ -333,7 +462,7 @@ router.get('/userinfo', authenticateOAuthToken, requireScope('read'), async (req
}
});
// 5. 令牌信息端点
// 6. 令牌信息端点
router.get('/tokeninfo', async (req, res) => {
try {
const { access_token } = req.query;