central-auth-js-sdk 1.0.10

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,386 @@
1
+ # Central Auth Welcome Flow SDK
2
+
3
+ Vanilla JavaScript signup and login flows — embeddable into any web application. No React, Angular, or Vue.
4
+
5
+ **Integrators:** See **[INTEGRATION_GUIDE.md](INTEGRATION_GUIDE.md)** for how to pull the SDK and use all functionalities (install, config, signup/login, callbacks, tokens, and a full example).
6
+
7
+ ## Distributing the SDK
8
+
9
+ **Simplest options:**
10
+
11
+ ### 1. **npm (recommended)**
12
+
13
+ Publish the `sdk` folder as its own package so integrators can install it with one command.
14
+
15
+ - Log in to npm (one-time): `npm login` (you need an account at [npmjs.com](https://www.npmjs.com/signup)).
16
+ - From the repo root, go to the SDK folder and publish:
17
+ ```bash
18
+ cd sdk
19
+ npm publish
20
+ ```
21
+ Use `npm publish --access public` if the package name is scoped (e.g. `@your-org/central-auth-sdk`).
22
+
23
+ - Integrators then:
24
+ ```bash
25
+ npm install central-auth-sdk
26
+ ```
27
+ ```javascript
28
+ import { startSignup, startLogin } from 'central-auth-sdk';
29
+ ```
30
+ They also need to load the CSS (e.g. in their app entry or HTML):
31
+ ```javascript
32
+ import 'central-auth-sdk/styles.css';
33
+ ```
34
+ or
35
+ ```html
36
+ <link rel="stylesheet" href="./node_modules/central-auth-sdk/styles.css" />
37
+ ```
38
+
39
+ **Pros:** Versioning, semver, one install command, works with any bundler.
40
+ **Cons:** Integrators need Node/npm (or equivalent).
41
+
42
+ ### 2. **Copy / embed (no build)**
43
+
44
+ Ship the SDK as source in a zip, Git repo, or copy the `sdk` folder into the parent app. The parent app points at the folder (e.g. Vite alias or relative path).
45
+
46
+ - Example (parent app has `sdk/` at repo root):
47
+ ```html
48
+ <link rel="stylesheet" href="/sdk/styles.css" />
49
+ <script type="module">
50
+ import { startLogin } from '/sdk/index.js';
51
+ startLogin({ container: '#auth', env: 'dev', loginMethods: { emailPassword: true } });
52
+ </script>
53
+ ```
54
+
55
+ **Pros:** No publish step, no npm; works with any dev server that serves the `sdk` folder.
56
+ **Cons:** No versioning via registry; updates are manual (replace files or pull from Git).
57
+
58
+ ### 3. **CDN / single-file bundle (optional)**
59
+
60
+ For environments without a bundler, you can add a build step that bundles the SDK into a single JS file (and keep `styles.css` separate). Then host it (or publish to npm and use unpkg/jsDelivr). This requires a small Vite/Rollup config that builds `sdk/index.js` into e.g. `dist/central-auth-sdk.js`. Many teams skip this and use option 1 or 2.
61
+
62
+ ---
63
+
64
+ ## Public API (recommended)
65
+
66
+ The SDK exposes **callable functions** so the host application can **initialize and trigger** Sign-Up or Login **programmatically** with **static configuration parameters**.
67
+
68
+ ### initializeAuth(config)
69
+
70
+ Call once (e.g. at app load) to set **default configuration**. Optional; you can pass all config when calling `startSignup` / `startLogin` instead.
71
+
72
+ | Parameter | Type | Default | Description |
73
+ |-----------|------|---------|-------------|
74
+ | `env` | string | `'local'` | Environment: `'local'` \| `'dev'` \| `'staging'` \| `'prod'`. SDK uses the predefined API URL for that env. |
75
+ | `appIdentifier` | string | `'control-center'` | App identifier for OAuth and custom login |
76
+ | `defaultContainer` | string \| HTMLElement | — | Default container for rendering (can override in `startSignup` / `startLogin`) |
77
+ | `loginMethods` | object | — | Default enabled login methods (see Login config below) |
78
+ | `frsAsSecondFactor` | boolean | `false` | Default: require face verification after primary auth |
79
+ | `enableFaceRegistration` | boolean | `true` | Whether sign-up flow includes face registration step |
80
+
81
+ **Returns:** `{ startSignup, startLogin, getConfig }`
82
+
83
+ ### startSignup(config)
84
+
85
+ Starts the **Sign-Up flow** in the given container. Configuration is **static** (passed when triggering, not collected from the user).
86
+
87
+ | Parameter | Type | Required | Description |
88
+ |-----------|------|----------|-------------|
89
+ | `container` | string \| HTMLElement | yes* | DOM selector or element where the SDK renders (*or set `defaultContainer` in `initializeAuth`) |
90
+ | `env` | string | no | Environment: `'local'` \| `'dev'` \| `'staging'` \| `'prod'` (default from init or `'local'`) |
91
+ | `appIdentifier` | string | no | Override app identifier |
92
+ | `enableFaceRegistration` | boolean | no | Include face registration step (default from init or `true`) |
93
+
94
+ **Returns:** `{ unmount, getState, getFlow, getCurrentStep }` or `null` if container invalid.
95
+
96
+ ### startLogin(config)
97
+
98
+ Starts the **Login flow** in the given container with **configurable login methods**.
99
+
100
+ | Parameter | Type | Required | Description |
101
+ |-----------|------|----------|-------------|
102
+ | `container` | string \| HTMLElement | yes* | DOM selector or element where the SDK renders |
103
+ | `env` | string | no | Environment: `'local'` \| `'dev'` \| `'staging'` \| `'prod'` (default from init or `'local'`) |
104
+ | `appIdentifier` | string | no | Override app identifier |
105
+ | `loginMethods` | object | no | Which login methods are enabled (see below) |
106
+ | `frsAsSecondFactor` | boolean | no | Require face verification after primary auth |
107
+ | `onSignUp` | function | no | Callback when user clicks “Sign up” (e.g. switch to signup) |
108
+
109
+ **Enabled login methods** (`loginMethods`):
110
+
111
+ - `emailPassword` — Email + Password
112
+ - `mobilePassword` — Mobile + Password
113
+ - `emailOtp` — Email + OTP
114
+ - `mobileOtp` — Mobile + OTP
115
+ - `social.google` / `social.microsoft` — Social login
116
+ - `emailFrs` — Email + Face (FRS only, no password/OTP)
117
+
118
+ **Returns:** `{ unmount, getState, getFlow, getCurrentStep }` or `null` if container invalid.
119
+
120
+ ### Theming (optional)
121
+
122
+ Pass a `theme` object to match the SDK to your app's look. All properties are optional — omit any to keep the SDK default.
123
+
124
+ ```javascript
125
+ startSignup({
126
+ container: '#auth-container',
127
+ env: 'dev',
128
+ theme: {
129
+ primaryColor: '#2563EB', // buttons, links, accent
130
+ backgroundColor: '#EFF6FF', // page background
131
+ backgroundImage: 'https://…', // optional full-bleed image
132
+ cardColor: '#FFFFFF', // card panel
133
+ textColor: '#1A1714', // primary text
134
+ fontFamily: '"DM Sans", sans-serif',
135
+ mode: 'auto', // 'light' | 'dark' | 'auto'
136
+ },
137
+ });
138
+ ```
139
+
140
+ Built-in presets: `import { THEME_PRESETS } from 'central-auth-sdk'` — keys: `default`, `dark`, `ocean`, `forest`, `purple`, `rose`, `midnight`.
141
+
142
+ See [INTEGRATION_GUIDE.md](INTEGRATION_GUIDE.md) § 5.1 for full details.
143
+
144
+ ### Example: initialize once, then trigger flows
145
+
146
+ ```html
147
+ <div id="auth-container"></div>
148
+ <link rel="stylesheet" href="/path/to/sdk/styles.css" />
149
+ <script type="module">
150
+ import { initializeAuth, startSignup, startLogin } from '/path/to/sdk/index.js';
151
+
152
+ const auth = initializeAuth({
153
+ env: 'dev',
154
+ appIdentifier: 'my-app',
155
+ defaultContainer: '#auth-container',
156
+ loginMethods: {
157
+ emailPassword: true,
158
+ emailOtp: true,
159
+ social: { google: true, microsoft: true },
160
+ emailFrs: false,
161
+ },
162
+ frsAsSecondFactor: true,
163
+ });
164
+
165
+ let currentInstance = null;
166
+
167
+ function showSignup() {
168
+ if (currentInstance) currentInstance.unmount();
169
+ currentInstance = auth.startSignup({}); // uses defaultContainer + init config
170
+ }
171
+
172
+ function showLogin() {
173
+ if (currentInstance) currentInstance.unmount();
174
+ currentInstance = auth.startLogin({
175
+ onSignUp: showSignup,
176
+ });
177
+ }
178
+
179
+ showLogin(); // or showSignup()
180
+ </script>
181
+ ```
182
+
183
+ ### Example: trigger without initializeAuth
184
+
185
+ You can pass **all configuration** when starting a flow (no need to call `initializeAuth`):
186
+
187
+ ```javascript
188
+ import { startSignup, startLogin } from '/path/to/sdk/index.js';
189
+
190
+ const signupInstance = startSignup({
191
+ container: '#auth-container',
192
+ env: 'dev',
193
+ appIdentifier: 'my-app',
194
+ });
195
+
196
+ const loginInstance = startLogin({
197
+ container: '#auth-container',
198
+ env: 'dev',
199
+ appIdentifier: 'my-app',
200
+ loginMethods: {
201
+ emailPassword: true,
202
+ mobilePassword: true,
203
+ emailOtp: true,
204
+ mobileOtp: true,
205
+ social: { google: true, microsoft: true },
206
+ emailFrs: true,
207
+ },
208
+ frsAsSecondFactor: false,
209
+ onSignUp() {
210
+ loginInstance.unmount();
211
+ startSignup({ container: '#auth-container', env: 'dev' });
212
+ },
213
+ });
214
+ ```
215
+
216
+ ### Sending signup result (token + details) back to the parent app
217
+
218
+ When signup **completes** (after the final step, e.g. face registration), the SDK gives the parent app the result in two ways:
219
+
220
+ 1. **`onSignupComplete(payload)`** — Pass this callback when starting signup. The SDK calls it **once** when signup finishes, with a single `payload` object that includes:
221
+ - `email`, `mobile`, `userId` (from the flow)
222
+ - Any fields returned by the **final API** call (e.g. `access_token`, `refresh_token`, `user_id`) — the backend can return whatever it needs here.
223
+
224
+ 2. **`instance.getState()`** — At any time (including on the success screen), the parent can call `getState()` to read current flow data. After completion, state also includes `signupResult` (the raw final API response).
225
+
226
+ **Example:**
227
+
228
+ ```javascript
229
+ const instance = startSignup({
230
+ container: '#auth-container',
231
+ env: 'dev',
232
+ appIdentifier: 'my-app',
233
+ onSignupComplete(payload) {
234
+ // payload.email, payload.mobile, payload.userId
235
+ // payload.access_token, payload.refresh_token (if your API returns them)
236
+ console.log('Signup complete', payload);
237
+ myApp.saveSession(payload.access_token);
238
+ instance.unmount();
239
+ myApp.redirectToDashboard();
240
+ },
241
+ onContinue() {
242
+ // User clicked "Go to Dashboard" / "Back to Home" on the success screen
243
+ instance.unmount();
244
+ myApp.redirectToDashboard();
245
+ },
246
+ });
247
+ ```
248
+
249
+ The backend should return token (and any other session data) in the **final signup API response** (e.g. `POST /signup/fr/enroll` or the social FR enroll endpoint). The SDK forwards that response into `payload` and into `getState().signupResult`.
250
+
251
+ ### Sending login result (token + details) back to the parent app
252
+
253
+ When **login completes** (after the final successful API call — password, OTP, or FRS), the SDK gives the parent app the result in the same way as signup:
254
+
255
+ 1. **`onLoginComplete(payload)`** — Pass this callback when starting login. The SDK calls it **once** when login finishes, with a single `payload` object that includes:
256
+ - `identifier` (email or mobile used to sign in)
257
+ - Any fields returned by the **final API** call (e.g. `access_token`, `refresh_token`, `user_id`)
258
+
259
+ 2. **`instance.getState()`** — After completion, state includes `loginResult` (the raw final API response).
260
+
261
+ 3. **`onContinue()`** — Called when the user clicks **Continue** on the login success screen (e.g. to unmount and redirect).
262
+
263
+ **Example:**
264
+
265
+ ```javascript
266
+ const instance = startLogin({
267
+ container: '#auth-container',
268
+ env: 'dev',
269
+ appIdentifier: 'my-app',
270
+ loginMethods: { emailPassword: true },
271
+ onLoginComplete(payload) {
272
+ // payload.identifier, payload.access_token, payload.refresh_token (if API returns them)
273
+ myApp.saveSession(payload.access_token);
274
+ instance.unmount();
275
+ myApp.redirectToApp();
276
+ },
277
+ onContinue() {
278
+ instance.unmount();
279
+ myApp.redirectToApp();
280
+ },
281
+ onSignUp() { /* switch to signup */ },
282
+ });
283
+ ```
284
+
285
+ The backend should return token (and any session data) in the **final login API response** (e.g. `POST /login/custom`, `POST /otp/verify`, `POST /login/email/fr`, or social FR enroll). The SDK forwards that response into `payload` and into `getState().loginResult`.
286
+
287
+ ### Instance API (returned by startSignup / startLogin)
288
+
289
+ ```javascript
290
+ instance.unmount() // Remove the flow from DOM
291
+ instance.getState() // Read form data (email, mobile, userId, signupResult / loginResult, etc.)
292
+ instance.getCurrentStep() // Current step name
293
+ instance.getFlow() // FlowController reference
294
+ ```
295
+
296
+ ---
297
+
298
+ ## Architecture
299
+
300
+ - **UI layer** (`sdk/ui/`) — Screens and layout, pure DOM
301
+ - **Core layer** (`sdk/core/`) — Flow, state, events; ready for API integration
302
+ - **Separation of concerns** — UI emits actions; core handles logic; APIs plug in later
303
+
304
+ ## Lower-level embedding (mount / mountLogin)
305
+
306
+ If you prefer not to use `initializeAuth` / `startSignup` / `startLogin`, you can use the same underlying functions:
307
+
308
+ ### 1. Include styles and mount
309
+
310
+ ```html
311
+ <div id="signup-container"></div>
312
+ <link rel="stylesheet" href="/path/to/sdk/styles.css" />
313
+ <script type="module">
314
+ import { mount } from '/path/to/sdk/index.js';
315
+ const instance = mount('#signup-container');
316
+ </script>
317
+ ```
318
+
319
+ ### 2. Mount options
320
+
321
+ ```javascript
322
+ mount('#signup', {
323
+ env: 'local',
324
+ appIdentifier: 'control-center', // for social OAuth
325
+ });
326
+ ```
327
+
328
+ Entity is derived from email in the backend; no need to pass entity name/domain.
329
+
330
+ ### 3. Login flow (lower-level)
331
+
332
+ ```javascript
333
+ import { mountLogin } from '/path/to/sdk/index.js';
334
+
335
+ const loginInstance = mountLogin('#login-container', {
336
+ env: 'local',
337
+ appIdentifier: 'control-center',
338
+ loginMethods: { emailPassword: true, /* ... */ },
339
+ frsAsSecondFactor: true,
340
+ onSignUp() { /* switch to signup */ },
341
+ });
342
+ ```
343
+
344
+ ### 4. Integration with existing apps
345
+
346
+ - Use any container selector or `HTMLElement`
347
+ - No global pollution; all logic is scoped
348
+ - Styles use `wf-` prefix to avoid conflicts
349
+
350
+ ## UI design rules
351
+
352
+ When adding or changing screens, follow **[DESIGN_RULES.md](DESIGN_RULES.md)** for tokens, typography, spacing, component patterns, and a new-screen checklist.
353
+
354
+ ## File structure
355
+
356
+ ```
357
+ sdk/
358
+ ├── core/
359
+ │ ├── api.js # Central Auth API client (central_auth_collection.json)
360
+ │ ├── flow.js # Step navigation, flow type (standard/social)
361
+ │ ├── state.js # Form data
362
+ │ ├── login-config.js
363
+ │ ├── login-flow.js
364
+ │ ├── login-state.js
365
+ │ └── events.js # Event bus (optional)
366
+ ├── ui/
367
+ │ ├── layout.js
368
+ │ ├── components.js
369
+ │ ├── screens/
370
+ │ │ ├── (signup screens)
371
+ │ │ └── login/
372
+ │ │ ├── entry.js, identifier-email.js, identifier-mobile.js
373
+ │ │ ├── password.js, otp.js, frs.js, success.js
374
+ │ │ └── index.js
375
+ │ └── screens/index.js
376
+ ├── index.js # Main entry: initializeAuth, startSignup, startLogin, mount, mountLogin
377
+ ├── auth.js # Public API: initializeAuth, startSignup, startLogin
378
+ ├── signup.js # Sign-up flow: mount()
379
+ ├── login.js # Login flow: mountLogin()
380
+ ├── styles.css
381
+ └── README.md
382
+ ```
383
+
384
+ ## API integration
385
+
386
+ APIs from `central_auth_collection.json` are wired in `core/api.js`. See [API_INTEGRATION.md](API_INTEGRATION.md) for endpoint mapping, options (`entityName`, `entityDomain`), and response assumptions (`user_id` from email verify).
package/auth.js ADDED
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Central Auth SDK — Public API for host applications
3
+ * Exposes initializeAuth, startSignup, startLogin with configurable static parameters.
4
+ */
5
+
6
+ import { resolveApiBaseUrl } from './core/env-config.js';
7
+ import { mount } from './signup.js';
8
+ import { mountLogin } from './login.js';
9
+
10
+ /** Default configuration stored after initializeAuth() */
11
+ let defaultConfig = {};
12
+
13
+ /**
14
+ * Merge config with defaults. Passed config overrides defaults.
15
+ * @param {Object} overrides - config passed to startSignup / startLogin
16
+ * @returns {Object} merged config
17
+ */
18
+ function mergeConfig(overrides = {}) {
19
+ return {
20
+ ...defaultConfig,
21
+ ...overrides,
22
+ loginMethods: overrides.loginMethods != null
23
+ ? { ...defaultConfig.loginMethods, ...overrides.loginMethods }
24
+ : defaultConfig.loginMethods,
25
+ social: overrides.social != null || (overrides.loginMethods && overrides.loginMethods.social != null)
26
+ ? { ...defaultConfig.social, ...(overrides.social || overrides.loginMethods?.social || {}) }
27
+ : defaultConfig.social,
28
+ theme: overrides.theme != null
29
+ ? { ...defaultConfig.theme, ...overrides.theme }
30
+ : defaultConfig.theme,
31
+ };
32
+ }
33
+
34
+ /**
35
+ * Resolve container: string selector or HTMLElement. Throws if missing/invalid.
36
+ * @param {string|HTMLElement} container
37
+ * @returns {HTMLElement}
38
+ */
39
+ function resolveContainer(container) {
40
+ if (container == null) return null;
41
+ const el = typeof container === 'string' ? document.querySelector(container) : container;
42
+ return el && el.nodeType === 1 ? el : null;
43
+ }
44
+
45
+ /**
46
+ * Initialize the SDK with optional default configuration.
47
+ * Call this once (e.g. at app load) to set shared defaults; startSignup/startLogin can override per call.
48
+ *
49
+ * @param {Object} config - Default static configuration
50
+ * @param {string} [config.env='local'] - Environment: 'local' | 'dev' | 'staging' | 'prod' — SDK uses the predefined API URL for that env
51
+ * @param {string} [config.appIdentifier] - Optional app identifier for OAuth / custom login; only sent when provided
52
+ * @param {string|HTMLElement} [config.defaultContainer] - Default container for rendering (can be overridden in start*)
53
+ * @param {Object} [config.loginMethods] - Default enabled login methods (see startLogin)
54
+ * @param {boolean} [config.frsAsSecondFactor] - Default for FRS as second factor (see startLogin)
55
+ * @param {boolean} [config.enableFaceRegistration=true] - Whether sign-up flow includes face registration step
56
+ * @param {Object} [config.theme] - Optional theme overrides (primaryColor, secondaryColor, backgroundColor, backgroundImage, cardColor, textColor, fontFamily, mode)
57
+ * @returns {{ startSignup: Function, startLogin: Function, getConfig: Function }}
58
+ */
59
+ export function initializeAuth(config = {}) {
60
+ defaultConfig = {
61
+ env: 'local',
62
+ appIdentifier: undefined,
63
+ defaultContainer: null,
64
+ loginMethods: null,
65
+ frsAsSecondFactor: false,
66
+ enableFaceRegistration: true,
67
+ theme: null,
68
+ ...config,
69
+ };
70
+ return {
71
+ startSignup: startSignup,
72
+ startLogin: startLogin,
73
+ getConfig() {
74
+ return { ...defaultConfig };
75
+ },
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Start the Sign-Up flow. Renders into the given container and runs the signup wizard.
81
+ *
82
+ * @param {Object} config - Static configuration for this flow (merged with initializeAuth defaults)
83
+ * @param {string|HTMLElement} config.container - DOM selector or element where the SDK will render (required)
84
+ * @param {string} [config.env] - Environment: 'local' | 'dev' | 'staging' | 'prod' (default from init or 'local')
85
+ * @param {string} [config.appIdentifier] - Override app identifier
86
+ * @param {boolean} [config.enableFaceRegistration] - Include face registration step (default from init or true)
87
+ * @param {Function} [config.onSignupComplete] - Called when signup completes with (payload). Payload: email, mobile, userId, plus any token/fields from the final API response (e.g. access_token, refresh_token).
88
+ * @param {Function} [config.onContinue] - Called when user clicks Continue on the success screen (e.g. unmount and redirect).
89
+ * @param {Function} [config.onSwitchToLogin] - Called when user clicks "Sign in" (e.g. unmount signup and start login, e.g. login + FRS).
90
+ * @param {Function} [config.onBack] - Called when user clicks Back on the first screen (e.g. unmount and return to parent app).
91
+ * @param {Object} [config.theme] - Optional theme overrides (primaryColor, secondaryColor, backgroundColor, backgroundImage, cardColor, textColor, fontFamily, mode)
92
+ * @returns {{ unmount: Function, getState: Function, getFlow: Function, getCurrentStep: Function }|null} SDK instance or null if container invalid
93
+ */
94
+ export function startSignup(config = {}) {
95
+ const merged = mergeConfig(config);
96
+ const container = config.container ?? merged.defaultContainer;
97
+ const el = resolveContainer(container);
98
+ if (!el) {
99
+ console.error('[Central Auth SDK] startSignup: container is required (pass config.container or set defaultContainer in initializeAuth)');
100
+ return null;
101
+ }
102
+ const options = {
103
+ apiBaseUrl: resolveApiBaseUrl(merged),
104
+ appIdentifier: merged.appIdentifier,
105
+ enableFaceRegistration: merged.enableFaceRegistration,
106
+ theme: merged.theme || undefined,
107
+ onSignupComplete: config.onSignupComplete ?? merged.onSignupComplete,
108
+ onContinue: config.onContinue ?? merged.onContinue,
109
+ onSwitchToLogin: config.onSwitchToLogin ?? merged.onSwitchToLogin,
110
+ onBack: config.onBack ?? merged.onBack,
111
+ };
112
+ return mount(el, options);
113
+ }
114
+
115
+ /**
116
+ * Start the Login flow. Renders into the given container with configurable login methods.
117
+ *
118
+ * @param {Object} config - Static configuration for this flow (merged with initializeAuth defaults)
119
+ * @param {string|HTMLElement} config.container - DOM selector or element where the SDK will render (required)
120
+ * @param {string} [config.env] - Environment: 'local' | 'dev' | 'staging' | 'prod' (default from init or 'local')
121
+ * @param {string} [config.appIdentifier] - Override app identifier
122
+ * @param {Object} [config.loginMethods] - Which login methods are enabled
123
+ * @param {boolean} [config.loginMethods.emailPassword] - Email + Password
124
+ * @param {boolean} [config.loginMethods.mobilePassword] - Mobile + Password
125
+ * @param {boolean} [config.loginMethods.emailOtp] - Email + OTP
126
+ * @param {boolean} [config.loginMethods.mobileOtp] - Mobile + OTP
127
+ * @param {Object} [config.loginMethods.social] - Social login
128
+ * @param {boolean} [config.loginMethods.social.google] - Google
129
+ * @param {boolean} [config.loginMethods.social.microsoft] - Microsoft
130
+ * @param {boolean} [config.loginMethods.emailFrs] - Email + Face (FRS only)
131
+ * @param {boolean} [config.frsAsSecondFactor] - Require face verification after primary auth
132
+ * @param {Function} [config.onSignUp] - Callback when user clicks "Sign up" (e.g. switch to signup flow)
133
+ * @param {Function} [config.onLoginComplete] - Called when login completes with (payload). Payload: identifier, plus any token/fields from the final API response (e.g. access_token, refresh_token).
134
+ * @param {Function} [config.onContinue] - Called when user clicks Continue on the success screen (e.g. unmount and redirect).
135
+ * @param {Function} [config.onBack] - Called when user clicks Back on the first screen (e.g. unmount and return to parent app).
136
+ * @param {Object} [config.theme] - Optional theme overrides (primaryColor, secondaryColor, backgroundColor, backgroundImage, cardColor, textColor, fontFamily, mode)
137
+ * @returns {{ unmount: Function, getState: Function, getFlow: Function, getCurrentStep: Function }|null} SDK instance or null if container invalid
138
+ */
139
+ export function startLogin(config = {}) {
140
+ const merged = mergeConfig(config);
141
+ const container = config.container ?? merged.defaultContainer;
142
+ const el = resolveContainer(container);
143
+ if (!el) {
144
+ console.error('[Central Auth SDK] startLogin: container is required (pass config.container or set defaultContainer in initializeAuth)');
145
+ return null;
146
+ }
147
+ const options = {
148
+ apiBaseUrl: resolveApiBaseUrl(merged),
149
+ appIdentifier: merged.appIdentifier,
150
+ loginMethods: merged.loginMethods,
151
+ frsAsSecondFactor: merged.frsAsSecondFactor,
152
+ theme: merged.theme || undefined,
153
+ onSignUp: config.onSignUp ?? merged.onSignUp,
154
+ onLoginComplete: config.onLoginComplete ?? merged.onLoginComplete,
155
+ onContinue: config.onContinue ?? merged.onContinue,
156
+ onBack: config.onBack ?? merged.onBack,
157
+ };
158
+ return mountLogin(el, options);
159
+ }