@luminaryworks/auth-react 0.3.1 → 0.4.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/README.md +97 -64
- package/dist/index.d.ts +72 -30
- package/dist/index.js +266 -172
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -39,6 +39,193 @@ function readIdpConfigFromEnv(env) {
|
|
|
39
39
|
experienceApiBase: resolveExperienceApiBase(env)
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
|
+
|
|
43
|
+
// src/login-experience-adapter.ts
|
|
44
|
+
var LOGIN_EXPERIENCE_CAPABILITIES = {
|
|
45
|
+
passwordSignIn: "password-sign-in",
|
|
46
|
+
socialConnectors: "social-connectors",
|
|
47
|
+
socialDirectSignIn: "social-direct-sign-in"
|
|
48
|
+
};
|
|
49
|
+
function createLoginExperienceAdapter(provider = "logto") {
|
|
50
|
+
switch (provider) {
|
|
51
|
+
case "logto":
|
|
52
|
+
return new LogtoExperienceAdapter();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
var defaultAdapter;
|
|
56
|
+
function resolveLoginExperienceAdapter(adapter) {
|
|
57
|
+
if (adapter) return adapter;
|
|
58
|
+
defaultAdapter ??= createLoginExperienceAdapter();
|
|
59
|
+
return defaultAdapter;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/logto-experience-adapter.ts
|
|
63
|
+
function experienceUrl(apiBase, path) {
|
|
64
|
+
return `${apiBase.replace(/\/$/, "")}/api/experience${path}`;
|
|
65
|
+
}
|
|
66
|
+
async function experienceFetch(apiBase, path, init) {
|
|
67
|
+
const res = await fetch(experienceUrl(apiBase, path), {
|
|
68
|
+
credentials: "include",
|
|
69
|
+
...init,
|
|
70
|
+
headers: {
|
|
71
|
+
"Content-Type": "application/json",
|
|
72
|
+
Accept: "application/json",
|
|
73
|
+
...init.headers ?? {}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
const text = await res.text();
|
|
77
|
+
let data = null;
|
|
78
|
+
if (text) {
|
|
79
|
+
try {
|
|
80
|
+
data = JSON.parse(text);
|
|
81
|
+
} catch {
|
|
82
|
+
data = text;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (!res.ok) {
|
|
86
|
+
const msg = data && typeof data === "object" && data !== null && "message" in data ? String(data.message) : `Experience API ${res.status}`;
|
|
87
|
+
throw new Error(msg);
|
|
88
|
+
}
|
|
89
|
+
return data;
|
|
90
|
+
}
|
|
91
|
+
function guessIdentifierType(identifier) {
|
|
92
|
+
if (identifier.includes("@")) return "email";
|
|
93
|
+
if (/^\+?\d{6,}$/.test(identifier.trim())) return "phone";
|
|
94
|
+
return "username";
|
|
95
|
+
}
|
|
96
|
+
function sameOriginAuthorizeUrl(authorizeUrl, apiBase) {
|
|
97
|
+
const u = new URL(authorizeUrl);
|
|
98
|
+
const base = new URL(apiBase);
|
|
99
|
+
if (u.origin === base.origin) return authorizeUrl;
|
|
100
|
+
return `${base.origin}${u.pathname}${u.search}${u.hash}`;
|
|
101
|
+
}
|
|
102
|
+
async function bootstrapOidcInteraction(input) {
|
|
103
|
+
if (!input.issuer || !input.clientId || !input.redirectUri) return;
|
|
104
|
+
if (typeof document === "undefined") return;
|
|
105
|
+
const config = {
|
|
106
|
+
issuer: input.issuer,
|
|
107
|
+
clientId: input.clientId,
|
|
108
|
+
redirectUri: input.redirectUri,
|
|
109
|
+
audience: input.audience,
|
|
110
|
+
scopes: input.scopes,
|
|
111
|
+
experienceApiBase: input.apiBase
|
|
112
|
+
};
|
|
113
|
+
const signinUrl = await prepareSignInRequestUrl(config, {
|
|
114
|
+
returnUrl: input.returnUrl
|
|
115
|
+
});
|
|
116
|
+
const authorizeUrl = sameOriginAuthorizeUrl(signinUrl, input.apiBase);
|
|
117
|
+
await new Promise((resolve) => {
|
|
118
|
+
const iframe = document.createElement("iframe");
|
|
119
|
+
iframe.setAttribute("aria-hidden", "true");
|
|
120
|
+
iframe.tabIndex = -1;
|
|
121
|
+
iframe.style.cssText = "position:absolute;width:0;height:0;border:0;visibility:hidden";
|
|
122
|
+
let settled = false;
|
|
123
|
+
const finish = () => {
|
|
124
|
+
if (settled) return;
|
|
125
|
+
settled = true;
|
|
126
|
+
iframe.remove();
|
|
127
|
+
resolve();
|
|
128
|
+
};
|
|
129
|
+
iframe.addEventListener("load", () => {
|
|
130
|
+
window.setTimeout(finish, 400);
|
|
131
|
+
});
|
|
132
|
+
iframe.src = authorizeUrl;
|
|
133
|
+
document.body.appendChild(iframe);
|
|
134
|
+
window.setTimeout(finish, 2500);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function createLogtoDirectSignInRequest(directSignIn) {
|
|
138
|
+
return { extraQueryParams: { direct_sign_in: directSignIn } };
|
|
139
|
+
}
|
|
140
|
+
var LogtoExperienceAdapter = class {
|
|
141
|
+
provider = "logto";
|
|
142
|
+
capabilities = [
|
|
143
|
+
LOGIN_EXPERIENCE_CAPABILITIES.passwordSignIn,
|
|
144
|
+
LOGIN_EXPERIENCE_CAPABILITIES.socialConnectors,
|
|
145
|
+
LOGIN_EXPERIENCE_CAPABILITIES.socialDirectSignIn
|
|
146
|
+
];
|
|
147
|
+
async experiencePasswordSignIn(input) {
|
|
148
|
+
const { apiBase, identifier, password } = input;
|
|
149
|
+
await bootstrapOidcInteraction(input);
|
|
150
|
+
const primary = input.identifierType ?? guessIdentifierType(identifier);
|
|
151
|
+
const alternates = primary === "phone" ? [primary] : primary === "email" ? ["email", "username"] : ["username", "email"];
|
|
152
|
+
let lastError;
|
|
153
|
+
for (const identifierType of alternates) {
|
|
154
|
+
try {
|
|
155
|
+
await experienceFetch(apiBase, "", {
|
|
156
|
+
method: "PUT",
|
|
157
|
+
body: JSON.stringify({ interactionEvent: "SignIn" })
|
|
158
|
+
});
|
|
159
|
+
const verified = await experienceFetch(
|
|
160
|
+
apiBase,
|
|
161
|
+
"/verification/password",
|
|
162
|
+
{
|
|
163
|
+
method: "POST",
|
|
164
|
+
body: JSON.stringify({
|
|
165
|
+
identifier: { type: identifierType, value: identifier },
|
|
166
|
+
password
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
const verificationId = verified?.verificationId;
|
|
171
|
+
if (!verificationId) {
|
|
172
|
+
throw new Error("Experience API did not return verificationId");
|
|
173
|
+
}
|
|
174
|
+
await experienceFetch(apiBase, "/identification", {
|
|
175
|
+
method: "POST",
|
|
176
|
+
body: JSON.stringify({ verificationId })
|
|
177
|
+
});
|
|
178
|
+
const submitted = await experienceFetch(
|
|
179
|
+
apiBase,
|
|
180
|
+
"/submit",
|
|
181
|
+
{
|
|
182
|
+
method: "POST",
|
|
183
|
+
body: JSON.stringify({})
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
return { redirectTo: submitted?.redirectTo, raw: submitted };
|
|
187
|
+
} catch (error) {
|
|
188
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
throw lastError ?? new Error("Experience password sign-in failed");
|
|
192
|
+
}
|
|
193
|
+
async fetchSocialConnectors(input) {
|
|
194
|
+
const url = new URL(`${input.apiBase.replace(/\/$/, "")}/api/.well-known/sign-in-exp`);
|
|
195
|
+
if (input.appId?.trim()) url.searchParams.set("appId", input.appId.trim());
|
|
196
|
+
const res = await fetch(url.toString(), {
|
|
197
|
+
// Public metadata — omit cookies so browsers do not require credentialed CORS.
|
|
198
|
+
credentials: "omit",
|
|
199
|
+
headers: { Accept: "application/json" }
|
|
200
|
+
});
|
|
201
|
+
if (!res.ok) {
|
|
202
|
+
throw new Error(`sign-in-exp ${res.status}`);
|
|
203
|
+
}
|
|
204
|
+
const data = await res.json();
|
|
205
|
+
return (data.socialConnectors ?? []).filter(
|
|
206
|
+
(connector) => Boolean(connector?.id && connector?.target)
|
|
207
|
+
).map((connector) => ({
|
|
208
|
+
id: connector.id,
|
|
209
|
+
target: connector.target,
|
|
210
|
+
name: localizeName(connector.name) || titleCase(connector.target),
|
|
211
|
+
logo: connector.logo
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
214
|
+
createSocialSignInRequest(target) {
|
|
215
|
+
return createLogtoDirectSignInRequest(`social:${target}`);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
function localizeName(name) {
|
|
219
|
+
if (!name) return "";
|
|
220
|
+
if (typeof name === "string") return name;
|
|
221
|
+
return name["zh-CN"] || name.en || Object.values(name)[0] || "";
|
|
222
|
+
}
|
|
223
|
+
function titleCase(target) {
|
|
224
|
+
if (!target) return "";
|
|
225
|
+
return target.charAt(0).toUpperCase() + target.slice(1);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/oidc-client.ts
|
|
42
229
|
var manager = null;
|
|
43
230
|
function storageKey(config) {
|
|
44
231
|
return config.tokenStorageKey ?? "luminary_access_token";
|
|
@@ -95,9 +282,14 @@ function resetUserManager() {
|
|
|
95
282
|
function signInArgs(config, options) {
|
|
96
283
|
const normalized = typeof options === "string" ? { returnUrl: options } : options ?? {};
|
|
97
284
|
const resource = config.audience?.trim();
|
|
98
|
-
const extraQueryParams = {};
|
|
285
|
+
const extraQueryParams = { ...normalized.extraQueryParams };
|
|
99
286
|
if (resource) extraQueryParams.resource = resource;
|
|
100
|
-
if (normalized.directSignIn)
|
|
287
|
+
if (normalized.directSignIn) {
|
|
288
|
+
Object.assign(
|
|
289
|
+
extraQueryParams,
|
|
290
|
+
createLogtoDirectSignInRequest(normalized.directSignIn).extraQueryParams
|
|
291
|
+
);
|
|
292
|
+
}
|
|
101
293
|
return {
|
|
102
294
|
state: normalized.returnUrl ? { returnUrl: normalized.returnUrl } : void 0,
|
|
103
295
|
extraQueryParams: Object.keys(extraQueryParams).length ? extraQueryParams : void 0
|
|
@@ -189,151 +381,19 @@ async function getCurrentUser(config) {
|
|
|
189
381
|
}
|
|
190
382
|
|
|
191
383
|
// src/experience-client.ts
|
|
192
|
-
function
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const res = await fetch(experienceUrl(apiBase, path), {
|
|
197
|
-
credentials: "include",
|
|
198
|
-
...init,
|
|
199
|
-
headers: {
|
|
200
|
-
"Content-Type": "application/json",
|
|
201
|
-
Accept: "application/json",
|
|
202
|
-
...init.headers ?? {}
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
const text = await res.text();
|
|
206
|
-
let data = null;
|
|
207
|
-
if (text) {
|
|
208
|
-
try {
|
|
209
|
-
data = JSON.parse(text);
|
|
210
|
-
} catch {
|
|
211
|
-
data = text;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
if (!res.ok) {
|
|
215
|
-
const msg = data && typeof data === "object" && data !== null && "message" in data ? String(data.message) : `Experience API ${res.status}`;
|
|
216
|
-
throw new Error(msg);
|
|
384
|
+
function experiencePasswordSignIn(input) {
|
|
385
|
+
const adapter = resolveLoginExperienceAdapter();
|
|
386
|
+
if (!adapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.passwordSignIn) || !adapter.experiencePasswordSignIn) {
|
|
387
|
+
return Promise.reject(new Error("Login experience does not support password sign-in"));
|
|
217
388
|
}
|
|
218
|
-
return
|
|
219
|
-
}
|
|
220
|
-
function guessIdentifierType(identifier) {
|
|
221
|
-
if (identifier.includes("@")) return "email";
|
|
222
|
-
if (/^\+?\d{6,}$/.test(identifier.trim())) return "phone";
|
|
223
|
-
return "username";
|
|
389
|
+
return adapter.experiencePasswordSignIn(input);
|
|
224
390
|
}
|
|
225
|
-
function
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
return `${base.origin}${u.pathname}${u.search}${u.hash}`;
|
|
230
|
-
}
|
|
231
|
-
async function bootstrapOidcInteraction(input) {
|
|
232
|
-
if (!input.issuer || !input.clientId || !input.redirectUri) return;
|
|
233
|
-
if (typeof document === "undefined") return;
|
|
234
|
-
const config = {
|
|
235
|
-
issuer: input.issuer,
|
|
236
|
-
clientId: input.clientId,
|
|
237
|
-
redirectUri: input.redirectUri,
|
|
238
|
-
audience: input.audience,
|
|
239
|
-
scopes: input.scopes,
|
|
240
|
-
experienceApiBase: input.apiBase
|
|
241
|
-
};
|
|
242
|
-
const signinUrl = await prepareSignInRequestUrl(config, {
|
|
243
|
-
returnUrl: input.returnUrl
|
|
244
|
-
});
|
|
245
|
-
const authorizeUrl = sameOriginAuthorizeUrl(signinUrl, input.apiBase);
|
|
246
|
-
await new Promise((resolve) => {
|
|
247
|
-
const iframe = document.createElement("iframe");
|
|
248
|
-
iframe.setAttribute("aria-hidden", "true");
|
|
249
|
-
iframe.tabIndex = -1;
|
|
250
|
-
iframe.style.cssText = "position:absolute;width:0;height:0;border:0;visibility:hidden";
|
|
251
|
-
let settled = false;
|
|
252
|
-
const finish = () => {
|
|
253
|
-
if (settled) return;
|
|
254
|
-
settled = true;
|
|
255
|
-
iframe.remove();
|
|
256
|
-
resolve();
|
|
257
|
-
};
|
|
258
|
-
iframe.addEventListener("load", () => {
|
|
259
|
-
window.setTimeout(finish, 400);
|
|
260
|
-
});
|
|
261
|
-
iframe.src = authorizeUrl;
|
|
262
|
-
document.body.appendChild(iframe);
|
|
263
|
-
window.setTimeout(finish, 2500);
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
async function experiencePasswordSignIn(input) {
|
|
267
|
-
const { apiBase, identifier, password } = input;
|
|
268
|
-
await bootstrapOidcInteraction(input);
|
|
269
|
-
const primary = input.identifierType ?? guessIdentifierType(identifier);
|
|
270
|
-
const alternates = primary === "phone" ? [primary] : primary === "email" ? ["email", "username"] : ["username", "email"];
|
|
271
|
-
let lastError;
|
|
272
|
-
for (const identifierType of alternates) {
|
|
273
|
-
try {
|
|
274
|
-
await experienceFetch(apiBase, "", {
|
|
275
|
-
method: "PUT",
|
|
276
|
-
body: JSON.stringify({ interactionEvent: "SignIn" })
|
|
277
|
-
});
|
|
278
|
-
const verified = await experienceFetch(
|
|
279
|
-
apiBase,
|
|
280
|
-
"/verification/password",
|
|
281
|
-
{
|
|
282
|
-
method: "POST",
|
|
283
|
-
body: JSON.stringify({
|
|
284
|
-
identifier: { type: identifierType, value: identifier },
|
|
285
|
-
password
|
|
286
|
-
})
|
|
287
|
-
}
|
|
288
|
-
);
|
|
289
|
-
const verificationId = verified?.verificationId;
|
|
290
|
-
if (!verificationId) {
|
|
291
|
-
throw new Error("Experience API did not return verificationId");
|
|
292
|
-
}
|
|
293
|
-
await experienceFetch(apiBase, "/identification", {
|
|
294
|
-
method: "POST",
|
|
295
|
-
body: JSON.stringify({ verificationId })
|
|
296
|
-
});
|
|
297
|
-
const submitted = await experienceFetch(apiBase, "/submit", {
|
|
298
|
-
method: "POST",
|
|
299
|
-
body: JSON.stringify({})
|
|
300
|
-
});
|
|
301
|
-
return { redirectTo: submitted?.redirectTo, raw: submitted };
|
|
302
|
-
} catch (e) {
|
|
303
|
-
lastError = e instanceof Error ? e : new Error(String(e));
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
throw lastError ?? new Error("Experience password sign-in failed");
|
|
307
|
-
}
|
|
308
|
-
async function fetchSocialConnectors(input) {
|
|
309
|
-
const url = new URL(`${input.apiBase.replace(/\/$/, "")}/api/.well-known/sign-in-exp`);
|
|
310
|
-
if (input.appId?.trim()) url.searchParams.set("appId", input.appId.trim());
|
|
311
|
-
const res = await fetch(url.toString(), {
|
|
312
|
-
// Public metadata — omit cookies so browsers do not require credentialed CORS.
|
|
313
|
-
credentials: "omit",
|
|
314
|
-
headers: { Accept: "application/json" }
|
|
315
|
-
});
|
|
316
|
-
if (!res.ok) {
|
|
317
|
-
throw new Error(`sign-in-exp ${res.status}`);
|
|
391
|
+
function fetchSocialConnectors(input) {
|
|
392
|
+
const adapter = resolveLoginExperienceAdapter();
|
|
393
|
+
if (!adapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.socialConnectors) || !adapter.fetchSocialConnectors) {
|
|
394
|
+
return Promise.reject(new Error("Login experience does not support social connectors"));
|
|
318
395
|
}
|
|
319
|
-
|
|
320
|
-
return (data.socialConnectors ?? []).filter(
|
|
321
|
-
(c) => Boolean(c?.id && c?.target)
|
|
322
|
-
).map((c) => ({
|
|
323
|
-
id: c.id,
|
|
324
|
-
target: c.target,
|
|
325
|
-
name: localizeName(c.name) || titleCase(c.target),
|
|
326
|
-
logo: c.logo
|
|
327
|
-
}));
|
|
328
|
-
}
|
|
329
|
-
function localizeName(name) {
|
|
330
|
-
if (!name) return "";
|
|
331
|
-
if (typeof name === "string") return name;
|
|
332
|
-
return name["zh-CN"] || name.en || Object.values(name)[0] || "";
|
|
333
|
-
}
|
|
334
|
-
function titleCase(target) {
|
|
335
|
-
if (!target) return "";
|
|
336
|
-
return target.charAt(0).toUpperCase() + target.slice(1);
|
|
396
|
+
return adapter.fetchSocialConnectors(input);
|
|
337
397
|
}
|
|
338
398
|
|
|
339
399
|
// src/auth-gate.ts
|
|
@@ -575,10 +635,6 @@ var defaults = {
|
|
|
575
635
|
showPassword: "Show password",
|
|
576
636
|
hidePassword: "Hide password"
|
|
577
637
|
};
|
|
578
|
-
var FALLBACK_SOCIAL = [
|
|
579
|
-
{ id: "google", target: "google", name: "Google" },
|
|
580
|
-
{ id: "github", target: "github", name: "GitHub" }
|
|
581
|
-
];
|
|
582
638
|
function resolveSieBase(config) {
|
|
583
639
|
const explicit = config.experienceApiBase?.trim();
|
|
584
640
|
if (explicit) return explicit.replace(/\/$/, "");
|
|
@@ -600,6 +656,7 @@ function cx(...parts) {
|
|
|
600
656
|
}
|
|
601
657
|
function HeadlessLoginPanel({
|
|
602
658
|
config,
|
|
659
|
+
experienceAdapter: experienceAdapterProp,
|
|
603
660
|
productName,
|
|
604
661
|
logoSrc,
|
|
605
662
|
labels: labelsProp,
|
|
@@ -616,6 +673,7 @@ function HeadlessLoginPanel({
|
|
|
616
673
|
style,
|
|
617
674
|
themeColor = DEFAULT_LOGIN_THEME_COLOR
|
|
618
675
|
}) {
|
|
676
|
+
const experienceAdapter = resolveLoginExperienceAdapter(experienceAdapterProp);
|
|
619
677
|
const labels = { ...defaults, ...labelsProp };
|
|
620
678
|
const configured = isIdpConfigured(config);
|
|
621
679
|
const [identifier, setIdentifier] = react.useState("");
|
|
@@ -628,7 +686,8 @@ function HeadlessLoginPanel({
|
|
|
628
686
|
const [loading, setLoading] = react.useState(null);
|
|
629
687
|
const [error, setError] = react.useState("");
|
|
630
688
|
const [connectors, setConnectors] = react.useState([]);
|
|
631
|
-
const socialEnabled = showSocialConnectors !== false && !(Array.isArray(socialProviders) && socialProviders.length === 0);
|
|
689
|
+
const socialEnabled = showSocialConnectors !== false && !(Array.isArray(socialProviders) && socialProviders.length === 0) && experienceAdapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.socialConnectors) && experienceAdapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.socialDirectSignIn) && typeof experienceAdapter.fetchSocialConnectors === "function" && typeof experienceAdapter.createSocialSignInRequest === "function";
|
|
690
|
+
const passwordEnabled = experienceAdapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.passwordSignIn) && typeof experienceAdapter.experiencePasswordSignIn === "function";
|
|
632
691
|
react.useEffect(() => {
|
|
633
692
|
const resetBusy = () => setLoading(null);
|
|
634
693
|
window.addEventListener("pageshow", resetBusy);
|
|
@@ -639,7 +698,8 @@ function HeadlessLoginPanel({
|
|
|
639
698
|
};
|
|
640
699
|
}, []);
|
|
641
700
|
react.useEffect(() => {
|
|
642
|
-
|
|
701
|
+
const fetchConnectors = experienceAdapter.fetchSocialConnectors;
|
|
702
|
+
if (!socialEnabled || !fetchConnectors) {
|
|
643
703
|
setConnectors([]);
|
|
644
704
|
return;
|
|
645
705
|
}
|
|
@@ -651,42 +711,57 @@ function HeadlessLoginPanel({
|
|
|
651
711
|
if (!cancelled) setConnectors(filtered);
|
|
652
712
|
};
|
|
653
713
|
if (!sieBase) {
|
|
654
|
-
|
|
655
|
-
allowlist ? FALLBACK_SOCIAL.filter((c) => allowlist.has(c.target)) : FALLBACK_SOCIAL
|
|
656
|
-
);
|
|
714
|
+
if (!cancelled) setConnectors([]);
|
|
657
715
|
return;
|
|
658
716
|
}
|
|
659
|
-
void
|
|
660
|
-
|
|
661
|
-
apply(list);
|
|
662
|
-
return;
|
|
663
|
-
}
|
|
664
|
-
apply(
|
|
665
|
-
allowlist ? FALLBACK_SOCIAL.filter((c) => allowlist.has(c.target)) : FALLBACK_SOCIAL
|
|
666
|
-
);
|
|
717
|
+
void fetchConnectors.call(experienceAdapter, { apiBase: sieBase, appId: config.clientId }).then((list) => {
|
|
718
|
+
apply(list);
|
|
667
719
|
}).catch(() => {
|
|
668
|
-
|
|
669
|
-
allowlist ? FALLBACK_SOCIAL.filter((c) => allowlist.has(c.target)) : FALLBACK_SOCIAL
|
|
670
|
-
);
|
|
720
|
+
if (!cancelled) setConnectors([]);
|
|
671
721
|
});
|
|
672
722
|
return () => {
|
|
673
723
|
cancelled = true;
|
|
674
724
|
};
|
|
675
|
-
}, [
|
|
676
|
-
|
|
725
|
+
}, [
|
|
726
|
+
config.clientId,
|
|
727
|
+
config.experienceApiBase,
|
|
728
|
+
config.issuer,
|
|
729
|
+
experienceAdapter,
|
|
730
|
+
socialEnabled,
|
|
731
|
+
socialProviders
|
|
732
|
+
]);
|
|
733
|
+
const runOidc = async (experienceRequest) => {
|
|
677
734
|
if (!configured) throw new Error("IdP not configured");
|
|
678
735
|
if (mode === "redirect") {
|
|
679
|
-
await signInRedirect(config, { returnUrl,
|
|
736
|
+
await signInRedirect(config, { returnUrl, ...experienceRequest });
|
|
680
737
|
return;
|
|
681
738
|
}
|
|
682
|
-
const { session, returnUrl: next } = await signInPopup(config, {
|
|
739
|
+
const { session, returnUrl: next } = await signInPopup(config, {
|
|
740
|
+
returnUrl,
|
|
741
|
+
...experienceRequest
|
|
742
|
+
});
|
|
683
743
|
await onOidcSession?.(session, next);
|
|
684
744
|
};
|
|
685
745
|
const runSocial = async (target) => {
|
|
686
746
|
setError("");
|
|
687
747
|
setLoading(target);
|
|
688
748
|
try {
|
|
689
|
-
|
|
749
|
+
if (!socialEnabled || !experienceAdapter.capabilities.includes(
|
|
750
|
+
LOGIN_EXPERIENCE_CAPABILITIES.socialDirectSignIn
|
|
751
|
+
) || !experienceAdapter.createSocialSignInRequest) {
|
|
752
|
+
throw new Error("Login experience does not support direct social sign-in");
|
|
753
|
+
}
|
|
754
|
+
await runOidc(experienceAdapter.createSocialSignInRequest(target));
|
|
755
|
+
} catch (e) {
|
|
756
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
757
|
+
setLoading(null);
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
const runHostedOidc = async () => {
|
|
761
|
+
setError("");
|
|
762
|
+
setLoading("sso");
|
|
763
|
+
try {
|
|
764
|
+
await runOidc();
|
|
690
765
|
} catch (e) {
|
|
691
766
|
setError(e instanceof Error ? e.message : String(e));
|
|
692
767
|
setLoading(null);
|
|
@@ -709,7 +784,12 @@ function HeadlessLoginPanel({
|
|
|
709
784
|
if (!apiBase) {
|
|
710
785
|
throw new Error(labels.experienceUnavailable);
|
|
711
786
|
}
|
|
712
|
-
|
|
787
|
+
if (!passwordEnabled || !experienceAdapter.capabilities.includes(
|
|
788
|
+
LOGIN_EXPERIENCE_CAPABILITIES.passwordSignIn
|
|
789
|
+
) || !experienceAdapter.experiencePasswordSignIn) {
|
|
790
|
+
throw new Error(labels.experienceUnavailable);
|
|
791
|
+
}
|
|
792
|
+
const result = await experienceAdapter.experiencePasswordSignIn({
|
|
713
793
|
apiBase,
|
|
714
794
|
identifier: identifier.trim(),
|
|
715
795
|
password,
|
|
@@ -743,7 +823,7 @@ function HeadlessLoginPanel({
|
|
|
743
823
|
] }),
|
|
744
824
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: HeadlessLoginPanel_module_default.subtitle, children: labels.subtitle }),
|
|
745
825
|
configured ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
746
|
-
/* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: (e) => void runPassword(e), className: HeadlessLoginPanel_module_default.form, children: [
|
|
826
|
+
passwordEnabled ? /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: (e) => void runPassword(e), className: HeadlessLoginPanel_module_default.form, children: [
|
|
747
827
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
748
828
|
"input",
|
|
749
829
|
{
|
|
@@ -785,7 +865,16 @@ function HeadlessLoginPanel({
|
|
|
785
865
|
)
|
|
786
866
|
] }),
|
|
787
867
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", className: HeadlessLoginPanel_module_default.primaryBtn, disabled: busy || !identifier || !password, children: loading === "password" ? "\u2026" : labels.submitPassword })
|
|
788
|
-
] }),
|
|
868
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: HeadlessLoginPanel_module_default.form, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
869
|
+
"button",
|
|
870
|
+
{
|
|
871
|
+
type: "button",
|
|
872
|
+
className: HeadlessLoginPanel_module_default.primaryBtn,
|
|
873
|
+
disabled: busy,
|
|
874
|
+
onClick: () => void runHostedOidc(),
|
|
875
|
+
children: loading === "sso" ? "\u2026" : labels.submitSso
|
|
876
|
+
}
|
|
877
|
+
) }),
|
|
789
878
|
showHint ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: HeadlessLoginPanel_module_default.hint, children: labels.hint }) : null,
|
|
790
879
|
showSocial ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: HeadlessLoginPanel_module_default.divider, "aria-hidden": true, children: [
|
|
791
880
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: HeadlessLoginPanel_module_default.dividerLine }),
|
|
@@ -1012,10 +1101,14 @@ function createPostLoginPathHelpers(options) {
|
|
|
1012
1101
|
exports.AuthGateProvider = AuthGateProvider;
|
|
1013
1102
|
exports.DEFAULT_LOGIN_THEME_COLOR = DEFAULT_LOGIN_THEME_COLOR;
|
|
1014
1103
|
exports.HeadlessLoginPanel = HeadlessLoginPanel;
|
|
1104
|
+
exports.LOGIN_EXPERIENCE_CAPABILITIES = LOGIN_EXPERIENCE_CAPABILITIES;
|
|
1105
|
+
exports.LogtoExperienceAdapter = LogtoExperienceAdapter;
|
|
1015
1106
|
exports.LuminaryAuthProvider = LuminaryAuthProvider;
|
|
1016
1107
|
exports.ReauthOverlay = ReauthOverlay;
|
|
1017
1108
|
exports.authGate = authGate;
|
|
1018
1109
|
exports.clearStoredSession = clearStoredSession;
|
|
1110
|
+
exports.createLoginExperienceAdapter = createLoginExperienceAdapter;
|
|
1111
|
+
exports.createLogtoDirectSignInRequest = createLogtoDirectSignInRequest;
|
|
1019
1112
|
exports.createPostLoginPathHelpers = createPostLoginPathHelpers;
|
|
1020
1113
|
exports.createUserManager = createUserManager;
|
|
1021
1114
|
exports.experiencePasswordSignIn = experiencePasswordSignIn;
|
|
@@ -1031,6 +1124,7 @@ exports.readIdpConfigFromEnv = readIdpConfigFromEnv;
|
|
|
1031
1124
|
exports.resetUserManager = resetUserManager;
|
|
1032
1125
|
exports.resolveExperienceApiBase = resolveExperienceApiBase;
|
|
1033
1126
|
exports.resolveIssuer = resolveIssuer;
|
|
1127
|
+
exports.resolveLoginExperienceAdapter = resolveLoginExperienceAdapter;
|
|
1034
1128
|
exports.sameOriginAuthorizeUrl = sameOriginAuthorizeUrl;
|
|
1035
1129
|
exports.signInPopup = signInPopup;
|
|
1036
1130
|
exports.signInRedirect = signInRedirect;
|