@ibgib/web-gib 0.0.48 → 0.0.50

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.
Files changed (37) hide show
  1. package/dist/AUTO-GENERATED-version.d.mts +1 -1
  2. package/dist/AUTO-GENERATED-version.mjs +1 -1
  3. package/dist/common/settings/settings-constants.d.mts +8 -1
  4. package/dist/common/settings/settings-constants.d.mts.map +1 -1
  5. package/dist/common/settings/settings-constants.mjs +7 -0
  6. package/dist/common/settings/settings-constants.mjs.map +1 -1
  7. package/dist/common/settings/settings-helpers.d.mts.map +1 -1
  8. package/dist/common/settings/settings-helpers.mjs +3 -1
  9. package/dist/common/settings/settings-helpers.mjs.map +1 -1
  10. package/dist/common/settings/settings-types.d.mts +5 -2
  11. package/dist/common/settings/settings-types.d.mts.map +1 -1
  12. package/dist/identity/sso/sso-config-helper.d.mts +7 -0
  13. package/dist/identity/sso/sso-config-helper.d.mts.map +1 -0
  14. package/dist/identity/sso/sso-config-helper.mjs +42 -0
  15. package/dist/identity/sso/sso-config-helper.mjs.map +1 -0
  16. package/dist/identity/sso/sso-custodian-service.d.mts +125 -0
  17. package/dist/identity/sso/sso-custodian-service.d.mts.map +1 -0
  18. package/dist/identity/sso/sso-custodian-service.mjs +373 -0
  19. package/dist/identity/sso/sso-custodian-service.mjs.map +1 -0
  20. package/dist/identity/sso/sso-custodian-service.respec.d.mts +2 -0
  21. package/dist/identity/sso/sso-custodian-service.respec.d.mts.map +1 -0
  22. package/dist/identity/sso/sso-custodian-service.respec.mjs +167 -0
  23. package/dist/identity/sso/sso-custodian-service.respec.mjs.map +1 -0
  24. package/dist/identity/sso/sso-types.d.mts +40 -0
  25. package/dist/identity/sso/sso-types.d.mts.map +1 -0
  26. package/dist/identity/sso/sso-types.mjs +7 -0
  27. package/dist/identity/sso/sso-types.mjs.map +1 -0
  28. package/package.json +4 -3
  29. package/src/AUTO-GENERATED-version.mts +1 -1
  30. package/src/common/settings/settings-constants.mts +9 -0
  31. package/src/common/settings/settings-helpers.mts +4 -1
  32. package/src/common/settings/settings-types.mts +7 -2
  33. package/src/identity/sso/sso-config-helper.mts +52 -0
  34. package/src/identity/sso/sso-custodian-service.mts +453 -0
  35. package/src/identity/sso/sso-custodian-service.respec.mts +206 -0
  36. package/src/identity/sso/sso-types.mts +44 -0
  37. package/src/ui/component/README.md +53 -1
@@ -0,0 +1,373 @@
1
+ import { createPublicKey, verify } from 'node:crypto';
2
+ import { hash, HashAlgorithm } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
3
+ import { kdf_recursiveSaltWrap } from '@ibgib/core-gib/dist/keystone/kdf/kdf-helpers.mjs';
4
+ import { POOL_ID_CUSTODIAN_MANAGE, KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE } from '@ibgib/core-gib/dist/keystone/keystone-constants.mjs';
5
+ import { createStandardPoolConfig } from '@ibgib/core-gib/dist/keystone/keystone-config-builder.mjs';
6
+ import { generateOpaqueChallengeId } from '@ibgib/core-gib/dist/keystone/keystone-helpers.mjs';
7
+ import { KeystoneStrategyFactory } from '@ibgib/core-gib/dist/keystone/strategy/keystone-strategy-factory.mjs';
8
+ /**
9
+ * Server-side service for token exchange, JWT validation, and user profile normalization.
10
+ */
11
+ export class SsoCustodianService {
12
+ config;
13
+ lc = `[${SsoCustodianService.name}]`;
14
+ // In-memory cache for JSON Web Key Sets (JWKS) per provider
15
+ jwksCache = new Map();
16
+ jwksTtlMs = 24 * 60 * 60 * 1000; // Cache certs for 24 hours
17
+ /**
18
+ * @param config The server's global SSO configuration.
19
+ * - Visibility: Private (Contains high-entropy secrets like `ssoServerKdfSecret` and `sessionSecret`).
20
+ * - Generator: Server (Bootstrapped from node environment variables on server initialization).
21
+ */
22
+ constructor(config) {
23
+ this.config = config;
24
+ }
25
+ /**
26
+ * Exchanges an authorization code for an OAuth2 token and retrieves normalized user info.
27
+ *
28
+ * @param providerId The string identifier of the OAuth provider (e.g. 'google' or 'github').
29
+ * - Visibility: Public (Non-sensitive metadata string).
30
+ * - Generator: Client (Sent from browser UI selection).
31
+ *
32
+ * @param code The temporary authorization code returned by the OAuth provider.
33
+ * - Visibility: Private (Confidential short-lived bearer credential. Must be kept private).
34
+ * - Generator: Provider (Created by provider's OAuth page and sent to client redirect URI).
35
+ *
36
+ * @param redirectUri The redirect URI registered in the provider portal.
37
+ * - Visibility: Public (Configuration URL).
38
+ * - Generator: Client/Server (Derived from environmental configuration and sent by client).
39
+ */
40
+ async getOAuthUserInfo(providerId, code, redirectUri) {
41
+ const lc = `${this.lc}[${this.getOAuthUserInfo.name}]`;
42
+ const providerConfig = this.config.providers[providerId];
43
+ if (!providerConfig) {
44
+ throw new Error(`Provider '${providerId}' is not configured on this server. (E: c28900abfefd23e89cf23f12)`);
45
+ }
46
+ console.log(`${lc} Exchanging code for provider: ${providerId}`);
47
+ // 1. Exchange authorization code for tokens
48
+ const tokens = await this.exchangeCodeForTokens(providerConfig, code, redirectUri);
49
+ // 2. Retrieve and normalize user info
50
+ if (providerId === 'google') {
51
+ // Google returns an id_token (JWT) directly containing user info
52
+ if (!tokens.id_token) {
53
+ throw new Error(`Google token exchange did not return id_token. (E: e28900abfefd23e89cf45f23)`);
54
+ }
55
+ return this.verifyAndNormalizeGoogleToken(tokens.id_token);
56
+ }
57
+ else if (providerId === 'github') {
58
+ // GitHub requires calling the /user API using the access_token
59
+ if (!tokens.access_token) {
60
+ throw new Error(`GitHub token exchange did not return access_token. (E: a28900abfefd23e89cf56f34)`);
61
+ }
62
+ return this.fetchAndNormalizeGithubUser(providerConfig, tokens.access_token);
63
+ }
64
+ throw new Error(`Unsupported provider: ${providerId}. (E: d28900abfefd23e89cf67f45)`);
65
+ }
66
+ /**
67
+ * Directly validates a Google ID Token (JWT) locally and returns normalized user info.
68
+ * Useful for client-side authentication handshakes.
69
+ *
70
+ * @param idToken The raw base64-encoded signed JSON Web Token (JWT) ID token returned by Google.
71
+ * - Visibility: Private (Sensitive bearer credential containing signature and claims. Must be kept private).
72
+ * - Generator: Provider (Generated and signed cryptographically by Google authentication servers).
73
+ */
74
+ async verifyAndNormalizeGoogleToken(idToken) {
75
+ const lc = `${this.lc}[${this.verifyAndNormalizeGoogleToken.name}]`;
76
+ const providerConfig = this.config.providers.google;
77
+ if (!providerConfig) {
78
+ throw new Error(`Google provider config is missing. (E: g28900abfefd23e89cf78f56)`);
79
+ }
80
+ // Verify signature and decode JWT payload
81
+ const payload = await this.verifyJwt(idToken, providerConfig);
82
+ // Assert claims
83
+ const nowSeconds = Math.floor(Date.now() / 1000);
84
+ if (payload.exp && payload.exp < nowSeconds) {
85
+ throw new Error(`Token has expired. (E: t28900abfefd23e89cf89f67)`);
86
+ }
87
+ if (payload.aud !== providerConfig.clientId) {
88
+ throw new Error(`Token audience mismatch. Expected ${providerConfig.clientId}, got ${payload.aud}. (E: m28900abfefd23e89cf90f78)`);
89
+ }
90
+ if (!payload.sub) {
91
+ throw new Error(`Google JWT is missing subject claim ('sub'). (E: s28900abfefd23e89cfa1f89)`);
92
+ }
93
+ return {
94
+ providerKey: `google:${payload.sub}`,
95
+ providerId: 'google',
96
+ sub: payload.sub,
97
+ email: payload.email,
98
+ name: payload.name,
99
+ picture: payload.picture
100
+ };
101
+ }
102
+ /**
103
+ * Internal: Exchange authorization code for access token / ID token.
104
+ *
105
+ * @param providerConfig Server configuration settings for the specific provider.
106
+ * - Visibility: Private (Contains provider client secrets. Must stay server-side).
107
+ * - Generator: Server (Configured from node environment variables).
108
+ *
109
+ * @param code The temporary authorization code to exchange.
110
+ * - Visibility: Private (Short-lived credential. Must be kept private).
111
+ * - Generator: Provider (Via OAuth flow).
112
+ *
113
+ * @param redirectUri The redirect URI registered for the exchange.
114
+ * - Visibility: Public (Configuration URL).
115
+ * - Generator: Client/Server (From app configuration).
116
+ */
117
+ async exchangeCodeForTokens(providerConfig, code, redirectUri) {
118
+ const body = new URLSearchParams({
119
+ client_id: providerConfig.clientId,
120
+ client_secret: providerConfig.clientSecret,
121
+ code,
122
+ redirect_uri: redirectUri,
123
+ grant_type: 'authorization_code'
124
+ });
125
+ const headers = {
126
+ 'Content-Type': 'application/x-www-form-urlencoded'
127
+ };
128
+ // GitHub requires an Accept header to return JSON instead of URL-encoded string
129
+ if (providerConfig.providerId === 'github') {
130
+ headers['Accept'] = 'application/json';
131
+ }
132
+ const res = await fetch(providerConfig.tokenUrl, {
133
+ method: 'POST',
134
+ headers,
135
+ body: body.toString()
136
+ });
137
+ if (!res.ok) {
138
+ const errText = await res.text();
139
+ throw new Error(`Token exchange failed (HTTP ${res.status}): ${errText}`);
140
+ }
141
+ return res.json();
142
+ }
143
+ /**
144
+ * Internal: Fetch GitHub user profile and normalize.
145
+ *
146
+ * @param providerConfig Server configuration settings for GitHub.
147
+ * - Visibility: Private (Contains GitHub client secret).
148
+ * - Generator: Server (From environment config).
149
+ *
150
+ * @param accessToken The active OAuth2 access token to authorize the API call.
151
+ * - Visibility: Private (Bearer token. Must be kept private).
152
+ * - Generator: Provider (GitHub token endpoint).
153
+ */
154
+ async fetchAndNormalizeGithubUser(providerConfig, accessToken) {
155
+ const res = await fetch(providerConfig.userInfoUrl, {
156
+ headers: {
157
+ 'Authorization': `Bearer ${accessToken}`,
158
+ 'User-Agent': 'space-gib-auth-agent' // GitHub API requires User-Agent
159
+ }
160
+ });
161
+ if (!res.ok) {
162
+ const errText = await res.text();
163
+ throw new Error(`Failed to fetch GitHub user info (HTTP ${res.status}): ${errText}`);
164
+ }
165
+ const rawUser = await res.json();
166
+ if (!rawUser.id) {
167
+ throw new Error(`GitHub user info response missing 'id' field.`);
168
+ }
169
+ const sub = String(rawUser.id);
170
+ return {
171
+ providerKey: `github:${sub}`,
172
+ providerId: 'github',
173
+ sub,
174
+ email: rawUser.email || undefined,
175
+ name: rawUser.name || rawUser.login || undefined,
176
+ picture: rawUser.avatar_url || undefined
177
+ };
178
+ }
179
+ /**
180
+ * Internal: Decodes and cryptographically verifies a JWT signature using JWKS public keys.
181
+ *
182
+ * @param token The raw base64-encoded JWT token to verify.
183
+ * - Visibility: Private (Sensitive token. Must be kept private).
184
+ * - Generator: Provider (Google / OIDC server).
185
+ *
186
+ * @param providerConfig The target provider server settings containing `jwksUrl`.
187
+ * - Visibility: Private (Contains secrets).
188
+ * - Generator: Server (From environment config).
189
+ */
190
+ async verifyJwt(token, providerConfig) {
191
+ const parts = token.split('.');
192
+ if (parts.length !== 3) {
193
+ throw new Error('JWT must have 3 parts separated by dots.');
194
+ }
195
+ const [headerB64, payloadB64, signatureB64] = parts;
196
+ // 1. Decode header and select algorithm/key ID
197
+ let header;
198
+ try {
199
+ header = JSON.parse(Buffer.from(headerB64, 'base64url').toString('utf-8'));
200
+ }
201
+ catch {
202
+ throw new Error('Failed to parse JWT header.');
203
+ }
204
+ if (header.alg !== 'RS256') {
205
+ throw new Error(`Only RS256 algorithm is supported, got: ${header.alg}`);
206
+ }
207
+ const kid = header.kid;
208
+ if (!kid) {
209
+ throw new Error('JWT header missing key ID (kid).');
210
+ }
211
+ // 2. Fetch public keys (JWKS)
212
+ const keys = await this.getJwksKeys(providerConfig);
213
+ const jwk = keys.find(key => key.kid === kid);
214
+ if (!jwk) {
215
+ throw new Error(`JWK public key matching kid '${kid}' not found.`);
216
+ }
217
+ // 3. Create Public Key object using Node native crypto
218
+ const publicKey = createPublicKey({
219
+ key: jwk,
220
+ format: 'jwk'
221
+ });
222
+ // 4. Verify signature against header + payload data
223
+ const signature = Buffer.from(signatureB64, 'base64url');
224
+ const data = Buffer.from(`${headerB64}.${payloadB64}`);
225
+ const isValid = verify('sha256', data, publicKey, signature);
226
+ if (!isValid) {
227
+ throw new Error('Cryptographic signature verification failed.');
228
+ }
229
+ // 5. Decode and return payload
230
+ try {
231
+ return JSON.parse(Buffer.from(payloadB64, 'base64url').toString('utf-8'));
232
+ }
233
+ catch {
234
+ throw new Error('Failed to parse JWT payload.');
235
+ }
236
+ }
237
+ /**
238
+ * Internal: Retrieves JWKS keys from cache or fetches them from the provider's jwksUrl.
239
+ *
240
+ * @param providerConfig The provider config containing the target `jwksUrl`.
241
+ * - Visibility: Private (Contains client secrets).
242
+ * - Generator: Server (From environment config).
243
+ */
244
+ async getJwksKeys(providerConfig) {
245
+ const providerId = providerConfig.providerId;
246
+ const cached = this.jwksCache.get(providerId);
247
+ if (cached && cached.expiresAt > Date.now()) {
248
+ return cached.keys;
249
+ }
250
+ if (!providerConfig.jwksUrl) {
251
+ throw new Error(`Provider '${providerId}' does not define a jwksUrl for JWT signature validation.`);
252
+ }
253
+ const res = await fetch(providerConfig.jwksUrl);
254
+ if (!res.ok) {
255
+ throw new Error(`Failed to fetch JWKS from ${providerConfig.jwksUrl} (HTTP ${res.status})`);
256
+ }
257
+ const body = (await res.json());
258
+ if (!body.keys || !Array.isArray(body.keys)) {
259
+ throw new Error(`Invalid JWKS response structure from ${providerConfig.jwksUrl}`);
260
+ }
261
+ this.jwksCache.set(providerId, {
262
+ keys: body.keys,
263
+ expiresAt: Date.now() + this.jwksTtlMs
264
+ });
265
+ return body.keys;
266
+ }
267
+ /**
268
+ * Derives a server-delegate master secret deterministically using the server's private KDF secret,
269
+ * the providerKey, and the user's public nonce.
270
+ *
271
+ * @param providerKey The unique key identifying the provider and user sub ID (e.g. `google:123...`).
272
+ * - Visibility: Public (Identifier saved on parent's delegate claim, safe for log/ledger storage).
273
+ * - Generator: Provider/Server (Derived by mapping provider user sub ID after authentication).
274
+ *
275
+ * @param userNonce The public, high-entropy nonce generated by the client to salt the KDF derivation.
276
+ * - Visibility: Public (Salt value stored on parent's delegate claim, safe for ledger storage).
277
+ * - Generator: Client (Generated offline on user's device).
278
+ */
279
+ async deriveServerDelegateSecret(providerKey, userNonce) {
280
+ const salt = await hash({
281
+ s: `${providerKey}:${userNonce}`,
282
+ algorithm: HashAlgorithm.sha_512
283
+ });
284
+ return await kdf_recursiveSaltWrap({
285
+ masterSecret: this.config.ssoServerKdfSecret,
286
+ salt,
287
+ rounds: 1000,
288
+ algorithm: HashAlgorithm.sha_512
289
+ });
290
+ }
291
+ /**
292
+ * Creates a public custodian challenge pool (custodian-manage) derived deterministically
293
+ * from the server's private KDF secret, providerKey, and userNonce.
294
+ *
295
+ * @param providerKey The unique key identifying the provider and user sub ID (e.g. `google:123...`).
296
+ * - Visibility: Public (Mapped identifier, safe for ledger).
297
+ * - Generator: Provider/Server (Derived after successful token exchange).
298
+ *
299
+ * @param userNonce The public, high-entropy nonce.
300
+ * - Visibility: Public (Derivation salt, safe for ledger).
301
+ * - Generator: Client (Generated on user's device).
302
+ */
303
+ async createCustodianChallengePool(providerKey, userNonce) {
304
+ const custodianSecret = await this.deriveServerDelegateSecret(providerKey, userNonce);
305
+ const poolSalt = await hash({
306
+ s: `${providerKey}:${userNonce}:salt`,
307
+ algorithm: HashAlgorithm.sha_256
308
+ });
309
+ const poolSaltShort = poolSalt.substring(0, 16);
310
+ const config = createStandardPoolConfig({
311
+ id: POOL_ID_CUSTODIAN_MANAGE,
312
+ salt: poolSaltShort
313
+ });
314
+ config.allowedVerbs = [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_REVOKE];
315
+ const strategy = KeystoneStrategyFactory.create({ config });
316
+ const poolSecret = await strategy.derivePoolSecret({ masterSecret: custodianSecret });
317
+ const challenges = {};
318
+ const timestamp = Date.now().toString();
319
+ for (let i = 0; i < config.behavior.size; i++) {
320
+ const challengeId = await generateOpaqueChallengeId({
321
+ salt: config.salt,
322
+ timestamp,
323
+ index: i
324
+ });
325
+ const solution = await strategy.generateSolution({
326
+ poolSecret,
327
+ poolId: config.id,
328
+ challengeId
329
+ });
330
+ challenges[challengeId] = await strategy.generateChallenge({ solution });
331
+ }
332
+ return {
333
+ id: POOL_ID_CUSTODIAN_MANAGE,
334
+ config,
335
+ challenges,
336
+ isForeign: true,
337
+ metadata: {
338
+ owner: 'custodian',
339
+ providerKey
340
+ }
341
+ };
342
+ }
343
+ /**
344
+ * Maps private server configuration to safe public client configurations.
345
+ *
346
+ * @param hostUrl The root URL host address of the server (e.g. `https://space-gib.localhost` or `https://ibgib.space`).
347
+ * - Visibility: Public (Non-sensitive hostname configuration).
348
+ * - Generator: Server (Extracted from the HTTP request context headers/protocol).
349
+ */
350
+ getSsoClientConfigs(hostUrl) {
351
+ const configs = [];
352
+ if (this.config.providers.google) {
353
+ configs.push({
354
+ providerId: 'google',
355
+ clientId: this.config.providers.google.clientId,
356
+ authUrl: 'https://accounts.google.com/o/oauth2/v2/auth',
357
+ redirectUri: `${hostUrl}/`,
358
+ scope: 'openid email profile'
359
+ });
360
+ }
361
+ if (this.config.providers.github) {
362
+ configs.push({
363
+ providerId: 'github',
364
+ clientId: this.config.providers.github.clientId,
365
+ authUrl: 'https://github.com/login/oauth/authorize',
366
+ redirectUri: `${hostUrl}/`,
367
+ scope: 'read:user user:email'
368
+ });
369
+ }
370
+ return configs;
371
+ }
372
+ }
373
+ //# sourceMappingURL=sso-custodian-service.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sso-custodian-service.mjs","sourceRoot":"","sources":["../../../src/identity/sso/sso-custodian-service.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,iDAAiD,CAAC;AACtF,OAAO,EAAE,qBAAqB,EAAE,MAAM,mDAAmD,CAAC;AAI1F,OAAO,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,sDAAsD,CAAC;AAC5I,OAAO,EAAE,wBAAwB,EAAE,MAAM,2DAA2D,CAAC;AACrG,OAAO,EAAE,yBAAyB,EAAE,MAAM,oDAAoD,CAAC;AAC/F,OAAO,EAAE,uBAAuB,EAAE,MAAM,sEAAsE,CAAC;AAuB/G;;GAEG;AACH,MAAM,OAAO,mBAAmB;IAYR;IAXZ,EAAE,GAAG,IAAI,mBAAmB,CAAC,IAAI,GAAG,CAAC;IAE7C,4DAA4D;IACpD,SAAS,GAA8D,IAAI,GAAG,EAAE,CAAC;IACjF,SAAS,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,2BAA2B;IAEpE;;;;OAIG;IACH,YAAoB,MAAuB;QAAvB,WAAM,GAAN,MAAM,CAAiB;IAAG,CAAC;IAE/C;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,gBAAgB,CAAC,UAAyB,EAAE,IAAY,EAAE,WAAmB;QAC/E,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,GAAG,CAAC;QACvD,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACzD,IAAI,CAAC,cAAc,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,aAAa,UAAU,mEAAmE,CAAC,CAAC;QAChH,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,kCAAkC,UAAU,EAAE,CAAC,CAAC;QAEjE,4CAA4C;QAC5C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,cAAc,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;QAEnF,sCAAsC;QACtC,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;YAC1B,iEAAiE;YACjE,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;gBACnB,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAC;YACpG,CAAC;YACD,OAAO,IAAI,CAAC,6BAA6B,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/D,CAAC;aAAM,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;YACjC,+DAA+D;YAC/D,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CAAC,kFAAkF,CAAC,CAAC;YACxG,CAAC;YACD,OAAO,IAAI,CAAC,2BAA2B,CAAC,cAAc,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;QACjF,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,yBAAyB,UAAU,iCAAiC,CAAC,CAAC;IAC1F,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,6BAA6B,CAAC,OAAe;QAC/C,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,6BAA6B,CAAC,IAAI,GAAG,CAAC;QACpE,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;QACpD,IAAI,CAAC,cAAc,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;QACxF,CAAC;QAED,0CAA0C;QAC1C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;QAE9D,gBAAgB;QAChB,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QACjD,IAAI,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,GAAG,UAAU,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACxE,CAAC;QACD,IAAI,OAAO,CAAC,GAAG,KAAK,cAAc,CAAC,QAAQ,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CAAC,qCAAqC,cAAc,CAAC,QAAQ,SAAS,OAAO,CAAC,GAAG,iCAAiC,CAAC,CAAC;QACvI,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,4EAA4E,CAAC,CAAC;QAClG,CAAC;QAED,OAAO;YACH,WAAW,EAAE,UAAU,OAAO,CAAC,GAAG,EAAE;YACpC,UAAU,EAAE,QAAQ;YACpB,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,OAAO,EAAE,OAAO,CAAC,OAAO;SAC3B,CAAC;IACN,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACK,KAAK,CAAC,qBAAqB,CAC/B,cAAuC,EACvC,IAAY,EACZ,WAAmB;QAEnB,MAAM,IAAI,GAAG,IAAI,eAAe,CAAC;YAC7B,SAAS,EAAE,cAAc,CAAC,QAAQ;YAClC,aAAa,EAAE,cAAc,CAAC,YAAY;YAC1C,IAAI;YACJ,YAAY,EAAE,WAAW;YACzB,UAAU,EAAE,oBAAoB;SACnC,CAAC,CAAC;QAEH,MAAM,OAAO,GAA2B;YACpC,cAAc,EAAE,mCAAmC;SACtD,CAAC;QAEF,gFAAgF;QAChF,IAAI,cAAc,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;YACzC,OAAO,CAAC,QAAQ,CAAC,GAAG,kBAAkB,CAAC;QAC3C,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,cAAc,CAAC,QAAQ,EAAE;YAC7C,MAAM,EAAE,MAAM;YACd,OAAO;YACP,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;SACxB,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACV,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,+BAA+B,GAAG,CAAC,MAAM,MAAM,OAAO,EAAE,CAAC,CAAC;QAC9E,CAAC;QAED,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;IACtB,CAAC;IAED;;;;;;;;;;OAUG;IACK,KAAK,CAAC,2BAA2B,CACrC,cAAuC,EACvC,WAAmB;QAEnB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,cAAc,CAAC,WAAW,EAAE;YAChD,OAAO,EAAE;gBACL,eAAe,EAAE,UAAU,WAAW,EAAE;gBACxC,YAAY,EAAE,sBAAsB,CAAC,iCAAiC;aACzE;SACJ,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACV,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,0CAA0C,GAAG,CAAC,MAAM,MAAM,OAAO,EAAE,CAAC,CAAC;QACzF,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAEjC,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QACrE,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAE/B,OAAO;YACH,WAAW,EAAE,UAAU,GAAG,EAAE;YAC5B,UAAU,EAAE,QAAQ;YACpB,GAAG;YACH,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,SAAS;YACjC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,KAAK,IAAI,SAAS;YAChD,OAAO,EAAE,OAAO,CAAC,UAAU,IAAI,SAAS;SAC3C,CAAC;IACN,CAAC;IAED;;;;;;;;;;OAUG;IACI,KAAK,CAAC,SAAS,CAAC,KAAa,EAAE,cAAuC;QACzE,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAChE,CAAC;QAED,MAAM,CAAC,SAAS,EAAE,UAAU,EAAE,YAAY,CAAC,GAAG,KAAK,CAAC;QAEpD,+CAA+C;QAC/C,IAAI,MAAW,CAAC;QAChB,IAAI,CAAC;YACD,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QAC/E,CAAC;QAAC,MAAM,CAAC;YACL,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACnD,CAAC;QAED,IAAI,MAAM,CAAC,GAAG,KAAK,OAAO,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,2CAA2C,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7E,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC;QACvB,IAAI,CAAC,GAAG,EAAE,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACxD,CAAC;QAED,8BAA8B;QAC9B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;QACpD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;QAC9C,IAAI,CAAC,GAAG,EAAE,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,gCAAgC,GAAG,cAAc,CAAC,CAAC;QACvE,CAAC;QAED,uDAAuD;QACvD,MAAM,SAAS,GAAG,eAAe,CAAC;YAC9B,GAAG,EAAE,GAAU;YACf,MAAM,EAAE,KAAK;SAChB,CAAC,CAAC;QAEH,oDAAoD;QACpD,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;QACzD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,SAAS,IAAI,UAAU,EAAE,CAAC,CAAC;QAEvD,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;QAC7D,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;QACpE,CAAC;QAED,+BAA+B;QAC/B,IAAI,CAAC;YACD,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QAC9E,CAAC;QAAC,MAAM,CAAC;YACL,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QACpD,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,WAAW,CAAC,cAAuC;QAC7D,MAAM,UAAU,GAAG,cAAc,CAAC,UAAU,CAAC;QAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAE9C,IAAI,MAAM,IAAI,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAC1C,OAAO,MAAM,CAAC,IAAI,CAAC;QACvB,CAAC;QAED,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,aAAa,UAAU,2DAA2D,CAAC,CAAC;QACxG,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAChD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,6BAA6B,cAAc,CAAC,OAAO,UAAU,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;QAChG,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAiB,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CAAC,wCAAwC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC;QACtF,CAAC;QAED,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE;YAC3B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS;SACzC,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC,IAAI,CAAC;IACrB,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,0BAA0B,CAAC,WAAmB,EAAE,SAAiB;QACnE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;YACpB,CAAC,EAAE,GAAG,WAAW,IAAI,SAAS,EAAE;YAChC,SAAS,EAAE,aAAa,CAAC,OAAO;SACnC,CAAC,CAAC;QACH,OAAO,MAAM,qBAAqB,CAAC;YAC/B,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,kBAAkB;YAC5C,IAAI;YACJ,MAAM,EAAE,IAAI;YACZ,SAAS,EAAE,aAAa,CAAC,OAAO;SACnC,CAAC,CAAC;IACP,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,4BAA4B,CAC9B,WAAmB,EACnB,SAAiB;QAEjB,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,0BAA0B,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;QAEtF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC;YACxB,CAAC,EAAE,GAAG,WAAW,IAAI,SAAS,OAAO;YACrC,SAAS,EAAE,aAAa,CAAC,OAAO;SACnC,CAAC,CAAC;QACH,MAAM,aAAa,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAEhD,MAAM,MAAM,GAAG,wBAAwB,CAAC;YACpC,EAAE,EAAE,wBAAwB;YAC5B,IAAI,EAAE,aAAa;SACtB,CAAC,CAAC;QACH,MAAM,CAAC,YAAY,GAAG,CAAC,oBAAoB,EAAE,oBAAoB,CAAC,CAAC;QAEnE,MAAM,QAAQ,GAAG,uBAAuB,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QAC5D,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC,CAAC;QACtF,MAAM,UAAU,GAA0B,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;QAExC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5C,MAAM,WAAW,GAAG,MAAM,yBAAyB,CAAC;gBAChD,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,SAAS;gBACT,KAAK,EAAE,CAAC;aACX,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC;gBAC7C,UAAU;gBACV,MAAM,EAAE,MAAM,CAAC,EAAE;gBACjB,WAAW;aACd,CAAC,CAAC;YACH,UAAU,CAAC,WAAW,CAAC,GAAG,MAAM,QAAQ,CAAC,iBAAiB,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC7E,CAAC;QAED,OAAO;YACH,EAAE,EAAE,wBAAwB;YAC5B,MAAM;YACN,UAAU;YACV,SAAS,EAAE,IAAI;YACf,QAAQ,EAAE;gBACN,KAAK,EAAE,WAAW;gBAClB,WAAW;aACd;SACJ,CAAC;IACN,CAAC;IAED;;;;;;OAMG;IACH,mBAAmB,CAAC,OAAe;QAC/B,MAAM,OAAO,GAA8B,EAAE,CAAC;QAE9C,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;YAC/B,OAAO,CAAC,IAAI,CAAC;gBACT,UAAU,EAAE,QAAQ;gBACpB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ;gBAC/C,OAAO,EAAE,8CAA8C;gBACvD,WAAW,EAAE,GAAG,OAAO,GAAG;gBAC1B,KAAK,EAAE,sBAAsB;aAChC,CAAC,CAAC;QACP,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;YAC/B,OAAO,CAAC,IAAI,CAAC;gBACT,UAAU,EAAE,QAAQ;gBACpB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ;gBAC/C,OAAO,EAAE,0CAA0C;gBACnD,WAAW,EAAE,GAAG,OAAO,GAAG;gBAC1B,KAAK,EAAE,sBAAsB;aAChC,CAAC,CAAC;QACP,CAAC;QAED,OAAO,OAAO,CAAC;IACnB,CAAC;CACJ"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sso-custodian-service.respec.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sso-custodian-service.respec.d.mts","sourceRoot":"","sources":["../../../src/identity/sso/sso-custodian-service.respec.mts"],"names":[],"mappings":""}
@@ -0,0 +1,167 @@
1
+ import { generateKeyPairSync, createSign } from 'node:crypto';
2
+ import { respecfully, ifWeMight, iReckon, firstOfAll, lastOfAll } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
3
+ import { SsoCustodianService } from './sso-custodian-service.mjs';
4
+ import { POOL_ID_CUSTODIAN_MANAGE } from '@ibgib/core-gib/dist/keystone/keystone-constants.mjs';
5
+ const maam = `[${import.meta.url}]`, sir = maam;
6
+ await respecfully(sir, 'SsoCustodianService', async () => {
7
+ let mockPrivateKey;
8
+ let mockPublicKeyJwk;
9
+ let originalFetch;
10
+ firstOfAll(sir, async () => {
11
+ // Generate mock RSA key pair
12
+ const { privateKey, publicKey } = generateKeyPairSync('rsa', {
13
+ modulusLength: 2048
14
+ });
15
+ mockPrivateKey = privateKey;
16
+ // Export public key as JWK
17
+ mockPublicKeyJwk = publicKey.export({ format: 'jwk' });
18
+ mockPublicKeyJwk.kid = 'mock-kid-123';
19
+ mockPublicKeyJwk.alg = 'RS256';
20
+ mockPublicKeyJwk.use = 'sig';
21
+ // Mock global fetch to return JWKS containing the mock public key
22
+ originalFetch = globalThis.fetch;
23
+ globalThis.fetch = async (url, options) => {
24
+ if (url === 'https://www.googleapis.com/oauth2/v3/certs') {
25
+ return {
26
+ ok: true,
27
+ status: 200,
28
+ json: async () => ({
29
+ keys: [mockPublicKeyJwk]
30
+ })
31
+ };
32
+ }
33
+ return {
34
+ ok: false,
35
+ status: 404,
36
+ text: async () => 'Not Found'
37
+ };
38
+ };
39
+ });
40
+ lastOfAll(sir, async () => {
41
+ // Restore global fetch
42
+ if (originalFetch) {
43
+ globalThis.fetch = originalFetch;
44
+ }
45
+ });
46
+ await ifWeMight(sir, 'verify dynamic RSA RS256 JWT signature successfully', async () => {
47
+ const config = {
48
+ ssoServerKdfSecret: 'mock-kdf-secret',
49
+ sessionSecret: 'mock-session-secret',
50
+ providers: {
51
+ google: {
52
+ providerId: 'google',
53
+ clientId: 'google-client-id-123',
54
+ clientSecret: 'google-client-secret-123',
55
+ tokenUrl: 'https://oauth2.googleapis.com/token',
56
+ userInfoUrl: 'https://openidconnect.googleapis.com/v1/userinfo',
57
+ jwksUrl: 'https://www.googleapis.com/oauth2/v3/certs'
58
+ }
59
+ }
60
+ };
61
+ const service = new SsoCustodianService(config);
62
+ // 1. Build JWT Header & Payload
63
+ const header = { alg: 'RS256', kid: 'mock-kid-123', typ: 'JWT' };
64
+ const payload = {
65
+ sub: 'user-sub-999',
66
+ email: 'user@example.com',
67
+ name: 'John Doe',
68
+ aud: 'google-client-id-123',
69
+ iss: 'https://accounts.google.com',
70
+ exp: Math.floor(Date.now() / 1000) + 3600 // expires in 1 hour
71
+ };
72
+ const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');
73
+ const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');
74
+ const data = `${headerB64}.${payloadB64}`;
75
+ // 2. Sign JWT using private key
76
+ const sign = createSign('RSA-SHA256');
77
+ sign.update(data);
78
+ const signatureB64 = sign.sign(mockPrivateKey, 'base64url');
79
+ const mockJwtToken = `${data}.${signatureB64}`;
80
+ // 3. Verify signature via service
81
+ const decodedPayload = await service.verifyJwt(mockJwtToken, config.providers.google);
82
+ iReckon(sir, decodedPayload.sub).isGonnaBe('user-sub-999');
83
+ iReckon(sir, decodedPayload.email).isGonnaBe('user@example.com');
84
+ iReckon(sir, decodedPayload.name).isGonnaBe('John Doe');
85
+ });
86
+ await ifWeMight(sir, 'fail validation if JWT has invalid signature', async () => {
87
+ const config = {
88
+ ssoServerKdfSecret: 'mock-kdf-secret',
89
+ sessionSecret: 'mock-session-secret',
90
+ providers: {
91
+ google: {
92
+ providerId: 'google',
93
+ clientId: 'google-client-id-123',
94
+ clientSecret: 'google-client-secret-123',
95
+ tokenUrl: 'https://oauth2.googleapis.com/token',
96
+ userInfoUrl: 'https://openidconnect.googleapis.com/v1/userinfo',
97
+ jwksUrl: 'https://www.googleapis.com/oauth2/v3/certs'
98
+ }
99
+ }
100
+ };
101
+ const service = new SsoCustodianService(config);
102
+ const header = { alg: 'RS256', kid: 'mock-kid-123', typ: 'JWT' };
103
+ const payload = {
104
+ sub: 'user-sub-999',
105
+ aud: 'google-client-id-123',
106
+ exp: Math.floor(Date.now() / 1000) + 3600
107
+ };
108
+ const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');
109
+ const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');
110
+ const data = `${headerB64}.${payloadB64}`;
111
+ // Create signature using a different/unregistered RSA key
112
+ const { privateKey: badPrivateKey } = generateKeyPairSync('rsa', { modulusLength: 2048 });
113
+ const sign = createSign('RSA-SHA256');
114
+ sign.update(data);
115
+ const badSignatureB64 = sign.sign(badPrivateKey, 'base64url');
116
+ const badJwtToken = `${data}.${badSignatureB64}`;
117
+ let verifyError;
118
+ try {
119
+ await service.verifyJwt(badJwtToken, config.providers.google);
120
+ }
121
+ catch (error) {
122
+ verifyError = error;
123
+ }
124
+ iReckon(sir, verifyError).not.isGonnaBeUndefined();
125
+ iReckon(sir, verifyError.message).includes('Cryptographic signature verification failed');
126
+ });
127
+ await ifWeMight(sir, 'derive server-delegate secret deterministically', async () => {
128
+ const config = {
129
+ ssoServerKdfSecret: 'high-entropy-server-kdf-secret',
130
+ sessionSecret: 'session-secret-123',
131
+ providers: {}
132
+ };
133
+ const service = new SsoCustodianService(config);
134
+ const key = 'google:123456789';
135
+ const nonce1 = 'nonce-abc-123';
136
+ const nonce2 = 'nonce-def-456';
137
+ const secret1_a = await service.deriveServerDelegateSecret(key, nonce1);
138
+ const secret1_b = await service.deriveServerDelegateSecret(key, nonce1);
139
+ const secret2 = await service.deriveServerDelegateSecret(key, nonce2);
140
+ // Identical inputs must yield identical outputs
141
+ iReckon(sir, secret1_a).isGonnaBe(secret1_b);
142
+ // Different nonces must yield different outputs
143
+ iReckon(sir, secret1_a).not.isGonnaBe(secret2);
144
+ // Derived secrets must be string type with high entropy
145
+ iReckon(sir, typeof secret1_a).isGonnaBe('string');
146
+ iReckon(sir, secret1_a.length).isGonnaBe(128); // sha512 output in hex/base64 wrap
147
+ });
148
+ await ifWeMight(sir, 'create custodian challenge pool successfully', async () => {
149
+ const config = {
150
+ ssoServerKdfSecret: 'high-entropy-server-kdf-secret',
151
+ sessionSecret: 'session-secret-123',
152
+ providers: {}
153
+ };
154
+ const service = new SsoCustodianService(config);
155
+ const key = 'google:123456789';
156
+ const nonce = 'nonce-abc-123';
157
+ const custodianPool = await service.createCustodianChallengePool(key, nonce);
158
+ iReckon(sir, custodianPool).not.isGonnaBeUndefined();
159
+ iReckon(sir, custodianPool.id).willEqual(POOL_ID_CUSTODIAN_MANAGE);
160
+ iReckon(sir, custodianPool.isForeign).isGonnaBeTrue();
161
+ iReckon(sir, custodianPool.metadata?.providerKey).willEqual(key);
162
+ // Structural validation of challenges
163
+ const challengesCount = Object.keys(custodianPool.challenges).length;
164
+ iReckon(sir, challengesCount).willEqual(custodianPool.config.behavior.size);
165
+ });
166
+ });
167
+ //# sourceMappingURL=sso-custodian-service.respec.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sso-custodian-service.respec.mjs","sourceRoot":"","sources":["../../../src/identity/sso/sso-custodian-service.respec.mts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @module identity/sso/sso-types
3
+ *
4
+ * Core types and interfaces for extensible SSO (OAuth2) authentication.
5
+ */
6
+ export type SsoProviderId = 'google' | 'github';
7
+ export interface SsoProviderClientConfig {
8
+ providerId: SsoProviderId;
9
+ clientId: string;
10
+ authUrl: string;
11
+ redirectUri: string;
12
+ scope: string;
13
+ }
14
+ export interface SsoProviderServerConfig {
15
+ providerId: SsoProviderId;
16
+ clientId: string;
17
+ clientSecret: string;
18
+ tokenUrl: string;
19
+ userInfoUrl: string;
20
+ jwksUrl?: string;
21
+ }
22
+ export interface SsoServerConfig {
23
+ /** Server's private KDF secret used to derive server-delegate keystones. */
24
+ ssoServerKdfSecret: string;
25
+ /** Server's private session encryption secret. */
26
+ sessionSecret: string;
27
+ /** Map of active OAuth2 providers. */
28
+ providers: Partial<Record<SsoProviderId, SsoProviderServerConfig>>;
29
+ }
30
+ export interface OAuthUserInfo {
31
+ /** Unique identity key combining provider and sub ID (e.g., `google:107632...`). */
32
+ providerKey: string;
33
+ providerId: SsoProviderId;
34
+ /** Unique user ID within the provider. */
35
+ sub: string;
36
+ email?: string;
37
+ name?: string;
38
+ picture?: string;
39
+ }
40
+ //# sourceMappingURL=sso-types.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sso-types.d.mts","sourceRoot":"","sources":["../../../src/identity/sso/sso-types.mts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEhD,MAAM,WAAW,uBAAuB;IACpC,UAAU,EAAE,aAAa,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACpC,UAAU,EAAE,aAAa,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC5B,4EAA4E;IAC5E,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kDAAkD;IAClD,aAAa,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,uBAAuB,CAAC,CAAC,CAAC;CACtE;AAED,MAAM,WAAW,aAAa;IAC1B,oFAAoF;IACpF,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,aAAa,CAAC;IAC1B,0CAA0C;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @module identity/sso/sso-types
3
+ *
4
+ * Core types and interfaces for extensible SSO (OAuth2) authentication.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=sso-types.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sso-types.mjs","sourceRoot":"","sources":["../../../src/identity/sso/sso-types.mts"],"names":[],"mappings":"AAAA;;;;GAIG"}