feat(server): support refresh token (#15218)

This commit is contained in:
DarkSky
2026-07-12 02:39:42 +08:00
committed by GitHub
parent aea128f0b9
commit 02b25e05d8
80 changed files with 5866 additions and 745 deletions
+20
View File
@@ -15,6 +15,14 @@ end
return value
`;
const INCREASE_WITH_TTL_LUA = `
local value = redis.call("INCRBY", KEYS[1], ARGV[1])
if value == tonumber(ARGV[1]) or redis.call("PTTL", KEYS[1]) == -1 then
redis.call("PEXPIRE", KEYS[1], ARGV[2])
end
return value
`;
export function isValidCacheTtl(ttl: unknown): ttl is number {
return typeof ttl === 'number' && Number.isSafeInteger(ttl) && ttl > 0;
}
@@ -60,6 +68,18 @@ export class CacheProvider {
return this.redis.incrby(key, count).catch(() => 0);
}
async increaseWithTtl(
key: string,
ttl: number,
count: number = 1
): Promise<number> {
if (!isValidCacheTtl(ttl)) return 0;
return this.redis
.eval(INCREASE_WITH_TTL_LUA, 1, key, count, ttl)
.then(value => Number(value))
.catch(() => 0);
}
async decrease(key: string, count: number = 1): Promise<number> {
return this.redis.decrby(key, count).catch(() => 0);
}
+2
View File
@@ -29,10 +29,12 @@ export const CORS_ALLOWED_HEADERS = [
'authorization',
'content-type',
'x-affine-version',
'x-affine-client-kind',
'x-operation-name',
'x-request-id',
'x-captcha-token',
'x-captcha-challenge',
'x-captcha-provider',
'x-affine-csrf-token',
'x-requested-with',
'range',
@@ -422,6 +422,34 @@ export const USER_FRIENDLY_ERRORS = {
type: 'authentication_required',
message: 'You must sign in first to access this resource.',
},
access_token_expired: {
type: 'authentication_required',
message: 'The access token has expired.',
},
access_token_invalid: {
type: 'authentication_required',
message: 'The access token is invalid.',
},
auth_session_expired: {
type: 'authentication_required',
message: 'The auth session has expired.',
},
auth_session_revoked: {
type: 'authentication_required',
message: 'The auth session has been revoked.',
},
refresh_token_invalid: {
type: 'authentication_required',
message: 'The refresh token is invalid.',
},
refresh_token_reused: {
type: 'authentication_required',
message: 'The refresh token has already been used.',
},
auth_session_temporarily_unavailable: {
type: 'network_error',
message: 'Auth session service is temporarily unavailable.',
},
action_forbidden: {
type: 'action_forbidden',
message: 'You are not allowed to perform this action.',
@@ -274,6 +274,48 @@ export class AuthenticationRequired extends UserFriendlyError {
}
}
export class AccessTokenExpired extends UserFriendlyError {
constructor(message?: string) {
super('authentication_required', 'access_token_expired', message);
}
}
export class AccessTokenInvalid extends UserFriendlyError {
constructor(message?: string) {
super('authentication_required', 'access_token_invalid', message);
}
}
export class AuthSessionExpired extends UserFriendlyError {
constructor(message?: string) {
super('authentication_required', 'auth_session_expired', message);
}
}
export class AuthSessionRevoked extends UserFriendlyError {
constructor(message?: string) {
super('authentication_required', 'auth_session_revoked', message);
}
}
export class RefreshTokenInvalid extends UserFriendlyError {
constructor(message?: string) {
super('authentication_required', 'refresh_token_invalid', message);
}
}
export class RefreshTokenReused extends UserFriendlyError {
constructor(message?: string) {
super('authentication_required', 'refresh_token_reused', message);
}
}
export class AuthSessionTemporarilyUnavailable extends UserFriendlyError {
constructor(message?: string) {
super('network_error', 'auth_session_temporarily_unavailable', message);
}
}
export class ActionForbidden extends UserFriendlyError {
constructor(message?: string) {
super('action_forbidden', 'action_forbidden', message);
@@ -1200,6 +1242,13 @@ export enum ErrorNames {
INVALID_EMAIL_TOKEN,
LINK_EXPIRED,
AUTHENTICATION_REQUIRED,
ACCESS_TOKEN_EXPIRED,
ACCESS_TOKEN_INVALID,
AUTH_SESSION_EXPIRED,
AUTH_SESSION_REVOKED,
REFRESH_TOKEN_INVALID,
REFRESH_TOKEN_REUSED,
AUTH_SESSION_TEMPORARILY_UNAVAILABLE,
ACTION_FORBIDDEN,
ACCESS_DENIED,
EMAIL_VERIFICATION_REQUIRED,