@absolutejs/auth 0.68.2 → 0.69.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -46,6 +46,141 @@ var __export = (target, all) => {
46
46
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
47
47
  var __require = import.meta.require;
48
48
 
49
+ // src/constants.ts
50
+ var SECONDS_IN_A_MINUTE = 60, MILLISECONDS_IN_A_SECOND = 1000, MILLISECONDS_IN_A_MINUTE, MINUTES_IN_AN_HOUR = 60, HOURS_IN_A_DAY = 24, MILLISECONDS_IN_A_DAY, MILLISECONDS_IN_AN_HOUR, COOKIE_MINUTES = 30, COOKIE_DURATION, DEFAULT_MAX_SESSIONS = 1e4;
51
+ var init_constants = __esm(() => {
52
+ MILLISECONDS_IN_A_MINUTE = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE;
53
+ MILLISECONDS_IN_A_DAY = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR * HOURS_IN_A_DAY;
54
+ MILLISECONDS_IN_AN_HOUR = MILLISECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR;
55
+ COOKIE_DURATION = SECONDS_IN_A_MINUTE * COOKIE_MINUTES;
56
+ });
57
+
58
+ // src/crypto.ts
59
+ var exports_crypto = {};
60
+ __export(exports_crypto, {
61
+ verifyTotp: () => verifyTotp,
62
+ verifyPassword: () => verifyPassword,
63
+ hashToken: () => hashToken,
64
+ hashPassword: () => hashPassword,
65
+ generateTotpSecret: () => generateTotpSecret,
66
+ generateTotp: () => generateTotp,
67
+ generateSecureToken: () => generateSecureToken,
68
+ generateEncryptionKey: () => generateEncryptionKey,
69
+ encryptSecret: () => encryptSecret,
70
+ decryptSecret: () => decryptSecret,
71
+ createTotpKeyUri: () => createTotpKeyUri,
72
+ constantTimeEqual: () => constantTimeEqual,
73
+ base32Encode: () => base32Encode,
74
+ base32Decode: () => base32Decode
75
+ });
76
+ var DEFAULT_TOKEN_BYTES = 32, AES_KEY_BYTES = 32, AES_IV_BYTES = 12, HOTP_COUNTER_BYTES = 8, TOTP_SECRET_BYTES = 20, TOTP_DIGITS = 6, TOTP_PERIOD_SECONDS = 30, DEFAULT_TOTP_WINDOW = 1, DECIMAL_RADIX = 10, LAST_NIBBLE_MASK = 15, SIGN_BIT_MASK = 2147483647, BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", BASE32_GROUP_BITS = 5, BASE32_MASK = 31, BYTE_BITS = 8, textEncoder, textDecoder3, base64UrlEncode = (bytes) => Buffer.from(bytes).toString("base64url"), base64UrlDecode = (encoded) => new Uint8Array(Buffer.from(encoded, "base64url")), sha256 = async (input) => {
77
+ const digest = await crypto.subtle.digest("SHA-256", textEncoder.encode(input));
78
+ return new Uint8Array(digest);
79
+ }, hmacSha1 = async (key, message) => {
80
+ const cryptoKey = await crypto.subtle.importKey("raw", Uint8Array.from(key), { hash: "SHA-1", name: "HMAC" }, false, ["sign"]);
81
+ const signature = await crypto.subtle.sign("HMAC", cryptoKey, Uint8Array.from(message));
82
+ return new Uint8Array(signature);
83
+ }, counterToBytes = (counter) => {
84
+ const bytes = new Uint8Array(HOTP_COUNTER_BYTES);
85
+ new DataView(bytes.buffer).setBigUint64(0, BigInt(counter), false);
86
+ return bytes;
87
+ }, generateHotp = async (secret, counter, digits = TOTP_DIGITS) => {
88
+ const hmac = await hmacSha1(secret, counterToBytes(counter));
89
+ const view = new DataView(hmac.buffer, hmac.byteOffset, hmac.byteLength);
90
+ const offset = view.getUint8(hmac.byteLength - 1) & LAST_NIBBLE_MASK;
91
+ const truncated = view.getUint32(offset, false) & SIGN_BIT_MASK;
92
+ const otp = truncated % DECIMAL_RADIX ** digits;
93
+ return otp.toString().padStart(digits, "0");
94
+ }, importAesKey = (keyMaterial) => crypto.subtle.importKey("raw", base64UrlDecode(keyMaterial), { name: "AES-GCM" }, false, ["decrypt", "encrypt"]), base32Decode = (encoded) => {
95
+ const normalized = encoded.toUpperCase().replace(/[^A-Z2-7]/gu, "");
96
+ const bits = [...normalized].map((char4) => BASE32_ALPHABET.indexOf(char4).toString(2).padStart(BASE32_GROUP_BITS, "0")).join("");
97
+ const byteChunks = bits.match(/.{8}/gu) ?? [];
98
+ return new Uint8Array(byteChunks.map((chunk) => parseInt(chunk, 2)));
99
+ }, base32Encode = (bytes) => {
100
+ const bits = Array.from(bytes, (byte) => byte.toString(2).padStart(BYTE_BITS, "0")).join("");
101
+ const groups = bits.match(/.{1,5}/gu) ?? [];
102
+ return groups.map((group) => BASE32_ALPHABET[parseInt(group.padEnd(BASE32_GROUP_BITS, "0"), 2) & BASE32_MASK] ?? "").join("");
103
+ }, constantTimeEqual = async (left, right) => {
104
+ const [leftDigest, rightDigest] = await Promise.all([
105
+ sha256(left),
106
+ sha256(right)
107
+ ]);
108
+ const leftView = new DataView(leftDigest.buffer, leftDigest.byteOffset, leftDigest.byteLength);
109
+ const rightView = new DataView(rightDigest.buffer, rightDigest.byteOffset, rightDigest.byteLength);
110
+ let mismatch = 0;
111
+ for (let index2 = 0;index2 < leftDigest.byteLength; index2 += 1) {
112
+ mismatch |= leftView.getUint8(index2) ^ rightView.getUint8(index2);
113
+ }
114
+ return mismatch === 0;
115
+ }, createTotpKeyUri = ({
116
+ accountName,
117
+ digits = TOTP_DIGITS,
118
+ issuer,
119
+ period = TOTP_PERIOD_SECONDS,
120
+ secret
121
+ }) => {
122
+ const params = new URLSearchParams({
123
+ algorithm: "SHA1",
124
+ digits: `${digits}`,
125
+ issuer,
126
+ period: `${period}`,
127
+ secret
128
+ });
129
+ const label = encodeURIComponent(`${issuer}:${accountName}`);
130
+ return `otpauth://totp/${label}?${params.toString()}`;
131
+ }, decryptSecret = async (ciphertext, keyMaterial) => {
132
+ const key = await importAesKey(keyMaterial);
133
+ const combined = base64UrlDecode(ciphertext);
134
+ const nonce = combined.subarray(0, AES_IV_BYTES);
135
+ const data = combined.subarray(AES_IV_BYTES);
136
+ const plaintext = await crypto.subtle.decrypt({ iv: nonce, name: "AES-GCM" }, key, data);
137
+ return textDecoder3.decode(plaintext);
138
+ }, encryptSecret = async (plaintext, keyMaterial) => {
139
+ const key = await importAesKey(keyMaterial);
140
+ const nonce = new Uint8Array(AES_IV_BYTES);
141
+ crypto.getRandomValues(nonce);
142
+ const ciphertext = await crypto.subtle.encrypt({ iv: nonce, name: "AES-GCM" }, key, textEncoder.encode(plaintext));
143
+ const combined = new Uint8Array(nonce.byteLength + ciphertext.byteLength);
144
+ combined.set(nonce, 0);
145
+ combined.set(new Uint8Array(ciphertext), nonce.byteLength);
146
+ return base64UrlEncode(combined);
147
+ }, generateEncryptionKey = () => generateSecureToken(AES_KEY_BYTES), generateSecureToken = (byteLength = DEFAULT_TOKEN_BYTES) => {
148
+ const bytes = new Uint8Array(byteLength);
149
+ crypto.getRandomValues(bytes);
150
+ return base64UrlEncode(bytes);
151
+ }, generateTotp = async ({
152
+ digits = TOTP_DIGITS,
153
+ now = Date.now(),
154
+ period = TOTP_PERIOD_SECONDS,
155
+ secret
156
+ }) => {
157
+ const counter = Math.floor(now / MILLISECONDS_IN_A_SECOND / period);
158
+ return generateHotp(base32Decode(secret), counter, digits);
159
+ }, generateTotpSecret = (byteLength = TOTP_SECRET_BYTES) => {
160
+ const bytes = new Uint8Array(byteLength);
161
+ crypto.getRandomValues(bytes);
162
+ return base32Encode(bytes);
163
+ }, hashPassword = (password) => Bun.password.hash(password, { algorithm: "argon2id" }), hashToken = async (token) => base64UrlEncode(await sha256(token)), verifyPassword = (password, hash) => Bun.password.verify(password, hash), verifyTotp = async ({
164
+ digits = TOTP_DIGITS,
165
+ now = Date.now(),
166
+ period = TOTP_PERIOD_SECONDS,
167
+ secret,
168
+ token,
169
+ window = DEFAULT_TOTP_WINDOW
170
+ }) => {
171
+ const secretBytes = base32Decode(secret);
172
+ const counter = Math.floor(now / MILLISECONDS_IN_A_SECOND / period);
173
+ const drifts = Array.from({ length: window * 2 + 1 }, (_, offset) => counter - window + offset);
174
+ const candidates = await Promise.all(drifts.map((value) => generateHotp(secretBytes, value, digits)));
175
+ const matches = await Promise.all(candidates.map((candidate) => constantTimeEqual(candidate, token)));
176
+ return matches.includes(true);
177
+ };
178
+ var init_crypto = __esm(() => {
179
+ init_constants();
180
+ textEncoder = new TextEncoder;
181
+ textDecoder3 = new TextDecoder;
182
+ });
183
+
49
184
  // src/oidc/keys.ts
50
185
  var ENCODER = new TextEncoder;
51
186
  var ES256 = { hash: "SHA-256", name: "ECDSA" };
@@ -11835,13 +11970,24 @@ var oauthRefreshTokensTable = pgTable("auth_oauth_refresh_tokens", {
11835
11970
  audience: varchar("audience", { length: URL_LENGTH }),
11836
11971
  claims_json: jsonb("claims_json").$type(),
11837
11972
  client_id: varchar("client_id", { length: ID_LENGTH }).notNull(),
11973
+ consumed_token_hashes: text("consumed_token_hashes").array().notNull().default([]),
11838
11974
  created_at_ms: bigint("created_at_ms", { mode: "number" }).notNull(),
11839
11975
  dpop_jkt: varchar("dpop_jkt", { length: ID_LENGTH }),
11840
11976
  expires_at_ms: bigint("expires_at_ms", { mode: "number" }).notNull(),
11977
+ family_id: varchar("family_id", { length: ID_LENGTH }).notNull(),
11978
+ revoked_at_ms: bigint("revoked_at_ms", { mode: "number" }),
11841
11979
  scopes: text("scopes").array().notNull(),
11842
11980
  token_hash: varchar("token_hash", { length: ID_LENGTH }).primaryKey(),
11843
11981
  user_id: varchar("user_id", { length: ID_LENGTH }).notNull()
11844
11982
  });
11983
+ var oauthSocketTicketsTable = pgTable("auth_oauth_socket_tickets", {
11984
+ audience: varchar("audience", { length: URL_LENGTH }).notNull(),
11985
+ client_id: varchar("client_id", { length: ID_LENGTH }).notNull(),
11986
+ expires_at_ms: bigint("expires_at_ms", { mode: "number" }).notNull(),
11987
+ scopes: text("scopes").array().notNull(),
11988
+ subject: varchar("subject", { length: ID_LENGTH }).notNull(),
11989
+ ticket_hash: varchar("ticket_hash", { length: ID_LENGTH }).primaryKey()
11990
+ });
11845
11991
  var toClient = (row) => ({
11846
11992
  backchannelLogoutUri: row.backchannel_logout_uri ?? undefined,
11847
11993
  clientId: row.client_id,
@@ -11915,6 +12061,7 @@ var toRefresh = (row) => ({
11915
12061
  createdAt: row.created_at_ms,
11916
12062
  dpopJkt: row.dpop_jkt ?? undefined,
11917
12063
  expiresAt: row.expires_at_ms,
12064
+ familyId: row.family_id,
11918
12065
  scopes: row.scopes,
11919
12066
  tokenHash: row.token_hash,
11920
12067
  userId: row.user_id
@@ -11924,9 +12071,12 @@ var toRefreshValues = (token) => ({
11924
12071
  audience: token.audience ?? null,
11925
12072
  claims_json: token.claims ?? null,
11926
12073
  client_id: token.clientId,
12074
+ consumed_token_hashes: [],
11927
12075
  created_at_ms: token.createdAt,
11928
12076
  dpop_jkt: token.dpopJkt ?? null,
11929
12077
  expires_at_ms: token.expiresAt,
12078
+ family_id: token.familyId,
12079
+ revoked_at_ms: null,
11930
12080
  scopes: token.scopes,
11931
12081
  token_hash: token.tokenHash,
11932
12082
  user_id: token.userId
@@ -12105,7 +12255,7 @@ var createPostgresOAuthClientStore = (db) => ({
12105
12255
  });
12106
12256
  var createPostgresOidcRefreshTokenStore = (db) => ({
12107
12257
  consumeToken: async (tokenHash) => {
12108
- const [row] = await db.delete(oauthRefreshTokensTable).where(eq(oauthRefreshTokensTable.token_hash, tokenHash)).returning();
12258
+ const [row] = await db.delete(oauthRefreshTokensTable).where(or(eq(oauthRefreshTokensTable.token_hash, tokenHash), sql`${tokenHash} = ANY(${oauthRefreshTokensTable.consumed_token_hashes})`)).returning();
12109
12259
  return row ? toRefresh(row) : undefined;
12110
12260
  },
12111
12261
  deleteForClient: async (clientId) => {
@@ -12120,20 +12270,46 @@ var createPostgresOidcRefreshTokenStore = (db) => ({
12120
12270
  return deleted.length;
12121
12271
  },
12122
12272
  getToken: async (tokenHash) => {
12123
- const [row] = await db.select().from(oauthRefreshTokensTable).where(eq(oauthRefreshTokensTable.token_hash, tokenHash)).limit(1);
12273
+ const [row] = await db.select().from(oauthRefreshTokensTable).where(and(eq(oauthRefreshTokensTable.token_hash, tokenHash), isNull(oauthRefreshTokensTable.revoked_at_ms))).limit(1);
12124
12274
  return row ? toRefresh(row) : undefined;
12125
12275
  },
12126
12276
  listClientIdsForUser: async (userId) => {
12127
- const rows = await db.selectDistinct({ client_id: oauthRefreshTokensTable.client_id }).from(oauthRefreshTokensTable).where(and(eq(oauthRefreshTokensTable.user_id, userId), gt(oauthRefreshTokensTable.expires_at_ms, Date.now())));
12277
+ const rows = await db.selectDistinct({ client_id: oauthRefreshTokensTable.client_id }).from(oauthRefreshTokensTable).where(and(eq(oauthRefreshTokensTable.user_id, userId), isNull(oauthRefreshTokensTable.revoked_at_ms), gt(oauthRefreshTokensTable.expires_at_ms, Date.now())));
12128
12278
  return rows.map((row) => row.client_id);
12129
12279
  },
12130
12280
  listConnections: async () => {
12131
12281
  const rows = await db.selectDistinct({
12132
12282
  clientId: oauthRefreshTokensTable.client_id,
12133
12283
  userId: oauthRefreshTokensTable.user_id
12134
- }).from(oauthRefreshTokensTable).where(gt(oauthRefreshTokensTable.expires_at_ms, Date.now()));
12284
+ }).from(oauthRefreshTokensTable).where(and(gt(oauthRefreshTokensTable.expires_at_ms, Date.now()), isNull(oauthRefreshTokensTable.revoked_at_ms)));
12135
12285
  return rows;
12136
12286
  },
12287
+ revokeByConsumedToken: async (tokenHash) => {
12288
+ const rows = await db.update(oauthRefreshTokensTable).set({ revoked_at_ms: Date.now() }).where(and(isNull(oauthRefreshTokensTable.revoked_at_ms), sql`${tokenHash} = ANY(${oauthRefreshTokensTable.consumed_token_hashes})`)).returning({ familyId: oauthRefreshTokensTable.family_id });
12289
+ return rows.length > 0;
12290
+ },
12291
+ rotateToken: async (currentTokenHash, replacement) => {
12292
+ const active = and(eq(oauthRefreshTokensTable.token_hash, currentTokenHash), isNull(oauthRefreshTokensTable.revoked_at_ms));
12293
+ const consumed = sql`${currentTokenHash} = ANY(${oauthRefreshTokensTable.consumed_token_hashes})`;
12294
+ const rows = await db.update(oauthRefreshTokensTable).set({
12295
+ acr: sql`CASE WHEN ${active} THEN ${replacement.acr ?? null} ELSE ${oauthRefreshTokensTable.acr} END`,
12296
+ audience: sql`CASE WHEN ${active} THEN ${replacement.audience ?? null} ELSE ${oauthRefreshTokensTable.audience} END`,
12297
+ claims_json: sql`CASE WHEN ${active} THEN ${JSON.stringify(replacement.claims ?? null)}::jsonb ELSE ${oauthRefreshTokensTable.claims_json} END`,
12298
+ client_id: sql`CASE WHEN ${active} THEN ${replacement.clientId} ELSE ${oauthRefreshTokensTable.client_id} END`,
12299
+ consumed_token_hashes: sql`CASE WHEN ${active} THEN array_append(${oauthRefreshTokensTable.consumed_token_hashes}, ${currentTokenHash}) ELSE ${oauthRefreshTokensTable.consumed_token_hashes} END`,
12300
+ created_at_ms: sql`CASE WHEN ${active} THEN ${replacement.createdAt} ELSE ${oauthRefreshTokensTable.created_at_ms} END`,
12301
+ dpop_jkt: sql`CASE WHEN ${active} THEN ${replacement.dpopJkt ?? null} ELSE ${oauthRefreshTokensTable.dpop_jkt} END`,
12302
+ expires_at_ms: sql`CASE WHEN ${active} THEN ${replacement.expiresAt} ELSE ${oauthRefreshTokensTable.expires_at_ms} END`,
12303
+ revoked_at_ms: sql`CASE WHEN ${consumed} THEN ${Date.now()} ELSE ${oauthRefreshTokensTable.revoked_at_ms} END`,
12304
+ scopes: sql`CASE WHEN ${active} THEN ${replacement.scopes} ELSE ${oauthRefreshTokensTable.scopes} END`,
12305
+ token_hash: sql`CASE WHEN ${active} THEN ${replacement.tokenHash} ELSE ${oauthRefreshTokensTable.token_hash} END`,
12306
+ user_id: sql`CASE WHEN ${active} THEN ${replacement.userId} ELSE ${oauthRefreshTokensTable.user_id} END`
12307
+ }).where(or(active, consumed)).returning({
12308
+ revokedAt: oauthRefreshTokensTable.revoked_at_ms,
12309
+ tokenHash: oauthRefreshTokensTable.token_hash
12310
+ });
12311
+ return rows.length === 1 && rows[0]?.revokedAt === null && rows[0]?.tokenHash === replacement.tokenHash;
12312
+ },
12137
12313
  saveToken: async (token) => {
12138
12314
  await db.insert(oauthRefreshTokensTable).values(toRefreshValues(token));
12139
12315
  }
@@ -12207,6 +12383,408 @@ var createPostgresBackchannelAuthStore = (db) => ({
12207
12383
  await db.update(oauthBackchannelAuthRequestsTable).set({ status, user_sub: userSub ?? null }).where(eq(oauthBackchannelAuthRequestsTable.auth_req_id, authReqId));
12208
12384
  }
12209
12385
  });
12386
+ var toSocketTicket = (row) => ({
12387
+ audience: row.audience,
12388
+ clientId: row.client_id,
12389
+ expiresAt: row.expires_at_ms,
12390
+ scopes: row.scopes,
12391
+ subject: row.subject,
12392
+ ticketHash: row.ticket_hash
12393
+ });
12394
+ var createNeonSocketTicketStore = (databaseUrl) => createPostgresSocketTicketStore(createNeonDatabase(databaseUrl));
12395
+ var createPostgresSocketTicketStore = (db) => ({
12396
+ consumeTicket: async (ticketHash, now = Date.now()) => {
12397
+ const [row] = await db.delete(oauthSocketTicketsTable).where(eq(oauthSocketTicketsTable.ticket_hash, ticketHash)).returning();
12398
+ if (!row || row.expires_at_ms <= now)
12399
+ return;
12400
+ return toSocketTicket(row);
12401
+ },
12402
+ saveTicket: async (ticket) => {
12403
+ await db.insert(oauthSocketTicketsTable).values({
12404
+ audience: ticket.audience,
12405
+ client_id: ticket.clientId,
12406
+ expires_at_ms: ticket.expiresAt,
12407
+ scopes: ticket.scopes,
12408
+ subject: ticket.subject,
12409
+ ticket_hash: ticket.ticketHash
12410
+ });
12411
+ }
12412
+ });
12413
+ // src/oidc/inMemoryStores.ts
12414
+ var DEFAULT_LIST_LIMIT2 = 100;
12415
+ var createInMemoryAuthorizationCodeStore = () => {
12416
+ const codes = new Map;
12417
+ return {
12418
+ consumeCode: async (codeHash) => {
12419
+ const record = codes.get(codeHash);
12420
+ codes.delete(codeHash);
12421
+ return record;
12422
+ },
12423
+ deleteForClient: async (clientId) => {
12424
+ let deleted = 0;
12425
+ for (const [hash, code] of codes) {
12426
+ if (code.clientId !== clientId)
12427
+ continue;
12428
+ codes.delete(hash);
12429
+ deleted += 1;
12430
+ }
12431
+ return deleted;
12432
+ },
12433
+ deleteForUserClient: async (userId, clientId) => {
12434
+ let deleted = 0;
12435
+ for (const [hash, code] of codes) {
12436
+ if (code.userId !== userId || code.clientId !== clientId)
12437
+ continue;
12438
+ codes.delete(hash);
12439
+ deleted += 1;
12440
+ }
12441
+ return deleted;
12442
+ },
12443
+ saveCode: async (code) => {
12444
+ codes.set(code.codeHash, { ...code });
12445
+ }
12446
+ };
12447
+ };
12448
+ var createInMemoryBackchannelAuthStore = () => {
12449
+ const byAuthReqId = new Map;
12450
+ return {
12451
+ deleteByAuthReqId: async (authReqId) => {
12452
+ byAuthReqId.delete(authReqId);
12453
+ },
12454
+ findByAuthReqId: async (authReqId) => byAuthReqId.get(authReqId),
12455
+ recordPoll: async (authReqId, polledAt) => {
12456
+ const record = byAuthReqId.get(authReqId);
12457
+ if (!record)
12458
+ return;
12459
+ byAuthReqId.set(authReqId, { ...record, lastPolledAt: polledAt });
12460
+ },
12461
+ saveBackchannelAuth: async (request) => {
12462
+ byAuthReqId.set(request.authReqId, { ...request });
12463
+ },
12464
+ updateStatus: async (authReqId, status, userSub) => {
12465
+ const record = byAuthReqId.get(authReqId);
12466
+ if (!record)
12467
+ return;
12468
+ byAuthReqId.set(authReqId, { ...record, status, userSub });
12469
+ }
12470
+ };
12471
+ };
12472
+ var createInMemoryClientAssertionJtiStore = () => {
12473
+ const seen = new Map;
12474
+ return {
12475
+ recordIfFresh: async (clientId, jti, expiresAt) => {
12476
+ const now = Date.now();
12477
+ for (const [key, expiry] of seen) {
12478
+ if (expiry < now)
12479
+ seen.delete(key);
12480
+ }
12481
+ const composite = `${clientId}|${jti}`;
12482
+ if (seen.has(composite))
12483
+ return false;
12484
+ seen.set(composite, expiresAt);
12485
+ return true;
12486
+ }
12487
+ };
12488
+ };
12489
+ var createInMemoryClientRegistrationTokenStore = () => {
12490
+ const byHash = new Map;
12491
+ return {
12492
+ deleteByClientId: async (clientId) => {
12493
+ for (const [hash, token] of byHash) {
12494
+ if (token.clientId === clientId)
12495
+ byHash.delete(hash);
12496
+ }
12497
+ },
12498
+ deleteForClient: async (clientId) => {
12499
+ let deleted = 0;
12500
+ for (const [hash, token] of byHash) {
12501
+ if (token.clientId !== clientId)
12502
+ continue;
12503
+ byHash.delete(hash);
12504
+ deleted += 1;
12505
+ }
12506
+ return deleted;
12507
+ },
12508
+ findByTokenHash: async (tokenHash) => byHash.get(tokenHash),
12509
+ saveToken: async (token) => {
12510
+ for (const [hash, existing] of byHash) {
12511
+ if (existing.clientId === token.clientId)
12512
+ byHash.delete(hash);
12513
+ }
12514
+ byHash.set(token.tokenHash, { ...token });
12515
+ }
12516
+ };
12517
+ };
12518
+ var createInMemoryDeviceAuthorizationStore = () => {
12519
+ const byDeviceCode = new Map;
12520
+ return {
12521
+ deleteByDeviceCodeHash: async (deviceCodeHash) => {
12522
+ byDeviceCode.delete(deviceCodeHash);
12523
+ },
12524
+ deleteForClient: async (clientId) => {
12525
+ let deleted = 0;
12526
+ for (const [hash, authorization] of byDeviceCode) {
12527
+ if (authorization.clientId !== clientId)
12528
+ continue;
12529
+ byDeviceCode.delete(hash);
12530
+ deleted += 1;
12531
+ }
12532
+ return deleted;
12533
+ },
12534
+ deleteForUserClient: async (userId, clientId) => {
12535
+ let deleted = 0;
12536
+ for (const [hash, authorization] of byDeviceCode) {
12537
+ if (authorization.userSub !== userId || authorization.clientId !== clientId)
12538
+ continue;
12539
+ byDeviceCode.delete(hash);
12540
+ deleted += 1;
12541
+ }
12542
+ return deleted;
12543
+ },
12544
+ findByDeviceCodeHash: async (deviceCodeHash) => byDeviceCode.get(deviceCodeHash),
12545
+ findByUserCode: async (userCode) => {
12546
+ for (const record of byDeviceCode.values()) {
12547
+ if (record.userCode === userCode)
12548
+ return record;
12549
+ }
12550
+ return;
12551
+ },
12552
+ saveDeviceAuthorization: async (deviceAuthorization) => {
12553
+ byDeviceCode.set(deviceAuthorization.deviceCodeHash, {
12554
+ ...deviceAuthorization
12555
+ });
12556
+ },
12557
+ updateStatus: async (deviceCodeHash, status, userSub) => {
12558
+ const record = byDeviceCode.get(deviceCodeHash);
12559
+ if (!record)
12560
+ return;
12561
+ byDeviceCode.set(deviceCodeHash, {
12562
+ ...record,
12563
+ status,
12564
+ userSub
12565
+ });
12566
+ }
12567
+ };
12568
+ };
12569
+ var createInMemoryInitialAccessTokenStore = (initialHashes = []) => {
12570
+ const remaining = new Set(initialHashes);
12571
+ return {
12572
+ consumeToken: async (tokenHash) => {
12573
+ if (!remaining.has(tokenHash))
12574
+ return false;
12575
+ remaining.delete(tokenHash);
12576
+ return true;
12577
+ }
12578
+ };
12579
+ };
12580
+ var createInMemoryLogoutDeliveryStore = () => {
12581
+ const failures = new Map;
12582
+ return {
12583
+ listFailed: async (limit = DEFAULT_LIST_LIMIT2) => Array.from(failures.values()).sort((left, right) => right.createdAt - left.createdAt).slice(0, limit),
12584
+ recordFailure: async (delivery) => {
12585
+ failures.set(delivery.id, delivery);
12586
+ },
12587
+ removeFailure: async (deliveryId) => {
12588
+ failures.delete(deliveryId);
12589
+ }
12590
+ };
12591
+ };
12592
+ var createInMemoryOAuthClientStore = (clients) => {
12593
+ const registry = new Map(clients.map((client) => [client.clientId, client]));
12594
+ return {
12595
+ deleteClient: async (clientId) => {
12596
+ registry.delete(clientId);
12597
+ },
12598
+ findClient: async (clientId) => registry.get(clientId),
12599
+ saveClient: async (client) => {
12600
+ registry.set(client.clientId, { ...client });
12601
+ },
12602
+ updateClient: async (clientId, client) => {
12603
+ registry.set(clientId, { ...client });
12604
+ }
12605
+ };
12606
+ };
12607
+ var createInMemoryOidcRefreshTokenStore = () => {
12608
+ const families = new Map;
12609
+ const activeForHash = (tokenHash) => {
12610
+ for (const family of families.values()) {
12611
+ if (!family.revoked && family.token.tokenHash === tokenHash)
12612
+ return family;
12613
+ }
12614
+ return;
12615
+ };
12616
+ const familyForConsumedHash = (tokenHash) => {
12617
+ for (const family of families.values())
12618
+ if (family.consumed.has(tokenHash))
12619
+ return family;
12620
+ return;
12621
+ };
12622
+ return {
12623
+ consumeToken: async (tokenHash) => {
12624
+ const family = activeForHash(tokenHash) ?? familyForConsumedHash(tokenHash);
12625
+ if (!family)
12626
+ return;
12627
+ families.delete(family.token.familyId);
12628
+ return family.token;
12629
+ },
12630
+ deleteForClient: async (clientId) => {
12631
+ let deleted = 0;
12632
+ for (const [familyId, family] of families) {
12633
+ if (family.token.clientId !== clientId)
12634
+ continue;
12635
+ families.delete(familyId);
12636
+ deleted += 1;
12637
+ }
12638
+ return deleted;
12639
+ },
12640
+ deleteForUser: async (userId) => {
12641
+ for (const [familyId, family] of families) {
12642
+ if (family.token.userId === userId)
12643
+ families.delete(familyId);
12644
+ }
12645
+ },
12646
+ deleteForUserClient: async (userId, clientId) => {
12647
+ let deleted = 0;
12648
+ for (const [familyId, family] of families) {
12649
+ if (family.token.userId !== userId || family.token.clientId !== clientId)
12650
+ continue;
12651
+ families.delete(familyId);
12652
+ deleted += 1;
12653
+ }
12654
+ return deleted;
12655
+ },
12656
+ getToken: async (tokenHash) => activeForHash(tokenHash)?.token,
12657
+ listClientIdsForUser: async (userId) => {
12658
+ const now = Date.now();
12659
+ const active = Array.from(families.values()).filter((family) => !family.revoked && family.token.userId === userId && family.token.expiresAt > now);
12660
+ return Array.from(new Set(active.map((family) => family.token.clientId)));
12661
+ },
12662
+ listConnections: async () => {
12663
+ const now = Date.now();
12664
+ const connections = new Map;
12665
+ for (const family of families.values()) {
12666
+ const { token } = family;
12667
+ if (family.revoked || token.expiresAt <= now)
12668
+ continue;
12669
+ const connection = {
12670
+ clientId: token.clientId,
12671
+ userId: token.userId
12672
+ };
12673
+ connections.set(`${connection.userId}\x00${connection.clientId}`, connection);
12674
+ }
12675
+ return Array.from(connections.values());
12676
+ },
12677
+ revokeByConsumedToken: async (tokenHash) => {
12678
+ const family = familyForConsumedHash(tokenHash);
12679
+ if (!family)
12680
+ return false;
12681
+ family.revoked = true;
12682
+ return true;
12683
+ },
12684
+ rotateToken: async (currentTokenHash, replacement) => {
12685
+ const family = activeForHash(currentTokenHash);
12686
+ if (family) {
12687
+ family.consumed.add(currentTokenHash);
12688
+ family.token = { ...replacement };
12689
+ return true;
12690
+ }
12691
+ const reused = familyForConsumedHash(currentTokenHash);
12692
+ if (reused)
12693
+ reused.revoked = true;
12694
+ return false;
12695
+ },
12696
+ saveToken: async (token) => {
12697
+ families.set(token.familyId, {
12698
+ consumed: new Set,
12699
+ revoked: false,
12700
+ token: { ...token }
12701
+ });
12702
+ }
12703
+ };
12704
+ };
12705
+ var createInMemoryPushedAuthorizationRequestStore = () => {
12706
+ const requests = new Map;
12707
+ return {
12708
+ consumeRequest: async (requestUriHash) => {
12709
+ const record = requests.get(requestUriHash);
12710
+ if (record === undefined)
12711
+ return;
12712
+ requests.delete(requestUriHash);
12713
+ if (record.expiresAt < Date.now())
12714
+ return;
12715
+ return record;
12716
+ },
12717
+ saveRequest: async (request) => {
12718
+ requests.set(request.requestUriHash, { ...request });
12719
+ }
12720
+ };
12721
+ };
12722
+ var createInMemorySocketTicketStore = () => {
12723
+ const tickets = new Map;
12724
+ return {
12725
+ consumeTicket: async (ticketHash, now = Date.now()) => {
12726
+ const ticket = tickets.get(ticketHash);
12727
+ tickets.delete(ticketHash);
12728
+ if (!ticket || ticket.expiresAt <= now)
12729
+ return;
12730
+ return { ...ticket, scopes: [...ticket.scopes] };
12731
+ },
12732
+ saveTicket: async (ticket) => {
12733
+ tickets.set(ticket.ticketHash, {
12734
+ ...ticket,
12735
+ scopes: [...ticket.scopes]
12736
+ });
12737
+ }
12738
+ };
12739
+ };
12740
+ // src/oidc/socketTickets.ts
12741
+ init_crypto();
12742
+ var DEFAULT_SOCKET_TICKET_TTL_MS = 30000;
12743
+ var TICKET_BYTES = 32;
12744
+ var consumeSocketTicket = async ({
12745
+ audience,
12746
+ getUser,
12747
+ now = Date.now(),
12748
+ store,
12749
+ ticket
12750
+ }) => {
12751
+ const record = await store.consumeTicket(await hashToken(ticket), now);
12752
+ if (!record || record.audience !== audience)
12753
+ return;
12754
+ const user = await getUser(record.subject);
12755
+ if (user === null)
12756
+ return;
12757
+ return {
12758
+ audience: record.audience,
12759
+ clientId: record.clientId,
12760
+ kind: "access-token",
12761
+ scopes: [...record.scopes],
12762
+ subject: record.subject,
12763
+ user
12764
+ };
12765
+ };
12766
+ var issueSocketTicket = async ({
12767
+ audience,
12768
+ clientId,
12769
+ now = Date.now(),
12770
+ scopes,
12771
+ store,
12772
+ subject,
12773
+ ttlMs = DEFAULT_SOCKET_TICKET_TTL_MS
12774
+ }) => {
12775
+ if (!Number.isFinite(ttlMs) || ttlMs <= 0)
12776
+ throw new Error("socketTicketTtlMs must be positive");
12777
+ const ticket = `ast_${generateSecureToken(TICKET_BYTES)}`;
12778
+ await store.saveTicket({
12779
+ audience,
12780
+ clientId,
12781
+ expiresAt: now + ttlMs,
12782
+ scopes: [...scopes],
12783
+ subject,
12784
+ ticketHash: await hashToken(ticket)
12785
+ });
12786
+ return { expiresInMs: ttlMs, ticket };
12787
+ };
12210
12788
  export {
12211
12789
  verifyJwtWithKeys,
12212
12790
  verifyJwt,
@@ -12215,7 +12793,9 @@ export {
12215
12793
  signJwt,
12216
12794
  revokeOAuthClientCredentials,
12217
12795
  jwkThumbprint,
12796
+ issueSocketTicket,
12218
12797
  generateSigningKey,
12798
+ createPostgresSocketTicketStore,
12219
12799
  createPostgresOidcRefreshTokenStore,
12220
12800
  createPostgresOAuthClientStore,
12221
12801
  createPostgresInitialAccessTokenStore,
@@ -12223,14 +12803,17 @@ export {
12223
12803
  createPostgresClientRegistrationTokenStore,
12224
12804
  createPostgresClientAssertionJtiStore,
12225
12805
  createPostgresAuthorizationCodeStore,
12806
+ createNeonSocketTicketStore,
12226
12807
  createNeonOidcRefreshTokenStore,
12227
12808
  createNeonOAuthClientStore,
12228
12809
  createNeonInitialAccessTokenStore,
12229
12810
  createNeonDeviceAuthorizationStore,
12230
12811
  createNeonClientRegistrationTokenStore,
12231
12812
  createNeonClientAssertionJtiStore,
12232
- createNeonAuthorizationCodeStore
12813
+ createNeonAuthorizationCodeStore,
12814
+ createInMemorySocketTicketStore,
12815
+ consumeSocketTicket
12233
12816
  };
12234
12817
 
12235
- //# debugId=09059391C2D6086264756E2164756E21
12818
+ //# debugId=B37CCAB8EEBB54A964756E2164756E21
12236
12819
  //# sourceMappingURL=index.js.map