@phila/sso-core 0.0.3 → 0.0.4
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 +194 -0
- package/package.json +1 -1
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
|