@open-mercato/enterprise 0.6.3-develop.3797.1.9d6338d7cd → 0.6.3-develop.3805.1.357827108c

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.
@@ -39,12 +39,12 @@ class OidcProvider {
39
39
  if (!email) {
40
40
  throw new Error("IdP did not return an email claim (checked: email, upn, unique_name)");
41
41
  }
42
- const emailVerified = mergedClaims.email_verified === true;
42
+ const emailVerified = normalizeEmailVerifiedClaim(mergedClaims.email_verified);
43
43
  const groups = extractIdentityGroups(mergedClaims);
44
44
  return {
45
45
  subject,
46
46
  email,
47
- emailVerified,
47
+ ...emailVerified === void 0 ? {} : { emailVerified },
48
48
  name: mergedClaims.name ?? void 0,
49
49
  groups
50
50
  };
@@ -88,6 +88,9 @@ async function mergeWithUserInfoClaims(oidcConfig, tokens, claims) {
88
88
  return claims;
89
89
  }
90
90
  }
91
+ function normalizeEmailVerifiedClaim(value) {
92
+ return typeof value === "boolean" ? value : void 0;
93
+ }
91
94
  function extractIdentityGroups(claims) {
92
95
  const groups = /* @__PURE__ */ new Set();
93
96
  const add = (value) => {
@@ -135,6 +138,7 @@ function coerceClaimValues(value) {
135
138
  export {
136
139
  OidcProvider,
137
140
  coerceClaimValues,
138
- extractIdentityGroups
141
+ extractIdentityGroups,
142
+ normalizeEmailVerifiedClaim
139
143
  };
140
144
  //# sourceMappingURL=oidc-provider.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sso/lib/oidc-provider.ts"],
4
- "sourcesContent": ["import * as client from 'openid-client'\nimport type { SsoConfig } from '../data/entities'\nimport type { SsoIdentityPayload, SsoProtocolProvider } from './types'\n\nexport class OidcProvider implements SsoProtocolProvider {\n readonly protocol = 'oidc' as const\n\n async buildAuthUrl(\n config: SsoConfig,\n params: {\n state: string\n nonce: string\n redirectUri: string\n codeVerifier?: string\n clientSecret?: string\n },\n ): Promise<string> {\n const oidcConfig = await this.discover(config, params.clientSecret)\n\n const codeChallenge = params.codeVerifier\n ? await client.calculatePKCECodeChallenge(params.codeVerifier)\n : undefined\n\n const authUrl = client.buildAuthorizationUrl(oidcConfig, {\n redirect_uri: params.redirectUri,\n scope: 'openid email profile',\n state: params.state,\n nonce: params.nonce,\n ...(codeChallenge\n ? { code_challenge: codeChallenge, code_challenge_method: 'S256' }\n : {}),\n })\n\n return authUrl.href\n }\n\n async handleCallback(\n config: SsoConfig,\n params: {\n callbackParams: Record<string, string>\n redirectUri: string\n expectedState: string\n expectedNonce: string\n codeVerifier?: string\n clientSecret?: string\n },\n ): Promise<SsoIdentityPayload> {\n const oidcConfig = await this.discover(config, params.clientSecret)\n\n const callbackUrl = new URL(params.redirectUri)\n for (const [key, value] of Object.entries(params.callbackParams)) {\n callbackUrl.searchParams.set(key, value)\n }\n\n const tokens = await client.authorizationCodeGrant(oidcConfig, callbackUrl, {\n pkceCodeVerifier: params.codeVerifier,\n expectedState: params.expectedState,\n expectedNonce: params.expectedNonce,\n })\n\n const claims = tokens.claims()\n if (!claims) {\n throw new Error('No ID token claims received from IdP')\n }\n\n const mergedClaims = await mergeWithUserInfoClaims(oidcConfig, tokens, claims)\n\n const subject = String(mergedClaims.sub ?? claims.sub ?? '')\n const rawEmail = mergedClaims.email as string | undefined\n const upnCandidate = (mergedClaims.upn ?? mergedClaims.unique_name) as string | undefined\n const upnAsEmail = typeof upnCandidate === 'string' && upnCandidate.includes('@') ? upnCandidate : undefined\n const email = rawEmail ?? upnAsEmail\n if (!email) {\n throw new Error('IdP did not return an email claim (checked: email, upn, unique_name)')\n }\n\n const emailVerified = mergedClaims.email_verified === true\n const groups = extractIdentityGroups(mergedClaims)\n\n \n\n return {\n subject,\n email,\n emailVerified,\n name: (mergedClaims.name as string) ?? undefined,\n groups,\n }\n }\n\n async validateConfig(\n config: SsoConfig,\n params?: { clientSecret?: string },\n ): Promise<{ ok: boolean; error?: string }> {\n try {\n await this.discover(config, params?.clientSecret)\n return { ok: true }\n } catch (err) {\n return {\n ok: false,\n error: err instanceof Error ? err.message : 'Discovery failed',\n }\n }\n }\n\n private async discover(\n config: SsoConfig,\n clientSecret?: string,\n ): Promise<client.Configuration> {\n if (!config.issuer) {\n throw new Error('SSO config is missing issuer URL')\n }\n if (!config.clientId) {\n throw new Error('SSO config is missing client ID')\n }\n\n return client.discovery(\n new URL(config.issuer),\n config.clientId,\n clientSecret ?? undefined,\n )\n }\n}\n\nasync function mergeWithUserInfoClaims(\n oidcConfig: client.Configuration,\n tokens: client.TokenEndpointResponse,\n claims: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n const accessToken = tokens.access_token\n if (!accessToken) return claims\n\n try {\n const userInfo = await client.fetchUserInfo(\n oidcConfig,\n accessToken,\n client.skipSubjectCheck,\n )\n return { ...(userInfo as Record<string, unknown>), ...claims }\n } catch {\n return claims\n }\n}\n\nexport function extractIdentityGroups(claims: Record<string, unknown>): string[] | undefined {\n const groups = new Set<string>()\n\n const add = (value: unknown) => {\n for (const group of coerceClaimValues(value)) {\n groups.add(group)\n }\n }\n\n add(claims.groups)\n add(claims.roles)\n add(claims.role)\n\n for (const [key, value] of Object.entries(claims)) {\n if (!key.endsWith(':roles')) continue\n add(value)\n }\n\n return groups.size > 0 ? Array.from(groups) : undefined\n}\n\nexport function coerceClaimValues(value: unknown): string[] {\n if (typeof value === 'string') {\n const normalized = value.trim()\n return normalized ? [normalized] : []\n }\n\n if (Array.isArray(value)) {\n return value.flatMap((entry) => coerceClaimValues(entry))\n }\n\n if (value && typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n const out = new Set<string>()\n for (const [key, nested] of entries) {\n const normalizedKey = key.trim()\n if (normalizedKey) out.add(normalizedKey)\n if (typeof nested === 'string') {\n const normalizedNested = nested.trim()\n if (normalizedNested) out.add(normalizedNested)\n } else if (nested && typeof nested === 'object') {\n const nestedName = (nested as Record<string, unknown>).name\n if (typeof nestedName === 'string' && nestedName.trim()) {\n out.add(nestedName.trim())\n }\n }\n }\n return Array.from(out)\n }\n\n return []\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as client from 'openid-client'\nimport type { SsoConfig } from '../data/entities'\nimport type { SsoIdentityPayload, SsoProtocolProvider } from './types'\n\nexport class OidcProvider implements SsoProtocolProvider {\n readonly protocol = 'oidc' as const\n\n async buildAuthUrl(\n config: SsoConfig,\n params: {\n state: string\n nonce: string\n redirectUri: string\n codeVerifier?: string\n clientSecret?: string\n },\n ): Promise<string> {\n const oidcConfig = await this.discover(config, params.clientSecret)\n\n const codeChallenge = params.codeVerifier\n ? await client.calculatePKCECodeChallenge(params.codeVerifier)\n : undefined\n\n const authUrl = client.buildAuthorizationUrl(oidcConfig, {\n redirect_uri: params.redirectUri,\n scope: 'openid email profile',\n state: params.state,\n nonce: params.nonce,\n ...(codeChallenge\n ? { code_challenge: codeChallenge, code_challenge_method: 'S256' }\n : {}),\n })\n\n return authUrl.href\n }\n\n async handleCallback(\n config: SsoConfig,\n params: {\n callbackParams: Record<string, string>\n redirectUri: string\n expectedState: string\n expectedNonce: string\n codeVerifier?: string\n clientSecret?: string\n },\n ): Promise<SsoIdentityPayload> {\n const oidcConfig = await this.discover(config, params.clientSecret)\n\n const callbackUrl = new URL(params.redirectUri)\n for (const [key, value] of Object.entries(params.callbackParams)) {\n callbackUrl.searchParams.set(key, value)\n }\n\n const tokens = await client.authorizationCodeGrant(oidcConfig, callbackUrl, {\n pkceCodeVerifier: params.codeVerifier,\n expectedState: params.expectedState,\n expectedNonce: params.expectedNonce,\n })\n\n const claims = tokens.claims()\n if (!claims) {\n throw new Error('No ID token claims received from IdP')\n }\n\n const mergedClaims = await mergeWithUserInfoClaims(oidcConfig, tokens, claims)\n\n const subject = String(mergedClaims.sub ?? claims.sub ?? '')\n const rawEmail = mergedClaims.email as string | undefined\n const upnCandidate = (mergedClaims.upn ?? mergedClaims.unique_name) as string | undefined\n const upnAsEmail = typeof upnCandidate === 'string' && upnCandidate.includes('@') ? upnCandidate : undefined\n const email = rawEmail ?? upnAsEmail\n if (!email) {\n throw new Error('IdP did not return an email claim (checked: email, upn, unique_name)')\n }\n\n const emailVerified = normalizeEmailVerifiedClaim(mergedClaims.email_verified)\n const groups = extractIdentityGroups(mergedClaims)\n\n return {\n subject,\n email,\n ...(emailVerified === undefined ? {} : { emailVerified }),\n name: (mergedClaims.name as string) ?? undefined,\n groups,\n }\n }\n\n async validateConfig(\n config: SsoConfig,\n params?: { clientSecret?: string },\n ): Promise<{ ok: boolean; error?: string }> {\n try {\n await this.discover(config, params?.clientSecret)\n return { ok: true }\n } catch (err) {\n return {\n ok: false,\n error: err instanceof Error ? err.message : 'Discovery failed',\n }\n }\n }\n\n private async discover(\n config: SsoConfig,\n clientSecret?: string,\n ): Promise<client.Configuration> {\n if (!config.issuer) {\n throw new Error('SSO config is missing issuer URL')\n }\n if (!config.clientId) {\n throw new Error('SSO config is missing client ID')\n }\n\n return client.discovery(\n new URL(config.issuer),\n config.clientId,\n clientSecret ?? undefined,\n )\n }\n}\n\nasync function mergeWithUserInfoClaims(\n oidcConfig: client.Configuration,\n tokens: client.TokenEndpointResponse,\n claims: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n const accessToken = tokens.access_token\n if (!accessToken) return claims\n\n try {\n const userInfo = await client.fetchUserInfo(\n oidcConfig,\n accessToken,\n client.skipSubjectCheck,\n )\n return { ...(userInfo as Record<string, unknown>), ...claims }\n } catch {\n return claims\n }\n}\n\nexport function normalizeEmailVerifiedClaim(value: unknown): boolean | undefined {\n return typeof value === 'boolean' ? value : undefined\n}\n\nexport function extractIdentityGroups(claims: Record<string, unknown>): string[] | undefined {\n const groups = new Set<string>()\n\n const add = (value: unknown) => {\n for (const group of coerceClaimValues(value)) {\n groups.add(group)\n }\n }\n\n add(claims.groups)\n add(claims.roles)\n add(claims.role)\n\n for (const [key, value] of Object.entries(claims)) {\n if (!key.endsWith(':roles')) continue\n add(value)\n }\n\n return groups.size > 0 ? Array.from(groups) : undefined\n}\n\nexport function coerceClaimValues(value: unknown): string[] {\n if (typeof value === 'string') {\n const normalized = value.trim()\n return normalized ? [normalized] : []\n }\n\n if (Array.isArray(value)) {\n return value.flatMap((entry) => coerceClaimValues(entry))\n }\n\n if (value && typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n const out = new Set<string>()\n for (const [key, nested] of entries) {\n const normalizedKey = key.trim()\n if (normalizedKey) out.add(normalizedKey)\n if (typeof nested === 'string') {\n const normalizedNested = nested.trim()\n if (normalizedNested) out.add(normalizedNested)\n } else if (nested && typeof nested === 'object') {\n const nestedName = (nested as Record<string, unknown>).name\n if (typeof nestedName === 'string' && nestedName.trim()) {\n out.add(nestedName.trim())\n }\n }\n }\n return Array.from(out)\n }\n\n return []\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/enterprise",
3
- "version": "0.6.3-develop.3797.1.9d6338d7cd",
3
+ "version": "0.6.3-develop.3805.1.357827108c",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -64,8 +64,8 @@
64
64
  }
65
65
  },
66
66
  "dependencies": {
67
- "@open-mercato/core": "0.6.3-develop.3797.1.9d6338d7cd",
68
- "@open-mercato/ui": "0.6.3-develop.3797.1.9d6338d7cd",
67
+ "@open-mercato/core": "0.6.3-develop.3805.1.357827108c",
68
+ "@open-mercato/ui": "0.6.3-develop.3805.1.357827108c",
69
69
  "@simplewebauthn/browser": "^13.3.0",
70
70
  "@simplewebauthn/server": "^13.3.0",
71
71
  "@simplewebauthn/types": "^12.0.0",
@@ -75,12 +75,12 @@
75
75
  "qrcode": "^1.5.4"
76
76
  },
77
77
  "peerDependencies": {
78
- "@open-mercato/shared": "0.6.3-develop.3797.1.9d6338d7cd",
78
+ "@open-mercato/shared": "0.6.3-develop.3805.1.357827108c",
79
79
  "react": "^19.0.0",
80
80
  "react-dom": "^19.0.0"
81
81
  },
82
82
  "devDependencies": {
83
- "@open-mercato/shared": "0.6.3-develop.3797.1.9d6338d7cd",
83
+ "@open-mercato/shared": "0.6.3-develop.3805.1.357827108c",
84
84
  "@types/jest": "^30.0.0",
85
85
  "@types/react": "^19.2.15",
86
86
  "@types/react-dom": "^19.2.3",
@@ -74,15 +74,13 @@ export class OidcProvider implements SsoProtocolProvider {
74
74
  throw new Error('IdP did not return an email claim (checked: email, upn, unique_name)')
75
75
  }
76
76
 
77
- const emailVerified = mergedClaims.email_verified === true
77
+ const emailVerified = normalizeEmailVerifiedClaim(mergedClaims.email_verified)
78
78
  const groups = extractIdentityGroups(mergedClaims)
79
79
 
80
-
81
-
82
80
  return {
83
81
  subject,
84
82
  email,
85
- emailVerified,
83
+ ...(emailVerified === undefined ? {} : { emailVerified }),
86
84
  name: (mergedClaims.name as string) ?? undefined,
87
85
  groups,
88
86
  }
@@ -142,6 +140,10 @@ async function mergeWithUserInfoClaims(
142
140
  }
143
141
  }
144
142
 
143
+ export function normalizeEmailVerifiedClaim(value: unknown): boolean | undefined {
144
+ return typeof value === 'boolean' ? value : undefined
145
+ }
146
+
145
147
  export function extractIdentityGroups(claims: Record<string, unknown>): string[] | undefined {
146
148
  const groups = new Set<string>()
147
149
 
@@ -35,7 +35,7 @@ export interface SsoProtocolProvider {
35
35
  export interface SsoIdentityPayload {
36
36
  subject: string
37
37
  email: string
38
- emailVerified: boolean
38
+ emailVerified?: boolean
39
39
  name?: string
40
40
  groups?: string[]
41
41
  }