kaneo (minimalist kanban) fork to experiment adding a tangled integration github.com/usekaneo/kaneo
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at cd7cada2f86b4e866a15b4323bb8d6d7ab5bba8b 94 lines 2.4 kB view raw
1import { createHash, randomUUID } from "node:crypto"; 2import { createId } from "@paralleldrive/cuid2"; 3import db from "../database"; 4import { sessionTable } from "../database/schema"; 5 6type RegisteredClient = { 7 clientId: string; 8 redirectUris: string[]; 9 clientName?: string; 10 issuedAt: number; 11}; 12 13type AuthCode = { 14 clientId: string; 15 userId: string; 16 codeChallenge: string; 17 redirectUri: string; 18 expiresAt: number; 19}; 20 21const clients = new Map<string, RegisteredClient>(); 22const codes = new Map<string, AuthCode>(); 23 24export function getClient(clientId: string): RegisteredClient | undefined { 25 return clients.get(clientId); 26} 27 28export function registerClient(params: { 29 redirectUris: string[]; 30 clientName?: string; 31}): RegisteredClient { 32 const clientId = randomUUID(); 33 const client: RegisteredClient = { 34 clientId, 35 redirectUris: params.redirectUris, 36 clientName: params.clientName, 37 issuedAt: Math.floor(Date.now() / 1000), 38 }; 39 clients.set(clientId, client); 40 return client; 41} 42 43export function createAuthCode(params: { 44 clientId: string; 45 userId: string; 46 codeChallenge: string; 47 redirectUri: string; 48}): string { 49 const code = randomUUID(); 50 codes.set(code, { 51 ...params, 52 expiresAt: Date.now() + 5 * 60 * 1000, 53 }); 54 return code; 55} 56 57function base64url(buf: Buffer): string { 58 return buf.toString("base64url"); 59} 60 61function verifyPkce(codeVerifier: string, codeChallenge: string): boolean { 62 const hash = createHash("sha256").update(codeVerifier).digest(); 63 return base64url(hash) === codeChallenge; 64} 65 66export async function exchangeCode( 67 code: string, 68 clientId: string, 69 codeVerifier: string, 70 redirectUri: string, 71): Promise<{ accessToken: string; expiresIn: number } | null> { 72 const stored = codes.get(code); 73 if (!stored) return null; 74 codes.delete(code); 75 76 if (stored.clientId !== clientId) return null; 77 if (stored.redirectUri !== redirectUri) return null; 78 if (stored.expiresAt < Date.now()) return null; 79 if (!verifyPkce(codeVerifier, stored.codeChallenge)) return null; 80 81 const sessionToken = randomUUID(); 82 const expiresIn = 30 * 24 * 60 * 60; 83 84 await db.insert(sessionTable).values({ 85 id: createId(), 86 token: sessionToken, 87 userId: stored.userId, 88 expiresAt: new Date(Date.now() + expiresIn * 1000), 89 createdAt: new Date(), 90 updatedAt: new Date(), 91 }); 92 93 return { accessToken: sessionToken, expiresIn }; 94}