@luminaryworks/auth-react 0.3.2 → 0.4.2

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 CHANGED
@@ -24,7 +24,7 @@ On first 401: try refresh → else open reauth UI (overlay + OIDC **popup**, not
24
24
 
25
25
  ## Headless login panel
26
26
 
27
- Branded panel with **social buttons** (auto-loaded from IdP Experience `socialConnectors` — google / github / x / …) plus **unified account** password. Social uses `direct_sign_in=social:<target>`. Buttons are hidden when the IdP has no social connectors (do not invent Google/GitHub — that dumped users on Logto `/sign-in`).
27
+ Branded panel with **social buttons** (auto-loaded from IdP Experience `socialConnectors` — google / github / x / …) plus **unified account** password. The default `LogtoExperienceAdapter` keeps Logto's `direct_sign_in=social:<target>` parameter inside provider-specific code. Buttons are hidden when the IdP has no social connectors (do not invent Google/GitHub — that dumped users on Logto `/sign-in`).
28
28
 
29
29
  Styles ship as **CSS Modules (SCSS)**. The built bundle auto-injects panel CSS in the browser. Optional explicit import (SSR / style control):
30
30
 
@@ -56,9 +56,46 @@ Override layout via `className` / `style` on the root.
56
56
  |------|---------|--------|
57
57
  | `showSocialConnectors` | `true` | `false` skips fetch and hides divider + social buttons |
58
58
  | `socialProviders` | `"auto"` | allowlist, or `[]` (same effect as `showSocialConnectors={false}`) |
59
+ | `experienceAdapter` | catalog default | optional custom `LoginExperienceAdapter`; otherwise `config.iamProvider` |
60
+ | `config.iamProvider` | `logto` | `logto` Headless; `oidc` / `zitadel` Hosted Redirect |
59
61
 
60
62
  IdP hosted `/sign-in` social row layout: `node scripts/apply-branding.mjs` (customCss wrap). Enable connectors: `ensure-sign-in-experience.mjs` + `verify-social-direct-signin.mjs`.
61
63
 
64
+ ### Login experience adapters
65
+
66
+ `HeadlessLoginPanel` delegates non-standard password and social-connector flows
67
+ to `LoginExperienceAdapter`; standard OIDC authorization code + PKCE remains in
68
+ the OIDC client. Adapter methods are optional and are used only when both the
69
+ matching capability and method are present. Without password support, the panel
70
+ renders `labels.submitSso` and starts a standard hosted OIDC flow. Built-in
71
+ factory ids: `logto` (default Headless), `hosted` / `oidc` / `zitadel` (Hosted
72
+ Redirect). ZITADEL is a reserved plugin — there is no empty Experience stub:
73
+
74
+ ```ts
75
+ import {
76
+ createLoginExperienceAdapter,
77
+ HostedOidcExperienceAdapter,
78
+ LogtoExperienceAdapter,
79
+ resolveLoginExperienceAdapter,
80
+ type LoginExperienceAdapter,
81
+ type LoginExperienceCapability,
82
+ } from "@luminaryworks/auth-react";
83
+
84
+ const logto = new LogtoExperienceAdapter();
85
+ const sameDefault = createLoginExperienceAdapter("logto");
86
+ const resolved = resolveLoginExperienceAdapter(logto);
87
+ const zitadelLogin = createLoginExperienceAdapter("zitadel"); // HostedOidcExperienceAdapter
88
+
89
+ // Hosted-only enterprise IdP: no Experience API methods are required.
90
+ const hostedOnly = {
91
+ provider: "enterprise",
92
+ capabilities: [],
93
+ } satisfies LoginExperienceAdapter;
94
+ ```
95
+
96
+ Existing `experiencePasswordSignIn` and `fetchSocialConnectors` imports remain
97
+ available as compatibility functions backed by the default Logto adapter.
98
+
62
99
  ## Popup callback
63
100
 
64
101
  Callback route must detect popup windows and call `handleSignInPopupCallback` (do not SSO-exchange inside the popup).
package/dist/index.d.ts CHANGED
@@ -27,6 +27,11 @@ interface LuminaryIdpConfig {
27
27
  * When set, Headless password sign-in is attempted before OIDC popup fallback.
28
28
  */
29
29
  experienceApiBase?: string;
30
+ /**
31
+ * IAM catalog id: `logto` (default Headless), `oidc` / `zitadel` (Hosted Redirect).
32
+ * Ignored when `HeadlessLoginPanel` receives an explicit `experienceAdapter`.
33
+ */
34
+ iamProvider?: string;
30
35
  }
31
36
  interface LuminaryAuthSession {
32
37
  accessToken: string;
@@ -45,9 +50,12 @@ declare function createUserManager(config: LuminaryIdpConfig): UserManager;
45
50
  declare function resetUserManager(): void;
46
51
  interface SignInOptions {
47
52
  returnUrl?: string;
53
+ /** Provider-specific authorize parameters supplied by an experience adapter. */
54
+ extraQueryParams?: Record<string, string>;
48
55
  /**
49
56
  * Logto direct sign-in, e.g. `social:google` / `social:github` / `sso:<connectorId>`.
50
57
  * Skips the hosted password page and opens the provider immediately.
58
+ * @deprecated Prefer a LoginExperienceAdapter, which supplies extraQueryParams.
51
59
  */
52
60
  directSignIn?: string;
53
61
  }
@@ -83,10 +91,17 @@ declare function clearStoredSession(config: LuminaryIdpConfig): void;
83
91
  declare function getCurrentUser(config: LuminaryIdpConfig): Promise<LuminaryAuthSession | null>;
84
92
 
85
93
  /**
86
- * Logto Experience API (Headless) client.
87
- * Prefer same-origin Experience base (SPA proxies /api/experience) so cookies work on HTTP localhost.
88
- * @see https://docs.logto.io/docs/recipes/customize-token-claims (Experience API recipes)
94
+ * Capabilities exposed by a headless login-experience provider.
95
+ *
96
+ * Consumers can use these flags to avoid assuming that every provider supports
97
+ * Logto's password, connector-discovery, or direct social-login flows.
89
98
  */
99
+ declare const LOGIN_EXPERIENCE_CAPABILITIES: {
100
+ readonly passwordSignIn: "password-sign-in";
101
+ readonly socialConnectors: "social-connectors";
102
+ readonly socialDirectSignIn: "social-direct-sign-in";
103
+ };
104
+ type LoginExperienceCapability = (typeof LOGIN_EXPERIENCE_CAPABILITIES)[keyof typeof LOGIN_EXPERIENCE_CAPABILITIES];
90
105
  type ExperienceIdentifierType = "email" | "username" | "phone";
91
106
  interface ExperiencePasswordSignInInput {
92
107
  /** Experience API origin (no trailing slash). Prefer SPA origin when proxied. */
@@ -108,39 +123,91 @@ interface ExperiencePasswordSignInResult {
108
123
  redirectTo?: string;
109
124
  raw?: unknown;
110
125
  }
111
- /**
112
- * Force authorize onto the Experience/SPA origin so Set-Cookie lands on the same
113
- * host as subsequent `/api/experience` calls (dev proxy strips Domain).
114
- */
115
- declare function sameOriginAuthorizeUrl(authorizeUrl: string, apiBase: string): string;
116
- /**
117
- * Headless password sign-in via Logto Experience API.
118
- * Requires an OIDC interaction cookie (see {@link bootstrapOidcInteraction}) and
119
- * same-site Experience calls (SPA proxy or Auth Gateway on the SPA origin).
120
- *
121
- * On success, follow {@link ExperiencePasswordSignInResult.redirectTo} in the same
122
- * window — that URL completes the PKCE round-trip started by createSigninRequest.
123
- * Do not start a second authorize (that shows Logto `/sign-in` again).
124
- *
125
- * Tries the guessed identifier type first, then the alternate email/username so
126
- * users can sign in with either when both methods are enabled on the IdP.
127
- */
128
- declare function experiencePasswordSignIn(input: ExperiencePasswordSignInInput): Promise<ExperiencePasswordSignInResult>;
129
126
  interface ExperienceSocialConnector {
130
127
  id: string;
131
128
  target: string;
132
129
  name: string;
133
130
  logo?: string;
134
131
  }
135
- /**
136
- * Public Logto sign-in experience (no auth). Returns enabled social connectors
137
- * in SIE order — use for Headless social buttons (google / github / x / …).
138
- */
139
- declare function fetchSocialConnectors(input: {
132
+ interface FetchSocialConnectorsInput {
140
133
  /** Same origin as Experience / IdP (e.g. SPA proxy or http://localhost:3001). */
141
134
  apiBase: string;
142
135
  appId?: string;
143
- }): Promise<ExperienceSocialConnector[]>;
136
+ }
137
+ interface SocialSignInRequest {
138
+ /** Provider-specific authorize parameters, passed through by the OIDC client. */
139
+ extraQueryParams?: Record<string, string>;
140
+ }
141
+ /**
142
+ * Provider boundary for non-standard login-experience APIs.
143
+ *
144
+ * Standard OIDC redirect/popup and PKCE handling intentionally remain in the
145
+ * OIDC client. Only provider-specific Experience operations belong here.
146
+ */
147
+ interface LoginExperienceAdapter {
148
+ readonly provider: string;
149
+ readonly capabilities: readonly LoginExperienceCapability[];
150
+ experiencePasswordSignIn?(input: ExperiencePasswordSignInInput): Promise<ExperiencePasswordSignInResult>;
151
+ fetchSocialConnectors?(input: FetchSocialConnectorsInput): Promise<ExperienceSocialConnector[]>;
152
+ createSocialSignInRequest?(target: string): SocialSignInRequest;
153
+ }
154
+ type LoginExperienceProvider = "logto" | "hosted" | "oidc" | "zitadel" | (string & {});
155
+ /**
156
+ * Factory for built-in login adapters.
157
+ *
158
+ * - `logto`: Experience API Headless (password + social connectors)
159
+ * - `hosted` / `oidc` / `zitadel`: standard OIDC Hosted Redirect (no empty vendor SDK)
160
+ *
161
+ * Products may still pass a custom `LoginExperienceAdapter`. Do not add stub
162
+ * adapters for unintegrated Headless APIs.
163
+ */
164
+ declare function createLoginExperienceAdapter(provider?: LoginExperienceProvider): LoginExperienceAdapter;
165
+ /**
166
+ * Resolve an explicit product adapter, or the catalog adapter for `iamProvider`.
167
+ * Defaults to Logto for backward compatibility.
168
+ */
169
+ declare function resolveLoginExperienceAdapter(adapter?: LoginExperienceAdapter | null, iamProvider?: string | null): LoginExperienceAdapter;
170
+ /** `VITE_IAM_PROVIDER` / `IAM_PROVIDER` / older `IDP_MODE`. */
171
+ declare function resolveLoginExperienceProviderFromEnv(env?: Record<string, string | undefined>): LoginExperienceProvider;
172
+
173
+ /**
174
+ * Logto Experience API (Headless) adapter.
175
+ * Prefer same-origin Experience base (SPA proxies /api/experience) so cookies
176
+ * work on HTTP localhost.
177
+ */
178
+
179
+ /**
180
+ * Force authorize onto the Experience/SPA origin so Set-Cookie lands on the
181
+ * same host as subsequent `/api/experience` calls (dev proxy strips Domain).
182
+ */
183
+ declare function sameOriginAuthorizeUrl(authorizeUrl: string, apiBase: string): string;
184
+ /** Build Logto's provider-specific direct sign-in authorize parameter. */
185
+ declare function createLogtoDirectSignInRequest(directSignIn: string): SocialSignInRequest;
186
+ /** Built-in adapter for Logto's non-standard Experience API. */
187
+ declare class LogtoExperienceAdapter implements LoginExperienceAdapter {
188
+ readonly provider = "logto";
189
+ readonly capabilities: readonly LoginExperienceCapability[];
190
+ experiencePasswordSignIn(input: ExperiencePasswordSignInInput): Promise<ExperiencePasswordSignInResult>;
191
+ fetchSocialConnectors(input: FetchSocialConnectorsInput): Promise<ExperienceSocialConnector[]>;
192
+ createSocialSignInRequest(target: string): SocialSignInRequest;
193
+ }
194
+
195
+ /**
196
+ * Standard OIDC Hosted Redirect adapter.
197
+ *
198
+ * Used for enterprise OIDC and reserved IAM plugins (ZITADEL) that have no
199
+ * shipped Headless Experience API. Password and social stay on the IdP hosted UI.
200
+ */
201
+ declare class HostedOidcExperienceAdapter implements LoginExperienceAdapter {
202
+ readonly provider: string;
203
+ readonly capabilities: readonly LoginExperienceCapability[];
204
+ constructor(provider?: string);
205
+ }
206
+
207
+ /** @deprecated Use a LoginExperienceAdapter instance. */
208
+ declare function experiencePasswordSignIn(input: ExperiencePasswordSignInInput): Promise<ExperiencePasswordSignInResult>;
209
+ /** @deprecated Use a LoginExperienceAdapter instance. */
210
+ declare function fetchSocialConnectors(input: FetchSocialConnectorsInput): Promise<ExperienceSocialConnector[]>;
144
211
 
145
212
  /**
146
213
  * AuthGate — single-flight 401 recovery with optional token refresh + reauth UI.
@@ -235,7 +302,7 @@ interface HeadlessLoginLabels {
235
302
  identifierPlaceholder?: string;
236
303
  passwordPlaceholder?: string;
237
304
  submitPassword?: string;
238
- /** @deprecated Use social buttons; kept for backward-compatible label overrides. */
305
+ /** Hosted OIDC button shown when the adapter has no password capability. */
239
306
  submitSso?: string;
240
307
  submitGoogle?: string;
241
308
  submitGithub?: string;
@@ -248,6 +315,8 @@ interface HeadlessLoginLabels {
248
315
  }
249
316
  interface HeadlessLoginPanelProps {
250
317
  config: Partial<LuminaryIdpConfig>;
318
+ /** Login-experience provider. Defaults to the catalog adapter for `config.iamProvider`. */
319
+ experienceAdapter?: LoginExperienceAdapter;
251
320
  /** Product display name shown as brand signal */
252
321
  productName: string;
253
322
  logoSrc?: string;
@@ -290,7 +359,7 @@ interface HeadlessLoginPanelProps {
290
359
  }
291
360
  /** Shared default accent for product login panels. */
292
361
  declare const DEFAULT_LOGIN_THEME_COLOR = "#3a84ff";
293
- declare function HeadlessLoginPanel({ config, productName, logoSrc, labels: labelsProp, returnUrl, mode, showSocialConnectors, socialProviders, showCancel, onCancel, onOidcSession, onExperienceRedirect, footer, className, style, themeColor, }: HeadlessLoginPanelProps): react.JSX.Element;
362
+ declare function HeadlessLoginPanel({ config, experienceAdapter: experienceAdapterProp, productName, logoSrc, labels: labelsProp, returnUrl, mode, showSocialConnectors, socialProviders, showCancel, onCancel, onOidcSession, onExperienceRedirect, footer, className, style, themeColor, }: HeadlessLoginPanelProps): react.JSX.Element;
294
363
 
295
364
  interface ReauthOverlayProps {
296
365
  config: Partial<LuminaryIdpConfig>;
@@ -353,4 +422,4 @@ declare function createPostLoginPathHelpers(options: PostLoginPathOptions): {
353
422
  isUnsafeReturnPath: (path: string) => boolean;
354
423
  };
355
424
 
356
- export { type AuthGateConfig, type AuthGatePhase, AuthGateProvider, type AuthGateProviderProps, type AuthGateSnapshot, DEFAULT_LOGIN_THEME_COLOR, type ExperienceIdentifierType, type ExperiencePasswordSignInInput, type ExperiencePasswordSignInResult, type ExperienceSocialConnector, type HeadlessLoginLabels, HeadlessLoginPanel, type HeadlessLoginPanelProps, LuminaryAuthProvider, type LuminaryAuthProviderProps, type LuminaryAuthSession, type LuminaryIdpConfig, type PostLoginPathOptions, ReauthOverlay, type ReauthOverlayProps, type SignInOptions, type SocialProviderTarget, type UnauthorizedDisposition, authGate, clearStoredSession, createPostLoginPathHelpers, createUserManager, experiencePasswordSignIn, fetchSocialConnectors, getCurrentUser, getStoredAccessToken, handleSignInCallback, handleSignInPopupCallback, isIdpConfigured, isOidcPopupWindow, prepareSignInRequestUrl, readIdpConfigFromEnv, resetUserManager, resolveExperienceApiBase, resolveIssuer, sameOriginAuthorizeUrl, signInPopup, signInRedirect, signOutRedirect, useAuthGate, useAuthGateOptional, useLuminaryAuth, withAuthGateRetry };
425
+ export { type AuthGateConfig, type AuthGatePhase, AuthGateProvider, type AuthGateProviderProps, type AuthGateSnapshot, DEFAULT_LOGIN_THEME_COLOR, type ExperienceIdentifierType, type ExperiencePasswordSignInInput, type ExperiencePasswordSignInResult, type ExperienceSocialConnector, type FetchSocialConnectorsInput, type HeadlessLoginLabels, HeadlessLoginPanel, type HeadlessLoginPanelProps, HostedOidcExperienceAdapter, LOGIN_EXPERIENCE_CAPABILITIES, type LoginExperienceAdapter, type LoginExperienceCapability, type LoginExperienceProvider, LogtoExperienceAdapter, LuminaryAuthProvider, type LuminaryAuthProviderProps, type LuminaryAuthSession, type LuminaryIdpConfig, type PostLoginPathOptions, ReauthOverlay, type ReauthOverlayProps, type SignInOptions, type SocialProviderTarget, type SocialSignInRequest, type UnauthorizedDisposition, authGate, clearStoredSession, createLoginExperienceAdapter, createLogtoDirectSignInRequest, createPostLoginPathHelpers, createUserManager, experiencePasswordSignIn, fetchSocialConnectors, getCurrentUser, getStoredAccessToken, handleSignInCallback, handleSignInPopupCallback, isIdpConfigured, isOidcPopupWindow, prepareSignInRequestUrl, readIdpConfigFromEnv, resetUserManager, resolveExperienceApiBase, resolveIssuer, resolveLoginExperienceAdapter, resolveLoginExperienceProviderFromEnv, sameOriginAuthorizeUrl, signInPopup, signInRedirect, signOutRedirect, useAuthGate, useAuthGateOptional, useLuminaryAuth, withAuthGateRetry };