@phila/sso-core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.d.ts +236 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +378 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 City of Philadelphia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { AccountInfo } from '@azure/msal-browser';
|
|
2
|
+
import { AuthenticationResult } from '@azure/msal-browser';
|
|
3
|
+
import { Configuration } from '@azure/msal-browser';
|
|
4
|
+
|
|
5
|
+
export declare interface AuthResponse extends AuthenticationResult {
|
|
6
|
+
customPostbackObject?: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export declare interface B2CPolicies {
|
|
10
|
+
signUpSignIn: string;
|
|
11
|
+
signInOnly: string;
|
|
12
|
+
resetPassword: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export declare class B2CProvider implements IAuthProvider {
|
|
16
|
+
readonly type: "b2c";
|
|
17
|
+
private readonly clientId;
|
|
18
|
+
private readonly b2cEnv;
|
|
19
|
+
private readonly authorityDomain;
|
|
20
|
+
private readonly redirectUri;
|
|
21
|
+
private readonly postLogoutRedirectUri;
|
|
22
|
+
private readonly policies;
|
|
23
|
+
private readonly apiScopes;
|
|
24
|
+
private readonly cacheLocation;
|
|
25
|
+
constructor(config: B2CProviderConfig);
|
|
26
|
+
buildMsalConfig(): Configuration;
|
|
27
|
+
getAuthority(policy?: string): string;
|
|
28
|
+
getKnownAuthorities(): string[];
|
|
29
|
+
identifyPolicy(response: AuthenticationResult): string | null;
|
|
30
|
+
getDefaultScopes(): string[];
|
|
31
|
+
getApiScopes(): string[];
|
|
32
|
+
/** Get the authority URL for the sign-in-only (city employee) policy */
|
|
33
|
+
getSignInOnlyAuthority(): string;
|
|
34
|
+
/** Get the authority URL for the password reset policy */
|
|
35
|
+
getResetPasswordAuthority(): string;
|
|
36
|
+
/** Get all configured policy names */
|
|
37
|
+
getPolicies(): Readonly<B2CPolicies>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export declare interface B2CProviderConfig {
|
|
41
|
+
clientId: string;
|
|
42
|
+
b2cEnvironment: string;
|
|
43
|
+
authorityDomain: string;
|
|
44
|
+
redirectUri: string;
|
|
45
|
+
postLogoutRedirectUri?: string;
|
|
46
|
+
policies?: Partial<B2CPolicies>;
|
|
47
|
+
apiScopes?: string[];
|
|
48
|
+
cacheLocation?: "sessionStorage" | "localStorage";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export declare const CACHE_CONFIG: {
|
|
52
|
+
readonly LOCATION: "sessionStorage";
|
|
53
|
+
readonly STORE_AUTH_STATE_IN_COOKIE: false;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Entra External ID (CIAM) provider — future implementation.
|
|
58
|
+
* Placeholder scaffolding to validate the provider interface.
|
|
59
|
+
*/
|
|
60
|
+
export declare class CIAMProvider implements IAuthProvider {
|
|
61
|
+
readonly type: "ciam";
|
|
62
|
+
private readonly config;
|
|
63
|
+
constructor(config: CIAMProviderConfig);
|
|
64
|
+
buildMsalConfig(): Configuration;
|
|
65
|
+
getAuthority(): string;
|
|
66
|
+
getKnownAuthorities(): string[];
|
|
67
|
+
identifyPolicy(_response: AuthenticationResult): string | null;
|
|
68
|
+
getDefaultScopes(): string[];
|
|
69
|
+
getApiScopes(): string[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export declare interface CIAMProviderConfig {
|
|
73
|
+
clientId: string;
|
|
74
|
+
tenantSubdomain: string;
|
|
75
|
+
redirectUri: string;
|
|
76
|
+
postLogoutRedirectUri?: string;
|
|
77
|
+
scopes?: string[];
|
|
78
|
+
cacheLocation?: "sessionStorage" | "localStorage";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Decode a base64-encoded state string back into an object.
|
|
83
|
+
*/
|
|
84
|
+
export declare function decodeState(encoded: string): Record<string, unknown> | null;
|
|
85
|
+
|
|
86
|
+
export declare const DEFAULT_POLICIES: {
|
|
87
|
+
readonly SIGN_UP_SIGN_IN: "B2C_1A_SIGNUP_SIGNIN";
|
|
88
|
+
readonly SIGN_IN_ONLY: "B2C_1A_AD_SIGNIN_ONLY";
|
|
89
|
+
readonly RESET_PASSWORD: "B2C_1A_PASSWORDRESET";
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export declare const DEFAULT_SCOPES: {
|
|
93
|
+
readonly OPENID: "openid";
|
|
94
|
+
readonly PROFILE: "profile";
|
|
95
|
+
readonly OFFLINE_ACCESS: "offline_access";
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Encode a custom state object into a base64 string that can be
|
|
100
|
+
* appended to the MSAL state parameter using a pipe separator.
|
|
101
|
+
*/
|
|
102
|
+
export declare function encodeState(stateObj: Record<string, unknown>): string;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Entra workforce (city employees) provider — future implementation.
|
|
106
|
+
* Placeholder scaffolding to validate the provider interface.
|
|
107
|
+
*/
|
|
108
|
+
export declare class EntraProvider implements IAuthProvider {
|
|
109
|
+
readonly type: "entra";
|
|
110
|
+
private readonly config;
|
|
111
|
+
constructor(config: EntraProviderConfig);
|
|
112
|
+
buildMsalConfig(): Configuration;
|
|
113
|
+
getAuthority(): string;
|
|
114
|
+
getKnownAuthorities(): string[];
|
|
115
|
+
identifyPolicy(_response: AuthenticationResult): string | null;
|
|
116
|
+
getDefaultScopes(): string[];
|
|
117
|
+
getApiScopes(): string[];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export declare interface EntraProviderConfig {
|
|
121
|
+
clientId: string;
|
|
122
|
+
tenantId: string;
|
|
123
|
+
redirectUri: string;
|
|
124
|
+
postLogoutRedirectUri?: string;
|
|
125
|
+
scopes?: string[];
|
|
126
|
+
cacheLocation?: "sessionStorage" | "localStorage";
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Extract custom postback data from the MSAL state string.
|
|
131
|
+
* MSAL prepends its own GUID before the pipe separator.
|
|
132
|
+
*/
|
|
133
|
+
export declare function extractCustomState(msalState: string | undefined): Record<string, unknown> | null;
|
|
134
|
+
|
|
135
|
+
export declare interface IAuthProvider {
|
|
136
|
+
readonly type: "b2c" | "ciam" | "entra";
|
|
137
|
+
buildMsalConfig(): Configuration;
|
|
138
|
+
getAuthority(policy?: string): string;
|
|
139
|
+
getKnownAuthorities(): string[];
|
|
140
|
+
identifyPolicy(response: AuthenticationResult): string | null;
|
|
141
|
+
getDefaultScopes(): string[];
|
|
142
|
+
getApiScopes(): string[];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
declare type Listener<T> = (data: T) => void;
|
|
146
|
+
|
|
147
|
+
export declare const MSAL_ERROR_CODES: {
|
|
148
|
+
readonly USER_CANCELLED: "user_cancelled";
|
|
149
|
+
readonly NO_CACHED_AUTHORITY: "no_cached_authority_error";
|
|
150
|
+
readonly INTERACTION_REQUIRED: "interaction_required";
|
|
151
|
+
readonly FORGOT_PASSWORD: "AADB2C90118";
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export declare interface SignInOptions {
|
|
155
|
+
scopes?: string[];
|
|
156
|
+
loginHint?: string;
|
|
157
|
+
domainHint?: string;
|
|
158
|
+
state?: Record<string, unknown>;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export declare interface SignOutOptions {
|
|
162
|
+
postLogoutRedirectUri?: string;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export declare class SSOClient {
|
|
166
|
+
readonly events: SSOEventEmitter;
|
|
167
|
+
private readonly provider;
|
|
168
|
+
private readonly debug;
|
|
169
|
+
private readonly encodedState;
|
|
170
|
+
private msalInstance;
|
|
171
|
+
private _state;
|
|
172
|
+
constructor(config: SSOClientConfig);
|
|
173
|
+
get state(): Readonly<SSOClientState>;
|
|
174
|
+
initialize(): Promise<AuthResponse | null>;
|
|
175
|
+
destroy(): void;
|
|
176
|
+
handleRedirect(): Promise<AuthResponse | null>;
|
|
177
|
+
signIn(options?: SignInOptions): Promise<void>;
|
|
178
|
+
signInCityEmployee(options?: SignInOptions): Promise<void>;
|
|
179
|
+
signOut(options?: SignOutOptions): Promise<void>;
|
|
180
|
+
forgotPassword(): Promise<void>;
|
|
181
|
+
acquireToken(options?: TokenOptions): Promise<string | null>;
|
|
182
|
+
private selectAccount;
|
|
183
|
+
private acquireTokenAfterRedirect;
|
|
184
|
+
private buildLoginRequest;
|
|
185
|
+
private isForgotPasswordPolicy;
|
|
186
|
+
private handleRedirectError;
|
|
187
|
+
private handleError;
|
|
188
|
+
private updateState;
|
|
189
|
+
private assertInitialized;
|
|
190
|
+
private log;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export declare interface SSOClientConfig {
|
|
194
|
+
provider: IAuthProvider;
|
|
195
|
+
autoInitialize?: boolean;
|
|
196
|
+
state?: Record<string, unknown> | null;
|
|
197
|
+
debug?: boolean;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export declare interface SSOClientState {
|
|
201
|
+
isAuthenticated: boolean;
|
|
202
|
+
isLoading: boolean;
|
|
203
|
+
user: AccountInfo | null;
|
|
204
|
+
token: string | null;
|
|
205
|
+
error: Error | null;
|
|
206
|
+
activePolicy: string | null;
|
|
207
|
+
authReady: boolean;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export declare class SSOEventEmitter {
|
|
211
|
+
private listeners;
|
|
212
|
+
on<K extends SSOEventName>(event: K, listener: Listener<SSOEventMap[K]>): () => void;
|
|
213
|
+
emit<K extends SSOEventName>(event: K, data: SSOEventMap[K]): void;
|
|
214
|
+
removeAllListeners(): void;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export declare interface SSOEventMap {
|
|
218
|
+
"auth:stateChanged": SSOClientState;
|
|
219
|
+
"auth:signedIn": AuthResponse;
|
|
220
|
+
"auth:signedOut": void;
|
|
221
|
+
"auth:tokenAcquired": string;
|
|
222
|
+
"auth:error": Error;
|
|
223
|
+
"auth:forgotPassword": void;
|
|
224
|
+
"auth:loading": boolean;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export declare type SSOEventName = keyof SSOEventMap;
|
|
228
|
+
|
|
229
|
+
export declare const STATE_SEPARATOR = "|";
|
|
230
|
+
|
|
231
|
+
export declare interface TokenOptions {
|
|
232
|
+
scopes?: string[];
|
|
233
|
+
forceRefresh?: boolean;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export { }
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("@azure/msal-browser");class p{listeners=new Map;on(t,e){this.listeners.has(t)||this.listeners.set(t,new Set);const i=this.listeners.get(t);return i.add(e),()=>{i.delete(e)}}emit(t,e){const i=this.listeners.get(t);if(i)for(const r of i)r(e)}removeAllListeners(){this.listeners.clear()}}const a={SIGN_UP_SIGN_IN:"B2C_1A_SIGNUP_SIGNIN",SIGN_IN_ONLY:"B2C_1A_AD_SIGNIN_ONLY",RESET_PASSWORD:"B2C_1A_PASSWORDRESET"},l={OPENID:"openid",PROFILE:"profile",OFFLINE_ACCESS:"offline_access"},u={LOCATION:"sessionStorage",STORE_AUTH_STATE_IN_COOKIE:!1},S={USER_CANCELLED:"user_cancelled",NO_CACHED_AUTHORITY:"no_cached_authority_error",INTERACTION_REQUIRED:"interaction_required",FORGOT_PASSWORD:"AADB2C90118"},h="|";function f(s){return btoa(JSON.stringify(s))}function A(s){try{return JSON.parse(atob(s))}catch{return null}}function I(s){if(!s)return null;const t=s.split(h);if(t.length<2)return null;const e=t.slice(1).join(h);return A(e)}class c{type="b2c";clientId;b2cEnv;authorityDomain;redirectUri;postLogoutRedirectUri;policies;apiScopes;cacheLocation;constructor(t){this.clientId=t.clientId,this.b2cEnv=t.b2cEnvironment,this.authorityDomain=t.authorityDomain,this.redirectUri=t.redirectUri,this.postLogoutRedirectUri=t.postLogoutRedirectUri??t.redirectUri,this.policies={signUpSignIn:t.policies?.signUpSignIn??a.SIGN_UP_SIGN_IN,signInOnly:t.policies?.signInOnly??a.SIGN_IN_ONLY,resetPassword:t.policies?.resetPassword??a.RESET_PASSWORD},this.apiScopes=t.apiScopes??[],this.cacheLocation=t.cacheLocation??u.LOCATION}buildMsalConfig(){return{auth:{clientId:this.clientId,authority:this.getAuthority(this.policies.signUpSignIn),knownAuthorities:this.getKnownAuthorities(),redirectUri:this.redirectUri,postLogoutRedirectUri:this.postLogoutRedirectUri,navigateToLoginRequestUrl:!1},cache:{cacheLocation:this.cacheLocation,storeAuthStateInCookie:u.STORE_AUTH_STATE_IN_COOKIE},system:{loggerOptions:{logLevel:n.LogLevel.Warning,loggerCallback:(t,e)=>{console.warn("[sso-core/b2c]",e)}}}}}getAuthority(t){const e=t??this.policies.signUpSignIn;return`https://${this.authorityDomain}/${this.b2cEnv}.onmicrosoft.com/${e}`}getKnownAuthorities(){return[this.authorityDomain]}identifyPolicy(t){const e=t.idTokenClaims;return e?(e.acr??e.tfp)?.toUpperCase()??null:null}getDefaultScopes(){return[l.OPENID,l.PROFILE]}getApiScopes(){return this.apiScopes}getSignInOnlyAuthority(){return this.getAuthority(this.policies.signInOnly)}getResetPasswordAuthority(){return this.getAuthority(this.policies.resetPassword)}getPolicies(){return this.policies}}const g={isAuthenticated:!1,isLoading:!1,user:null,token:null,error:null,activePolicy:null,authReady:!1};class _{events=new p;provider;debug;encodedState;msalInstance=null;_state={...g};constructor(t){this.provider=t.provider,this.debug=t.debug??!1,this.encodedState=t.state?f(t.state):null}get state(){return this._state}async initialize(){this.log("Initializing SSOClient..."),this.updateState({isLoading:!0});const t=this.provider.buildMsalConfig();this.msalInstance=new n.PublicClientApplication(t),await this.msalInstance.initialize();const e=await this.handleRedirect();return this.updateState({isLoading:!1,authReady:!0}),e}destroy(){this.events.removeAllListeners(),this.msalInstance=null,this._state={...g}}async handleRedirect(){this.assertInitialized(),this.log("Handling redirect promise...");try{const t=await this.msalInstance.handleRedirectPromise();if(!t)return this.selectAccount(null),null;this.log("Redirect response received",t);const e=I(t.state),i=this.provider.identifyPolicy(t);if(this.updateState({activePolicy:i}),this.isForgotPasswordPolicy(i))return this.log("Forgot password flow completed"),this.events.emit("auth:forgotPassword",void 0),{...t,customPostbackObject:e??void 0};this.updateState({isLoading:!0}),this.selectAccount(i),await this.acquireTokenAfterRedirect(t);const r={...t,customPostbackObject:e??void 0};return this.events.emit("auth:signedIn",r),r}catch(t){return this.handleRedirectError(t)}}async signIn(t){this.assertInitialized(),this.log("Initiating sign-in..."),this.updateState({isLoading:!0}),this.events.emit("auth:loading",!0);const e=this.buildLoginRequest(this.provider.getAuthority(),t);await this.msalInstance.loginRedirect(e)}async signInCityEmployee(t){if(this.assertInitialized(),this.log("Initiating city employee sign-in..."),!(this.provider instanceof c))return this.signIn(t);const e=this.provider.getSignInOnlyAuthority(),i=this.buildLoginRequest(e,t);await this.msalInstance.loginRedirect(i)}async signOut(t){this.assertInitialized(),this.log("Initiating sign-out...");const e={postLogoutRedirectUri:t?.postLogoutRedirectUri,authority:this.provider.getAuthority()};this.updateState({isLoading:!0}),this.events.emit("auth:signedOut",void 0),await this.msalInstance.logoutRedirect(e)}async forgotPassword(){if(this.assertInitialized(),!(this.provider instanceof c)){this.log("Forgot password is only supported for B2C providers");return}this.log("Initiating forgot password flow...");const t=this.provider.getResetPasswordAuthority();await this.msalInstance.loginRedirect({scopes:[],authority:t})}async acquireToken(t){this.assertInitialized(),this.log("Acquiring token...");const e=this._state.user;if(!e)return this.log("No account found, cannot acquire token"),null;const r={scopes:t?.scopes??this.provider.getApiScopes(),forceRefresh:t?.forceRefresh??!1,account:e,authority:this._state.activePolicy?this.provider.getAuthority(this._state.activePolicy):this.provider.getAuthority()};try{const o=await this.msalInstance.acquireTokenSilent(r);if(!o.accessToken)throw new n.InteractionRequiredAuthError("empty_token");return this.log("Token acquired silently"),this.updateState({token:o.accessToken,error:null}),this.events.emit("auth:tokenAcquired",o.accessToken),o.accessToken}catch(o){if(o instanceof n.InteractionRequiredAuthError){this.log("Silent token acquisition failed, falling back to redirect");try{return await this.msalInstance.acquireTokenRedirect(r),null}catch(d){return this.handleError(d),null}}return this.handleError(o),null}}selectAccount(t){const e=this.msalInstance.getAllAccounts();if(e.length===0){this.updateState({isAuthenticated:!1,user:null});return}let i=null;if(e.length===1)i=e[0];else if(t){const r=e.filter(o=>{const y=o.idTokenClaims?.iss??"",m=this.provider.getKnownAuthorities().some(E=>y.toUpperCase().includes(E.toUpperCase())),R=o.homeAccountId.toUpperCase().includes(t.toUpperCase());return m&&R});r.length>=1&&(i=r[0])}!i&&e.length>0&&(i=e[0]),i&&(this.log("Account selected",i.username),this.updateState({isAuthenticated:!0,user:i}))}async acquireTokenAfterRedirect(t){const e=this.msalInstance.getAccountByHomeId(t.account?.homeAccountId??"")??t.account??null;e&&this.updateState({isAuthenticated:!0,user:e}),await this.acquireToken(),this.updateState({isLoading:!1})}buildLoginRequest(t,e){return{scopes:e?.scopes??this.provider.getDefaultScopes(),authority:t,state:this.encodedState?`${this.encodedState}`:void 0,loginHint:e?.loginHint,domainHint:e?.domainHint}}isForgotPasswordPolicy(t){return!t||!(this.provider instanceof c)?!1:t.toUpperCase()===this.provider.getPolicies().resetPassword.toUpperCase()}handleRedirectError(t){return t.errorMessage?.includes(S.FORGOT_PASSWORD)?(this.log("Forgot password error detected, redirecting..."),this.forgotPassword(),null):(this.handleError(t),this.updateState({isLoading:!1}),null)}handleError(t){const e=t instanceof Error?t:new Error(String(t));this.log("Error:",e.message),this.updateState({error:e}),this.events.emit("auth:error",e)}updateState(t){this._state={...this._state,...t},this.events.emit("auth:stateChanged",this._state)}assertInitialized(){if(!this.msalInstance)throw new Error("SSOClient not initialized. Call initialize() first.")}log(...t){this.debug&&console.log("[sso-core]",...t)}}class O{type="ciam";config;constructor(t){this.config=t}buildMsalConfig(){return{auth:{clientId:this.config.clientId,authority:this.getAuthority(),knownAuthorities:this.getKnownAuthorities(),redirectUri:this.config.redirectUri,postLogoutRedirectUri:this.config.postLogoutRedirectUri??this.config.redirectUri,navigateToLoginRequestUrl:!1},cache:{cacheLocation:this.config.cacheLocation??"sessionStorage"}}}getAuthority(){return`https://${this.config.tenantSubdomain}.ciamlogin.com/`}getKnownAuthorities(){return[`${this.config.tenantSubdomain}.ciamlogin.com`]}identifyPolicy(t){return null}getDefaultScopes(){return["openid","profile"]}getApiScopes(){return this.config.scopes??[]}}class C{type="entra";config;constructor(t){this.config=t}buildMsalConfig(){return{auth:{clientId:this.config.clientId,authority:this.getAuthority(),knownAuthorities:this.getKnownAuthorities(),redirectUri:this.config.redirectUri,postLogoutRedirectUri:this.config.postLogoutRedirectUri??this.config.redirectUri,navigateToLoginRequestUrl:!1},cache:{cacheLocation:this.config.cacheLocation??"sessionStorage"}}}getAuthority(){return`https://login.microsoftonline.com/${this.config.tenantId}`}getKnownAuthorities(){return["login.microsoftonline.com"]}identifyPolicy(t){return null}getDefaultScopes(){return["openid","profile"]}getApiScopes(){return this.config.scopes??[]}}exports.B2CProvider=c;exports.CACHE_CONFIG=u;exports.CIAMProvider=O;exports.DEFAULT_POLICIES=a;exports.DEFAULT_SCOPES=l;exports.EntraProvider=C;exports.MSAL_ERROR_CODES=S;exports.SSOClient=_;exports.SSOEventEmitter=p;exports.STATE_SEPARATOR=h;exports.decodeState=A;exports.encodeState=f;exports.extractCustomState=I;
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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":["SSOEventEmitter","event","listener","set","data","DEFAULT_POLICIES","DEFAULT_SCOPES","CACHE_CONFIG","MSAL_ERROR_CODES","STATE_SEPARATOR","encodeState","stateObj","decodeState","encoded","extractCustomState","msalState","parts","customPart","B2CProvider","config","LogLevel","_level","message","policy","p","response","claims","INITIAL_STATE","SSOClient","msalConfig","PublicClientApplication","result","customState","authResponse","error","options","request","authority","logoutRequest","account","tokenRequest","InteractionRequiredAuthError","redirectError","accounts","selected","filtered","iss","matchesAuthority","auth","matchesPolicy","err","partial","args","CIAMProvider","_response","EntraProvider"],"mappings":"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"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import { LogLevel as A, PublicClientApplication as y, InteractionRequiredAuthError as l } from "@azure/msal-browser";
|
|
2
|
+
class m {
|
|
3
|
+
listeners = /* @__PURE__ */ new Map();
|
|
4
|
+
on(t, e) {
|
|
5
|
+
this.listeners.has(t) || this.listeners.set(t, /* @__PURE__ */ new Set());
|
|
6
|
+
const i = this.listeners.get(t);
|
|
7
|
+
return i.add(e), () => {
|
|
8
|
+
i.delete(e);
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
emit(t, e) {
|
|
12
|
+
const i = this.listeners.get(t);
|
|
13
|
+
if (i)
|
|
14
|
+
for (const o of i)
|
|
15
|
+
o(e);
|
|
16
|
+
}
|
|
17
|
+
removeAllListeners() {
|
|
18
|
+
this.listeners.clear();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const r = {
|
|
22
|
+
SIGN_UP_SIGN_IN: "B2C_1A_SIGNUP_SIGNIN",
|
|
23
|
+
SIGN_IN_ONLY: "B2C_1A_AD_SIGNIN_ONLY",
|
|
24
|
+
RESET_PASSWORD: "B2C_1A_PASSWORDRESET"
|
|
25
|
+
}, h = {
|
|
26
|
+
OPENID: "openid",
|
|
27
|
+
PROFILE: "profile",
|
|
28
|
+
OFFLINE_ACCESS: "offline_access"
|
|
29
|
+
}, u = {
|
|
30
|
+
LOCATION: "sessionStorage",
|
|
31
|
+
STORE_AUTH_STATE_IN_COOKIE: !1
|
|
32
|
+
}, R = {
|
|
33
|
+
USER_CANCELLED: "user_cancelled",
|
|
34
|
+
NO_CACHED_AUTHORITY: "no_cached_authority_error",
|
|
35
|
+
INTERACTION_REQUIRED: "interaction_required",
|
|
36
|
+
FORGOT_PASSWORD: "AADB2C90118"
|
|
37
|
+
}, d = "|";
|
|
38
|
+
function _(s) {
|
|
39
|
+
return btoa(JSON.stringify(s));
|
|
40
|
+
}
|
|
41
|
+
function E(s) {
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(atob(s));
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function L(s) {
|
|
49
|
+
if (!s) return null;
|
|
50
|
+
const t = s.split(d);
|
|
51
|
+
if (t.length < 2) return null;
|
|
52
|
+
const e = t.slice(1).join(d);
|
|
53
|
+
return E(e);
|
|
54
|
+
}
|
|
55
|
+
class a {
|
|
56
|
+
type = "b2c";
|
|
57
|
+
clientId;
|
|
58
|
+
b2cEnv;
|
|
59
|
+
authorityDomain;
|
|
60
|
+
redirectUri;
|
|
61
|
+
postLogoutRedirectUri;
|
|
62
|
+
policies;
|
|
63
|
+
apiScopes;
|
|
64
|
+
cacheLocation;
|
|
65
|
+
constructor(t) {
|
|
66
|
+
this.clientId = t.clientId, this.b2cEnv = t.b2cEnvironment, this.authorityDomain = t.authorityDomain, this.redirectUri = t.redirectUri, this.postLogoutRedirectUri = t.postLogoutRedirectUri ?? t.redirectUri, this.policies = {
|
|
67
|
+
signUpSignIn: t.policies?.signUpSignIn ?? r.SIGN_UP_SIGN_IN,
|
|
68
|
+
signInOnly: t.policies?.signInOnly ?? r.SIGN_IN_ONLY,
|
|
69
|
+
resetPassword: t.policies?.resetPassword ?? r.RESET_PASSWORD
|
|
70
|
+
}, this.apiScopes = t.apiScopes ?? [], this.cacheLocation = t.cacheLocation ?? u.LOCATION;
|
|
71
|
+
}
|
|
72
|
+
buildMsalConfig() {
|
|
73
|
+
return {
|
|
74
|
+
auth: {
|
|
75
|
+
clientId: this.clientId,
|
|
76
|
+
authority: this.getAuthority(this.policies.signUpSignIn),
|
|
77
|
+
knownAuthorities: this.getKnownAuthorities(),
|
|
78
|
+
redirectUri: this.redirectUri,
|
|
79
|
+
postLogoutRedirectUri: this.postLogoutRedirectUri,
|
|
80
|
+
navigateToLoginRequestUrl: !1
|
|
81
|
+
},
|
|
82
|
+
cache: {
|
|
83
|
+
cacheLocation: this.cacheLocation,
|
|
84
|
+
storeAuthStateInCookie: u.STORE_AUTH_STATE_IN_COOKIE
|
|
85
|
+
},
|
|
86
|
+
system: {
|
|
87
|
+
loggerOptions: {
|
|
88
|
+
logLevel: A.Warning,
|
|
89
|
+
loggerCallback: (t, e) => {
|
|
90
|
+
console.warn("[sso-core/b2c]", e);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
getAuthority(t) {
|
|
97
|
+
const e = t ?? this.policies.signUpSignIn;
|
|
98
|
+
return `https://${this.authorityDomain}/${this.b2cEnv}.onmicrosoft.com/${e}`;
|
|
99
|
+
}
|
|
100
|
+
getKnownAuthorities() {
|
|
101
|
+
return [this.authorityDomain];
|
|
102
|
+
}
|
|
103
|
+
identifyPolicy(t) {
|
|
104
|
+
const e = t.idTokenClaims;
|
|
105
|
+
return e ? (e.acr ?? e.tfp)?.toUpperCase() ?? null : null;
|
|
106
|
+
}
|
|
107
|
+
getDefaultScopes() {
|
|
108
|
+
return [h.OPENID, h.PROFILE];
|
|
109
|
+
}
|
|
110
|
+
getApiScopes() {
|
|
111
|
+
return this.apiScopes;
|
|
112
|
+
}
|
|
113
|
+
/** Get the authority URL for the sign-in-only (city employee) policy */
|
|
114
|
+
getSignInOnlyAuthority() {
|
|
115
|
+
return this.getAuthority(this.policies.signInOnly);
|
|
116
|
+
}
|
|
117
|
+
/** Get the authority URL for the password reset policy */
|
|
118
|
+
getResetPasswordAuthority() {
|
|
119
|
+
return this.getAuthority(this.policies.resetPassword);
|
|
120
|
+
}
|
|
121
|
+
/** Get all configured policy names */
|
|
122
|
+
getPolicies() {
|
|
123
|
+
return this.policies;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const g = {
|
|
127
|
+
isAuthenticated: !1,
|
|
128
|
+
isLoading: !1,
|
|
129
|
+
user: null,
|
|
130
|
+
token: null,
|
|
131
|
+
error: null,
|
|
132
|
+
activePolicy: null,
|
|
133
|
+
authReady: !1
|
|
134
|
+
};
|
|
135
|
+
class w {
|
|
136
|
+
events = new m();
|
|
137
|
+
provider;
|
|
138
|
+
debug;
|
|
139
|
+
encodedState;
|
|
140
|
+
msalInstance = null;
|
|
141
|
+
_state = { ...g };
|
|
142
|
+
constructor(t) {
|
|
143
|
+
this.provider = t.provider, this.debug = t.debug ?? !1, this.encodedState = t.state ? _(t.state) : null;
|
|
144
|
+
}
|
|
145
|
+
get state() {
|
|
146
|
+
return this._state;
|
|
147
|
+
}
|
|
148
|
+
// ── Lifecycle ──
|
|
149
|
+
async initialize() {
|
|
150
|
+
this.log("Initializing SSOClient..."), this.updateState({ isLoading: !0 });
|
|
151
|
+
const t = this.provider.buildMsalConfig();
|
|
152
|
+
this.msalInstance = new y(t), await this.msalInstance.initialize();
|
|
153
|
+
const e = await this.handleRedirect();
|
|
154
|
+
return this.updateState({ isLoading: !1, authReady: !0 }), e;
|
|
155
|
+
}
|
|
156
|
+
destroy() {
|
|
157
|
+
this.events.removeAllListeners(), this.msalInstance = null, this._state = { ...g };
|
|
158
|
+
}
|
|
159
|
+
// ── Auth Actions ──
|
|
160
|
+
async handleRedirect() {
|
|
161
|
+
this.assertInitialized(), this.log("Handling redirect promise...");
|
|
162
|
+
try {
|
|
163
|
+
const t = await this.msalInstance.handleRedirectPromise();
|
|
164
|
+
if (!t)
|
|
165
|
+
return this.selectAccount(null), null;
|
|
166
|
+
this.log("Redirect response received", t);
|
|
167
|
+
const e = L(t.state), i = this.provider.identifyPolicy(t);
|
|
168
|
+
if (this.updateState({ activePolicy: i }), this.isForgotPasswordPolicy(i))
|
|
169
|
+
return this.log("Forgot password flow completed"), this.events.emit("auth:forgotPassword", void 0), { ...t, customPostbackObject: e ?? void 0 };
|
|
170
|
+
this.updateState({ isLoading: !0 }), this.selectAccount(i), await this.acquireTokenAfterRedirect(t);
|
|
171
|
+
const o = {
|
|
172
|
+
...t,
|
|
173
|
+
customPostbackObject: e ?? void 0
|
|
174
|
+
};
|
|
175
|
+
return this.events.emit("auth:signedIn", o), o;
|
|
176
|
+
} catch (t) {
|
|
177
|
+
return this.handleRedirectError(t);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async signIn(t) {
|
|
181
|
+
this.assertInitialized(), this.log("Initiating sign-in..."), this.updateState({ isLoading: !0 }), this.events.emit("auth:loading", !0);
|
|
182
|
+
const e = this.buildLoginRequest(this.provider.getAuthority(), t);
|
|
183
|
+
await this.msalInstance.loginRedirect(e);
|
|
184
|
+
}
|
|
185
|
+
async signInCityEmployee(t) {
|
|
186
|
+
if (this.assertInitialized(), this.log("Initiating city employee sign-in..."), !(this.provider instanceof a))
|
|
187
|
+
return this.signIn(t);
|
|
188
|
+
const e = this.provider.getSignInOnlyAuthority(), i = this.buildLoginRequest(e, t);
|
|
189
|
+
await this.msalInstance.loginRedirect(i);
|
|
190
|
+
}
|
|
191
|
+
async signOut(t) {
|
|
192
|
+
this.assertInitialized(), this.log("Initiating sign-out...");
|
|
193
|
+
const e = {
|
|
194
|
+
postLogoutRedirectUri: t?.postLogoutRedirectUri,
|
|
195
|
+
authority: this.provider.getAuthority()
|
|
196
|
+
};
|
|
197
|
+
this.updateState({ isLoading: !0 }), this.events.emit("auth:signedOut", void 0), await this.msalInstance.logoutRedirect(e);
|
|
198
|
+
}
|
|
199
|
+
async forgotPassword() {
|
|
200
|
+
if (this.assertInitialized(), !(this.provider instanceof a)) {
|
|
201
|
+
this.log("Forgot password is only supported for B2C providers");
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
this.log("Initiating forgot password flow...");
|
|
205
|
+
const t = this.provider.getResetPasswordAuthority();
|
|
206
|
+
await this.msalInstance.loginRedirect({ scopes: [], authority: t });
|
|
207
|
+
}
|
|
208
|
+
async acquireToken(t) {
|
|
209
|
+
this.assertInitialized(), this.log("Acquiring token...");
|
|
210
|
+
const e = this._state.user;
|
|
211
|
+
if (!e)
|
|
212
|
+
return this.log("No account found, cannot acquire token"), null;
|
|
213
|
+
const o = {
|
|
214
|
+
scopes: t?.scopes ?? this.provider.getApiScopes(),
|
|
215
|
+
forceRefresh: t?.forceRefresh ?? !1,
|
|
216
|
+
account: e,
|
|
217
|
+
authority: this._state.activePolicy ? this.provider.getAuthority(this._state.activePolicy) : this.provider.getAuthority()
|
|
218
|
+
};
|
|
219
|
+
try {
|
|
220
|
+
const n = await this.msalInstance.acquireTokenSilent(o);
|
|
221
|
+
if (!n.accessToken)
|
|
222
|
+
throw new l("empty_token");
|
|
223
|
+
return this.log("Token acquired silently"), this.updateState({ token: n.accessToken, error: null }), this.events.emit("auth:tokenAcquired", n.accessToken), n.accessToken;
|
|
224
|
+
} catch (n) {
|
|
225
|
+
if (n instanceof l) {
|
|
226
|
+
this.log("Silent token acquisition failed, falling back to redirect");
|
|
227
|
+
try {
|
|
228
|
+
return await this.msalInstance.acquireTokenRedirect(o), null;
|
|
229
|
+
} catch (c) {
|
|
230
|
+
return this.handleError(c), null;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return this.handleError(n), null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// ── Internal Helpers ──
|
|
237
|
+
selectAccount(t) {
|
|
238
|
+
const e = this.msalInstance.getAllAccounts();
|
|
239
|
+
if (e.length === 0) {
|
|
240
|
+
this.updateState({ isAuthenticated: !1, user: null });
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
let i = null;
|
|
244
|
+
if (e.length === 1)
|
|
245
|
+
i = e[0];
|
|
246
|
+
else if (t) {
|
|
247
|
+
const o = e.filter((n) => {
|
|
248
|
+
const p = n.idTokenClaims?.iss ?? "", f = this.provider.getKnownAuthorities().some((I) => p.toUpperCase().includes(I.toUpperCase())), S = n.homeAccountId.toUpperCase().includes(t.toUpperCase());
|
|
249
|
+
return f && S;
|
|
250
|
+
});
|
|
251
|
+
o.length >= 1 && (i = o[0]);
|
|
252
|
+
}
|
|
253
|
+
!i && e.length > 0 && (i = e[0]), i && (this.log("Account selected", i.username), this.updateState({ isAuthenticated: !0, user: i }));
|
|
254
|
+
}
|
|
255
|
+
async acquireTokenAfterRedirect(t) {
|
|
256
|
+
const e = this.msalInstance.getAccountByHomeId(t.account?.homeAccountId ?? "") ?? t.account ?? null;
|
|
257
|
+
e && this.updateState({ isAuthenticated: !0, user: e }), await this.acquireToken(), this.updateState({ isLoading: !1 });
|
|
258
|
+
}
|
|
259
|
+
buildLoginRequest(t, e) {
|
|
260
|
+
return {
|
|
261
|
+
scopes: e?.scopes ?? this.provider.getDefaultScopes(),
|
|
262
|
+
authority: t,
|
|
263
|
+
state: this.encodedState ? `${this.encodedState}` : void 0,
|
|
264
|
+
loginHint: e?.loginHint,
|
|
265
|
+
domainHint: e?.domainHint
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
isForgotPasswordPolicy(t) {
|
|
269
|
+
return !t || !(this.provider instanceof a) ? !1 : t.toUpperCase() === this.provider.getPolicies().resetPassword.toUpperCase();
|
|
270
|
+
}
|
|
271
|
+
handleRedirectError(t) {
|
|
272
|
+
return t.errorMessage?.includes(R.FORGOT_PASSWORD) ? (this.log("Forgot password error detected, redirecting..."), this.forgotPassword(), null) : (this.handleError(t), this.updateState({ isLoading: !1 }), null);
|
|
273
|
+
}
|
|
274
|
+
handleError(t) {
|
|
275
|
+
const e = t instanceof Error ? t : new Error(String(t));
|
|
276
|
+
this.log("Error:", e.message), this.updateState({ error: e }), this.events.emit("auth:error", e);
|
|
277
|
+
}
|
|
278
|
+
updateState(t) {
|
|
279
|
+
this._state = { ...this._state, ...t }, this.events.emit("auth:stateChanged", this._state);
|
|
280
|
+
}
|
|
281
|
+
assertInitialized() {
|
|
282
|
+
if (!this.msalInstance)
|
|
283
|
+
throw new Error("SSOClient not initialized. Call initialize() first.");
|
|
284
|
+
}
|
|
285
|
+
log(...t) {
|
|
286
|
+
this.debug && console.log("[sso-core]", ...t);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
class C {
|
|
290
|
+
type = "ciam";
|
|
291
|
+
config;
|
|
292
|
+
constructor(t) {
|
|
293
|
+
this.config = t;
|
|
294
|
+
}
|
|
295
|
+
buildMsalConfig() {
|
|
296
|
+
return {
|
|
297
|
+
auth: {
|
|
298
|
+
clientId: this.config.clientId,
|
|
299
|
+
authority: this.getAuthority(),
|
|
300
|
+
knownAuthorities: this.getKnownAuthorities(),
|
|
301
|
+
redirectUri: this.config.redirectUri,
|
|
302
|
+
postLogoutRedirectUri: this.config.postLogoutRedirectUri ?? this.config.redirectUri,
|
|
303
|
+
navigateToLoginRequestUrl: !1
|
|
304
|
+
},
|
|
305
|
+
cache: {
|
|
306
|
+
cacheLocation: this.config.cacheLocation ?? "sessionStorage"
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
getAuthority() {
|
|
311
|
+
return `https://${this.config.tenantSubdomain}.ciamlogin.com/`;
|
|
312
|
+
}
|
|
313
|
+
getKnownAuthorities() {
|
|
314
|
+
return [`${this.config.tenantSubdomain}.ciamlogin.com`];
|
|
315
|
+
}
|
|
316
|
+
identifyPolicy(t) {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
getDefaultScopes() {
|
|
320
|
+
return ["openid", "profile"];
|
|
321
|
+
}
|
|
322
|
+
getApiScopes() {
|
|
323
|
+
return this.config.scopes ?? [];
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
class P {
|
|
327
|
+
type = "entra";
|
|
328
|
+
config;
|
|
329
|
+
constructor(t) {
|
|
330
|
+
this.config = t;
|
|
331
|
+
}
|
|
332
|
+
buildMsalConfig() {
|
|
333
|
+
return {
|
|
334
|
+
auth: {
|
|
335
|
+
clientId: this.config.clientId,
|
|
336
|
+
authority: this.getAuthority(),
|
|
337
|
+
knownAuthorities: this.getKnownAuthorities(),
|
|
338
|
+
redirectUri: this.config.redirectUri,
|
|
339
|
+
postLogoutRedirectUri: this.config.postLogoutRedirectUri ?? this.config.redirectUri,
|
|
340
|
+
navigateToLoginRequestUrl: !1
|
|
341
|
+
},
|
|
342
|
+
cache: {
|
|
343
|
+
cacheLocation: this.config.cacheLocation ?? "sessionStorage"
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
getAuthority() {
|
|
348
|
+
return `https://login.microsoftonline.com/${this.config.tenantId}`;
|
|
349
|
+
}
|
|
350
|
+
getKnownAuthorities() {
|
|
351
|
+
return ["login.microsoftonline.com"];
|
|
352
|
+
}
|
|
353
|
+
identifyPolicy(t) {
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
getDefaultScopes() {
|
|
357
|
+
return ["openid", "profile"];
|
|
358
|
+
}
|
|
359
|
+
getApiScopes() {
|
|
360
|
+
return this.config.scopes ?? [];
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
export {
|
|
364
|
+
a as B2CProvider,
|
|
365
|
+
u as CACHE_CONFIG,
|
|
366
|
+
C as CIAMProvider,
|
|
367
|
+
r as DEFAULT_POLICIES,
|
|
368
|
+
h as DEFAULT_SCOPES,
|
|
369
|
+
P as EntraProvider,
|
|
370
|
+
R as MSAL_ERROR_CODES,
|
|
371
|
+
w as SSOClient,
|
|
372
|
+
m as SSOEventEmitter,
|
|
373
|
+
d as STATE_SEPARATOR,
|
|
374
|
+
E as decodeState,
|
|
375
|
+
_ as encodeState,
|
|
376
|
+
L as extractCustomState
|
|
377
|
+
};
|
|
378
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +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":["SSOEventEmitter","event","listener","set","data","DEFAULT_POLICIES","DEFAULT_SCOPES","CACHE_CONFIG","MSAL_ERROR_CODES","STATE_SEPARATOR","encodeState","stateObj","decodeState","encoded","extractCustomState","msalState","parts","customPart","B2CProvider","config","LogLevel","_level","message","policy","p","response","claims","INITIAL_STATE","SSOClient","msalConfig","PublicClientApplication","result","customState","authResponse","error","options","request","authority","logoutRequest","account","tokenRequest","InteractionRequiredAuthError","redirectError","accounts","selected","filtered","iss","matchesAuthority","auth","matchesPolicy","err","partial","args","CIAMProvider","_response","EntraProvider"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@phila/sso-core",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Framework-agnostic SSO client for Azure AD B2C / Entra External ID",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"sso",
|
|
21
|
+
"msal",
|
|
22
|
+
"azure-ad-b2c",
|
|
23
|
+
"entra",
|
|
24
|
+
"authentication"
|
|
25
|
+
],
|
|
26
|
+
"author": "City of Philadelphia",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@azure/msal-browser": "^3.1.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^24.0.0",
|
|
33
|
+
"eslint": "^9.0.0",
|
|
34
|
+
"typescript": "^5.9.3",
|
|
35
|
+
"vite": "^7.0.6",
|
|
36
|
+
"vite-plugin-dts": "^3.9.1"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "vite build",
|
|
40
|
+
"dev": "vite build --watch",
|
|
41
|
+
"lint": "eslint src/**/*.ts",
|
|
42
|
+
"lint:fix": "eslint src/**/*.ts --fix",
|
|
43
|
+
"type-check": "tsc --noEmit",
|
|
44
|
+
"clean": "rm -rf dist",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"test:watch": "vitest",
|
|
47
|
+
"format": "prettier --write .",
|
|
48
|
+
"format:check": "prettier --check ."
|
|
49
|
+
}
|
|
50
|
+
}
|