1
21import { Inject, Injectable } from "@nestjs/common";
22import { Prisma } from "@prisma/client";
23import { sha256hex } from "../../lib/crypto.js";
24import { PrismaService } from "../../common/prisma.service.js";
25import { RedisService } from "../../common/redis.service.js";
26
27const INDEX_KEY = "scim:index";
28const ORG_HASH_KEY = (orgId: string) => `scim:index:org:${orgId}`;
29
30@Injectable()
31export class ScimAuthService {
32 constructor(
33 @Inject(PrismaService) private readonly prisma: PrismaService,
34 @Inject(RedisService) private readonly redis: RedisService
35 ) {}
36
37
38 async resolveOrg(rawToken: string): Promise<{ orgId: string } | null> {
39 const hash = sha256hex(rawToken);
40 const r = this.redis.getRedis();
41 let orgId = await r.hget(INDEX_KEY, hash);
42 if (!orgId) {
43
44
45 await this.rebuildFromDb();
46 orgId = await r.hget(INDEX_KEY, hash);
47 }
48 if (!orgId) return null;
49 return { orgId };
50 }
51
52
53 async upsertIndex(sha256Hash: string, orgId: string): Promise<void> {
54 const r = this.redis.getRedis();
55
56 const oldHash = await r.get(ORG_HASH_KEY(orgId));
57 if (oldHash && oldHash !== sha256Hash) await r.hdel(INDEX_KEY, oldHash);
58 await r.hset(INDEX_KEY, sha256Hash, orgId);
59 await r.set(ORG_HASH_KEY(orgId), sha256Hash);
60 }
61
62
63 async removeIndex(sha256Hash: string, orgId: string): Promise<void> {
64 const r = this.redis.getRedis();
65 const current = await r.get(ORG_HASH_KEY(orgId));
66 if (current === sha256Hash) {
67 await r.del(ORG_HASH_KEY(orgId));
68 await r.hdel(INDEX_KEY, sha256Hash);
69 }
70 }
71
72
73 async rebuildFromDb(): Promise<void> {
74 const r = this.redis.getRedis();
75 const orgs = await this.prisma.organization.findMany({
76 where: { scimConfig: { not: Prisma.JsonNull } },
77 select: { id: true, scimConfig: true }
78 });
79 for (const org of orgs) {
80 const cfg = org.scimConfig as Record<string, unknown> | null;
81 const hash = (cfg?.bearerTokenHash as string | undefined) ?? null;
82 if (!hash) continue;
83 await r.hset(INDEX_KEY, hash, org.id);
84 await r.set(ORG_HASH_KEY(org.id), hash);
85 }
86 }
87}
88