diff --git a/src/auth/models/user.ts b/src/auth/models/user.ts index f0cb228..04c5be9 100644 --- a/src/auth/models/user.ts +++ b/src/auth/models/user.ts @@ -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) { 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:', ''), diff --git a/src/modules/n5/index.ts b/src/modules/n5/index.ts index b98332e..3d7b0a8 100644 --- a/src/modules/n5/index.ts +++ b/src/modules/n5/index.ts @@ -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()) diff --git a/src/routes/user/jwks.ts b/src/routes/user/jwks.ts index 8ab6826..82ef78d 100644 --- a/src/routes/user/jwks.ts +++ b/src/routes/user/jwks.ts @@ -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) \ No newline at end of file