mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-07-01 18:52:20 +08:00
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:
parent
a52352719b
commit
f2c3f54109
@ -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> {}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user