feat: 更新JWKS token创建逻辑,支持refresh token选项

This commit is contained in:
2026-03-03 19:10:57 +08:00
parent bb4096ce7e
commit d2913dd32d
3 changed files with 24 additions and 14 deletions

View File

@@ -46,6 +46,7 @@ type TokenOptions = {
host?: string; // 主机信息
wx?: any;
loginWith?: string; // 登录方式,如 'cli', 'web', 'plugin' 等
hasRefreshToken?: boolean; // 是否需要 refresh token默认为 false
}
/**
* 用户模型,使用 Drizzle ORM
@@ -85,15 +86,15 @@ export class User {
/**
* 创建JWKS token的通用方法
*/
static async createJwksTokenResponse(user: { id: string; username: string }, opts: { expire?: number, save?: boolean } = {}) {
static async createJwksTokenResponse(user: { id: string; username: string }, opts: { expire?: number, hasRefreshToken?: boolean } = {}) {
const expiresIn = opts?.expire ?? JWKS_TOKEN_EXPIRY;
const save = opts?.save ?? true;
const hasRefreshToken = opts?.hasRefreshToken ?? true;
const accessToken = await jwksManager.sign({
sub: 'user:' + user.id,
name: user.username,
exp: Math.floor(Date.now() / 1000) + expiresIn,
});
if (save) {
if (hasRefreshToken) {
await oauth.setJwksToken(accessToken, { id: user.id, expire: expiresIn });
}
@@ -113,6 +114,7 @@ export class User {
async createToken(uid?: string, loginType?: 'default' | 'plugin' | 'month' | 'season' | 'year' | 'week' | 'jwks', opts: TokenOptions = {}) {
const { id, username, type } = this;
const hasRefreshToken = opts.hasRefreshToken ?? true;
const oauthUser: OauthUser = {
id,
username,
@@ -126,7 +128,7 @@ export class User {
if (loginType === 'jwks') {
return await User.createJwksTokenResponse(this, opts);
}
const token = await oauth.generateToken(oauthUser, { type: loginType, hasRefreshToken: true, ...opts });
const token = await oauth.generateToken(oauthUser, { type: loginType, hasRefreshToken, ...opts });
return {
type: 'default',
...token,
@@ -140,6 +142,17 @@ export class User {
static async verifyToken(token: string) {
return await UserSecret.verifyToken(token);
}
static async checkJwksValid(token: string) {
const verified = await User.verifyToken(token);
let isValid = false;
if (verified) {
isValid = true;
}
const jwksToken = await oauth.getJwksToken(token);
if (!isValid && !jwksToken) {
throw new CustomError('Invalid refresh token');
}
}
/**
* 刷新token
* @param refreshToken
@@ -150,10 +163,7 @@ export class User {
let jwsRefreshToken = accessToken || refreshToken;
if (oauth.getTokenType(jwsRefreshToken) === 'jwks') {
// 可能是 jwks token
const jwksToken = await oauth.getJwksToken(jwsRefreshToken);
if (!jwksToken) {
throw new CustomError('Invalid refresh token');
}
await User.checkJwksValid(jwsRefreshToken);
const decoded = await jwksManager.decode(jwsRefreshToken);
return await User.createJwksTokenResponse({
id: decoded.sub.replace('user:', ''),
@@ -212,10 +222,7 @@ export class User {
static async resetToken(refreshToken: string, expand?: Record<string, any>) {
if (oauth.getTokenType(refreshToken) === 'jwks') {
// 可能是 jwks token
const jwksToken = await oauth.getJwksToken(refreshToken);
if (!jwksToken) {
throw new CustomError('Invalid refresh token');
}
await User.checkJwksValid(refreshToken);
const decoded = await jwksManager.decode(refreshToken);
return await User.createJwksTokenResponse({
id: decoded.sub.replace('user:', ''),

View File

@@ -34,7 +34,7 @@ export const N5Proxy = async (req: IncomingMessage, res: ServerResponse, opts?:
}
try {
const user = await User.findByPk(userId);
const token = await User.createJwksTokenResponse({ id: userId, username: user?.username || '' }, { save: false });
const token = await User.createJwksTokenResponse({ id: userId, username: user?.username || '' }, { hasRefreshToken: false });
const urlObj = new URL(link);
urlObj.searchParams.set('token', token.accessToken);
const resultLink = await fetch(urlObj.toString(), { method: 'GET' }).then(res => res.json())

View File

@@ -9,12 +9,14 @@ app.route({
middleware: ['auth'],
metadata: {
args: {
loginType: z.enum(['jwks']).optional(),
loginType: z.enum(['jwks']).optional().describe('登录类型默认为jwks'),
hasRefreshToken: z.boolean().optional().describe('是否需要refresh token默认为false'),
}
}
}).define(async (ctx) => {
const user = await UserModel.getUserByToken(ctx.query.token);
const loginType = ctx.query?.loginType ?? 'jwks';
const hasRefreshToken = ctx.query?.hasRefreshToken ?? false;
if (!user) {
ctx.throw(404, 'user not found');
}
@@ -28,6 +30,7 @@ app.route({
}
const value = await user.createToken(null, loginType, {
expire: expire, // 24小时过期
hasRefreshToken: hasRefreshToken,
})
ctx.body = value
}).addTo(app)