v0.1.0
This commit is contained in:
162
routes/auth.js
Normal file
162
routes/auth.js
Normal file
@ -0,0 +1,162 @@
|
||||
const express = require('express');
|
||||
const User = require('../models/User');
|
||||
const { generateToken, authenticateToken } = require('../middleware/auth');
|
||||
const {
|
||||
registerValidation,
|
||||
loginValidation,
|
||||
handleValidationErrors
|
||||
} = require('../middleware/validation');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// 注册路由
|
||||
router.post('/register', registerValidation, handleValidationErrors, async (req, res) => {
|
||||
try {
|
||||
const { username, email, password } = req.body;
|
||||
|
||||
// 检查用户名是否已存在
|
||||
const existingUserByUsername = await User.findByUsername(username);
|
||||
if (existingUserByUsername) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '用户名已存在'
|
||||
});
|
||||
}
|
||||
|
||||
// 检查邮箱是否已存在
|
||||
const existingUserByEmail = await User.findByEmail(email);
|
||||
if (existingUserByEmail) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '邮箱已被注册'
|
||||
});
|
||||
}
|
||||
|
||||
// 创建新用户
|
||||
const newUser = await User.create({ username, email, password });
|
||||
|
||||
// 生成JWT token
|
||||
const token = generateToken(newUser.id, newUser.username);
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: '注册成功',
|
||||
data: {
|
||||
user: {
|
||||
id: newUser.id,
|
||||
username: newUser.username,
|
||||
email: newUser.email,
|
||||
created_at: newUser.created_at
|
||||
},
|
||||
token
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('注册失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 登录路由
|
||||
router.post('/login', loginValidation, handleValidationErrors, async (req, res) => {
|
||||
try {
|
||||
const { username, password } = req.body;
|
||||
|
||||
// 查找用户(支持用户名或邮箱登录)
|
||||
let user = await User.findByUsername(username);
|
||||
if (!user) {
|
||||
// 如果不是用户名,尝试用邮箱查找
|
||||
user = await User.findByEmail(username);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: '用户名或密码错误'
|
||||
});
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
const isPasswordValid = await User.verifyPassword(password, user.password);
|
||||
if (!isPasswordValid) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: '用户名或密码错误'
|
||||
});
|
||||
}
|
||||
|
||||
// 生成JWT token
|
||||
const token = generateToken(user.id, user.username);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '登录成功',
|
||||
data: {
|
||||
user: {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
created_at: user.created_at
|
||||
},
|
||||
token
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('登录失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取当前用户信息(需要认证)
|
||||
router.get('/profile', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const user = await User.findByUsername(req.user.username);
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '用户不存在'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
user: {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
created_at: user.created_at
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 测试路由(需要认证)
|
||||
router.get('/test', authenticateToken, (req, res) => {
|
||||
res.json({
|
||||
success: true,
|
||||
message: '认证成功',
|
||||
data: {
|
||||
user: req.user
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
349
routes/oauth-clients.js
Normal file
349
routes/oauth-clients.js
Normal file
@ -0,0 +1,349 @@
|
||||
const express = require('express');
|
||||
const { body, validationResult } = require('express-validator');
|
||||
const OAuthClient = require('../models/OAuthClient');
|
||||
const { authenticateToken } = require('../middleware/auth');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// 验证中间件
|
||||
const handleValidationErrors = (req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '输入验证失败',
|
||||
errors: errors.array().map(error => ({
|
||||
field: error.path,
|
||||
message: error.msg
|
||||
}))
|
||||
});
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
// 自定义URL验证函数
|
||||
const isValidRedirectUri = (value) => {
|
||||
console.log('验证URL:', value);
|
||||
try {
|
||||
const url = new URL(value);
|
||||
console.log('URL解析结果:', {
|
||||
protocol: url.protocol,
|
||||
hostname: url.hostname,
|
||||
pathname: url.pathname,
|
||||
isValid: (url.protocol === 'http:' || url.protocol === 'https:') &&
|
||||
url.hostname &&
|
||||
url.pathname
|
||||
});
|
||||
// 允许http和https协议,包括localhost
|
||||
return (url.protocol === 'http:' || url.protocol === 'https:') &&
|
||||
url.hostname &&
|
||||
url.pathname;
|
||||
} catch (error) {
|
||||
console.log('URL验证失败:', value, error.message);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// 创建OAuth客户端验证规则
|
||||
const createClientValidation = [
|
||||
body('name')
|
||||
.isLength({ min: 1, max: 100 })
|
||||
.withMessage('应用名称长度必须在1-100个字符之间'),
|
||||
|
||||
body('description')
|
||||
.optional()
|
||||
.isLength({ max: 500 })
|
||||
.withMessage('描述长度不能超过500个字符'),
|
||||
|
||||
body('redirect_uris')
|
||||
.isArray({ min: 1 })
|
||||
.withMessage('至少需要一个重定向URI'),
|
||||
|
||||
body('scopes')
|
||||
.optional()
|
||||
.isArray()
|
||||
.withMessage('权限范围必须是数组格式')
|
||||
];
|
||||
|
||||
// 自定义验证中间件
|
||||
const validateRedirectUris = (req, res, next) => {
|
||||
const { redirect_uris } = req.body;
|
||||
|
||||
if (!Array.isArray(redirect_uris)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'redirect_uris必须是数组'
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 0; i < redirect_uris.length; i++) {
|
||||
if (!isValidRedirectUri(redirect_uris[i])) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '输入验证失败',
|
||||
errors: [{
|
||||
field: `redirect_uris[${i}]`,
|
||||
message: '重定向URI必须是有效的URL'
|
||||
}]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
// 1. 创建OAuth客户端
|
||||
router.post('/clients', authenticateToken, createClientValidation, validateRedirectUris, handleValidationErrors, async (req, res) => {
|
||||
try {
|
||||
const { name, description, redirect_uris, scopes } = req.body;
|
||||
const userId = req.user.userId;
|
||||
|
||||
const clientData = {
|
||||
name,
|
||||
description: description || '',
|
||||
redirectUris: redirect_uris,
|
||||
scopes: scopes || ['read', 'write'],
|
||||
userId
|
||||
};
|
||||
|
||||
const newClient = await OAuthClient.create(clientData);
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: 'OAuth客户端创建成功',
|
||||
data: {
|
||||
client_id: newClient.client_id,
|
||||
client_secret: newClient.client_secret, // 返回客户端密钥
|
||||
name: newClient.name,
|
||||
description: newClient.description,
|
||||
redirect_uris: newClient.redirect_uris,
|
||||
scopes: newClient.scopes,
|
||||
created_at: newClient.created_at
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('创建OAuth客户端失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 2. 获取用户的所有OAuth客户端
|
||||
router.get('/clients', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const userId = req.user.userId;
|
||||
const clients = await OAuthClient.findByUserId(userId);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
clients: clients.map(client => ({
|
||||
client_id: client.client_id,
|
||||
name: client.name,
|
||||
description: client.description,
|
||||
redirect_uris: client.redirect_uris,
|
||||
scopes: client.scopes,
|
||||
created_at: client.created_at
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取OAuth客户端失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 3. 获取特定OAuth客户端详情
|
||||
router.get('/clients/:clientId', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const { clientId } = req.params;
|
||||
const userId = req.user.userId;
|
||||
|
||||
const client = await OAuthClient.findByClientId(clientId);
|
||||
if (!client) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'OAuth客户端不存在'
|
||||
});
|
||||
}
|
||||
|
||||
// 检查是否是客户端所有者
|
||||
if (client.user_id !== userId) {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: '无权访问此客户端'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
client_id: client.client_id,
|
||||
name: client.name,
|
||||
description: client.description,
|
||||
redirect_uris: client.redirect_uris,
|
||||
scopes: client.scopes,
|
||||
created_at: client.created_at
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取OAuth客户端详情失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 4. 删除OAuth客户端
|
||||
router.delete('/clients/:clientId', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const { clientId } = req.params;
|
||||
const userId = req.user.userId;
|
||||
|
||||
const deleted = await OAuthClient.delete(clientId, userId);
|
||||
if (!deleted) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'OAuth客户端不存在或无权删除'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'OAuth客户端已删除'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('删除OAuth客户端失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 4. 获取客户端密钥(仅限客户端所有者)
|
||||
router.get('/clients/:clientId/secret', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const { clientId } = req.params;
|
||||
const userId = req.user.userId;
|
||||
|
||||
const client = await OAuthClient.findByClientId(clientId);
|
||||
if (!client) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'OAuth客户端不存在'
|
||||
});
|
||||
}
|
||||
|
||||
// 检查是否是客户端所有者
|
||||
if (client.user_id !== userId) {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: '无权访问此客户端'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
client_id: client.client_id,
|
||||
client_secret: client.client_secret
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取客户端密钥失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 5. 重置客户端密钥
|
||||
router.post('/clients/:clientId/reset-secret', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const { clientId } = req.params;
|
||||
const userId = req.user.userId;
|
||||
|
||||
const client = await OAuthClient.findByClientId(clientId);
|
||||
if (!client) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'OAuth客户端不存在'
|
||||
});
|
||||
}
|
||||
|
||||
// 检查是否是客户端所有者
|
||||
if (client.user_id !== userId) {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: '无权操作此客户端'
|
||||
});
|
||||
}
|
||||
|
||||
const newSecret = await OAuthClient.resetClientSecret(clientId);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '客户端密钥重置成功',
|
||||
data: {
|
||||
client_id: clientId,
|
||||
client_secret: newSecret
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('重置客户端密钥失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 5. 获取OAuth客户端统计信息
|
||||
router.get('/clients/:clientId/stats', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const { clientId } = req.params;
|
||||
const userId = req.user.userId;
|
||||
|
||||
const client = await OAuthClient.findByClientId(clientId);
|
||||
if (!client || client.user_id !== userId) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'OAuth客户端不存在'
|
||||
});
|
||||
}
|
||||
|
||||
// 这里可以添加更多统计信息,比如活跃令牌数量等
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
client_id: client.client_id,
|
||||
name: client.name,
|
||||
created_at: client.created_at,
|
||||
// 可以添加更多统计信息
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取OAuth客户端统计失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
375
routes/oauth.js
Normal file
375
routes/oauth.js
Normal file
@ -0,0 +1,375 @@
|
||||
const express = require('express');
|
||||
const { body, validationResult } = require('express-validator');
|
||||
const OAuthClient = require('../models/OAuthClient');
|
||||
const OAuthToken = require('../models/OAuthToken');
|
||||
const User = require('../models/User');
|
||||
const { authenticateToken } = require('../middleware/auth');
|
||||
const { authenticateOAuthToken, requireScope } = require('../middleware/oauth');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// 验证中间件
|
||||
const handleValidationErrors = (req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '输入验证失败',
|
||||
errors: errors.array().map(error => ({
|
||||
field: error.path,
|
||||
message: error.msg
|
||||
}))
|
||||
});
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
// 1. 授权端点 - 用户授权第三方应用
|
||||
router.get('/authorize', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
response_type,
|
||||
client_id,
|
||||
redirect_uri,
|
||||
scope,
|
||||
state
|
||||
} = req.query;
|
||||
|
||||
// 验证必需参数
|
||||
if (!response_type || !client_id || !redirect_uri) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '缺少必需参数'
|
||||
});
|
||||
}
|
||||
|
||||
// 验证response_type
|
||||
if (response_type !== 'code') {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '不支持的response_type'
|
||||
});
|
||||
}
|
||||
|
||||
// 验证客户端
|
||||
const client = await OAuthClient.findByClientId(client_id);
|
||||
if (!client) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '无效的客户端ID'
|
||||
});
|
||||
}
|
||||
|
||||
// 验证重定向URI
|
||||
if (!client.redirect_uris.includes(redirect_uri)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '无效的重定向URI'
|
||||
});
|
||||
}
|
||||
|
||||
// 获取用户信息(通过JWT中间件已经验证)
|
||||
const user = await User.findByUsername(req.user.username);
|
||||
if (!user) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: '用户不存在'
|
||||
});
|
||||
}
|
||||
|
||||
// 生成授权码
|
||||
const authCode = OAuthToken.generateAuthCode();
|
||||
const scopes = scope ? scope.split(' ') : ['read', 'write'];
|
||||
|
||||
await OAuthToken.createAuthCode({
|
||||
code: authCode,
|
||||
clientId: client_id,
|
||||
userId: user.id,
|
||||
redirectUri: redirect_uri,
|
||||
scopes: scopes
|
||||
});
|
||||
|
||||
// 构建重定向URL
|
||||
const redirectUrl = new URL(redirect_uri);
|
||||
redirectUrl.searchParams.set('code', authCode);
|
||||
if (state) {
|
||||
redirectUrl.searchParams.set('state', state);
|
||||
}
|
||||
|
||||
// 对于API测试,直接返回授权码
|
||||
// 对于生产环境,应该重定向到redirectUrl
|
||||
res.json({
|
||||
success: true,
|
||||
message: '授权成功',
|
||||
data: {
|
||||
redirect_url: redirectUrl.toString(),
|
||||
code: authCode,
|
||||
state: state
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('授权失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 2. 令牌端点 - 交换授权码获取访问令牌
|
||||
router.post('/token', [
|
||||
body('grant_type').notEmpty().withMessage('grant_type不能为空'),
|
||||
body('client_id').notEmpty().withMessage('client_id不能为空'),
|
||||
body('client_secret').notEmpty().withMessage('client_secret不能为空'),
|
||||
handleValidationErrors
|
||||
], async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
grant_type,
|
||||
client_id,
|
||||
client_secret,
|
||||
code,
|
||||
redirect_uri,
|
||||
refresh_token,
|
||||
code_verifier // PKCE支持
|
||||
} = req.body;
|
||||
|
||||
// 验证客户端
|
||||
const isValidClient = await OAuthClient.validateClient(client_id, client_secret);
|
||||
if (!isValidClient) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: '无效的客户端凭据'
|
||||
});
|
||||
}
|
||||
|
||||
if (grant_type === 'authorization_code') {
|
||||
// 授权码流程
|
||||
if (!code || !redirect_uri) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '缺少code或redirect_uri'
|
||||
});
|
||||
}
|
||||
|
||||
// 验证授权码
|
||||
const authCode = await OAuthToken.validateAuthCode(code, client_id, redirect_uri);
|
||||
if (!authCode) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '无效的授权码'
|
||||
});
|
||||
}
|
||||
|
||||
// 验证重定向URI
|
||||
const isValidRedirectUri = await OAuthClient.validateRedirectUri(client_id, redirect_uri);
|
||||
if (!isValidRedirectUri) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '无效的重定向URI'
|
||||
});
|
||||
}
|
||||
|
||||
// 生成访问令牌和刷新令牌
|
||||
const accessToken = OAuthToken.generateAccessToken();
|
||||
const refreshToken = OAuthToken.generateRefreshToken();
|
||||
|
||||
// 创建访问令牌
|
||||
const accessTokenData = await OAuthToken.createAccessToken({
|
||||
token: accessToken,
|
||||
clientId: client_id,
|
||||
userId: authCode.user_id,
|
||||
scopes: authCode.scopes
|
||||
});
|
||||
|
||||
// 创建刷新令牌
|
||||
await OAuthToken.createRefreshToken({
|
||||
token: refreshToken,
|
||||
accessTokenId: accessTokenData.id,
|
||||
clientId: client_id,
|
||||
userId: authCode.user_id
|
||||
});
|
||||
|
||||
// 删除已使用的授权码
|
||||
await OAuthToken.deleteAuthCode(code);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
access_token: accessToken,
|
||||
token_type: 'Bearer',
|
||||
expires_in: 3600, // 1小时
|
||||
refresh_token: refreshToken,
|
||||
scope: authCode.scopes.join(' ')
|
||||
}
|
||||
});
|
||||
|
||||
} else if (grant_type === 'refresh_token') {
|
||||
// 刷新令牌流程
|
||||
if (!refresh_token) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '缺少refresh_token'
|
||||
});
|
||||
}
|
||||
|
||||
// 验证刷新令牌
|
||||
const refreshTokenData = await OAuthToken.validateRefreshToken(refresh_token, client_id);
|
||||
if (!refreshTokenData) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '无效的刷新令牌'
|
||||
});
|
||||
}
|
||||
|
||||
// 生成新的访问令牌
|
||||
const newAccessToken = OAuthToken.generateAccessToken();
|
||||
const newRefreshToken = OAuthToken.generateRefreshToken();
|
||||
|
||||
// 创建新的访问令牌
|
||||
const newAccessTokenData = await OAuthToken.createAccessToken({
|
||||
token: newAccessToken,
|
||||
clientId: client_id,
|
||||
userId: refreshTokenData.user_id,
|
||||
scopes: refreshTokenData.scopes
|
||||
});
|
||||
|
||||
// 创建新的刷新令牌
|
||||
await OAuthToken.createRefreshToken({
|
||||
token: newRefreshToken,
|
||||
accessTokenId: newAccessTokenData.id,
|
||||
clientId: client_id,
|
||||
userId: refreshTokenData.user_id
|
||||
});
|
||||
|
||||
// 撤销旧的刷新令牌
|
||||
await OAuthToken.revokeRefreshToken(refresh_token);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
access_token: newAccessToken,
|
||||
token_type: 'Bearer',
|
||||
expires_in: 3600,
|
||||
refresh_token: newRefreshToken,
|
||||
scope: refreshTokenData.scopes.join(' ')
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '不支持的授权类型'
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('令牌交换失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 3. 撤销令牌端点
|
||||
router.post('/revoke', [
|
||||
body('token').notEmpty().withMessage('token不能为空'),
|
||||
body('client_id').notEmpty().withMessage('client_id不能为空'),
|
||||
body('client_secret').notEmpty().withMessage('client_secret不能为空'),
|
||||
handleValidationErrors
|
||||
], async (req, res) => {
|
||||
try {
|
||||
const { token, client_id, client_secret } = req.body;
|
||||
|
||||
// 验证客户端
|
||||
const isValidClient = await OAuthClient.validateClient(client_id, client_secret);
|
||||
if (!isValidClient) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: '无效的客户端凭据'
|
||||
});
|
||||
}
|
||||
|
||||
// 撤销令牌
|
||||
const revoked = await OAuthToken.revokeAccessToken(token);
|
||||
if (!revoked) {
|
||||
await OAuthToken.revokeRefreshToken(token);
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '令牌已撤销'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('撤销令牌失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 4. 用户信息端点
|
||||
router.get('/userinfo', authenticateOAuthToken, requireScope('read'), async (req, res) => {
|
||||
try {
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
user_id: req.oauth.userId,
|
||||
username: req.oauth.username,
|
||||
email: req.oauth.email,
|
||||
scopes: req.oauth.scopes
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 5. 令牌信息端点
|
||||
router.get('/tokeninfo', async (req, res) => {
|
||||
try {
|
||||
const { access_token } = req.query;
|
||||
|
||||
if (!access_token) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '缺少access_token参数'
|
||||
});
|
||||
}
|
||||
|
||||
const tokenData = await OAuthToken.validateAccessToken(access_token);
|
||||
if (!tokenData) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '无效的访问令牌'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
user_id: tokenData.user_id,
|
||||
client_id: tokenData.client_id,
|
||||
scopes: tokenData.scopes,
|
||||
expires_in: Math.floor((new Date(tokenData.expires_at) - new Date()) / 1000)
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取令牌信息失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
Reference in New Issue
Block a user