v.0.2.0-beta
目前oauth已经可以正常使用
This commit is contained in:
151
routes/oauth.js
151
routes/oauth.js
@ -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;
|
||||
|
Reference in New Issue
Block a user