@phila/sso-core 0.0.3 → 0.0.5

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.
package/README.md ADDED
@@ -0,0 +1,194 @@
1
+ # @phila/sso-core
2
+
3
+ Framework-agnostic SSO client for Azure AD B2C / Entra External ID, built on top of `@azure/msal-browser`.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @phila/sso-core @azure/msal-browser
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```ts
14
+ import { SSOClient, B2CProvider } from "@phila/sso-core";
15
+
16
+ const client = new SSOClient({
17
+ provider: new B2CProvider({
18
+ clientId: "your-client-id",
19
+ b2cEnvironment: "YourTenant",
20
+ authorityDomain: "YourTenant.b2clogin.com",
21
+ redirectUri: "http://localhost:3000",
22
+ }),
23
+ debug: true,
24
+ });
25
+
26
+ await client.initialize();
27
+
28
+ if (!client.state.isAuthenticated) {
29
+ await client.signIn();
30
+ }
31
+ ```
32
+
33
+ ## Providers
34
+
35
+ ### B2CProvider
36
+
37
+ Azure AD B2C authentication provider.
38
+
39
+ ```ts
40
+ import { B2CProvider } from "@phila/sso-core";
41
+
42
+ const provider = new B2CProvider({
43
+ clientId: "your-client-id",
44
+ b2cEnvironment: "YourTenant",
45
+ authorityDomain: "YourTenant.b2clogin.com",
46
+ redirectUri: "http://localhost:3000",
47
+ postLogoutRedirectUri: "http://localhost:3000", // optional, defaults to redirectUri
48
+ apiScopes: ["https://YourTenant.onmicrosoft.com/api/read"], // optional
49
+ policies: {
50
+ signUpSignIn: "B2C_1A_SIGNUP_SIGNIN", // optional, shown values are defaults
51
+ signInOnly: "B2C_1A_AD_SIGNIN_ONLY",
52
+ resetPassword: "B2C_1A_PASSWORDRESET",
53
+ },
54
+ cacheLocation: "sessionStorage", // optional, "sessionStorage" | "localStorage"
55
+ });
56
+ ```
57
+
58
+ ### CIAMProvider
59
+
60
+ Entra External ID (CIAM) provider.
61
+
62
+ ```ts
63
+ import { CIAMProvider } from "@phila/sso-core";
64
+
65
+ const provider = new CIAMProvider({
66
+ clientId: "your-client-id",
67
+ tenantSubdomain: "yoursubdomain",
68
+ redirectUri: "http://localhost:3000",
69
+ scopes: ["api://your-api/scope"], // optional
70
+ });
71
+ ```
72
+
73
+ ### EntraProvider
74
+
75
+ Entra workforce (Azure AD) provider.
76
+
77
+ ```ts
78
+ import { EntraProvider } from "@phila/sso-core";
79
+
80
+ const provider = new EntraProvider({
81
+ clientId: "your-client-id",
82
+ tenantId: "your-tenant-guid",
83
+ redirectUri: "http://localhost:3000",
84
+ scopes: ["User.Read"], // optional
85
+ });
86
+ ```
87
+
88
+ ## SSOClient API
89
+
90
+ ### Constructor
91
+
92
+ ```ts
93
+ const client = new SSOClient({
94
+ provider: B2CProvider | CIAMProvider | EntraProvider,
95
+ debug?: boolean,
96
+ state?: Record<string, unknown>, // custom state preserved across redirects
97
+ });
98
+ ```
99
+
100
+ ### Lifecycle
101
+
102
+ | Method | Returns | Description |
103
+ | -------------- | ------------------------------- | ---------------------------------------------------------- |
104
+ | `initialize()` | `Promise<AuthResponse \| null>` | Initialize MSAL, process redirect, check existing sessions |
105
+ | `destroy()` | `void` | Clean up resources and reset state |
106
+
107
+ ### Authentication
108
+
109
+ | Method | Returns | Description |
110
+ | ------------------------------ | ------------------------- | ----------------------------------------------------- |
111
+ | `signIn(options?)` | `Promise<void>` | Start sign-in redirect flow |
112
+ | `signInCityEmployee(options?)` | `Promise<void>` | Sign in with the sign-in-only policy (B2C) |
113
+ | `signOut(options?)` | `Promise<void>` | Sign out and redirect |
114
+ | `forgotPassword()` | `Promise<void>` | Start password reset flow (B2C) |
115
+ | `acquireToken(options?)` | `Promise<string \| null>` | Get access token (silent first, interactive fallback) |
116
+
117
+ ### Options
118
+
119
+ ```ts
120
+ interface SignInOptions {
121
+ scopes?: string[];
122
+ loginHint?: string;
123
+ domainHint?: string;
124
+ state?: Record<string, unknown>;
125
+ }
126
+
127
+ interface SignOutOptions {
128
+ postLogoutRedirectUri?: string;
129
+ }
130
+
131
+ interface TokenOptions {
132
+ scopes?: string[];
133
+ forceRefresh?: boolean;
134
+ }
135
+ ```
136
+
137
+ ### State
138
+
139
+ Access the current auth state via `client.state`:
140
+
141
+ ```ts
142
+ interface SSOClientState {
143
+ isAuthenticated: boolean;
144
+ isLoading: boolean;
145
+ user: AccountInfo | null;
146
+ token: string | null;
147
+ error: Error | null;
148
+ activePolicy: string | null;
149
+ authReady: boolean;
150
+ }
151
+ ```
152
+
153
+ ### Events
154
+
155
+ Subscribe to auth lifecycle events:
156
+
157
+ ```ts
158
+ const unsubscribe = client.events.on("auth:signedIn", response => {
159
+ console.log("User signed in:", response.account);
160
+ });
161
+
162
+ // Clean up
163
+ unsubscribe();
164
+ ```
165
+
166
+ | Event | Payload | Description |
167
+ | --------------------- | ---------------- | ------------------------ |
168
+ | `auth:stateChanged` | `SSOClientState` | Any state change |
169
+ | `auth:signedIn` | `AuthResponse` | Sign-in completed |
170
+ | `auth:signedOut` | `void` | Sign-out initiated |
171
+ | `auth:tokenAcquired` | `string` | Token acquired silently |
172
+ | `auth:error` | `Error` | Authentication error |
173
+ | `auth:forgotPassword` | `void` | Password reset completed |
174
+ | `auth:loading` | `boolean` | Loading state changed |
175
+
176
+ ## Custom State
177
+
178
+ Preserve arbitrary data across auth redirects:
179
+
180
+ ```ts
181
+ const client = new SSOClient({
182
+ provider,
183
+ state: { returnUrl: "/dashboard" },
184
+ });
185
+
186
+ const response = await client.initialize();
187
+ if (response?.customPostbackObject) {
188
+ console.log(response.customPostbackObject.returnUrl); // "/dashboard"
189
+ }
190
+ ```
191
+
192
+ ## License
193
+
194
+ MIT
package/dist/index.js CHANGED
@@ -237,6 +237,7 @@ class SSOClient {
237
237
  this.assertInitialized();
238
238
  this.log("Initiating sign-out...");
239
239
  const logoutRequest = {
240
+ account: this._state.user ?? void 0,
240
241
  postLogoutRedirectUri: options?.postLogoutRedirectUri,
241
242
  authority: this.provider.getAuthority()
242
243
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/events.ts","../src/constants.ts","../src/state-encoding.ts","../src/providers/b2c.ts","../src/client.ts","../src/providers/ciam.ts","../src/providers/entra.ts"],"sourcesContent":["import type { SSOEventMap, SSOEventName } from \"./types\";\n\ntype Listener<T> = (data: T) => void;\n\nexport class SSOEventEmitter {\n private listeners = new Map<SSOEventName, Set<Listener<unknown>>>();\n\n on<K extends SSOEventName>(event: K, listener: Listener<SSOEventMap[K]>): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n const set = this.listeners.get(event)!;\n set.add(listener as Listener<unknown>);\n\n // Return unsubscribe function\n return () => {\n set.delete(listener as Listener<unknown>);\n };\n }\n\n emit<K extends SSOEventName>(event: K, data: SSOEventMap[K]): void {\n const set = this.listeners.get(event);\n if (set) {\n for (const listener of set) {\n listener(data);\n }\n }\n }\n\n removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","export const DEFAULT_POLICIES = {\n SIGN_UP_SIGN_IN: \"B2C_1A_SIGNUP_SIGNIN\",\n SIGN_IN_ONLY: \"B2C_1A_AD_SIGNIN_ONLY\",\n RESET_PASSWORD: \"B2C_1A_PASSWORDRESET\",\n} as const;\n\nexport const DEFAULT_SCOPES = {\n OPENID: \"openid\",\n PROFILE: \"profile\",\n OFFLINE_ACCESS: \"offline_access\",\n} as const;\n\nexport const CACHE_CONFIG = {\n LOCATION: \"sessionStorage\" as const,\n STORE_AUTH_STATE_IN_COOKIE: false,\n} as const;\n\nexport const MSAL_ERROR_CODES = {\n USER_CANCELLED: \"user_cancelled\",\n NO_CACHED_AUTHORITY: \"no_cached_authority_error\",\n INTERACTION_REQUIRED: \"interaction_required\",\n FORGOT_PASSWORD: \"AADB2C90118\",\n} as const;\n\nexport const STATE_SEPARATOR = \"|\";\n","import { STATE_SEPARATOR } from \"./constants\";\n\n/**\n * Encode a custom state object into a base64 string that can be\n * appended to the MSAL state parameter using a pipe separator.\n */\nexport function encodeState(stateObj: Record<string, unknown>): string {\n return btoa(JSON.stringify(stateObj));\n}\n\n/**\n * Decode a base64-encoded state string back into an object.\n */\nexport function decodeState(encoded: string): Record<string, unknown> | null {\n try {\n return JSON.parse(atob(encoded));\n } catch {\n return null;\n }\n}\n\n/**\n * Extract custom postback data from the MSAL state string.\n * MSAL prepends its own GUID before the pipe separator.\n */\nexport function extractCustomState(msalState: string | undefined): Record<string, unknown> | null {\n if (!msalState) return null;\n\n const parts = msalState.split(STATE_SEPARATOR);\n if (parts.length < 2) return null;\n\n // Everything after the first pipe is our encoded state\n const customPart = parts.slice(1).join(STATE_SEPARATOR);\n return decodeState(customPart);\n}\n","import { LogLevel } from \"@azure/msal-browser\";\nimport type { Configuration, AuthenticationResult } from \"@azure/msal-browser\";\nimport type { IAuthProvider, B2CProviderConfig, B2CPolicies } from \"../types\";\nimport { DEFAULT_POLICIES, DEFAULT_SCOPES, CACHE_CONFIG } from \"../constants\";\n\nexport class B2CProvider implements IAuthProvider {\n readonly type = \"b2c\" as const;\n\n private readonly clientId: string;\n private readonly b2cEnv: string;\n private readonly authorityDomain: string;\n private readonly redirectUri: string;\n private readonly postLogoutRedirectUri: string;\n private readonly policies: B2CPolicies;\n private readonly apiScopes: string[];\n private readonly cacheLocation: \"sessionStorage\" | \"localStorage\";\n\n constructor(config: B2CProviderConfig) {\n this.clientId = config.clientId;\n this.b2cEnv = config.b2cEnvironment;\n this.authorityDomain = config.authorityDomain;\n this.redirectUri = config.redirectUri;\n this.postLogoutRedirectUri = config.postLogoutRedirectUri ?? config.redirectUri;\n this.policies = {\n signUpSignIn: config.policies?.signUpSignIn ?? DEFAULT_POLICIES.SIGN_UP_SIGN_IN,\n signInOnly: config.policies?.signInOnly ?? DEFAULT_POLICIES.SIGN_IN_ONLY,\n resetPassword: config.policies?.resetPassword ?? DEFAULT_POLICIES.RESET_PASSWORD,\n };\n this.apiScopes = config.apiScopes ?? [];\n this.cacheLocation = config.cacheLocation ?? CACHE_CONFIG.LOCATION;\n }\n\n buildMsalConfig(): Configuration {\n return {\n auth: {\n clientId: this.clientId,\n authority: this.getAuthority(this.policies.signUpSignIn),\n knownAuthorities: this.getKnownAuthorities(),\n redirectUri: this.redirectUri,\n postLogoutRedirectUri: this.postLogoutRedirectUri,\n navigateToLoginRequestUrl: false,\n },\n cache: {\n cacheLocation: this.cacheLocation,\n storeAuthStateInCookie: CACHE_CONFIG.STORE_AUTH_STATE_IN_COOKIE,\n },\n system: {\n loggerOptions: {\n logLevel: LogLevel.Warning,\n loggerCallback: (_level: LogLevel, message: string) => {\n console.warn(\"[sso-core/b2c]\", message);\n },\n },\n },\n };\n }\n\n getAuthority(policy?: string): string {\n const p = policy ?? this.policies.signUpSignIn;\n return `https://${this.authorityDomain}/${this.b2cEnv}.onmicrosoft.com/${p}`;\n }\n\n getKnownAuthorities(): string[] {\n return [this.authorityDomain];\n }\n\n identifyPolicy(response: AuthenticationResult): string | null {\n const claims = response.idTokenClaims as Record<string, unknown> | undefined;\n if (!claims) return null;\n\n const acr = (claims.acr ?? claims.tfp) as string | undefined;\n return acr?.toUpperCase() ?? null;\n }\n\n getDefaultScopes(): string[] {\n return [DEFAULT_SCOPES.OPENID, DEFAULT_SCOPES.PROFILE];\n }\n\n getApiScopes(): string[] {\n return this.apiScopes;\n }\n\n /** Get the authority URL for the sign-in-only (city employee) policy */\n getSignInOnlyAuthority(): string {\n return this.getAuthority(this.policies.signInOnly);\n }\n\n /** Get the authority URL for the password reset policy */\n getResetPasswordAuthority(): string {\n return this.getAuthority(this.policies.resetPassword);\n }\n\n /** Get all configured policy names */\n getPolicies(): Readonly<B2CPolicies> {\n return this.policies;\n }\n}\n","import { PublicClientApplication, InteractionRequiredAuthError } from \"@azure/msal-browser\";\nimport type { AccountInfo, AuthenticationResult } from \"@azure/msal-browser\";\nimport { SSOEventEmitter } from \"./events\";\nimport { encodeState, extractCustomState } from \"./state-encoding\";\nimport { MSAL_ERROR_CODES } from \"./constants\";\nimport { B2CProvider } from \"./providers/b2c\";\nimport type {\n IAuthProvider,\n SSOClientConfig,\n SSOClientState,\n SignInOptions,\n SignOutOptions,\n TokenOptions,\n AuthResponse,\n} from \"./types\";\n\nconst INITIAL_STATE: SSOClientState = {\n isAuthenticated: false,\n isLoading: false,\n user: null,\n token: null,\n error: null,\n activePolicy: null,\n authReady: false,\n};\n\nexport class SSOClient {\n readonly events = new SSOEventEmitter();\n private readonly provider: IAuthProvider;\n private readonly debug: boolean;\n private readonly encodedState: string | null;\n\n private msalInstance: PublicClientApplication | null = null;\n private _state: SSOClientState = { ...INITIAL_STATE };\n\n constructor(config: SSOClientConfig) {\n this.provider = config.provider;\n this.debug = config.debug ?? false;\n this.encodedState = config.state ? encodeState(config.state) : null;\n }\n\n get state(): Readonly<SSOClientState> {\n return this._state;\n }\n\n // ── Lifecycle ──\n\n async initialize(): Promise<AuthResponse | null> {\n this.log(\"Initializing SSOClient...\");\n this.updateState({ isLoading: true });\n\n const msalConfig = this.provider.buildMsalConfig();\n this.msalInstance = new PublicClientApplication(msalConfig);\n\n await this.msalInstance.initialize();\n\n const result = await this.handleRedirect();\n\n this.updateState({ isLoading: false, authReady: true });\n return result;\n }\n\n destroy(): void {\n this.events.removeAllListeners();\n this.msalInstance = null;\n this._state = { ...INITIAL_STATE };\n }\n\n // ── Auth Actions ──\n\n async handleRedirect(): Promise<AuthResponse | null> {\n this.assertInitialized();\n this.log(\"Handling redirect promise...\");\n\n try {\n const response = await this.msalInstance!.handleRedirectPromise();\n\n if (!response) {\n // No redirect response — check for existing sessions\n this.selectAccount(null);\n return null;\n }\n\n this.log(\"Redirect response received\", response);\n\n // Extract custom postback state\n const customState = extractCustomState(response.state);\n\n // Identify which policy was used via the acr/tfp claim\n const policy = this.provider.identifyPolicy(response);\n this.updateState({ activePolicy: policy });\n\n // Check if this was a forgot-password completion\n if (this.isForgotPasswordPolicy(policy)) {\n this.log(\"Forgot password flow completed\");\n this.events.emit(\"auth:forgotPassword\", undefined as never);\n return { ...response, customPostbackObject: customState ?? undefined } as AuthResponse;\n }\n\n // Normal sign-in flow\n this.updateState({ isLoading: true });\n this.selectAccount(policy);\n await this.acquireTokenAfterRedirect(response);\n\n const authResponse: AuthResponse = {\n ...response,\n customPostbackObject: customState ?? undefined,\n };\n\n this.events.emit(\"auth:signedIn\", authResponse);\n return authResponse;\n } catch (error) {\n return this.handleRedirectError(error);\n }\n }\n\n async signIn(options?: SignInOptions): Promise<void> {\n this.assertInitialized();\n this.log(\"Initiating sign-in...\");\n this.updateState({ isLoading: true });\n this.events.emit(\"auth:loading\", true);\n\n const request = this.buildLoginRequest(this.provider.getAuthority(), options);\n await this.msalInstance!.loginRedirect(request);\n }\n\n async signInCityEmployee(options?: SignInOptions): Promise<void> {\n this.assertInitialized();\n this.log(\"Initiating city employee sign-in...\");\n\n if (!(this.provider instanceof B2CProvider)) {\n // For non-B2C providers, fall back to regular sign-in\n return this.signIn(options);\n }\n\n const authority = this.provider.getSignInOnlyAuthority();\n const request = this.buildLoginRequest(authority, options);\n await this.msalInstance!.loginRedirect(request);\n }\n\n async signOut(options?: SignOutOptions): Promise<void> {\n this.assertInitialized();\n this.log(\"Initiating sign-out...\");\n\n const logoutRequest = {\n postLogoutRedirectUri: options?.postLogoutRedirectUri,\n authority: this.provider.getAuthority(),\n };\n\n this.updateState({ isLoading: true });\n this.events.emit(\"auth:signedOut\", undefined as never);\n await this.msalInstance!.logoutRedirect(logoutRequest);\n }\n\n async forgotPassword(): Promise<void> {\n this.assertInitialized();\n\n if (!(this.provider instanceof B2CProvider)) {\n this.log(\"Forgot password is only supported for B2C providers\");\n return;\n }\n\n this.log(\"Initiating forgot password flow...\");\n const authority = this.provider.getResetPasswordAuthority();\n await this.msalInstance!.loginRedirect({ scopes: [], authority });\n }\n\n async acquireToken(options?: TokenOptions): Promise<string | null> {\n this.assertInitialized();\n this.log(\"Acquiring token...\");\n\n const account = this._state.user;\n if (!account) {\n this.log(\"No account found, cannot acquire token\");\n return null;\n }\n\n const scopes = options?.scopes ?? this.provider.getApiScopes();\n const tokenRequest = {\n scopes,\n forceRefresh: options?.forceRefresh ?? false,\n account,\n authority: this._state.activePolicy\n ? this.provider.getAuthority(this._state.activePolicy)\n : this.provider.getAuthority(),\n };\n\n try {\n const response = await this.msalInstance!.acquireTokenSilent(tokenRequest);\n\n if (!response.accessToken) {\n throw new InteractionRequiredAuthError(\"empty_token\");\n }\n\n this.log(\"Token acquired silently\");\n this.updateState({ token: response.accessToken, error: null });\n this.events.emit(\"auth:tokenAcquired\", response.accessToken);\n return response.accessToken;\n } catch (error) {\n if (error instanceof InteractionRequiredAuthError) {\n this.log(\"Silent token acquisition failed, falling back to redirect\");\n try {\n await this.msalInstance!.acquireTokenRedirect(tokenRequest);\n return null; // Page will redirect\n } catch (redirectError) {\n this.handleError(redirectError);\n return null;\n }\n }\n this.handleError(error);\n return null;\n }\n }\n\n // ── Internal Helpers ──\n\n private selectAccount(policy: string | null): void {\n const accounts = this.msalInstance!.getAllAccounts();\n\n if (accounts.length === 0) {\n this.updateState({ isAuthenticated: false, user: null });\n return;\n }\n\n let selected: AccountInfo | null = null;\n\n if (accounts.length === 1) {\n selected = accounts[0];\n } else if (policy) {\n // Filter accounts by policy and authority domain\n const filtered = accounts.filter(account => {\n const claims = account.idTokenClaims as Record<string, unknown> | undefined;\n const iss = (claims?.iss as string) ?? \"\";\n const knownAuthorities = this.provider.getKnownAuthorities();\n const matchesAuthority = knownAuthorities.some(auth => iss.toUpperCase().includes(auth.toUpperCase()));\n const matchesPolicy = account.homeAccountId.toUpperCase().includes(policy.toUpperCase());\n return matchesAuthority && matchesPolicy;\n });\n\n if (filtered.length >= 1) {\n selected = filtered[0];\n }\n }\n\n if (!selected && accounts.length > 0) {\n selected = accounts[0];\n }\n\n if (selected) {\n this.log(\"Account selected\", selected.username);\n this.updateState({ isAuthenticated: true, user: selected });\n }\n }\n\n private async acquireTokenAfterRedirect(response: AuthenticationResult): Promise<void> {\n // Set the account first so acquireToken can use it\n const account =\n this.msalInstance!.getAccountByHomeId(response.account?.homeAccountId ?? \"\") ?? response.account ?? null;\n\n if (account) {\n this.updateState({ isAuthenticated: true, user: account });\n }\n\n await this.acquireToken();\n this.updateState({ isLoading: false });\n }\n\n private buildLoginRequest(authority: string, options?: SignInOptions) {\n const scopes = options?.scopes ?? this.provider.getDefaultScopes();\n return {\n scopes,\n authority,\n state: this.encodedState ? `${this.encodedState}` : undefined,\n loginHint: options?.loginHint,\n domainHint: options?.domainHint,\n };\n }\n\n private isForgotPasswordPolicy(policy: string | null): boolean {\n if (!policy) return false;\n if (!(this.provider instanceof B2CProvider)) return false;\n return policy.toUpperCase() === this.provider.getPolicies().resetPassword.toUpperCase();\n }\n\n private handleRedirectError(error: unknown): null {\n const err = error as { errorMessage?: string; errorCode?: string };\n\n // Detect B2C forgot-password error code\n if (err.errorMessage?.includes(MSAL_ERROR_CODES.FORGOT_PASSWORD)) {\n this.log(\"Forgot password error detected, redirecting...\");\n this.forgotPassword();\n return null;\n }\n\n this.handleError(error);\n this.updateState({ isLoading: false });\n return null;\n }\n\n private handleError(error: unknown): void {\n const err = error instanceof Error ? error : new Error(String(error));\n this.log(\"Error:\", err.message);\n this.updateState({ error: err });\n this.events.emit(\"auth:error\", err);\n }\n\n private updateState(partial: Partial<SSOClientState>): void {\n this._state = { ...this._state, ...partial };\n this.events.emit(\"auth:stateChanged\", this._state);\n }\n\n private assertInitialized(): void {\n if (!this.msalInstance) {\n throw new Error(\"SSOClient not initialized. Call initialize() first.\");\n }\n }\n\n private log(...args: unknown[]): void {\n if (this.debug) {\n console.log(\"[sso-core]\", ...args);\n }\n }\n}\n","import type { Configuration, AuthenticationResult } from \"@azure/msal-browser\";\nimport type { IAuthProvider, CIAMProviderConfig } from \"../types\";\n\n/**\n * Entra External ID (CIAM) provider — future implementation.\n * Placeholder scaffolding to validate the provider interface.\n */\nexport class CIAMProvider implements IAuthProvider {\n readonly type = \"ciam\" as const;\n\n private readonly config: CIAMProviderConfig;\n\n constructor(config: CIAMProviderConfig) {\n this.config = config;\n }\n\n buildMsalConfig(): Configuration {\n return {\n auth: {\n clientId: this.config.clientId,\n authority: this.getAuthority(),\n knownAuthorities: this.getKnownAuthorities(),\n redirectUri: this.config.redirectUri,\n postLogoutRedirectUri: this.config.postLogoutRedirectUri ?? this.config.redirectUri,\n navigateToLoginRequestUrl: false,\n },\n cache: {\n cacheLocation: this.config.cacheLocation ?? \"sessionStorage\",\n },\n };\n }\n\n getAuthority(): string {\n return `https://${this.config.tenantSubdomain}.ciamlogin.com/`;\n }\n\n getKnownAuthorities(): string[] {\n return [`${this.config.tenantSubdomain}.ciamlogin.com`];\n }\n\n identifyPolicy(_response: AuthenticationResult): string | null {\n // CIAM doesn't use client-side policy selection\n return null;\n }\n\n getDefaultScopes(): string[] {\n return [\"openid\", \"profile\"];\n }\n\n getApiScopes(): string[] {\n return this.config.scopes ?? [];\n }\n}\n","import type { Configuration, AuthenticationResult } from \"@azure/msal-browser\";\nimport type { IAuthProvider, EntraProviderConfig } from \"../types\";\n\n/**\n * Entra workforce (city employees) provider — future implementation.\n * Placeholder scaffolding to validate the provider interface.\n */\nexport class EntraProvider implements IAuthProvider {\n readonly type = \"entra\" as const;\n\n private readonly config: EntraProviderConfig;\n\n constructor(config: EntraProviderConfig) {\n this.config = config;\n }\n\n buildMsalConfig(): Configuration {\n return {\n auth: {\n clientId: this.config.clientId,\n authority: this.getAuthority(),\n knownAuthorities: this.getKnownAuthorities(),\n redirectUri: this.config.redirectUri,\n postLogoutRedirectUri: this.config.postLogoutRedirectUri ?? this.config.redirectUri,\n navigateToLoginRequestUrl: false,\n },\n cache: {\n cacheLocation: this.config.cacheLocation ?? \"sessionStorage\",\n },\n };\n }\n\n getAuthority(): string {\n return `https://login.microsoftonline.com/${this.config.tenantId}`;\n }\n\n getKnownAuthorities(): string[] {\n return [\"login.microsoftonline.com\"];\n }\n\n identifyPolicy(_response: AuthenticationResult): string | null {\n // Entra workforce doesn't use B2C policies\n return null;\n }\n\n getDefaultScopes(): string[] {\n return [\"openid\", \"profile\"];\n }\n\n getApiScopes(): string[] {\n return this.config.scopes ?? [];\n }\n}\n"],"names":["LogLevel","PublicClientApplication","InteractionRequiredAuthError"],"mappings":";;;AAIO,MAAM,gBAAgB;AAAA,EACnB,gCAAgB,IAAA;AAAA,EAExB,GAA2B,OAAU,UAAgD;AACnF,QAAI,CAAC,KAAK,UAAU,IAAI,KAAK,GAAG;AAC9B,WAAK,UAAU,IAAI,OAAO,oBAAI,KAAK;AAAA,IACrC;AACA,UAAM,MAAM,KAAK,UAAU,IAAI,KAAK;AACpC,QAAI,IAAI,QAA6B;AAGrC,WAAO,MAAM;AACX,UAAI,OAAO,QAA6B;AAAA,IAC1C;AAAA,EACF;AAAA,EAEA,KAA6B,OAAU,MAA4B;AACjE,UAAM,MAAM,KAAK,UAAU,IAAI,KAAK;AACpC,QAAI,KAAK;AACP,iBAAW,YAAY,KAAK;AAC1B,iBAAS,IAAI;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAAA,EAEA,qBAA2B;AACzB,SAAK,UAAU,MAAA;AAAA,EACjB;AACF;AChCO,MAAM,mBAAmB;AAAA,EAC9B,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,gBAAgB;AAClB;AAEO,MAAM,iBAAiB;AAAA,EAC5B,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,gBAAgB;AAClB;AAEO,MAAM,eAAe;AAAA,EAC1B,UAAU;AAAA,EACV,4BAA4B;AAC9B;AAEO,MAAM,mBAAmB;AAAA,EAC9B,gBAAgB;AAAA,EAChB,qBAAqB;AAAA,EACrB,sBAAsB;AAAA,EACtB,iBAAiB;AACnB;AAEO,MAAM,kBAAkB;AClBxB,SAAS,YAAY,UAA2C;AACrE,SAAO,KAAK,KAAK,UAAU,QAAQ,CAAC;AACtC;AAKO,SAAS,YAAY,SAAiD;AAC3E,MAAI;AACF,WAAO,KAAK,MAAM,KAAK,OAAO,CAAC;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMO,SAAS,mBAAmB,WAA+D;AAChG,MAAI,CAAC,UAAW,QAAO;AAEvB,QAAM,QAAQ,UAAU,MAAM,eAAe;AAC7C,MAAI,MAAM,SAAS,EAAG,QAAO;AAG7B,QAAM,aAAa,MAAM,MAAM,CAAC,EAAE,KAAK,eAAe;AACtD,SAAO,YAAY,UAAU;AAC/B;AC7BO,MAAM,YAAqC;AAAA,EACvC,OAAO;AAAA,EAEC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,QAA2B;AACrC,SAAK,WAAW,OAAO;AACvB,SAAK,SAAS,OAAO;AACrB,SAAK,kBAAkB,OAAO;AAC9B,SAAK,cAAc,OAAO;AAC1B,SAAK,wBAAwB,OAAO,yBAAyB,OAAO;AACpE,SAAK,WAAW;AAAA,MACd,cAAc,OAAO,UAAU,gBAAgB,iBAAiB;AAAA,MAChE,YAAY,OAAO,UAAU,cAAc,iBAAiB;AAAA,MAC5D,eAAe,OAAO,UAAU,iBAAiB,iBAAiB;AAAA,IAAA;AAEpE,SAAK,YAAY,OAAO,aAAa,CAAA;AACrC,SAAK,gBAAgB,OAAO,iBAAiB,aAAa;AAAA,EAC5D;AAAA,EAEA,kBAAiC;AAC/B,WAAO;AAAA,MACL,MAAM;AAAA,QACJ,UAAU,KAAK;AAAA,QACf,WAAW,KAAK,aAAa,KAAK,SAAS,YAAY;AAAA,QACvD,kBAAkB,KAAK,oBAAA;AAAA,QACvB,aAAa,KAAK;AAAA,QAClB,uBAAuB,KAAK;AAAA,QAC5B,2BAA2B;AAAA,MAAA;AAAA,MAE7B,OAAO;AAAA,QACL,eAAe,KAAK;AAAA,QACpB,wBAAwB,aAAa;AAAA,MAAA;AAAA,MAEvC,QAAQ;AAAA,QACN,eAAe;AAAA,UACb,UAAUA,YAAAA,SAAS;AAAA,UACnB,gBAAgB,CAAC,QAAkB,YAAoB;AACrD,oBAAQ,KAAK,kBAAkB,OAAO;AAAA,UACxC;AAAA,QAAA;AAAA,MACF;AAAA,IACF;AAAA,EAEJ;AAAA,EAEA,aAAa,QAAyB;AACpC,UAAM,IAAI,UAAU,KAAK,SAAS;AAClC,WAAO,WAAW,KAAK,eAAe,IAAI,KAAK,MAAM,oBAAoB,CAAC;AAAA,EAC5E;AAAA,EAEA,sBAAgC;AAC9B,WAAO,CAAC,KAAK,eAAe;AAAA,EAC9B;AAAA,EAEA,eAAe,UAA+C;AAC5D,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ,QAAO;AAEpB,UAAM,MAAO,OAAO,OAAO,OAAO;AAClC,WAAO,KAAK,iBAAiB;AAAA,EAC/B;AAAA,EAEA,mBAA6B;AAC3B,WAAO,CAAC,eAAe,QAAQ,eAAe,OAAO;AAAA,EACvD;AAAA,EAEA,eAAyB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,yBAAiC;AAC/B,WAAO,KAAK,aAAa,KAAK,SAAS,UAAU;AAAA,EACnD;AAAA;AAAA,EAGA,4BAAoC;AAClC,WAAO,KAAK,aAAa,KAAK,SAAS,aAAa;AAAA,EACtD;AAAA;AAAA,EAGA,cAAqC;AACnC,WAAO,KAAK;AAAA,EACd;AACF;AChFA,MAAM,gBAAgC;AAAA,EACpC,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,EACP,cAAc;AAAA,EACd,WAAW;AACb;AAEO,MAAM,UAAU;AAAA,EACZ,SAAS,IAAI,gBAAA;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,EAET,eAA+C;AAAA,EAC/C,SAAyB,EAAE,GAAG,cAAA;AAAA,EAEtC,YAAY,QAAyB;AACnC,SAAK,WAAW,OAAO;AACvB,SAAK,QAAQ,OAAO,SAAS;AAC7B,SAAK,eAAe,OAAO,QAAQ,YAAY,OAAO,KAAK,IAAI;AAAA,EACjE;AAAA,EAEA,IAAI,QAAkC;AACpC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAIA,MAAM,aAA2C;AAC/C,SAAK,IAAI,2BAA2B;AACpC,SAAK,YAAY,EAAE,WAAW,KAAA,CAAM;AAEpC,UAAM,aAAa,KAAK,SAAS,gBAAA;AACjC,SAAK,eAAe,IAAIC,YAAAA,wBAAwB,UAAU;AAE1D,UAAM,KAAK,aAAa,WAAA;AAExB,UAAM,SAAS,MAAM,KAAK,eAAA;AAE1B,SAAK,YAAY,EAAE,WAAW,OAAO,WAAW,MAAM;AACtD,WAAO;AAAA,EACT;AAAA,EAEA,UAAgB;AACd,SAAK,OAAO,mBAAA;AACZ,SAAK,eAAe;AACpB,SAAK,SAAS,EAAE,GAAG,cAAA;AAAA,EACrB;AAAA;AAAA,EAIA,MAAM,iBAA+C;AACnD,SAAK,kBAAA;AACL,SAAK,IAAI,8BAA8B;AAEvC,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,aAAc,sBAAA;AAE1C,UAAI,CAAC,UAAU;AAEb,aAAK,cAAc,IAAI;AACvB,eAAO;AAAA,MACT;AAEA,WAAK,IAAI,8BAA8B,QAAQ;AAG/C,YAAM,cAAc,mBAAmB,SAAS,KAAK;AAGrD,YAAM,SAAS,KAAK,SAAS,eAAe,QAAQ;AACpD,WAAK,YAAY,EAAE,cAAc,OAAA,CAAQ;AAGzC,UAAI,KAAK,uBAAuB,MAAM,GAAG;AACvC,aAAK,IAAI,gCAAgC;AACzC,aAAK,OAAO,KAAK,uBAAuB,MAAkB;AAC1D,eAAO,EAAE,GAAG,UAAU,sBAAsB,eAAe,OAAA;AAAA,MAC7D;AAGA,WAAK,YAAY,EAAE,WAAW,KAAA,CAAM;AACpC,WAAK,cAAc,MAAM;AACzB,YAAM,KAAK,0BAA0B,QAAQ;AAE7C,YAAM,eAA6B;AAAA,QACjC,GAAG;AAAA,QACH,sBAAsB,eAAe;AAAA,MAAA;AAGvC,WAAK,OAAO,KAAK,iBAAiB,YAAY;AAC9C,aAAO;AAAA,IACT,SAAS,OAAO;AACd,aAAO,KAAK,oBAAoB,KAAK;AAAA,IACvC;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,SAAwC;AACnD,SAAK,kBAAA;AACL,SAAK,IAAI,uBAAuB;AAChC,SAAK,YAAY,EAAE,WAAW,KAAA,CAAM;AACpC,SAAK,OAAO,KAAK,gBAAgB,IAAI;AAErC,UAAM,UAAU,KAAK,kBAAkB,KAAK,SAAS,aAAA,GAAgB,OAAO;AAC5E,UAAM,KAAK,aAAc,cAAc,OAAO;AAAA,EAChD;AAAA,EAEA,MAAM,mBAAmB,SAAwC;AAC/D,SAAK,kBAAA;AACL,SAAK,IAAI,qCAAqC;AAE9C,QAAI,EAAE,KAAK,oBAAoB,cAAc;AAE3C,aAAO,KAAK,OAAO,OAAO;AAAA,IAC5B;AAEA,UAAM,YAAY,KAAK,SAAS,uBAAA;AAChC,UAAM,UAAU,KAAK,kBAAkB,WAAW,OAAO;AACzD,UAAM,KAAK,aAAc,cAAc,OAAO;AAAA,EAChD;AAAA,EAEA,MAAM,QAAQ,SAAyC;AACrD,SAAK,kBAAA;AACL,SAAK,IAAI,wBAAwB;AAEjC,UAAM,gBAAgB;AAAA,MACpB,uBAAuB,SAAS;AAAA,MAChC,WAAW,KAAK,SAAS,aAAA;AAAA,IAAa;AAGxC,SAAK,YAAY,EAAE,WAAW,KAAA,CAAM;AACpC,SAAK,OAAO,KAAK,kBAAkB,MAAkB;AACrD,UAAM,KAAK,aAAc,eAAe,aAAa;AAAA,EACvD;AAAA,EAEA,MAAM,iBAAgC;AACpC,SAAK,kBAAA;AAEL,QAAI,EAAE,KAAK,oBAAoB,cAAc;AAC3C,WAAK,IAAI,qDAAqD;AAC9D;AAAA,IACF;AAEA,SAAK,IAAI,oCAAoC;AAC7C,UAAM,YAAY,KAAK,SAAS,0BAAA;AAChC,UAAM,KAAK,aAAc,cAAc,EAAE,QAAQ,CAAA,GAAI,WAAW;AAAA,EAClE;AAAA,EAEA,MAAM,aAAa,SAAgD;AACjE,SAAK,kBAAA;AACL,SAAK,IAAI,oBAAoB;AAE7B,UAAM,UAAU,KAAK,OAAO;AAC5B,QAAI,CAAC,SAAS;AACZ,WAAK,IAAI,wCAAwC;AACjD,aAAO;AAAA,IACT;AAEA,UAAM,SAAS,SAAS,UAAU,KAAK,SAAS,aAAA;AAChD,UAAM,eAAe;AAAA,MACnB;AAAA,MACA,cAAc,SAAS,gBAAgB;AAAA,MACvC;AAAA,MACA,WAAW,KAAK,OAAO,eACnB,KAAK,SAAS,aAAa,KAAK,OAAO,YAAY,IACnD,KAAK,SAAS,aAAA;AAAA,IAAa;AAGjC,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,aAAc,mBAAmB,YAAY;AAEzE,UAAI,CAAC,SAAS,aAAa;AACzB,cAAM,IAAIC,YAAAA,6BAA6B,aAAa;AAAA,MACtD;AAEA,WAAK,IAAI,yBAAyB;AAClC,WAAK,YAAY,EAAE,OAAO,SAAS,aAAa,OAAO,MAAM;AAC7D,WAAK,OAAO,KAAK,sBAAsB,SAAS,WAAW;AAC3D,aAAO,SAAS;AAAA,IAClB,SAAS,OAAO;AACd,UAAI,iBAAiBA,YAAAA,8BAA8B;AACjD,aAAK,IAAI,2DAA2D;AACpE,YAAI;AACF,gBAAM,KAAK,aAAc,qBAAqB,YAAY;AAC1D,iBAAO;AAAA,QACT,SAAS,eAAe;AACtB,eAAK,YAAY,aAAa;AAC9B,iBAAO;AAAA,QACT;AAAA,MACF;AACA,WAAK,YAAY,KAAK;AACtB,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAIQ,cAAc,QAA6B;AACjD,UAAM,WAAW,KAAK,aAAc,eAAA;AAEpC,QAAI,SAAS,WAAW,GAAG;AACzB,WAAK,YAAY,EAAE,iBAAiB,OAAO,MAAM,MAAM;AACvD;AAAA,IACF;AAEA,QAAI,WAA+B;AAEnC,QAAI,SAAS,WAAW,GAAG;AACzB,iBAAW,SAAS,CAAC;AAAA,IACvB,WAAW,QAAQ;AAEjB,YAAM,WAAW,SAAS,OAAO,CAAA,YAAW;AAC1C,cAAM,SAAS,QAAQ;AACvB,cAAM,MAAO,QAAQ,OAAkB;AACvC,cAAM,mBAAmB,KAAK,SAAS,oBAAA;AACvC,cAAM,mBAAmB,iBAAiB,KAAK,CAAA,SAAQ,IAAI,YAAA,EAAc,SAAS,KAAK,YAAA,CAAa,CAAC;AACrG,cAAM,gBAAgB,QAAQ,cAAc,YAAA,EAAc,SAAS,OAAO,aAAa;AACvF,eAAO,oBAAoB;AAAA,MAC7B,CAAC;AAED,UAAI,SAAS,UAAU,GAAG;AACxB,mBAAW,SAAS,CAAC;AAAA,MACvB;AAAA,IACF;AAEA,QAAI,CAAC,YAAY,SAAS,SAAS,GAAG;AACpC,iBAAW,SAAS,CAAC;AAAA,IACvB;AAEA,QAAI,UAAU;AACZ,WAAK,IAAI,oBAAoB,SAAS,QAAQ;AAC9C,WAAK,YAAY,EAAE,iBAAiB,MAAM,MAAM,UAAU;AAAA,IAC5D;AAAA,EACF;AAAA,EAEA,MAAc,0BAA0B,UAA+C;AAErF,UAAM,UACJ,KAAK,aAAc,mBAAmB,SAAS,SAAS,iBAAiB,EAAE,KAAK,SAAS,WAAW;AAEtG,QAAI,SAAS;AACX,WAAK,YAAY,EAAE,iBAAiB,MAAM,MAAM,SAAS;AAAA,IAC3D;AAEA,UAAM,KAAK,aAAA;AACX,SAAK,YAAY,EAAE,WAAW,MAAA,CAAO;AAAA,EACvC;AAAA,EAEQ,kBAAkB,WAAmB,SAAyB;AACpE,UAAM,SAAS,SAAS,UAAU,KAAK,SAAS,iBAAA;AAChD,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,OAAO,KAAK,eAAe,GAAG,KAAK,YAAY,KAAK;AAAA,MACpD,WAAW,SAAS;AAAA,MACpB,YAAY,SAAS;AAAA,IAAA;AAAA,EAEzB;AAAA,EAEQ,uBAAuB,QAAgC;AAC7D,QAAI,CAAC,OAAQ,QAAO;AACpB,QAAI,EAAE,KAAK,oBAAoB,aAAc,QAAO;AACpD,WAAO,OAAO,kBAAkB,KAAK,SAAS,YAAA,EAAc,cAAc,YAAA;AAAA,EAC5E;AAAA,EAEQ,oBAAoB,OAAsB;AAChD,UAAM,MAAM;AAGZ,QAAI,IAAI,cAAc,SAAS,iBAAiB,eAAe,GAAG;AAChE,WAAK,IAAI,gDAAgD;AACzD,WAAK,eAAA;AACL,aAAO;AAAA,IACT;AAEA,SAAK,YAAY,KAAK;AACtB,SAAK,YAAY,EAAE,WAAW,MAAA,CAAO;AACrC,WAAO;AAAA,EACT;AAAA,EAEQ,YAAY,OAAsB;AACxC,UAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,SAAK,IAAI,UAAU,IAAI,OAAO;AAC9B,SAAK,YAAY,EAAE,OAAO,IAAA,CAAK;AAC/B,SAAK,OAAO,KAAK,cAAc,GAAG;AAAA,EACpC;AAAA,EAEQ,YAAY,SAAwC;AAC1D,SAAK,SAAS,EAAE,GAAG,KAAK,QAAQ,GAAG,QAAA;AACnC,SAAK,OAAO,KAAK,qBAAqB,KAAK,MAAM;AAAA,EACnD;AAAA,EAEQ,oBAA0B;AAChC,QAAI,CAAC,KAAK,cAAc;AACtB,YAAM,IAAI,MAAM,qDAAqD;AAAA,IACvE;AAAA,EACF;AAAA,EAEQ,OAAO,MAAuB;AACpC,QAAI,KAAK,OAAO;AACd,cAAQ,IAAI,cAAc,GAAG,IAAI;AAAA,IACnC;AAAA,EACF;AACF;AC3TO,MAAM,aAAsC;AAAA,EACxC,OAAO;AAAA,EAEC;AAAA,EAEjB,YAAY,QAA4B;AACtC,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,kBAAiC;AAC/B,WAAO;AAAA,MACL,MAAM;AAAA,QACJ,UAAU,KAAK,OAAO;AAAA,QACtB,WAAW,KAAK,aAAA;AAAA,QAChB,kBAAkB,KAAK,oBAAA;AAAA,QACvB,aAAa,KAAK,OAAO;AAAA,QACzB,uBAAuB,KAAK,OAAO,yBAAyB,KAAK,OAAO;AAAA,QACxE,2BAA2B;AAAA,MAAA;AAAA,MAE7B,OAAO;AAAA,QACL,eAAe,KAAK,OAAO,iBAAiB;AAAA,MAAA;AAAA,IAC9C;AAAA,EAEJ;AAAA,EAEA,eAAuB;AACrB,WAAO,WAAW,KAAK,OAAO,eAAe;AAAA,EAC/C;AAAA,EAEA,sBAAgC;AAC9B,WAAO,CAAC,GAAG,KAAK,OAAO,eAAe,gBAAgB;AAAA,EACxD;AAAA,EAEA,eAAe,WAAgD;AAE7D,WAAO;AAAA,EACT;AAAA,EAEA,mBAA6B;AAC3B,WAAO,CAAC,UAAU,SAAS;AAAA,EAC7B;AAAA,EAEA,eAAyB;AACvB,WAAO,KAAK,OAAO,UAAU,CAAA;AAAA,EAC/B;AACF;AC7CO,MAAM,cAAuC;AAAA,EACzC,OAAO;AAAA,EAEC;AAAA,EAEjB,YAAY,QAA6B;AACvC,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,kBAAiC;AAC/B,WAAO;AAAA,MACL,MAAM;AAAA,QACJ,UAAU,KAAK,OAAO;AAAA,QACtB,WAAW,KAAK,aAAA;AAAA,QAChB,kBAAkB,KAAK,oBAAA;AAAA,QACvB,aAAa,KAAK,OAAO;AAAA,QACzB,uBAAuB,KAAK,OAAO,yBAAyB,KAAK,OAAO;AAAA,QACxE,2BAA2B;AAAA,MAAA;AAAA,MAE7B,OAAO;AAAA,QACL,eAAe,KAAK,OAAO,iBAAiB;AAAA,MAAA;AAAA,IAC9C;AAAA,EAEJ;AAAA,EAEA,eAAuB;AACrB,WAAO,qCAAqC,KAAK,OAAO,QAAQ;AAAA,EAClE;AAAA,EAEA,sBAAgC;AAC9B,WAAO,CAAC,2BAA2B;AAAA,EACrC;AAAA,EAEA,eAAe,WAAgD;AAE7D,WAAO;AAAA,EACT;AAAA,EAEA,mBAA6B;AAC3B,WAAO,CAAC,UAAU,SAAS;AAAA,EAC7B;AAAA,EAEA,eAAyB;AACvB,WAAO,KAAK,OAAO,UAAU,CAAA;AAAA,EAC/B;AACF;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../src/events.ts","../src/constants.ts","../src/state-encoding.ts","../src/providers/b2c.ts","../src/client.ts","../src/providers/ciam.ts","../src/providers/entra.ts"],"sourcesContent":["import type { SSOEventMap, SSOEventName } from \"./types\";\n\ntype Listener<T> = (data: T) => void;\n\nexport class SSOEventEmitter {\n private listeners = new Map<SSOEventName, Set<Listener<unknown>>>();\n\n on<K extends SSOEventName>(event: K, listener: Listener<SSOEventMap[K]>): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n const set = this.listeners.get(event)!;\n set.add(listener as Listener<unknown>);\n\n // Return unsubscribe function\n return () => {\n set.delete(listener as Listener<unknown>);\n };\n }\n\n emit<K extends SSOEventName>(event: K, data: SSOEventMap[K]): void {\n const set = this.listeners.get(event);\n if (set) {\n for (const listener of set) {\n listener(data);\n }\n }\n }\n\n removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","export const DEFAULT_POLICIES = {\n SIGN_UP_SIGN_IN: \"B2C_1A_SIGNUP_SIGNIN\",\n SIGN_IN_ONLY: \"B2C_1A_AD_SIGNIN_ONLY\",\n RESET_PASSWORD: \"B2C_1A_PASSWORDRESET\",\n} as const;\n\nexport const DEFAULT_SCOPES = {\n OPENID: \"openid\",\n PROFILE: \"profile\",\n OFFLINE_ACCESS: \"offline_access\",\n} as const;\n\nexport const CACHE_CONFIG = {\n LOCATION: \"sessionStorage\" as const,\n STORE_AUTH_STATE_IN_COOKIE: false,\n} as const;\n\nexport const MSAL_ERROR_CODES = {\n USER_CANCELLED: \"user_cancelled\",\n NO_CACHED_AUTHORITY: \"no_cached_authority_error\",\n INTERACTION_REQUIRED: \"interaction_required\",\n FORGOT_PASSWORD: \"AADB2C90118\",\n} as const;\n\nexport const STATE_SEPARATOR = \"|\";\n","import { STATE_SEPARATOR } from \"./constants\";\n\n/**\n * Encode a custom state object into a base64 string that can be\n * appended to the MSAL state parameter using a pipe separator.\n */\nexport function encodeState(stateObj: Record<string, unknown>): string {\n return btoa(JSON.stringify(stateObj));\n}\n\n/**\n * Decode a base64-encoded state string back into an object.\n */\nexport function decodeState(encoded: string): Record<string, unknown> | null {\n try {\n return JSON.parse(atob(encoded));\n } catch {\n return null;\n }\n}\n\n/**\n * Extract custom postback data from the MSAL state string.\n * MSAL prepends its own GUID before the pipe separator.\n */\nexport function extractCustomState(msalState: string | undefined): Record<string, unknown> | null {\n if (!msalState) return null;\n\n const parts = msalState.split(STATE_SEPARATOR);\n if (parts.length < 2) return null;\n\n // Everything after the first pipe is our encoded state\n const customPart = parts.slice(1).join(STATE_SEPARATOR);\n return decodeState(customPart);\n}\n","import { LogLevel } from \"@azure/msal-browser\";\nimport type { Configuration, AuthenticationResult } from \"@azure/msal-browser\";\nimport type { IAuthProvider, B2CProviderConfig, B2CPolicies } from \"../types\";\nimport { DEFAULT_POLICIES, DEFAULT_SCOPES, CACHE_CONFIG } from \"../constants\";\n\nexport class B2CProvider implements IAuthProvider {\n readonly type = \"b2c\" as const;\n\n private readonly clientId: string;\n private readonly b2cEnv: string;\n private readonly authorityDomain: string;\n private readonly redirectUri: string;\n private readonly postLogoutRedirectUri: string;\n private readonly policies: B2CPolicies;\n private readonly apiScopes: string[];\n private readonly cacheLocation: \"sessionStorage\" | \"localStorage\";\n\n constructor(config: B2CProviderConfig) {\n this.clientId = config.clientId;\n this.b2cEnv = config.b2cEnvironment;\n this.authorityDomain = config.authorityDomain;\n this.redirectUri = config.redirectUri;\n this.postLogoutRedirectUri = config.postLogoutRedirectUri ?? config.redirectUri;\n this.policies = {\n signUpSignIn: config.policies?.signUpSignIn ?? DEFAULT_POLICIES.SIGN_UP_SIGN_IN,\n signInOnly: config.policies?.signInOnly ?? DEFAULT_POLICIES.SIGN_IN_ONLY,\n resetPassword: config.policies?.resetPassword ?? DEFAULT_POLICIES.RESET_PASSWORD,\n };\n this.apiScopes = config.apiScopes ?? [];\n this.cacheLocation = config.cacheLocation ?? CACHE_CONFIG.LOCATION;\n }\n\n buildMsalConfig(): Configuration {\n return {\n auth: {\n clientId: this.clientId,\n authority: this.getAuthority(this.policies.signUpSignIn),\n knownAuthorities: this.getKnownAuthorities(),\n redirectUri: this.redirectUri,\n postLogoutRedirectUri: this.postLogoutRedirectUri,\n navigateToLoginRequestUrl: false,\n },\n cache: {\n cacheLocation: this.cacheLocation,\n storeAuthStateInCookie: CACHE_CONFIG.STORE_AUTH_STATE_IN_COOKIE,\n },\n system: {\n loggerOptions: {\n logLevel: LogLevel.Warning,\n loggerCallback: (_level: LogLevel, message: string) => {\n console.warn(\"[sso-core/b2c]\", message);\n },\n },\n },\n };\n }\n\n getAuthority(policy?: string): string {\n const p = policy ?? this.policies.signUpSignIn;\n return `https://${this.authorityDomain}/${this.b2cEnv}.onmicrosoft.com/${p}`;\n }\n\n getKnownAuthorities(): string[] {\n return [this.authorityDomain];\n }\n\n identifyPolicy(response: AuthenticationResult): string | null {\n const claims = response.idTokenClaims as Record<string, unknown> | undefined;\n if (!claims) return null;\n\n const acr = (claims.acr ?? claims.tfp) as string | undefined;\n return acr?.toUpperCase() ?? null;\n }\n\n getDefaultScopes(): string[] {\n return [DEFAULT_SCOPES.OPENID, DEFAULT_SCOPES.PROFILE];\n }\n\n getApiScopes(): string[] {\n return this.apiScopes;\n }\n\n /** Get the authority URL for the sign-in-only (city employee) policy */\n getSignInOnlyAuthority(): string {\n return this.getAuthority(this.policies.signInOnly);\n }\n\n /** Get the authority URL for the password reset policy */\n getResetPasswordAuthority(): string {\n return this.getAuthority(this.policies.resetPassword);\n }\n\n /** Get all configured policy names */\n getPolicies(): Readonly<B2CPolicies> {\n return this.policies;\n }\n}\n","import { PublicClientApplication, InteractionRequiredAuthError } from \"@azure/msal-browser\";\nimport type { AccountInfo, AuthenticationResult } from \"@azure/msal-browser\";\nimport { SSOEventEmitter } from \"./events\";\nimport { encodeState, extractCustomState } from \"./state-encoding\";\nimport { MSAL_ERROR_CODES } from \"./constants\";\nimport { B2CProvider } from \"./providers/b2c\";\nimport type {\n IAuthProvider,\n SSOClientConfig,\n SSOClientState,\n SignInOptions,\n SignOutOptions,\n TokenOptions,\n AuthResponse,\n} from \"./types\";\n\nconst INITIAL_STATE: SSOClientState = {\n isAuthenticated: false,\n isLoading: false,\n user: null,\n token: null,\n error: null,\n activePolicy: null,\n authReady: false,\n};\n\nexport class SSOClient {\n readonly events = new SSOEventEmitter();\n private readonly provider: IAuthProvider;\n private readonly debug: boolean;\n private readonly encodedState: string | null;\n\n private msalInstance: PublicClientApplication | null = null;\n private _state: SSOClientState = { ...INITIAL_STATE };\n\n constructor(config: SSOClientConfig) {\n this.provider = config.provider;\n this.debug = config.debug ?? false;\n this.encodedState = config.state ? encodeState(config.state) : null;\n }\n\n get state(): Readonly<SSOClientState> {\n return this._state;\n }\n\n // ── Lifecycle ──\n\n async initialize(): Promise<AuthResponse | null> {\n this.log(\"Initializing SSOClient...\");\n this.updateState({ isLoading: true });\n\n const msalConfig = this.provider.buildMsalConfig();\n this.msalInstance = new PublicClientApplication(msalConfig);\n\n await this.msalInstance.initialize();\n\n const result = await this.handleRedirect();\n\n this.updateState({ isLoading: false, authReady: true });\n return result;\n }\n\n destroy(): void {\n this.events.removeAllListeners();\n this.msalInstance = null;\n this._state = { ...INITIAL_STATE };\n }\n\n // ── Auth Actions ──\n\n async handleRedirect(): Promise<AuthResponse | null> {\n this.assertInitialized();\n this.log(\"Handling redirect promise...\");\n\n try {\n const response = await this.msalInstance!.handleRedirectPromise();\n\n if (!response) {\n // No redirect response — check for existing sessions\n this.selectAccount(null);\n return null;\n }\n\n this.log(\"Redirect response received\", response);\n\n // Extract custom postback state\n const customState = extractCustomState(response.state);\n\n // Identify which policy was used via the acr/tfp claim\n const policy = this.provider.identifyPolicy(response);\n this.updateState({ activePolicy: policy });\n\n // Check if this was a forgot-password completion\n if (this.isForgotPasswordPolicy(policy)) {\n this.log(\"Forgot password flow completed\");\n this.events.emit(\"auth:forgotPassword\", undefined as never);\n return { ...response, customPostbackObject: customState ?? undefined } as AuthResponse;\n }\n\n // Normal sign-in flow\n this.updateState({ isLoading: true });\n this.selectAccount(policy);\n await this.acquireTokenAfterRedirect(response);\n\n const authResponse: AuthResponse = {\n ...response,\n customPostbackObject: customState ?? undefined,\n };\n\n this.events.emit(\"auth:signedIn\", authResponse);\n return authResponse;\n } catch (error) {\n return this.handleRedirectError(error);\n }\n }\n\n async signIn(options?: SignInOptions): Promise<void> {\n this.assertInitialized();\n this.log(\"Initiating sign-in...\");\n this.updateState({ isLoading: true });\n this.events.emit(\"auth:loading\", true);\n\n const request = this.buildLoginRequest(this.provider.getAuthority(), options);\n await this.msalInstance!.loginRedirect(request);\n }\n\n async signInCityEmployee(options?: SignInOptions): Promise<void> {\n this.assertInitialized();\n this.log(\"Initiating city employee sign-in...\");\n\n if (!(this.provider instanceof B2CProvider)) {\n // For non-B2C providers, fall back to regular sign-in\n return this.signIn(options);\n }\n\n const authority = this.provider.getSignInOnlyAuthority();\n const request = this.buildLoginRequest(authority, options);\n await this.msalInstance!.loginRedirect(request);\n }\n\n async signOut(options?: SignOutOptions): Promise<void> {\n this.assertInitialized();\n this.log(\"Initiating sign-out...\");\n\n const logoutRequest = {\n account: this._state.user ?? undefined,\n postLogoutRedirectUri: options?.postLogoutRedirectUri,\n authority: this.provider.getAuthority(),\n };\n\n this.updateState({ isLoading: true });\n this.events.emit(\"auth:signedOut\", undefined as never);\n await this.msalInstance!.logoutRedirect(logoutRequest);\n }\n\n async forgotPassword(): Promise<void> {\n this.assertInitialized();\n\n if (!(this.provider instanceof B2CProvider)) {\n this.log(\"Forgot password is only supported for B2C providers\");\n return;\n }\n\n this.log(\"Initiating forgot password flow...\");\n const authority = this.provider.getResetPasswordAuthority();\n await this.msalInstance!.loginRedirect({ scopes: [], authority });\n }\n\n async acquireToken(options?: TokenOptions): Promise<string | null> {\n this.assertInitialized();\n this.log(\"Acquiring token...\");\n\n const account = this._state.user;\n if (!account) {\n this.log(\"No account found, cannot acquire token\");\n return null;\n }\n\n const scopes = options?.scopes ?? this.provider.getApiScopes();\n const tokenRequest = {\n scopes,\n forceRefresh: options?.forceRefresh ?? false,\n account,\n authority: this._state.activePolicy\n ? this.provider.getAuthority(this._state.activePolicy)\n : this.provider.getAuthority(),\n };\n\n try {\n const response = await this.msalInstance!.acquireTokenSilent(tokenRequest);\n\n if (!response.accessToken) {\n throw new InteractionRequiredAuthError(\"empty_token\");\n }\n\n this.log(\"Token acquired silently\");\n this.updateState({ token: response.accessToken, error: null });\n this.events.emit(\"auth:tokenAcquired\", response.accessToken);\n return response.accessToken;\n } catch (error) {\n if (error instanceof InteractionRequiredAuthError) {\n this.log(\"Silent token acquisition failed, falling back to redirect\");\n try {\n await this.msalInstance!.acquireTokenRedirect(tokenRequest);\n return null; // Page will redirect\n } catch (redirectError) {\n this.handleError(redirectError);\n return null;\n }\n }\n this.handleError(error);\n return null;\n }\n }\n\n // ── Internal Helpers ──\n\n private selectAccount(policy: string | null): void {\n const accounts = this.msalInstance!.getAllAccounts();\n\n if (accounts.length === 0) {\n this.updateState({ isAuthenticated: false, user: null });\n return;\n }\n\n let selected: AccountInfo | null = null;\n\n if (accounts.length === 1) {\n selected = accounts[0];\n } else if (policy) {\n // Filter accounts by policy and authority domain\n const filtered = accounts.filter(account => {\n const claims = account.idTokenClaims as Record<string, unknown> | undefined;\n const iss = (claims?.iss as string) ?? \"\";\n const knownAuthorities = this.provider.getKnownAuthorities();\n const matchesAuthority = knownAuthorities.some(auth => iss.toUpperCase().includes(auth.toUpperCase()));\n const matchesPolicy = account.homeAccountId.toUpperCase().includes(policy.toUpperCase());\n return matchesAuthority && matchesPolicy;\n });\n\n if (filtered.length >= 1) {\n selected = filtered[0];\n }\n }\n\n if (!selected && accounts.length > 0) {\n selected = accounts[0];\n }\n\n if (selected) {\n this.log(\"Account selected\", selected.username);\n this.updateState({ isAuthenticated: true, user: selected });\n }\n }\n\n private async acquireTokenAfterRedirect(response: AuthenticationResult): Promise<void> {\n // Set the account first so acquireToken can use it\n const account =\n this.msalInstance!.getAccountByHomeId(response.account?.homeAccountId ?? \"\") ?? response.account ?? null;\n\n if (account) {\n this.updateState({ isAuthenticated: true, user: account });\n }\n\n await this.acquireToken();\n this.updateState({ isLoading: false });\n }\n\n private buildLoginRequest(authority: string, options?: SignInOptions) {\n const scopes = options?.scopes ?? this.provider.getDefaultScopes();\n return {\n scopes,\n authority,\n state: this.encodedState ? `${this.encodedState}` : undefined,\n loginHint: options?.loginHint,\n domainHint: options?.domainHint,\n };\n }\n\n private isForgotPasswordPolicy(policy: string | null): boolean {\n if (!policy) return false;\n if (!(this.provider instanceof B2CProvider)) return false;\n return policy.toUpperCase() === this.provider.getPolicies().resetPassword.toUpperCase();\n }\n\n private handleRedirectError(error: unknown): null {\n const err = error as { errorMessage?: string; errorCode?: string };\n\n // Detect B2C forgot-password error code\n if (err.errorMessage?.includes(MSAL_ERROR_CODES.FORGOT_PASSWORD)) {\n this.log(\"Forgot password error detected, redirecting...\");\n this.forgotPassword();\n return null;\n }\n\n this.handleError(error);\n this.updateState({ isLoading: false });\n return null;\n }\n\n private handleError(error: unknown): void {\n const err = error instanceof Error ? error : new Error(String(error));\n this.log(\"Error:\", err.message);\n this.updateState({ error: err });\n this.events.emit(\"auth:error\", err);\n }\n\n private updateState(partial: Partial<SSOClientState>): void {\n this._state = { ...this._state, ...partial };\n this.events.emit(\"auth:stateChanged\", this._state);\n }\n\n private assertInitialized(): void {\n if (!this.msalInstance) {\n throw new Error(\"SSOClient not initialized. Call initialize() first.\");\n }\n }\n\n private log(...args: unknown[]): void {\n if (this.debug) {\n console.log(\"[sso-core]\", ...args);\n }\n }\n}\n","import type { Configuration, AuthenticationResult } from \"@azure/msal-browser\";\nimport type { IAuthProvider, CIAMProviderConfig } from \"../types\";\n\n/**\n * Entra External ID (CIAM) provider — future implementation.\n * Placeholder scaffolding to validate the provider interface.\n */\nexport class CIAMProvider implements IAuthProvider {\n readonly type = \"ciam\" as const;\n\n private readonly config: CIAMProviderConfig;\n\n constructor(config: CIAMProviderConfig) {\n this.config = config;\n }\n\n buildMsalConfig(): Configuration {\n return {\n auth: {\n clientId: this.config.clientId,\n authority: this.getAuthority(),\n knownAuthorities: this.getKnownAuthorities(),\n redirectUri: this.config.redirectUri,\n postLogoutRedirectUri: this.config.postLogoutRedirectUri ?? this.config.redirectUri,\n navigateToLoginRequestUrl: false,\n },\n cache: {\n cacheLocation: this.config.cacheLocation ?? \"sessionStorage\",\n },\n };\n }\n\n getAuthority(): string {\n return `https://${this.config.tenantSubdomain}.ciamlogin.com/`;\n }\n\n getKnownAuthorities(): string[] {\n return [`${this.config.tenantSubdomain}.ciamlogin.com`];\n }\n\n identifyPolicy(_response: AuthenticationResult): string | null {\n // CIAM doesn't use client-side policy selection\n return null;\n }\n\n getDefaultScopes(): string[] {\n return [\"openid\", \"profile\"];\n }\n\n getApiScopes(): string[] {\n return this.config.scopes ?? [];\n }\n}\n","import type { Configuration, AuthenticationResult } from \"@azure/msal-browser\";\nimport type { IAuthProvider, EntraProviderConfig } from \"../types\";\n\n/**\n * Entra workforce (city employees) provider — future implementation.\n * Placeholder scaffolding to validate the provider interface.\n */\nexport class EntraProvider implements IAuthProvider {\n readonly type = \"entra\" as const;\n\n private readonly config: EntraProviderConfig;\n\n constructor(config: EntraProviderConfig) {\n this.config = config;\n }\n\n buildMsalConfig(): Configuration {\n return {\n auth: {\n clientId: this.config.clientId,\n authority: this.getAuthority(),\n knownAuthorities: this.getKnownAuthorities(),\n redirectUri: this.config.redirectUri,\n postLogoutRedirectUri: this.config.postLogoutRedirectUri ?? this.config.redirectUri,\n navigateToLoginRequestUrl: false,\n },\n cache: {\n cacheLocation: this.config.cacheLocation ?? \"sessionStorage\",\n },\n };\n }\n\n getAuthority(): string {\n return `https://login.microsoftonline.com/${this.config.tenantId}`;\n }\n\n getKnownAuthorities(): string[] {\n return [\"login.microsoftonline.com\"];\n }\n\n identifyPolicy(_response: AuthenticationResult): string | null {\n // Entra workforce doesn't use B2C policies\n return null;\n }\n\n getDefaultScopes(): string[] {\n return [\"openid\", \"profile\"];\n }\n\n getApiScopes(): string[] {\n return this.config.scopes ?? [];\n }\n}\n"],"names":["LogLevel","PublicClientApplication","InteractionRequiredAuthError"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -235,6 +235,7 @@ class SSOClient {
235
235
  this.assertInitialized();
236
236
  this.log("Initiating sign-out...");
237
237
  const logoutRequest = {
238
+ account: this._state.user ?? void 0,
238
239
  postLogoutRedirectUri: options?.postLogoutRedirectUri,
239
240
  authority: this.provider.getAuthority()
240
241
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/events.ts","../src/constants.ts","../src/state-encoding.ts","../src/providers/b2c.ts","../src/client.ts","../src/providers/ciam.ts","../src/providers/entra.ts"],"sourcesContent":["import type { SSOEventMap, SSOEventName } from \"./types\";\n\ntype Listener<T> = (data: T) => void;\n\nexport class SSOEventEmitter {\n private listeners = new Map<SSOEventName, Set<Listener<unknown>>>();\n\n on<K extends SSOEventName>(event: K, listener: Listener<SSOEventMap[K]>): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n const set = this.listeners.get(event)!;\n set.add(listener as Listener<unknown>);\n\n // Return unsubscribe function\n return () => {\n set.delete(listener as Listener<unknown>);\n };\n }\n\n emit<K extends SSOEventName>(event: K, data: SSOEventMap[K]): void {\n const set = this.listeners.get(event);\n if (set) {\n for (const listener of set) {\n listener(data);\n }\n }\n }\n\n removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","export const DEFAULT_POLICIES = {\n SIGN_UP_SIGN_IN: \"B2C_1A_SIGNUP_SIGNIN\",\n SIGN_IN_ONLY: \"B2C_1A_AD_SIGNIN_ONLY\",\n RESET_PASSWORD: \"B2C_1A_PASSWORDRESET\",\n} as const;\n\nexport const DEFAULT_SCOPES = {\n OPENID: \"openid\",\n PROFILE: \"profile\",\n OFFLINE_ACCESS: \"offline_access\",\n} as const;\n\nexport const CACHE_CONFIG = {\n LOCATION: \"sessionStorage\" as const,\n STORE_AUTH_STATE_IN_COOKIE: false,\n} as const;\n\nexport const MSAL_ERROR_CODES = {\n USER_CANCELLED: \"user_cancelled\",\n NO_CACHED_AUTHORITY: \"no_cached_authority_error\",\n INTERACTION_REQUIRED: \"interaction_required\",\n FORGOT_PASSWORD: \"AADB2C90118\",\n} as const;\n\nexport const STATE_SEPARATOR = \"|\";\n","import { STATE_SEPARATOR } from \"./constants\";\n\n/**\n * Encode a custom state object into a base64 string that can be\n * appended to the MSAL state parameter using a pipe separator.\n */\nexport function encodeState(stateObj: Record<string, unknown>): string {\n return btoa(JSON.stringify(stateObj));\n}\n\n/**\n * Decode a base64-encoded state string back into an object.\n */\nexport function decodeState(encoded: string): Record<string, unknown> | null {\n try {\n return JSON.parse(atob(encoded));\n } catch {\n return null;\n }\n}\n\n/**\n * Extract custom postback data from the MSAL state string.\n * MSAL prepends its own GUID before the pipe separator.\n */\nexport function extractCustomState(msalState: string | undefined): Record<string, unknown> | null {\n if (!msalState) return null;\n\n const parts = msalState.split(STATE_SEPARATOR);\n if (parts.length < 2) return null;\n\n // Everything after the first pipe is our encoded state\n const customPart = parts.slice(1).join(STATE_SEPARATOR);\n return decodeState(customPart);\n}\n","import { LogLevel } from \"@azure/msal-browser\";\nimport type { Configuration, AuthenticationResult } from \"@azure/msal-browser\";\nimport type { IAuthProvider, B2CProviderConfig, B2CPolicies } from \"../types\";\nimport { DEFAULT_POLICIES, DEFAULT_SCOPES, CACHE_CONFIG } from \"../constants\";\n\nexport class B2CProvider implements IAuthProvider {\n readonly type = \"b2c\" as const;\n\n private readonly clientId: string;\n private readonly b2cEnv: string;\n private readonly authorityDomain: string;\n private readonly redirectUri: string;\n private readonly postLogoutRedirectUri: string;\n private readonly policies: B2CPolicies;\n private readonly apiScopes: string[];\n private readonly cacheLocation: \"sessionStorage\" | \"localStorage\";\n\n constructor(config: B2CProviderConfig) {\n this.clientId = config.clientId;\n this.b2cEnv = config.b2cEnvironment;\n this.authorityDomain = config.authorityDomain;\n this.redirectUri = config.redirectUri;\n this.postLogoutRedirectUri = config.postLogoutRedirectUri ?? config.redirectUri;\n this.policies = {\n signUpSignIn: config.policies?.signUpSignIn ?? DEFAULT_POLICIES.SIGN_UP_SIGN_IN,\n signInOnly: config.policies?.signInOnly ?? DEFAULT_POLICIES.SIGN_IN_ONLY,\n resetPassword: config.policies?.resetPassword ?? DEFAULT_POLICIES.RESET_PASSWORD,\n };\n this.apiScopes = config.apiScopes ?? [];\n this.cacheLocation = config.cacheLocation ?? CACHE_CONFIG.LOCATION;\n }\n\n buildMsalConfig(): Configuration {\n return {\n auth: {\n clientId: this.clientId,\n authority: this.getAuthority(this.policies.signUpSignIn),\n knownAuthorities: this.getKnownAuthorities(),\n redirectUri: this.redirectUri,\n postLogoutRedirectUri: this.postLogoutRedirectUri,\n navigateToLoginRequestUrl: false,\n },\n cache: {\n cacheLocation: this.cacheLocation,\n storeAuthStateInCookie: CACHE_CONFIG.STORE_AUTH_STATE_IN_COOKIE,\n },\n system: {\n loggerOptions: {\n logLevel: LogLevel.Warning,\n loggerCallback: (_level: LogLevel, message: string) => {\n console.warn(\"[sso-core/b2c]\", message);\n },\n },\n },\n };\n }\n\n getAuthority(policy?: string): string {\n const p = policy ?? this.policies.signUpSignIn;\n return `https://${this.authorityDomain}/${this.b2cEnv}.onmicrosoft.com/${p}`;\n }\n\n getKnownAuthorities(): string[] {\n return [this.authorityDomain];\n }\n\n identifyPolicy(response: AuthenticationResult): string | null {\n const claims = response.idTokenClaims as Record<string, unknown> | undefined;\n if (!claims) return null;\n\n const acr = (claims.acr ?? claims.tfp) as string | undefined;\n return acr?.toUpperCase() ?? null;\n }\n\n getDefaultScopes(): string[] {\n return [DEFAULT_SCOPES.OPENID, DEFAULT_SCOPES.PROFILE];\n }\n\n getApiScopes(): string[] {\n return this.apiScopes;\n }\n\n /** Get the authority URL for the sign-in-only (city employee) policy */\n getSignInOnlyAuthority(): string {\n return this.getAuthority(this.policies.signInOnly);\n }\n\n /** Get the authority URL for the password reset policy */\n getResetPasswordAuthority(): string {\n return this.getAuthority(this.policies.resetPassword);\n }\n\n /** Get all configured policy names */\n getPolicies(): Readonly<B2CPolicies> {\n return this.policies;\n }\n}\n","import { PublicClientApplication, InteractionRequiredAuthError } from \"@azure/msal-browser\";\nimport type { AccountInfo, AuthenticationResult } from \"@azure/msal-browser\";\nimport { SSOEventEmitter } from \"./events\";\nimport { encodeState, extractCustomState } from \"./state-encoding\";\nimport { MSAL_ERROR_CODES } from \"./constants\";\nimport { B2CProvider } from \"./providers/b2c\";\nimport type {\n IAuthProvider,\n SSOClientConfig,\n SSOClientState,\n SignInOptions,\n SignOutOptions,\n TokenOptions,\n AuthResponse,\n} from \"./types\";\n\nconst INITIAL_STATE: SSOClientState = {\n isAuthenticated: false,\n isLoading: false,\n user: null,\n token: null,\n error: null,\n activePolicy: null,\n authReady: false,\n};\n\nexport class SSOClient {\n readonly events = new SSOEventEmitter();\n private readonly provider: IAuthProvider;\n private readonly debug: boolean;\n private readonly encodedState: string | null;\n\n private msalInstance: PublicClientApplication | null = null;\n private _state: SSOClientState = { ...INITIAL_STATE };\n\n constructor(config: SSOClientConfig) {\n this.provider = config.provider;\n this.debug = config.debug ?? false;\n this.encodedState = config.state ? encodeState(config.state) : null;\n }\n\n get state(): Readonly<SSOClientState> {\n return this._state;\n }\n\n // ── Lifecycle ──\n\n async initialize(): Promise<AuthResponse | null> {\n this.log(\"Initializing SSOClient...\");\n this.updateState({ isLoading: true });\n\n const msalConfig = this.provider.buildMsalConfig();\n this.msalInstance = new PublicClientApplication(msalConfig);\n\n await this.msalInstance.initialize();\n\n const result = await this.handleRedirect();\n\n this.updateState({ isLoading: false, authReady: true });\n return result;\n }\n\n destroy(): void {\n this.events.removeAllListeners();\n this.msalInstance = null;\n this._state = { ...INITIAL_STATE };\n }\n\n // ── Auth Actions ──\n\n async handleRedirect(): Promise<AuthResponse | null> {\n this.assertInitialized();\n this.log(\"Handling redirect promise...\");\n\n try {\n const response = await this.msalInstance!.handleRedirectPromise();\n\n if (!response) {\n // No redirect response — check for existing sessions\n this.selectAccount(null);\n return null;\n }\n\n this.log(\"Redirect response received\", response);\n\n // Extract custom postback state\n const customState = extractCustomState(response.state);\n\n // Identify which policy was used via the acr/tfp claim\n const policy = this.provider.identifyPolicy(response);\n this.updateState({ activePolicy: policy });\n\n // Check if this was a forgot-password completion\n if (this.isForgotPasswordPolicy(policy)) {\n this.log(\"Forgot password flow completed\");\n this.events.emit(\"auth:forgotPassword\", undefined as never);\n return { ...response, customPostbackObject: customState ?? undefined } as AuthResponse;\n }\n\n // Normal sign-in flow\n this.updateState({ isLoading: true });\n this.selectAccount(policy);\n await this.acquireTokenAfterRedirect(response);\n\n const authResponse: AuthResponse = {\n ...response,\n customPostbackObject: customState ?? undefined,\n };\n\n this.events.emit(\"auth:signedIn\", authResponse);\n return authResponse;\n } catch (error) {\n return this.handleRedirectError(error);\n }\n }\n\n async signIn(options?: SignInOptions): Promise<void> {\n this.assertInitialized();\n this.log(\"Initiating sign-in...\");\n this.updateState({ isLoading: true });\n this.events.emit(\"auth:loading\", true);\n\n const request = this.buildLoginRequest(this.provider.getAuthority(), options);\n await this.msalInstance!.loginRedirect(request);\n }\n\n async signInCityEmployee(options?: SignInOptions): Promise<void> {\n this.assertInitialized();\n this.log(\"Initiating city employee sign-in...\");\n\n if (!(this.provider instanceof B2CProvider)) {\n // For non-B2C providers, fall back to regular sign-in\n return this.signIn(options);\n }\n\n const authority = this.provider.getSignInOnlyAuthority();\n const request = this.buildLoginRequest(authority, options);\n await this.msalInstance!.loginRedirect(request);\n }\n\n async signOut(options?: SignOutOptions): Promise<void> {\n this.assertInitialized();\n this.log(\"Initiating sign-out...\");\n\n const logoutRequest = {\n postLogoutRedirectUri: options?.postLogoutRedirectUri,\n authority: this.provider.getAuthority(),\n };\n\n this.updateState({ isLoading: true });\n this.events.emit(\"auth:signedOut\", undefined as never);\n await this.msalInstance!.logoutRedirect(logoutRequest);\n }\n\n async forgotPassword(): Promise<void> {\n this.assertInitialized();\n\n if (!(this.provider instanceof B2CProvider)) {\n this.log(\"Forgot password is only supported for B2C providers\");\n return;\n }\n\n this.log(\"Initiating forgot password flow...\");\n const authority = this.provider.getResetPasswordAuthority();\n await this.msalInstance!.loginRedirect({ scopes: [], authority });\n }\n\n async acquireToken(options?: TokenOptions): Promise<string | null> {\n this.assertInitialized();\n this.log(\"Acquiring token...\");\n\n const account = this._state.user;\n if (!account) {\n this.log(\"No account found, cannot acquire token\");\n return null;\n }\n\n const scopes = options?.scopes ?? this.provider.getApiScopes();\n const tokenRequest = {\n scopes,\n forceRefresh: options?.forceRefresh ?? false,\n account,\n authority: this._state.activePolicy\n ? this.provider.getAuthority(this._state.activePolicy)\n : this.provider.getAuthority(),\n };\n\n try {\n const response = await this.msalInstance!.acquireTokenSilent(tokenRequest);\n\n if (!response.accessToken) {\n throw new InteractionRequiredAuthError(\"empty_token\");\n }\n\n this.log(\"Token acquired silently\");\n this.updateState({ token: response.accessToken, error: null });\n this.events.emit(\"auth:tokenAcquired\", response.accessToken);\n return response.accessToken;\n } catch (error) {\n if (error instanceof InteractionRequiredAuthError) {\n this.log(\"Silent token acquisition failed, falling back to redirect\");\n try {\n await this.msalInstance!.acquireTokenRedirect(tokenRequest);\n return null; // Page will redirect\n } catch (redirectError) {\n this.handleError(redirectError);\n return null;\n }\n }\n this.handleError(error);\n return null;\n }\n }\n\n // ── Internal Helpers ──\n\n private selectAccount(policy: string | null): void {\n const accounts = this.msalInstance!.getAllAccounts();\n\n if (accounts.length === 0) {\n this.updateState({ isAuthenticated: false, user: null });\n return;\n }\n\n let selected: AccountInfo | null = null;\n\n if (accounts.length === 1) {\n selected = accounts[0];\n } else if (policy) {\n // Filter accounts by policy and authority domain\n const filtered = accounts.filter(account => {\n const claims = account.idTokenClaims as Record<string, unknown> | undefined;\n const iss = (claims?.iss as string) ?? \"\";\n const knownAuthorities = this.provider.getKnownAuthorities();\n const matchesAuthority = knownAuthorities.some(auth => iss.toUpperCase().includes(auth.toUpperCase()));\n const matchesPolicy = account.homeAccountId.toUpperCase().includes(policy.toUpperCase());\n return matchesAuthority && matchesPolicy;\n });\n\n if (filtered.length >= 1) {\n selected = filtered[0];\n }\n }\n\n if (!selected && accounts.length > 0) {\n selected = accounts[0];\n }\n\n if (selected) {\n this.log(\"Account selected\", selected.username);\n this.updateState({ isAuthenticated: true, user: selected });\n }\n }\n\n private async acquireTokenAfterRedirect(response: AuthenticationResult): Promise<void> {\n // Set the account first so acquireToken can use it\n const account =\n this.msalInstance!.getAccountByHomeId(response.account?.homeAccountId ?? \"\") ?? response.account ?? null;\n\n if (account) {\n this.updateState({ isAuthenticated: true, user: account });\n }\n\n await this.acquireToken();\n this.updateState({ isLoading: false });\n }\n\n private buildLoginRequest(authority: string, options?: SignInOptions) {\n const scopes = options?.scopes ?? this.provider.getDefaultScopes();\n return {\n scopes,\n authority,\n state: this.encodedState ? `${this.encodedState}` : undefined,\n loginHint: options?.loginHint,\n domainHint: options?.domainHint,\n };\n }\n\n private isForgotPasswordPolicy(policy: string | null): boolean {\n if (!policy) return false;\n if (!(this.provider instanceof B2CProvider)) return false;\n return policy.toUpperCase() === this.provider.getPolicies().resetPassword.toUpperCase();\n }\n\n private handleRedirectError(error: unknown): null {\n const err = error as { errorMessage?: string; errorCode?: string };\n\n // Detect B2C forgot-password error code\n if (err.errorMessage?.includes(MSAL_ERROR_CODES.FORGOT_PASSWORD)) {\n this.log(\"Forgot password error detected, redirecting...\");\n this.forgotPassword();\n return null;\n }\n\n this.handleError(error);\n this.updateState({ isLoading: false });\n return null;\n }\n\n private handleError(error: unknown): void {\n const err = error instanceof Error ? error : new Error(String(error));\n this.log(\"Error:\", err.message);\n this.updateState({ error: err });\n this.events.emit(\"auth:error\", err);\n }\n\n private updateState(partial: Partial<SSOClientState>): void {\n this._state = { ...this._state, ...partial };\n this.events.emit(\"auth:stateChanged\", this._state);\n }\n\n private assertInitialized(): void {\n if (!this.msalInstance) {\n throw new Error(\"SSOClient not initialized. Call initialize() first.\");\n }\n }\n\n private log(...args: unknown[]): void {\n if (this.debug) {\n console.log(\"[sso-core]\", ...args);\n }\n }\n}\n","import type { Configuration, AuthenticationResult } from \"@azure/msal-browser\";\nimport type { IAuthProvider, CIAMProviderConfig } from \"../types\";\n\n/**\n * Entra External ID (CIAM) provider — future implementation.\n * Placeholder scaffolding to validate the provider interface.\n */\nexport class CIAMProvider implements IAuthProvider {\n readonly type = \"ciam\" as const;\n\n private readonly config: CIAMProviderConfig;\n\n constructor(config: CIAMProviderConfig) {\n this.config = config;\n }\n\n buildMsalConfig(): Configuration {\n return {\n auth: {\n clientId: this.config.clientId,\n authority: this.getAuthority(),\n knownAuthorities: this.getKnownAuthorities(),\n redirectUri: this.config.redirectUri,\n postLogoutRedirectUri: this.config.postLogoutRedirectUri ?? this.config.redirectUri,\n navigateToLoginRequestUrl: false,\n },\n cache: {\n cacheLocation: this.config.cacheLocation ?? \"sessionStorage\",\n },\n };\n }\n\n getAuthority(): string {\n return `https://${this.config.tenantSubdomain}.ciamlogin.com/`;\n }\n\n getKnownAuthorities(): string[] {\n return [`${this.config.tenantSubdomain}.ciamlogin.com`];\n }\n\n identifyPolicy(_response: AuthenticationResult): string | null {\n // CIAM doesn't use client-side policy selection\n return null;\n }\n\n getDefaultScopes(): string[] {\n return [\"openid\", \"profile\"];\n }\n\n getApiScopes(): string[] {\n return this.config.scopes ?? [];\n }\n}\n","import type { Configuration, AuthenticationResult } from \"@azure/msal-browser\";\nimport type { IAuthProvider, EntraProviderConfig } from \"../types\";\n\n/**\n * Entra workforce (city employees) provider — future implementation.\n * Placeholder scaffolding to validate the provider interface.\n */\nexport class EntraProvider implements IAuthProvider {\n readonly type = \"entra\" as const;\n\n private readonly config: EntraProviderConfig;\n\n constructor(config: EntraProviderConfig) {\n this.config = config;\n }\n\n buildMsalConfig(): Configuration {\n return {\n auth: {\n clientId: this.config.clientId,\n authority: this.getAuthority(),\n knownAuthorities: this.getKnownAuthorities(),\n redirectUri: this.config.redirectUri,\n postLogoutRedirectUri: this.config.postLogoutRedirectUri ?? this.config.redirectUri,\n navigateToLoginRequestUrl: false,\n },\n cache: {\n cacheLocation: this.config.cacheLocation ?? \"sessionStorage\",\n },\n };\n }\n\n getAuthority(): string {\n return `https://login.microsoftonline.com/${this.config.tenantId}`;\n }\n\n getKnownAuthorities(): string[] {\n return [\"login.microsoftonline.com\"];\n }\n\n identifyPolicy(_response: AuthenticationResult): string | null {\n // Entra workforce doesn't use B2C policies\n return null;\n }\n\n getDefaultScopes(): string[] {\n return [\"openid\", \"profile\"];\n }\n\n getApiScopes(): string[] {\n return this.config.scopes ?? [];\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/events.ts","../src/constants.ts","../src/state-encoding.ts","../src/providers/b2c.ts","../src/client.ts","../src/providers/ciam.ts","../src/providers/entra.ts"],"sourcesContent":["import type { SSOEventMap, SSOEventName } from \"./types\";\n\ntype Listener<T> = (data: T) => void;\n\nexport class SSOEventEmitter {\n private listeners = new Map<SSOEventName, Set<Listener<unknown>>>();\n\n on<K extends SSOEventName>(event: K, listener: Listener<SSOEventMap[K]>): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n const set = this.listeners.get(event)!;\n set.add(listener as Listener<unknown>);\n\n // Return unsubscribe function\n return () => {\n set.delete(listener as Listener<unknown>);\n };\n }\n\n emit<K extends SSOEventName>(event: K, data: SSOEventMap[K]): void {\n const set = this.listeners.get(event);\n if (set) {\n for (const listener of set) {\n listener(data);\n }\n }\n }\n\n removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","export const DEFAULT_POLICIES = {\n SIGN_UP_SIGN_IN: \"B2C_1A_SIGNUP_SIGNIN\",\n SIGN_IN_ONLY: \"B2C_1A_AD_SIGNIN_ONLY\",\n RESET_PASSWORD: \"B2C_1A_PASSWORDRESET\",\n} as const;\n\nexport const DEFAULT_SCOPES = {\n OPENID: \"openid\",\n PROFILE: \"profile\",\n OFFLINE_ACCESS: \"offline_access\",\n} as const;\n\nexport const CACHE_CONFIG = {\n LOCATION: \"sessionStorage\" as const,\n STORE_AUTH_STATE_IN_COOKIE: false,\n} as const;\n\nexport const MSAL_ERROR_CODES = {\n USER_CANCELLED: \"user_cancelled\",\n NO_CACHED_AUTHORITY: \"no_cached_authority_error\",\n INTERACTION_REQUIRED: \"interaction_required\",\n FORGOT_PASSWORD: \"AADB2C90118\",\n} as const;\n\nexport const STATE_SEPARATOR = \"|\";\n","import { STATE_SEPARATOR } from \"./constants\";\n\n/**\n * Encode a custom state object into a base64 string that can be\n * appended to the MSAL state parameter using a pipe separator.\n */\nexport function encodeState(stateObj: Record<string, unknown>): string {\n return btoa(JSON.stringify(stateObj));\n}\n\n/**\n * Decode a base64-encoded state string back into an object.\n */\nexport function decodeState(encoded: string): Record<string, unknown> | null {\n try {\n return JSON.parse(atob(encoded));\n } catch {\n return null;\n }\n}\n\n/**\n * Extract custom postback data from the MSAL state string.\n * MSAL prepends its own GUID before the pipe separator.\n */\nexport function extractCustomState(msalState: string | undefined): Record<string, unknown> | null {\n if (!msalState) return null;\n\n const parts = msalState.split(STATE_SEPARATOR);\n if (parts.length < 2) return null;\n\n // Everything after the first pipe is our encoded state\n const customPart = parts.slice(1).join(STATE_SEPARATOR);\n return decodeState(customPart);\n}\n","import { LogLevel } from \"@azure/msal-browser\";\nimport type { Configuration, AuthenticationResult } from \"@azure/msal-browser\";\nimport type { IAuthProvider, B2CProviderConfig, B2CPolicies } from \"../types\";\nimport { DEFAULT_POLICIES, DEFAULT_SCOPES, CACHE_CONFIG } from \"../constants\";\n\nexport class B2CProvider implements IAuthProvider {\n readonly type = \"b2c\" as const;\n\n private readonly clientId: string;\n private readonly b2cEnv: string;\n private readonly authorityDomain: string;\n private readonly redirectUri: string;\n private readonly postLogoutRedirectUri: string;\n private readonly policies: B2CPolicies;\n private readonly apiScopes: string[];\n private readonly cacheLocation: \"sessionStorage\" | \"localStorage\";\n\n constructor(config: B2CProviderConfig) {\n this.clientId = config.clientId;\n this.b2cEnv = config.b2cEnvironment;\n this.authorityDomain = config.authorityDomain;\n this.redirectUri = config.redirectUri;\n this.postLogoutRedirectUri = config.postLogoutRedirectUri ?? config.redirectUri;\n this.policies = {\n signUpSignIn: config.policies?.signUpSignIn ?? DEFAULT_POLICIES.SIGN_UP_SIGN_IN,\n signInOnly: config.policies?.signInOnly ?? DEFAULT_POLICIES.SIGN_IN_ONLY,\n resetPassword: config.policies?.resetPassword ?? DEFAULT_POLICIES.RESET_PASSWORD,\n };\n this.apiScopes = config.apiScopes ?? [];\n this.cacheLocation = config.cacheLocation ?? CACHE_CONFIG.LOCATION;\n }\n\n buildMsalConfig(): Configuration {\n return {\n auth: {\n clientId: this.clientId,\n authority: this.getAuthority(this.policies.signUpSignIn),\n knownAuthorities: this.getKnownAuthorities(),\n redirectUri: this.redirectUri,\n postLogoutRedirectUri: this.postLogoutRedirectUri,\n navigateToLoginRequestUrl: false,\n },\n cache: {\n cacheLocation: this.cacheLocation,\n storeAuthStateInCookie: CACHE_CONFIG.STORE_AUTH_STATE_IN_COOKIE,\n },\n system: {\n loggerOptions: {\n logLevel: LogLevel.Warning,\n loggerCallback: (_level: LogLevel, message: string) => {\n console.warn(\"[sso-core/b2c]\", message);\n },\n },\n },\n };\n }\n\n getAuthority(policy?: string): string {\n const p = policy ?? this.policies.signUpSignIn;\n return `https://${this.authorityDomain}/${this.b2cEnv}.onmicrosoft.com/${p}`;\n }\n\n getKnownAuthorities(): string[] {\n return [this.authorityDomain];\n }\n\n identifyPolicy(response: AuthenticationResult): string | null {\n const claims = response.idTokenClaims as Record<string, unknown> | undefined;\n if (!claims) return null;\n\n const acr = (claims.acr ?? claims.tfp) as string | undefined;\n return acr?.toUpperCase() ?? null;\n }\n\n getDefaultScopes(): string[] {\n return [DEFAULT_SCOPES.OPENID, DEFAULT_SCOPES.PROFILE];\n }\n\n getApiScopes(): string[] {\n return this.apiScopes;\n }\n\n /** Get the authority URL for the sign-in-only (city employee) policy */\n getSignInOnlyAuthority(): string {\n return this.getAuthority(this.policies.signInOnly);\n }\n\n /** Get the authority URL for the password reset policy */\n getResetPasswordAuthority(): string {\n return this.getAuthority(this.policies.resetPassword);\n }\n\n /** Get all configured policy names */\n getPolicies(): Readonly<B2CPolicies> {\n return this.policies;\n }\n}\n","import { PublicClientApplication, InteractionRequiredAuthError } from \"@azure/msal-browser\";\nimport type { AccountInfo, AuthenticationResult } from \"@azure/msal-browser\";\nimport { SSOEventEmitter } from \"./events\";\nimport { encodeState, extractCustomState } from \"./state-encoding\";\nimport { MSAL_ERROR_CODES } from \"./constants\";\nimport { B2CProvider } from \"./providers/b2c\";\nimport type {\n IAuthProvider,\n SSOClientConfig,\n SSOClientState,\n SignInOptions,\n SignOutOptions,\n TokenOptions,\n AuthResponse,\n} from \"./types\";\n\nconst INITIAL_STATE: SSOClientState = {\n isAuthenticated: false,\n isLoading: false,\n user: null,\n token: null,\n error: null,\n activePolicy: null,\n authReady: false,\n};\n\nexport class SSOClient {\n readonly events = new SSOEventEmitter();\n private readonly provider: IAuthProvider;\n private readonly debug: boolean;\n private readonly encodedState: string | null;\n\n private msalInstance: PublicClientApplication | null = null;\n private _state: SSOClientState = { ...INITIAL_STATE };\n\n constructor(config: SSOClientConfig) {\n this.provider = config.provider;\n this.debug = config.debug ?? false;\n this.encodedState = config.state ? encodeState(config.state) : null;\n }\n\n get state(): Readonly<SSOClientState> {\n return this._state;\n }\n\n // ── Lifecycle ──\n\n async initialize(): Promise<AuthResponse | null> {\n this.log(\"Initializing SSOClient...\");\n this.updateState({ isLoading: true });\n\n const msalConfig = this.provider.buildMsalConfig();\n this.msalInstance = new PublicClientApplication(msalConfig);\n\n await this.msalInstance.initialize();\n\n const result = await this.handleRedirect();\n\n this.updateState({ isLoading: false, authReady: true });\n return result;\n }\n\n destroy(): void {\n this.events.removeAllListeners();\n this.msalInstance = null;\n this._state = { ...INITIAL_STATE };\n }\n\n // ── Auth Actions ──\n\n async handleRedirect(): Promise<AuthResponse | null> {\n this.assertInitialized();\n this.log(\"Handling redirect promise...\");\n\n try {\n const response = await this.msalInstance!.handleRedirectPromise();\n\n if (!response) {\n // No redirect response — check for existing sessions\n this.selectAccount(null);\n return null;\n }\n\n this.log(\"Redirect response received\", response);\n\n // Extract custom postback state\n const customState = extractCustomState(response.state);\n\n // Identify which policy was used via the acr/tfp claim\n const policy = this.provider.identifyPolicy(response);\n this.updateState({ activePolicy: policy });\n\n // Check if this was a forgot-password completion\n if (this.isForgotPasswordPolicy(policy)) {\n this.log(\"Forgot password flow completed\");\n this.events.emit(\"auth:forgotPassword\", undefined as never);\n return { ...response, customPostbackObject: customState ?? undefined } as AuthResponse;\n }\n\n // Normal sign-in flow\n this.updateState({ isLoading: true });\n this.selectAccount(policy);\n await this.acquireTokenAfterRedirect(response);\n\n const authResponse: AuthResponse = {\n ...response,\n customPostbackObject: customState ?? undefined,\n };\n\n this.events.emit(\"auth:signedIn\", authResponse);\n return authResponse;\n } catch (error) {\n return this.handleRedirectError(error);\n }\n }\n\n async signIn(options?: SignInOptions): Promise<void> {\n this.assertInitialized();\n this.log(\"Initiating sign-in...\");\n this.updateState({ isLoading: true });\n this.events.emit(\"auth:loading\", true);\n\n const request = this.buildLoginRequest(this.provider.getAuthority(), options);\n await this.msalInstance!.loginRedirect(request);\n }\n\n async signInCityEmployee(options?: SignInOptions): Promise<void> {\n this.assertInitialized();\n this.log(\"Initiating city employee sign-in...\");\n\n if (!(this.provider instanceof B2CProvider)) {\n // For non-B2C providers, fall back to regular sign-in\n return this.signIn(options);\n }\n\n const authority = this.provider.getSignInOnlyAuthority();\n const request = this.buildLoginRequest(authority, options);\n await this.msalInstance!.loginRedirect(request);\n }\n\n async signOut(options?: SignOutOptions): Promise<void> {\n this.assertInitialized();\n this.log(\"Initiating sign-out...\");\n\n const logoutRequest = {\n account: this._state.user ?? undefined,\n postLogoutRedirectUri: options?.postLogoutRedirectUri,\n authority: this.provider.getAuthority(),\n };\n\n this.updateState({ isLoading: true });\n this.events.emit(\"auth:signedOut\", undefined as never);\n await this.msalInstance!.logoutRedirect(logoutRequest);\n }\n\n async forgotPassword(): Promise<void> {\n this.assertInitialized();\n\n if (!(this.provider instanceof B2CProvider)) {\n this.log(\"Forgot password is only supported for B2C providers\");\n return;\n }\n\n this.log(\"Initiating forgot password flow...\");\n const authority = this.provider.getResetPasswordAuthority();\n await this.msalInstance!.loginRedirect({ scopes: [], authority });\n }\n\n async acquireToken(options?: TokenOptions): Promise<string | null> {\n this.assertInitialized();\n this.log(\"Acquiring token...\");\n\n const account = this._state.user;\n if (!account) {\n this.log(\"No account found, cannot acquire token\");\n return null;\n }\n\n const scopes = options?.scopes ?? this.provider.getApiScopes();\n const tokenRequest = {\n scopes,\n forceRefresh: options?.forceRefresh ?? false,\n account,\n authority: this._state.activePolicy\n ? this.provider.getAuthority(this._state.activePolicy)\n : this.provider.getAuthority(),\n };\n\n try {\n const response = await this.msalInstance!.acquireTokenSilent(tokenRequest);\n\n if (!response.accessToken) {\n throw new InteractionRequiredAuthError(\"empty_token\");\n }\n\n this.log(\"Token acquired silently\");\n this.updateState({ token: response.accessToken, error: null });\n this.events.emit(\"auth:tokenAcquired\", response.accessToken);\n return response.accessToken;\n } catch (error) {\n if (error instanceof InteractionRequiredAuthError) {\n this.log(\"Silent token acquisition failed, falling back to redirect\");\n try {\n await this.msalInstance!.acquireTokenRedirect(tokenRequest);\n return null; // Page will redirect\n } catch (redirectError) {\n this.handleError(redirectError);\n return null;\n }\n }\n this.handleError(error);\n return null;\n }\n }\n\n // ── Internal Helpers ──\n\n private selectAccount(policy: string | null): void {\n const accounts = this.msalInstance!.getAllAccounts();\n\n if (accounts.length === 0) {\n this.updateState({ isAuthenticated: false, user: null });\n return;\n }\n\n let selected: AccountInfo | null = null;\n\n if (accounts.length === 1) {\n selected = accounts[0];\n } else if (policy) {\n // Filter accounts by policy and authority domain\n const filtered = accounts.filter(account => {\n const claims = account.idTokenClaims as Record<string, unknown> | undefined;\n const iss = (claims?.iss as string) ?? \"\";\n const knownAuthorities = this.provider.getKnownAuthorities();\n const matchesAuthority = knownAuthorities.some(auth => iss.toUpperCase().includes(auth.toUpperCase()));\n const matchesPolicy = account.homeAccountId.toUpperCase().includes(policy.toUpperCase());\n return matchesAuthority && matchesPolicy;\n });\n\n if (filtered.length >= 1) {\n selected = filtered[0];\n }\n }\n\n if (!selected && accounts.length > 0) {\n selected = accounts[0];\n }\n\n if (selected) {\n this.log(\"Account selected\", selected.username);\n this.updateState({ isAuthenticated: true, user: selected });\n }\n }\n\n private async acquireTokenAfterRedirect(response: AuthenticationResult): Promise<void> {\n // Set the account first so acquireToken can use it\n const account =\n this.msalInstance!.getAccountByHomeId(response.account?.homeAccountId ?? \"\") ?? response.account ?? null;\n\n if (account) {\n this.updateState({ isAuthenticated: true, user: account });\n }\n\n await this.acquireToken();\n this.updateState({ isLoading: false });\n }\n\n private buildLoginRequest(authority: string, options?: SignInOptions) {\n const scopes = options?.scopes ?? this.provider.getDefaultScopes();\n return {\n scopes,\n authority,\n state: this.encodedState ? `${this.encodedState}` : undefined,\n loginHint: options?.loginHint,\n domainHint: options?.domainHint,\n };\n }\n\n private isForgotPasswordPolicy(policy: string | null): boolean {\n if (!policy) return false;\n if (!(this.provider instanceof B2CProvider)) return false;\n return policy.toUpperCase() === this.provider.getPolicies().resetPassword.toUpperCase();\n }\n\n private handleRedirectError(error: unknown): null {\n const err = error as { errorMessage?: string; errorCode?: string };\n\n // Detect B2C forgot-password error code\n if (err.errorMessage?.includes(MSAL_ERROR_CODES.FORGOT_PASSWORD)) {\n this.log(\"Forgot password error detected, redirecting...\");\n this.forgotPassword();\n return null;\n }\n\n this.handleError(error);\n this.updateState({ isLoading: false });\n return null;\n }\n\n private handleError(error: unknown): void {\n const err = error instanceof Error ? error : new Error(String(error));\n this.log(\"Error:\", err.message);\n this.updateState({ error: err });\n this.events.emit(\"auth:error\", err);\n }\n\n private updateState(partial: Partial<SSOClientState>): void {\n this._state = { ...this._state, ...partial };\n this.events.emit(\"auth:stateChanged\", this._state);\n }\n\n private assertInitialized(): void {\n if (!this.msalInstance) {\n throw new Error(\"SSOClient not initialized. Call initialize() first.\");\n }\n }\n\n private log(...args: unknown[]): void {\n if (this.debug) {\n console.log(\"[sso-core]\", ...args);\n }\n }\n}\n","import type { Configuration, AuthenticationResult } from \"@azure/msal-browser\";\nimport type { IAuthProvider, CIAMProviderConfig } from \"../types\";\n\n/**\n * Entra External ID (CIAM) provider — future implementation.\n * Placeholder scaffolding to validate the provider interface.\n */\nexport class CIAMProvider implements IAuthProvider {\n readonly type = \"ciam\" as const;\n\n private readonly config: CIAMProviderConfig;\n\n constructor(config: CIAMProviderConfig) {\n this.config = config;\n }\n\n buildMsalConfig(): Configuration {\n return {\n auth: {\n clientId: this.config.clientId,\n authority: this.getAuthority(),\n knownAuthorities: this.getKnownAuthorities(),\n redirectUri: this.config.redirectUri,\n postLogoutRedirectUri: this.config.postLogoutRedirectUri ?? this.config.redirectUri,\n navigateToLoginRequestUrl: false,\n },\n cache: {\n cacheLocation: this.config.cacheLocation ?? \"sessionStorage\",\n },\n };\n }\n\n getAuthority(): string {\n return `https://${this.config.tenantSubdomain}.ciamlogin.com/`;\n }\n\n getKnownAuthorities(): string[] {\n return [`${this.config.tenantSubdomain}.ciamlogin.com`];\n }\n\n identifyPolicy(_response: AuthenticationResult): string | null {\n // CIAM doesn't use client-side policy selection\n return null;\n }\n\n getDefaultScopes(): string[] {\n return [\"openid\", \"profile\"];\n }\n\n getApiScopes(): string[] {\n return this.config.scopes ?? [];\n }\n}\n","import type { Configuration, AuthenticationResult } from \"@azure/msal-browser\";\nimport type { IAuthProvider, EntraProviderConfig } from \"../types\";\n\n/**\n * Entra workforce (city employees) provider — future implementation.\n * Placeholder scaffolding to validate the provider interface.\n */\nexport class EntraProvider implements IAuthProvider {\n readonly type = \"entra\" as const;\n\n private readonly config: EntraProviderConfig;\n\n constructor(config: EntraProviderConfig) {\n this.config = config;\n }\n\n buildMsalConfig(): Configuration {\n return {\n auth: {\n clientId: this.config.clientId,\n authority: this.getAuthority(),\n knownAuthorities: this.getKnownAuthorities(),\n redirectUri: this.config.redirectUri,\n postLogoutRedirectUri: this.config.postLogoutRedirectUri ?? this.config.redirectUri,\n navigateToLoginRequestUrl: false,\n },\n cache: {\n cacheLocation: this.config.cacheLocation ?? \"sessionStorage\",\n },\n };\n }\n\n getAuthority(): string {\n return `https://login.microsoftonline.com/${this.config.tenantId}`;\n }\n\n getKnownAuthorities(): string[] {\n return [\"login.microsoftonline.com\"];\n }\n\n identifyPolicy(_response: AuthenticationResult): string | null {\n // Entra workforce doesn't use B2C policies\n return null;\n }\n\n getDefaultScopes(): string[] {\n return [\"openid\", \"profile\"];\n }\n\n getApiScopes(): string[] {\n return this.config.scopes ?? [];\n }\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phila/sso-core",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "description": "Framework-agnostic SSO client for Azure AD B2C / Entra External ID",
6
6
  "main": "./dist/index.js",