Last active
April 12, 2023 18:53
-
-
Save ShaharIlany/b0be76dae7caeace4cf23fb33d02e182 to your computer and use it in GitHub Desktop.
Blitz.js Auth Session Config for Redis
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { AuthServerPlugin, simpleRolesIsAuthorized } from "@blitzjs/auth" | |
import { setupBlitzServer } from "@blitzjs/next" | |
import { authConfig } from "./blitz-client" | |
import SessionStorage from "@/auth/SessionStorageRedis" | |
export const { api, gSSP } = setupBlitzServer({ | |
plugins: [ | |
AuthServerPlugin({ | |
...authConfig, | |
storage: SessionStorage, | |
}), | |
], | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { SessionConfigMethods, SessionModel } from "@blitzjs/auth" | |
import { loadEnvConfig } from "@next/env" | |
import IoRedis from "ioredis" | |
const notEmpty = <TValue>(value: TValue | null | undefined): value is TValue => | |
!(value === null || value === undefined) | |
const { REDIS_HOST, REDIS_PORT, REDIS_USER, REDIS_PASSWORD } = loadEnvConfig( | |
process.cwd() | |
).combinedEnv | |
const differenceInSeconds = (left: Date, right: Date) => | |
Math.floor((left.getTime() - right.getTime()) / 1000) | |
/** | |
* Global is used here to ensure the connection | |
* is cached across hot-reloads in development | |
* | |
* see https://github.com/vercel/next.js/discussions/12229#discussioncomment-83372 | |
*/ | |
let redisClient: Record<string, IoRedis> = global.redisClient | |
if (!redisClient) redisClient = global.redisClient = {} | |
const getRedisClient = () => { | |
if (!redisClient.authClient) { | |
redisClient.authClient = new IoRedis({ | |
port: Number(REDIS_PORT!), | |
host: REDIS_HOST!, | |
username: REDIS_USER!, | |
password: REDIS_PASSWORD!, | |
tls: {}, | |
}) | |
} | |
return redisClient.authClient | |
} | |
const getSession: SessionConfigMethods["getSession"] = async (handle: string) => { | |
try { | |
const client = getRedisClient() | |
const session = await client.get(`session:${handle}`) | |
if (!session) { | |
return null | |
} | |
const parsedSession = JSON.parse(session) as SessionModel | |
if (parsedSession.expiresAt) { | |
parsedSession.expiresAt = new Date(parsedSession.expiresAt) | |
const expiryInSeconds = differenceInSeconds(parsedSession.expiresAt!, new Date()) | |
await client.expire(`session:${parsedSession.handle}`, expiryInSeconds) | |
await client.expire(`user:${parsedSession.userId!}`, expiryInSeconds) | |
} | |
return parsedSession | |
} catch { | |
throw new Error("Can't get session") | |
} | |
} | |
const getSessions: SessionConfigMethods["getSessions"] = async (userId: string) => { | |
try { | |
const client = getRedisClient() | |
const sessionKeys = await client.lrange(`user:${userId}`, 0, -1) | |
const sessions = ( | |
await Promise.all( | |
sessionKeys.map(async (handle) => { | |
const session = await getSession(handle) | |
return session | |
}) | |
) | |
).filter(notEmpty) | |
return sessions | |
} catch { | |
throw new Error("Can't get sessions") | |
} | |
} | |
const createSession: SessionConfigMethods["createSession"] = async (session: SessionModel) => { | |
try { | |
const client = getRedisClient() | |
const expiryInSeconds = differenceInSeconds(session.expiresAt!, new Date()) | |
await client.set(`session:${session.handle}`, JSON.stringify(session), "EX", expiryInSeconds) | |
await client.lpush(`user:${session.userId}`, session.handle) | |
await client.expire(`user:${session.userId}`, expiryInSeconds) | |
return session | |
} catch { | |
throw new Error("Can't create session") | |
} | |
} | |
const deleteSession: SessionConfigMethods["deleteSession"] = async (handle: string) => { | |
try { | |
const client = getRedisClient() | |
const session = await getSession(handle) | |
if (session) { | |
await client.lrem(`user:${session.userId}`, 0, handle) | |
await client.del(`session:${session.handle}`) | |
return session | |
} | |
return undefined | |
} catch { | |
throw new Error("Can't delete session") | |
} | |
} | |
const updateSession: SessionConfigMethods["updateSession"] = async ( | |
handle: string, | |
session: SessionModel | |
) => { | |
try { | |
const client = getRedisClient() | |
const oldSession = await getSession(handle) | |
if (oldSession) { | |
const newSession = Object.assign(oldSession, session) | |
const expiryInSeconds = differenceInSeconds(newSession.expiresAt!, new Date()) | |
await client.set(`session:${handle}`, JSON.stringify(newSession), "EX", expiryInSeconds) | |
await client.expire(`user:${session.userId}`, expiryInSeconds) | |
return newSession | |
} else { | |
return undefined | |
} | |
} catch { | |
throw new Error("Can't update session") | |
} | |
} | |
export default { | |
getSession, | |
getSessions, | |
createSession, | |
deleteSession, | |
updateSession, | |
} as SessionConfigMethods |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment