fix(auth): accidentally logged out due to WebSocket authorization. (#6342)

* fix(auth): improve logging by including full context and error details

* fix(auth): enhance logging with error handling for token renewal

* feat(auth): init checkToken

* feat(auth): implement checkToken method with detailed token status and user information

* fix(auth): update check method to handle expired tokens and improve token renewal process
This commit is contained in:
Sheldon Guo 2025-03-04 06:54:01 +08:00 committed by GitHub
parent a52352719b
commit f2c3f54109
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 62 additions and 15 deletions

View File

@ -83,6 +83,14 @@ export abstract class Auth implements IAuth {
// The abstract methods are required to be implemented by all authentications.
abstract check(): Promise<Model>;
abstract checkToken(): Promise<{
tokenStatus: 'valid' | 'expired' | 'invalid';
user: Awaited<ReturnType<Auth['check']>>;
jti?: string;
temp: any;
roleName?: any;
signInTime?: number;
}>;
// The following methods are mainly designed for user authentications.
async signIn(): Promise<any> {}

View File

@ -70,10 +70,16 @@ export class BaseAuth extends Auth {
return /^[^@.<>"'/]{1,50}$/.test(username);
}
async check(): ReturnType<Auth['check']> {
const token = this.ctx.getBearerToken();
async checkToken(): Promise<{
tokenStatus: 'valid' | 'expired' | 'invalid';
user: Awaited<ReturnType<Auth['check']>>;
jti?: string;
temp: any;
roleName?: any;
signInTime?: number;
}> {
const cache = this.ctx.cache as Cache;
const token = this.ctx.getBearerToken();
if (!token) {
this.ctx.throw(401, {
message: this.ctx.t('Unauthenticated. Please sign in to continue.', { ns: localeNamespace }),
@ -127,7 +133,7 @@ export class BaseAuth extends Auth {
// api token check first
if (!temp) {
if (tokenStatus === 'valid') {
return user;
return { tokenStatus, user, temp };
} else {
this.ctx.throw(401, {
message: this.ctx.t('Your session has expired. Please sign in again.', { ns: localeNamespace }),
@ -164,11 +170,41 @@ export class BaseAuth extends Auth {
});
}
this.ctx.logger.info('token renewing', {
method: 'auth.check',
url: this.ctx.originalUrl,
currentJti: jti,
});
const isStreamRequest = this.ctx?.req?.headers?.accept === 'text/event-stream';
if (isStreamRequest) {
this.ctx.throw(401, {
message: 'Stream api not allow renew token.',
code: AuthErrorCode.SKIP_TOKEN_RENEW,
});
}
if (!jti) {
this.ctx.throw(401, {
message: this.ctx.t('Your session has expired. Please sign in again.', { ns: localeNamespace }),
code: AuthErrorCode.INVALID_TOKEN,
});
}
return { tokenStatus, user, jti, signInTime, temp };
}
return { tokenStatus, user, jti, signInTime, temp };
}
async check(): ReturnType<Auth['check']> {
const { tokenStatus, user, jti, temp, signInTime, roleName } = await this.checkToken();
if (tokenStatus === 'expired') {
const tokenPolicy = await this.tokenController.getConfig();
try {
this.ctx.logger.info('token renewing', {
method: 'auth.check',
url: this.ctx.originalUrl,
headers: JSON.stringify(this.ctx?.req?.headers),
jti,
});
const isStreamRequest = this.ctx?.req?.headers?.accept === 'text/event-stream';
@ -187,24 +223,23 @@ export class BaseAuth extends Auth {
}
const renewedResult = await this.tokenController.renew(jti);
this.ctx.logger.info('token renewed', {
method: 'auth.check',
url: this.ctx.originalUrl,
headers: JSON.stringify(this.ctx?.req?.headers),
oldJti: jti,
newJti: renewedResult.jti,
});
const expiresIn = Math.floor(tokenPolicy.tokenExpirationTime / 1000);
const newToken = this.jwt.sign(
{ userId, roleName, temp, signInTime, iat: Math.floor(renewedResult.issuedTime / 1000) },
{ userId: user.id, roleName, temp, signInTime, iat: Math.floor(renewedResult.issuedTime / 1000) },
{ jwtid: renewedResult.jti, expiresIn },
);
this.ctx.res.setHeader('x-new-token', newToken);
return user;
} catch (err) {
this.ctx.logger.info('token renew failed', {
this.ctx.logger.error('token renew failed', {
method: 'auth.check',
url: this.ctx.originalUrl,
err,
headers: JSON.stringify(this.ctx?.req?.headers),
jti,
});
const options =
err instanceof AuthError

View File

@ -154,11 +154,15 @@ export class PluginAuthServer extends Plugin {
cache: this.app.cache,
logger: this.app.logger,
log: this.app.log,
throw: (...args) => {
throw new Error(...args);
},
t: this.app.i18n.t,
} as any);
let user: Model;
try {
user = await auth.check();
user = (await auth.checkToken()).user;
} catch (error) {
if (!user) {
this.app.logger.error(error);