feat(server): auto refresh session (#6613)

This commit is contained in:
EYHN
2024-04-19 07:00:12 +00:00
parent a2fa9149ff
commit 5e243de392
7 changed files with 82 additions and 16 deletions

View File

@@ -36,7 +36,7 @@ export class AuthGuard implements CanActivate, OnModuleInit {
}
async canActivate(context: ExecutionContext) {
const { req } = getRequestResponseFromContext(context);
const { req, res } = getRequestResponseFromContext(context);
// check cookie
let sessionToken: string | undefined =
@@ -51,7 +51,19 @@ export class AuthGuard implements CanActivate, OnModuleInit {
req.headers[AuthService.authUserSeqHeaderName]
);
const user = await this.auth.getUser(sessionToken, userSeq);
const { user, expiresAt } = await this.auth.getUser(
sessionToken,
userSeq
);
if (res && user && expiresAt) {
await this.auth.refreshUserSessionIfNeeded(
req,
res,
sessionToken,
user.id,
expiresAt
);
}
if (user) {
req.sid = sessionToken;

View File

@@ -145,24 +145,27 @@ export class AuthService implements OnApplicationBootstrap {
return sessionUser(user);
}
async getUser(token: string, seq = 0): Promise<CurrentUser | null> {
async getUser(
token: string,
seq = 0
): Promise<{ user: CurrentUser | null; expiresAt: Date | null }> {
const session = await this.getSession(token);
// no such session
if (!session) {
return null;
return { user: null, expiresAt: null };
}
const userSession = session.userSessions.at(seq);
// no such user session
if (!userSession) {
return null;
return { user: null, expiresAt: null };
}
// user session expired
if (userSession.expiresAt && userSession.expiresAt <= new Date()) {
return null;
return { user: null, expiresAt: null };
}
const user = await this.db.user.findUnique({
@@ -170,10 +173,10 @@ export class AuthService implements OnApplicationBootstrap {
});
if (!user) {
return null;
return { user: null, expiresAt: null };
}
return sessionUser(user);
return { user: sessionUser(user), expiresAt: userSession.expiresAt };
}
async getUserList(token: string) {
@@ -269,6 +272,43 @@ export class AuthService implements OnApplicationBootstrap {
});
}
async refreshUserSessionIfNeeded(
_req: Request,
res: Response,
sessionId: string,
userId: string,
expiresAt: Date,
ttr = this.config.auth.session.ttr
): Promise<boolean> {
if (expiresAt && expiresAt.getTime() - Date.now() > ttr * 1000) {
// no need to refresh
return false;
}
const newExpiresAt = new Date(
Date.now() + this.config.auth.session.ttl * 1000
);
await this.db.userSession.update({
where: {
sessionId_userId: {
sessionId,
userId,
},
},
data: {
expiresAt: newExpiresAt,
},
});
res.cookie(AuthService.sessionCookieName, sessionId, {
expires: newExpiresAt,
...this.cookieOptions,
});
return true;
}
async createUserSession(
user: { id: string },
existingSession?: string,

View File

@@ -240,6 +240,13 @@ export interface AFFiNEConfig {
* @default 15 days
*/
ttl: number;
/**
* Application auth time to refresh in seconds
*
* @default 7 days
*/
ttr: number;
};
/**

View File

@@ -153,6 +153,7 @@ export const getDefaultAFFiNEConfig: () => AFFiNEConfig = () => {
},
session: {
ttl: 15 * ONE_DAY_IN_SEC,
ttr: 7 * ONE_DAY_IN_SEC,
},
accessToken: {
ttl: 7 * ONE_DAY_IN_SEC,