mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-20 20:29:06 +00:00
refactor: remove Redis dependencies from frontend authentication
- Replace Redis/Redlock with in-memory cache for token management - Remove @vercel/kv, ioredis, and redlock dependencies from package.json - Implement simple lock mechanism for concurrent token refresh prevention - Use Map-based cache with TTL for token storage - Maintain same authentication flow without external dependencies This simplifies the infrastructure requirements and removes the need for Redis while maintaining the same functionality through in-memory caching.
This commit is contained in:
@@ -1,26 +1,19 @@
|
||||
// import { kv } from "@vercel/kv";
|
||||
import Redlock, { ResourceLockedError } from "redlock";
|
||||
import { AuthOptions } from "next-auth";
|
||||
import AuthentikProvider from "next-auth/providers/authentik";
|
||||
import { JWT } from "next-auth/jwt";
|
||||
import { JWTWithAccessToken, CustomSession } from "./types";
|
||||
import Redis from "ioredis";
|
||||
|
||||
const PRETIMEOUT = 60; // seconds before token expires to refresh it
|
||||
const DEFAULT_REDIS_KEY_TIMEOUT = 60 * 60 * 24 * 30; // 30 days (refresh token expires in 30 days)
|
||||
const kv = new Redis(process.env.KV_URL || "", {
|
||||
tls: {},
|
||||
});
|
||||
const redlock = new Redlock([kv], {});
|
||||
|
||||
redlock.on("error", (error) => {
|
||||
if (error instanceof ResourceLockedError) {
|
||||
return;
|
||||
}
|
||||
// Simple in-memory cache for tokens (in production, consider using a proper cache solution)
|
||||
const tokenCache = new Map<
|
||||
string,
|
||||
{ token: JWTWithAccessToken; timestamp: number }
|
||||
>();
|
||||
const TOKEN_CACHE_TTL = 60 * 60 * 24 * 30 * 1000; // 30 days in milliseconds
|
||||
|
||||
// Log all other errors.
|
||||
console.error(error);
|
||||
});
|
||||
// Simple lock mechanism to prevent concurrent token refreshes
|
||||
const refreshLocks = new Map<string, Promise<JWTWithAccessToken>>();
|
||||
|
||||
export const authOptions: AuthOptions = {
|
||||
providers: [
|
||||
@@ -51,12 +44,11 @@ export const authOptions: AuthOptions = {
|
||||
accessTokenExpires: expiresAt * 1000,
|
||||
refreshToken: account.refresh_token,
|
||||
};
|
||||
kv.set(
|
||||
`token:${jwtToken.sub}`,
|
||||
JSON.stringify(jwtToken),
|
||||
"EX",
|
||||
DEFAULT_REDIS_KEY_TIMEOUT,
|
||||
);
|
||||
// Store in memory cache
|
||||
tokenCache.set(`token:${jwtToken.sub}`, {
|
||||
token: jwtToken,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
return jwtToken;
|
||||
}
|
||||
|
||||
@@ -65,7 +57,7 @@ export const authOptions: AuthOptions = {
|
||||
}
|
||||
|
||||
// access token has expired, try to update it
|
||||
return await redisLockedrefreshAccessToken(token);
|
||||
return await lockedRefreshAccessToken(token);
|
||||
},
|
||||
async session({ session, token }) {
|
||||
const extendedToken = token as JWTWithAccessToken;
|
||||
@@ -83,32 +75,51 @@ export const authOptions: AuthOptions = {
|
||||
},
|
||||
};
|
||||
|
||||
async function redisLockedrefreshAccessToken(token: JWT) {
|
||||
return await redlock.using(
|
||||
[token.sub as string, "jwt-refresh"],
|
||||
5000,
|
||||
async () => {
|
||||
const redisToken = await kv.get(`token:${token.sub}`);
|
||||
const currentToken = JSON.parse(
|
||||
redisToken as string,
|
||||
) as JWTWithAccessToken;
|
||||
async function lockedRefreshAccessToken(
|
||||
token: JWT,
|
||||
): Promise<JWTWithAccessToken> {
|
||||
const lockKey = `${token.sub}-refresh`;
|
||||
|
||||
// if there is multiple requests for the same token, it may already have been refreshed
|
||||
if (Date.now() < currentToken.accessTokenExpires) {
|
||||
return currentToken;
|
||||
// Check if there's already a refresh in progress
|
||||
const existingRefresh = refreshLocks.get(lockKey);
|
||||
if (existingRefresh) {
|
||||
return existingRefresh;
|
||||
}
|
||||
|
||||
// Create a new refresh promise
|
||||
const refreshPromise = (async () => {
|
||||
try {
|
||||
// Check cache for recent token
|
||||
const cached = tokenCache.get(`token:${token.sub}`);
|
||||
if (cached) {
|
||||
// Clean up old cache entries
|
||||
if (Date.now() - cached.timestamp > TOKEN_CACHE_TTL) {
|
||||
tokenCache.delete(`token:${token.sub}`);
|
||||
} else if (Date.now() < cached.token.accessTokenExpires) {
|
||||
// Token is still valid
|
||||
return cached.token;
|
||||
}
|
||||
}
|
||||
|
||||
// now really do the request
|
||||
// Refresh the token
|
||||
const currentToken = cached?.token || (token as JWTWithAccessToken);
|
||||
const newToken = await refreshAccessToken(currentToken);
|
||||
await kv.set(
|
||||
`token:${currentToken.sub}`,
|
||||
JSON.stringify(newToken),
|
||||
"EX",
|
||||
DEFAULT_REDIS_KEY_TIMEOUT,
|
||||
);
|
||||
|
||||
// Update cache
|
||||
tokenCache.set(`token:${token.sub}`, {
|
||||
token: newToken,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
return newToken;
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
// Clean up the lock after a short delay
|
||||
setTimeout(() => refreshLocks.delete(lockKey), 100);
|
||||
}
|
||||
})();
|
||||
|
||||
refreshLocks.set(lockKey, refreshPromise);
|
||||
return refreshPromise;
|
||||
}
|
||||
|
||||
async function refreshAccessToken(token: JWT): Promise<JWTWithAccessToken> {
|
||||
|
||||
@@ -19,14 +19,12 @@
|
||||
"@sentry/nextjs": "^7.77.0",
|
||||
"@tanstack/react-query": "^5.85.5",
|
||||
"@vercel/edge-config": "^0.4.1",
|
||||
"@vercel/kv": "^2.0.0",
|
||||
"@whereby.com/browser-sdk": "^3.3.4",
|
||||
"autoprefixer": "10.4.20",
|
||||
"axios": "^1.8.2",
|
||||
"eslint": "^9.33.0",
|
||||
"eslint-config-next": "^14.2.31",
|
||||
"fontawesome": "^5.6.3",
|
||||
"ioredis": "^5.4.1",
|
||||
"jest-worker": "^29.6.2",
|
||||
"lucide-react": "^0.525.0",
|
||||
"next": "^14.2.30",
|
||||
@@ -44,7 +42,6 @@
|
||||
"react-markdown": "^9.0.0",
|
||||
"react-qr-code": "^2.0.12",
|
||||
"react-select-search": "^4.1.7",
|
||||
"redlock": "^5.0.0-beta.2",
|
||||
"sass": "^1.63.6",
|
||||
"simple-peer": "^9.11.1",
|
||||
"tailwindcss": "^3.3.2",
|
||||
|
||||
148
www/pnpm-lock.yaml
generated
148
www/pnpm-lock.yaml
generated
@@ -31,9 +31,6 @@ importers:
|
||||
"@vercel/edge-config":
|
||||
specifier: ^0.4.1
|
||||
version: 0.4.1
|
||||
"@vercel/kv":
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
"@whereby.com/browser-sdk":
|
||||
specifier: ^3.3.4
|
||||
version: 3.13.1(@types/react@18.2.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@@ -52,9 +49,6 @@ importers:
|
||||
fontawesome:
|
||||
specifier: ^5.6.3
|
||||
version: 5.6.3
|
||||
ioredis:
|
||||
specifier: ^5.4.1
|
||||
version: 5.7.0
|
||||
jest-worker:
|
||||
specifier: ^29.6.2
|
||||
version: 29.7.0
|
||||
@@ -106,9 +100,6 @@ importers:
|
||||
react-select-search:
|
||||
specifier: ^4.1.7
|
||||
version: 4.1.8(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
redlock:
|
||||
specifier: ^5.0.0-beta.2
|
||||
version: 5.0.0-beta.2
|
||||
sass:
|
||||
specifier: ^1.63.6
|
||||
version: 1.90.0
|
||||
@@ -565,12 +556,6 @@ packages:
|
||||
integrity: sha512-p+Zh1sb6EfrfVaS86jlHGQ9HA66fJhV9x5LiE5vCbZtXEHAuhcmUZUdZ4WrFpUBfNalr2OkAJI5AcKEQF+Lebw==,
|
||||
}
|
||||
|
||||
"@ioredis/commands@1.3.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-M/T6Zewn7sDaBQEqIZ8Rb+i9y8qfGmq+5SDFSf9sA2lUZTmdDLVdOiQaeDp+Q4wElZ9HG1GAX5KhDaidp6LQsQ==,
|
||||
}
|
||||
|
||||
"@isaacs/cliui@8.0.2":
|
||||
resolution:
|
||||
{
|
||||
@@ -1907,12 +1892,6 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
"@upstash/redis@1.35.3":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-hSjv66NOuahW3MisRGlSgoszU2uONAY2l5Qo3Sae8OT3/Tng9K+2/cBRuyPBX8egwEGcNNCF9+r0V6grNnhL+w==,
|
||||
}
|
||||
|
||||
"@vercel/build-utils@8.4.12":
|
||||
resolution:
|
||||
{
|
||||
@@ -1969,13 +1948,6 @@ packages:
|
||||
integrity: sha512-IPAVaALuGAzt2apvTtBs5tB+8zZRzn/yG3AGp8dFyCsw/v5YOuk0Q5s8Z3fayLvJbFpjrKtqRNDZzVJBBU3MrQ==,
|
||||
}
|
||||
|
||||
"@vercel/kv@2.0.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-zdVrhbzZBYo5d1Hfn4bKtqCeKf0FuzW8rSHauzQVMUgv1+1JOwof2mWcBuI+YMJy8s0G0oqAUfQ7HgUDzb8EbA==,
|
||||
}
|
||||
engines: { node: ">=14.6" }
|
||||
|
||||
"@vercel/next@4.3.18":
|
||||
resolution:
|
||||
{
|
||||
@@ -3053,13 +3025,6 @@ packages:
|
||||
}
|
||||
engines: { node: ">=6" }
|
||||
|
||||
cluster-key-slot@1.1.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==,
|
||||
}
|
||||
engines: { node: ">=0.10.0" }
|
||||
|
||||
code-block-writer@10.1.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -3304,13 +3269,6 @@ packages:
|
||||
integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==,
|
||||
}
|
||||
|
||||
denque@2.1.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==,
|
||||
}
|
||||
engines: { node: ">=0.10" }
|
||||
|
||||
depd@1.1.2:
|
||||
resolution:
|
||||
{
|
||||
@@ -4549,13 +4507,6 @@ packages:
|
||||
}
|
||||
engines: { node: ">= 0.4" }
|
||||
|
||||
ioredis@5.7.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-NUcA93i1lukyXU+riqEyPtSEkyFq8tX90uL659J+qpCZ3rEdViB/APC58oAhIh3+bJln2hzdlZbBZsGNrlsR8g==,
|
||||
}
|
||||
engines: { node: ">=12.22.0" }
|
||||
|
||||
ip-address@9.0.5:
|
||||
resolution:
|
||||
{
|
||||
@@ -5052,18 +5003,6 @@ packages:
|
||||
}
|
||||
engines: { node: ">=10" }
|
||||
|
||||
lodash.defaults@4.2.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==,
|
||||
}
|
||||
|
||||
lodash.isarguments@3.1.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==,
|
||||
}
|
||||
|
||||
lodash.merge@4.6.2:
|
||||
resolution:
|
||||
{
|
||||
@@ -5542,12 +5481,6 @@ packages:
|
||||
sass:
|
||||
optional: true
|
||||
|
||||
node-abort-controller@3.1.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==,
|
||||
}
|
||||
|
||||
node-addon-api@7.1.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -6348,27 +6281,6 @@ packages:
|
||||
}
|
||||
engines: { node: ">= 14.18.0" }
|
||||
|
||||
redis-errors@1.2.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==,
|
||||
}
|
||||
engines: { node: ">=4" }
|
||||
|
||||
redis-parser@3.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==,
|
||||
}
|
||||
engines: { node: ">=4" }
|
||||
|
||||
redlock@5.0.0-beta.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-2RDWXg5jgRptDrB1w9O/JgSZC0j7y4SlaXnor93H/UJm/QyDiFgBKNtrh0TI6oCXqYSaSoXxFh6Sd3VtYfhRXw==,
|
||||
}
|
||||
engines: { node: ">=12" }
|
||||
|
||||
redux-thunk@3.1.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -6750,12 +6662,6 @@ packages:
|
||||
}
|
||||
engines: { node: ">=6" }
|
||||
|
||||
standard-as-callback@2.1.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==,
|
||||
}
|
||||
|
||||
stat-mode@0.3.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -7246,12 +7152,6 @@ packages:
|
||||
}
|
||||
engines: { node: ">= 0.4" }
|
||||
|
||||
uncrypto@0.1.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==,
|
||||
}
|
||||
|
||||
undici-types@7.10.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -8012,8 +7912,6 @@ snapshots:
|
||||
dependencies:
|
||||
"@swc/helpers": 0.5.17
|
||||
|
||||
"@ioredis/commands@1.3.0": {}
|
||||
|
||||
"@isaacs/cliui@8.0.2":
|
||||
dependencies:
|
||||
string-width: 5.1.2
|
||||
@@ -8841,10 +8739,6 @@ snapshots:
|
||||
"@unrs/resolver-binding-win32-x64-msvc@1.11.1":
|
||||
optional: true
|
||||
|
||||
"@upstash/redis@1.35.3":
|
||||
dependencies:
|
||||
uncrypto: 0.1.3
|
||||
|
||||
"@vercel/build-utils@8.4.12": {}
|
||||
|
||||
"@vercel/edge-config-fs@0.1.0": {}
|
||||
@@ -8901,10 +8795,6 @@ snapshots:
|
||||
"@vercel/static-config": 3.0.0
|
||||
ts-morph: 12.0.0
|
||||
|
||||
"@vercel/kv@2.0.0":
|
||||
dependencies:
|
||||
"@upstash/redis": 1.35.3
|
||||
|
||||
"@vercel/next@4.3.18":
|
||||
dependencies:
|
||||
"@vercel/nft": 0.27.3
|
||||
@@ -9900,8 +9790,6 @@ snapshots:
|
||||
|
||||
clsx@2.1.1: {}
|
||||
|
||||
cluster-key-slot@1.1.2: {}
|
||||
|
||||
code-block-writer@10.1.1: {}
|
||||
|
||||
color-convert@2.0.1:
|
||||
@@ -10022,8 +9910,6 @@ snapshots:
|
||||
|
||||
delegates@1.0.0: {}
|
||||
|
||||
denque@2.1.0: {}
|
||||
|
||||
depd@1.1.2: {}
|
||||
|
||||
dequal@2.0.3: {}
|
||||
@@ -10903,20 +10789,6 @@ snapshots:
|
||||
hasown: 2.0.2
|
||||
side-channel: 1.1.0
|
||||
|
||||
ioredis@5.7.0:
|
||||
dependencies:
|
||||
"@ioredis/commands": 1.3.0
|
||||
cluster-key-slot: 1.1.2
|
||||
debug: 4.4.1(supports-color@9.4.0)
|
||||
denque: 2.1.0
|
||||
lodash.defaults: 4.2.0
|
||||
lodash.isarguments: 3.1.0
|
||||
redis-errors: 1.2.0
|
||||
redis-parser: 3.0.0
|
||||
standard-as-callback: 2.1.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
ip-address@9.0.5:
|
||||
dependencies:
|
||||
jsbn: 1.1.0
|
||||
@@ -11206,10 +11078,6 @@ snapshots:
|
||||
dependencies:
|
||||
p-locate: 5.0.0
|
||||
|
||||
lodash.defaults@4.2.0: {}
|
||||
|
||||
lodash.isarguments@3.1.0: {}
|
||||
|
||||
lodash.merge@4.6.2: {}
|
||||
|
||||
longest-streak@3.1.0: {}
|
||||
@@ -11609,8 +11477,6 @@ snapshots:
|
||||
- "@babel/core"
|
||||
- babel-plugin-macros
|
||||
|
||||
node-abort-controller@3.1.1: {}
|
||||
|
||||
node-addon-api@7.1.1:
|
||||
optional: true
|
||||
|
||||
@@ -12057,16 +11923,6 @@ snapshots:
|
||||
|
||||
readdirp@4.1.2: {}
|
||||
|
||||
redis-errors@1.2.0: {}
|
||||
|
||||
redis-parser@3.0.0:
|
||||
dependencies:
|
||||
redis-errors: 1.2.0
|
||||
|
||||
redlock@5.0.0-beta.2:
|
||||
dependencies:
|
||||
node-abort-controller: 3.1.1
|
||||
|
||||
redux-thunk@3.1.0(redux@5.0.1):
|
||||
dependencies:
|
||||
redux: 5.0.1
|
||||
@@ -12316,8 +12172,6 @@ snapshots:
|
||||
dependencies:
|
||||
type-fest: 0.7.1
|
||||
|
||||
standard-as-callback@2.1.0: {}
|
||||
|
||||
stat-mode@0.3.0: {}
|
||||
|
||||
statuses@1.5.0: {}
|
||||
@@ -12679,8 +12533,6 @@ snapshots:
|
||||
has-symbols: 1.1.0
|
||||
which-boxed-primitive: 1.1.1
|
||||
|
||||
uncrypto@0.1.3: {}
|
||||
|
||||
undici-types@7.10.0: {}
|
||||
|
||||
undici@5.28.4:
|
||||
|
||||
Reference in New Issue
Block a user