Skip to content

Commit f4333e2

Browse files
feat: Implement invalidateAllSessions function in Basic API (#1767)
Co-authored-by: pilcrowOnPaper <[email protected]>
1 parent 999c797 commit f4333e2

File tree

6 files changed

+118
-4
lines changed

6 files changed

+118
-4
lines changed

pages/sessions/basic-api/drizzle-orm.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ export async function invalidateSession(sessionId: string): Promise<void> {
132132
// TODO
133133
}
134134

135+
export async function invalidateAllSessions(userId: number): Promise<void> {
136+
// TODO
137+
}
138+
135139
export type SessionValidationResult =
136140
| { session: Session; user: User }
137141
| { session: null; user: null };
@@ -230,14 +234,18 @@ export async function validateSessionToken(token: string): Promise<SessionValida
230234
Finally, invalidate sessions by simply deleting it from the database.
231235

232236
```ts
233-
import { eq } from "drizzle-orm";
234237
import { db, userTable, sessionTable } from "./db.js";
238+
import { eq } from "drizzle-orm";
235239

236240
// ...
237241

238242
export async function invalidateSession(sessionId: string): Promise<void> {
239243
await db.delete(sessionTable).where(eq(sessionTable.id, sessionId));
240244
}
245+
246+
export async function invalidateAllSessions(userId: number): Promise<void> {
247+
await db.delete(sessionTable).where(eq(sessionTable.userId, userId));
248+
}
241249
```
242250

243251
Here's the full code:
@@ -299,6 +307,10 @@ export async function invalidateSession(sessionId: string): Promise<void> {
299307
await db.delete(sessionTable).where(eq(sessionTable.id, sessionId));
300308
}
301309

310+
export async function invalidateAllSessions(userId: number): Promise<void> {
311+
await db.delete(sessionTable).where(eq(sessionTable.userId, userId));
312+
}
313+
302314
export type SessionValidationResult =
303315
| { session: Session; user: User }
304316
| { session: null; user: null };

pages/sessions/basic-api/mysql.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ export async function invalidateSession(sessionId: string): Promise<void> {
5656
// TODO
5757
}
5858

59+
export async function invalidateAllSessions(userId: number): Promise<void> {
60+
// TODO
61+
}
62+
5963
export type SessionValidationResult =
6064
| { session: Session; user: User }
6165
| { session: null; user: null };
@@ -179,6 +183,10 @@ import { db } from "./db.js";
179183
export async function invalidateSession(sessionId: string): Promise<void> {
180184
await db.execute("DELETE FROM user_session WHERE id = ?", sessionId);
181185
}
186+
187+
export async function invalidateAllSessions(userId: number): Promise<void> {
188+
await db.execute("DELETE FROM user_session WHERE user_id = ?", userId);
189+
}
182190
```
183191

184192
Here's the full code:
@@ -247,6 +255,10 @@ export async function invalidateSession(sessionId: string): Promise<void> {
247255
await db.execute("DELETE FROM user_session WHERE id = ?", sessionId);
248256
}
249257

258+
export async function invalidateAllSessions(userId: number): Promise<void> {
259+
await db.execute("DELETE FROM user_session WHERE user_id = ?", userId);
260+
}
261+
250262
export type SessionValidationResult =
251263
| { session: Session; user: User }
252264
| { session: null; user: null };

pages/sessions/basic-api/postgresql.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ export async function invalidateSession(sessionId: string): Promise<void> {
5656
// TODO
5757
}
5858

59+
export async function invalidateAllSessions(userId: number): Promise<void> {
60+
// TODO
61+
}
62+
5963
export type SessionValidationResult =
6064
| { session: Session; user: User }
6165
| { session: null; user: null };
@@ -179,6 +183,10 @@ import { db } from "./db.js";
179183
export async function invalidateSession(sessionId: string): Promise<void> {
180184
await db.execute("DELETE FROM user_session WHERE id = ?", sessionId);
181185
}
186+
187+
export async function invalidateAllSessions(userId: number): Promise<void> {
188+
await db.execute("DELETE FROM user_session WHERE user_id = ?", userId);
189+
}
182190
```
183191

184192
Here's the full code:
@@ -247,6 +255,10 @@ export async function invalidateSession(sessionId: string): Promise<void> {
247255
await db.execute("DELETE FROM user_session WHERE id = ?", sessionId);
248256
}
249257

258+
export async function invalidateAllSessions(userId: number): Promise<void> {
259+
await db.execute("DELETE FROM user_session WHERE user_id = ?", userId);
260+
}
261+
250262
export type SessionValidationResult =
251263
| { session: Session; user: User }
252264
| { session: null; user: null };

pages/sessions/basic-api/prisma.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ export async function invalidateSession(sessionId: string): Promise<void> {
5858
// TODO
5959
}
6060

61+
export async function invalidateAllSessions(userId: number): Promise<void> {
62+
// TODO
63+
}
64+
6165
export type SessionValidationResult =
6266
| { session: Session; user: User }
6367
| { session: null; user: null };
@@ -168,6 +172,14 @@ import { prisma } from "./db.js";
168172
export async function invalidateSession(sessionId: string): Promise<void> {
169173
await prisma.session.delete({ where: { id: sessionId } });
170174
}
175+
176+
export async function invalidateAllSessions(userId: number): Promise<void> {
177+
await prisma.session.deleteMany({
178+
where: {
179+
userId: userId
180+
}
181+
});
182+
}
171183
```
172184

173185
Here's the full code:
@@ -235,6 +247,14 @@ export async function invalidateSession(sessionId: string): Promise<void> {
235247
await prisma.session.delete({ where: { id: sessionId } });
236248
}
237249

250+
export async function invalidateAllSessions(userId: number): Promise<void> {
251+
await prisma.session.deleteMany({
252+
where: {
253+
userId: userId
254+
}
255+
});
256+
}
257+
238258
export type SessionValidationResult =
239259
| { session: Session; user: User }
240260
| { session: null; user: null };

pages/sessions/basic-api/redis.md

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ export async function invalidateSession(sessionId: string): Promise<void> {
3333
// TODO
3434
}
3535

36+
export async function invalidateAllSessions(userId: number): Promise<void> {
37+
// TODO
38+
}
39+
3640
export interface Session {
3741
id: string;
3842
userId: number;
@@ -63,7 +67,7 @@ export function generateSessionToken(): string {
6367

6468
> You can use UUID v4 here but the RFC does not mandate that IDs are generated using a secure random source. Do not use libraries that are not clear on the source they use. Do not use other UUID versions as they do not offer the same entropy size as v4. Consider using [`Crypto.randomUUID()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID).
6569
66-
The session ID will be SHA-256 hash of the token. We'll set the expiration to 30 days.
70+
The session ID will be SHA-256 hash of the token. We'll set the expiration to 30 days. We'll also keep a list of sessions linked to each user.
6771

6872
```ts
6973
import { redis } from "./redis.js";
@@ -90,6 +94,8 @@ export async function createSession(token: string, userId: number): Promise<Sess
9094
EXAT: Math.floor(session.expiresAt / 1000)
9195
}
9296
);
97+
await redis.sadd(`user_sessions:${userId}`, sessionId);
98+
9399
return session;
94100
}
95101
```
@@ -114,6 +120,7 @@ export async function validateSessionToken(token: string): Promise<Session | nul
114120
if (item === null) {
115121
return null;
116122
}
123+
117124
const result = JSON.parse(item);
118125
const session: Session = {
119126
id: result.id,
@@ -122,6 +129,7 @@ export async function validateSessionToken(token: string): Promise<Session | nul
122129
};
123130
if (Date.now() >= session.expiresAt.getTime()) {
124131
await redis.delete(`session:${sessionId}`);
132+
await redis.srem(`user_sessions:${userId}`, sessionId);
125133
return null;
126134
}
127135
if (Date.now() >= session.expiresAt.getTime() - 1000 * 60 * 60 * 24 * 15) {
@@ -149,8 +157,25 @@ import { redis } from "./redis.js";
149157

150158
// ...
151159

152-
export async function invalidateSession(sessionId: string): Promise<void> {
160+
export async function invalidateSession(sessionId: string, userId: number): Promise<void> {
153161
await redis.delete(`session:${sessionId}`);
162+
await redis.srem(`user_sessions:${userId}`, sessionId);
163+
}
164+
165+
export async function invalidateAllSessions(userId: number): Promise<void> {
166+
const sessionIds = await redis.smembers(`user_sessions:${userId}`);
167+
if (sessionIds.length < 1) {
168+
return;
169+
}
170+
171+
const pipeline = redis.pipeline();
172+
173+
for (const sessionId of sessionIds) {
174+
pipeline.unlink(`session:${sessionId}`);
175+
}
176+
pipeline.unlink(`user_sessions:${userId}`);
177+
178+
await pipeline.exec();
154179
}
155180
```
156181

@@ -186,6 +211,8 @@ export async function createSession(token: string, userId: number): Promise<Sess
186211
EXAT: Math.floor(session.expiresAt / 1000)
187212
}
188213
);
214+
await redis.sadd(`user_sessions:${userId}`, sessionId);
215+
189216
return session;
190217
}
191218

@@ -195,6 +222,7 @@ export async function validateSessionToken(token: string): Promise<Session | nul
195222
if (item === null) {
196223
return null;
197224
}
225+
198226
const result = JSON.parse(item);
199227
const session: Session = {
200228
id: result.id,
@@ -203,6 +231,7 @@ export async function validateSessionToken(token: string): Promise<Session | nul
203231
};
204232
if (Date.now() >= session.expiresAt.getTime()) {
205233
await redis.delete(`session:${sessionId}`);
234+
await redis.srem(`user_sessions:${userId}`, sessionId);
206235
return null;
207236
}
208237
if (Date.now() >= session.expiresAt.getTime() - 1000 * 60 * 60 * 24 * 15) {
@@ -222,8 +251,25 @@ export async function validateSessionToken(token: string): Promise<Session | nul
222251
return session;
223252
}
224253

225-
export async function invalidateSession(sessionId: string): Promise<void> {
254+
export async function invalidateSession(sessionId: string, userId: number): Promise<void> {
226255
await redis.delete(`session:${sessionId}`);
256+
await redis.srem(`user_sessions:${userId}`, sessionId);
257+
}
258+
259+
export async function invalidateAllSessions(userId: number): Promise<void> {
260+
const sessionIds = await redis.smembers(`user_sessions:${userId}`);
261+
if (sessionIds.length < 1) {
262+
return;
263+
}
264+
265+
const pipeline = redis.pipeline();
266+
267+
for (const sessionId of sessionIds) {
268+
pipeline.unlink(`session:${sessionId}`);
269+
}
270+
pipeline.unlink(`user_sessions:${userId}`);
271+
272+
await pipeline.exec();
227273
}
228274

229275
export interface Session {

pages/sessions/basic-api/sqlite.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ export function invalidateSession(sessionId: string): void {
5656
// TODO
5757
}
5858

59+
export async function invalidateAllSessions(userId: number): Promise<void> {
60+
// TODO
61+
}
62+
5963
export type SessionValidationResult =
6064
| { session: Session; user: User }
6165
| { session: null; user: null };
@@ -179,6 +183,10 @@ import { db } from "./db.js";
179183
export function invalidateSession(sessionId: string): void {
180184
db.execute("DELETE FROM session WHERE id = ?", sessionId);
181185
}
186+
187+
export async function invalidateAllSessions(userId: number): Promise<void> {
188+
await db.execute("DELETE FROM user_session WHERE user_id = ?", userId);
189+
}
182190
```
183191

184192
Here's the full code:
@@ -247,6 +255,10 @@ export function invalidateSession(sessionId: string): void {
247255
db.execute("DELETE FROM session WHERE id = ?", sessionId);
248256
}
249257

258+
export async function invalidateAllSessions(userId: number): Promise<void> {
259+
await db.execute("DELETE FROM user_session WHERE user_id = ?", userId);
260+
}
261+
250262
export type SessionValidationResult =
251263
| { session: Session; user: User }
252264
| { session: null; user: null };

0 commit comments

Comments
 (0)