@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 +38 -1
- package/dist/index.d.ts +99 -30
- package/dist/index.js +294 -156
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -36,9 +36,221 @@ function readIdpConfigFromEnv(env) {
|
|
|
36
36
|
scopes: env.VITE_IDP_SCOPES ?? env.PUBLIC_IDP_SCOPES ?? "openid profile email offline_access",
|
|
37
37
|
audience: env.VITE_IDP_AUDIENCE ?? env.PUBLIC_IDP_AUDIENCE,
|
|
38
38
|
tokenStorageKey: env.VITE_IDP_TOKEN_KEY ?? "luminary_access_token",
|
|
39
|
-
experienceApiBase: resolveExperienceApiBase(env)
|
|
39
|
+
experienceApiBase: resolveExperienceApiBase(env),
|
|
40
|
+
iamProvider: env.VITE_IAM_PROVIDER ?? env.PUBLIC_IAM_PROVIDER ?? env.IAM_PROVIDER ?? env.IDP_MODE
|
|
40
41
|
};
|
|
41
42
|
}
|
|
43
|
+
|
|
44
|
+
// src/hosted-oidc-experience-adapter.ts
|
|
45
|
+
var HostedOidcExperienceAdapter = class {
|
|
46
|
+
constructor(provider = "hosted") {
|
|
47
|
+
this.provider = provider;
|
|
48
|
+
}
|
|
49
|
+
provider;
|
|
50
|
+
capabilities = [];
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// src/login-experience-adapter.ts
|
|
54
|
+
var LOGIN_EXPERIENCE_CAPABILITIES = {
|
|
55
|
+
passwordSignIn: "password-sign-in",
|
|
56
|
+
socialConnectors: "social-connectors",
|
|
57
|
+
socialDirectSignIn: "social-direct-sign-in"
|
|
58
|
+
};
|
|
59
|
+
var HOSTED_LOGIN_PROVIDERS = /* @__PURE__ */ new Set(["hosted", "oidc", "external_oidc", "zitadel"]);
|
|
60
|
+
function normalizeLoginExperienceProvider(provider) {
|
|
61
|
+
const key = provider.trim().toLowerCase();
|
|
62
|
+
if (!key || key === "logto") return "logto";
|
|
63
|
+
if (HOSTED_LOGIN_PROVIDERS.has(key)) return "hosted";
|
|
64
|
+
throw new Error(
|
|
65
|
+
`Unknown login experience provider "${provider}". Shipped: logto (default). Hosted OIDC: oidc / hosted / zitadel.`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
function createLoginExperienceAdapter(provider = "logto") {
|
|
69
|
+
const kind = normalizeLoginExperienceProvider(provider);
|
|
70
|
+
if (kind === "logto") return new LogtoExperienceAdapter();
|
|
71
|
+
return new HostedOidcExperienceAdapter(provider.trim().toLowerCase() || "hosted");
|
|
72
|
+
}
|
|
73
|
+
var adapterCache = /* @__PURE__ */ new Map();
|
|
74
|
+
function resolveLoginExperienceAdapter(adapter, iamProvider) {
|
|
75
|
+
if (adapter) return adapter;
|
|
76
|
+
const key = iamProvider?.trim() || "logto";
|
|
77
|
+
const cached = adapterCache.get(key);
|
|
78
|
+
if (cached) return cached;
|
|
79
|
+
const created = createLoginExperienceAdapter(key);
|
|
80
|
+
adapterCache.set(key, created);
|
|
81
|
+
return created;
|
|
82
|
+
}
|
|
83
|
+
function resolveLoginExperienceProviderFromEnv(env = {}) {
|
|
84
|
+
return env.VITE_IAM_PROVIDER ?? env.PUBLIC_IAM_PROVIDER ?? env.IAM_PROVIDER ?? env.IDP_MODE ?? "logto";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// src/logto-experience-adapter.ts
|
|
88
|
+
function experienceUrl(apiBase, path) {
|
|
89
|
+
return `${apiBase.replace(/\/$/, "")}/api/experience${path}`;
|
|
90
|
+
}
|
|
91
|
+
async function experienceFetch(apiBase, path, init) {
|
|
92
|
+
const res = await fetch(experienceUrl(apiBase, path), {
|
|
93
|
+
credentials: "include",
|
|
94
|
+
...init,
|
|
95
|
+
headers: {
|
|
96
|
+
"Content-Type": "application/json",
|
|
97
|
+
Accept: "application/json",
|
|
98
|
+
...init.headers ?? {}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
const text = await res.text();
|
|
102
|
+
let data = null;
|
|
103
|
+
if (text) {
|
|
104
|
+
try {
|
|
105
|
+
data = JSON.parse(text);
|
|
106
|
+
} catch {
|
|
107
|
+
data = text;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (!res.ok) {
|
|
111
|
+
const msg = data && typeof data === "object" && data !== null && "message" in data ? String(data.message) : `Experience API ${res.status}`;
|
|
112
|
+
throw new Error(msg);
|
|
113
|
+
}
|
|
114
|
+
return data;
|
|
115
|
+
}
|
|
116
|
+
function guessIdentifierType(identifier) {
|
|
117
|
+
if (identifier.includes("@")) return "email";
|
|
118
|
+
if (/^\+?\d{6,}$/.test(identifier.trim())) return "phone";
|
|
119
|
+
return "username";
|
|
120
|
+
}
|
|
121
|
+
function sameOriginAuthorizeUrl(authorizeUrl, apiBase) {
|
|
122
|
+
const u = new URL(authorizeUrl);
|
|
123
|
+
const base = new URL(apiBase);
|
|
124
|
+
if (u.origin === base.origin) return authorizeUrl;
|
|
125
|
+
return `${base.origin}${u.pathname}${u.search}${u.hash}`;
|
|
126
|
+
}
|
|
127
|
+
async function bootstrapOidcInteraction(input) {
|
|
128
|
+
if (!input.issuer || !input.clientId || !input.redirectUri) return;
|
|
129
|
+
if (typeof document === "undefined") return;
|
|
130
|
+
const config = {
|
|
131
|
+
issuer: input.issuer,
|
|
132
|
+
clientId: input.clientId,
|
|
133
|
+
redirectUri: input.redirectUri,
|
|
134
|
+
audience: input.audience,
|
|
135
|
+
scopes: input.scopes,
|
|
136
|
+
experienceApiBase: input.apiBase
|
|
137
|
+
};
|
|
138
|
+
const signinUrl = await prepareSignInRequestUrl(config, {
|
|
139
|
+
returnUrl: input.returnUrl
|
|
140
|
+
});
|
|
141
|
+
const authorizeUrl = sameOriginAuthorizeUrl(signinUrl, input.apiBase);
|
|
142
|
+
await new Promise((resolve) => {
|
|
143
|
+
const iframe = document.createElement("iframe");
|
|
144
|
+
iframe.setAttribute("aria-hidden", "true");
|
|
145
|
+
iframe.tabIndex = -1;
|
|
146
|
+
iframe.style.cssText = "position:absolute;width:0;height:0;border:0;visibility:hidden";
|
|
147
|
+
let settled = false;
|
|
148
|
+
const finish = () => {
|
|
149
|
+
if (settled) return;
|
|
150
|
+
settled = true;
|
|
151
|
+
iframe.remove();
|
|
152
|
+
resolve();
|
|
153
|
+
};
|
|
154
|
+
iframe.addEventListener("load", () => {
|
|
155
|
+
window.setTimeout(finish, 400);
|
|
156
|
+
});
|
|
157
|
+
iframe.src = authorizeUrl;
|
|
158
|
+
document.body.appendChild(iframe);
|
|
159
|
+
window.setTimeout(finish, 2500);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function createLogtoDirectSignInRequest(directSignIn) {
|
|
163
|
+
return { extraQueryParams: { direct_sign_in: directSignIn } };
|
|
164
|
+
}
|
|
165
|
+
var LogtoExperienceAdapter = class {
|
|
166
|
+
provider = "logto";
|
|
167
|
+
capabilities = [
|
|
168
|
+
LOGIN_EXPERIENCE_CAPABILITIES.passwordSignIn,
|
|
169
|
+
LOGIN_EXPERIENCE_CAPABILITIES.socialConnectors,
|
|
170
|
+
LOGIN_EXPERIENCE_CAPABILITIES.socialDirectSignIn
|
|
171
|
+
];
|
|
172
|
+
async experiencePasswordSignIn(input) {
|
|
173
|
+
const { apiBase, identifier, password } = input;
|
|
174
|
+
await bootstrapOidcInteraction(input);
|
|
175
|
+
const primary = input.identifierType ?? guessIdentifierType(identifier);
|
|
176
|
+
const alternates = primary === "phone" ? [primary] : primary === "email" ? ["email", "username"] : ["username", "email"];
|
|
177
|
+
let lastError;
|
|
178
|
+
for (const identifierType of alternates) {
|
|
179
|
+
try {
|
|
180
|
+
await experienceFetch(apiBase, "", {
|
|
181
|
+
method: "PUT",
|
|
182
|
+
body: JSON.stringify({ interactionEvent: "SignIn" })
|
|
183
|
+
});
|
|
184
|
+
const verified = await experienceFetch(
|
|
185
|
+
apiBase,
|
|
186
|
+
"/verification/password",
|
|
187
|
+
{
|
|
188
|
+
method: "POST",
|
|
189
|
+
body: JSON.stringify({
|
|
190
|
+
identifier: { type: identifierType, value: identifier },
|
|
191
|
+
password
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
const verificationId = verified?.verificationId;
|
|
196
|
+
if (!verificationId) {
|
|
197
|
+
throw new Error("Experience API did not return verificationId");
|
|
198
|
+
}
|
|
199
|
+
await experienceFetch(apiBase, "/identification", {
|
|
200
|
+
method: "POST",
|
|
201
|
+
body: JSON.stringify({ verificationId })
|
|
202
|
+
});
|
|
203
|
+
const submitted = await experienceFetch(
|
|
204
|
+
apiBase,
|
|
205
|
+
"/submit",
|
|
206
|
+
{
|
|
207
|
+
method: "POST",
|
|
208
|
+
body: JSON.stringify({})
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
return { redirectTo: submitted?.redirectTo, raw: submitted };
|
|
212
|
+
} catch (error) {
|
|
213
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
throw lastError ?? new Error("Experience password sign-in failed");
|
|
217
|
+
}
|
|
218
|
+
async fetchSocialConnectors(input) {
|
|
219
|
+
const url = new URL(`${input.apiBase.replace(/\/$/, "")}/api/.well-known/sign-in-exp`);
|
|
220
|
+
if (input.appId?.trim()) url.searchParams.set("appId", input.appId.trim());
|
|
221
|
+
const res = await fetch(url.toString(), {
|
|
222
|
+
// Public metadata — omit cookies so browsers do not require credentialed CORS.
|
|
223
|
+
credentials: "omit",
|
|
224
|
+
headers: { Accept: "application/json" }
|
|
225
|
+
});
|
|
226
|
+
if (!res.ok) {
|
|
227
|
+
throw new Error(`sign-in-exp ${res.status}`);
|
|
228
|
+
}
|
|
229
|
+
const data = await res.json();
|
|
230
|
+
return (data.socialConnectors ?? []).filter(
|
|
231
|
+
(connector) => Boolean(connector?.id && connector?.target)
|
|
232
|
+
).map((connector) => ({
|
|
233
|
+
id: connector.id,
|
|
234
|
+
target: connector.target,
|
|
235
|
+
name: localizeName(connector.name) || titleCase(connector.target),
|
|
236
|
+
logo: connector.logo
|
|
237
|
+
}));
|
|
238
|
+
}
|
|
239
|
+
createSocialSignInRequest(target) {
|
|
240
|
+
return createLogtoDirectSignInRequest(`social:${target}`);
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
function localizeName(name) {
|
|
244
|
+
if (!name) return "";
|
|
245
|
+
if (typeof name === "string") return name;
|
|
246
|
+
return name["zh-CN"] || name.en || Object.values(name)[0] || "";
|
|
247
|
+
}
|
|
248
|
+
function titleCase(target) {
|
|
249
|
+
if (!target) return "";
|
|
250
|
+
return target.charAt(0).toUpperCase() + target.slice(1);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// src/oidc-client.ts
|
|
42
254
|
var manager = null;
|
|
43
255
|
function storageKey(config) {
|
|
44
256
|
return config.tokenStorageKey ?? "luminary_access_token";
|
|
@@ -95,9 +307,14 @@ function resetUserManager() {
|
|
|
95
307
|
function signInArgs(config, options) {
|
|
96
308
|
const normalized = typeof options === "string" ? { returnUrl: options } : options ?? {};
|
|
97
309
|
const resource = config.audience?.trim();
|
|
98
|
-
const extraQueryParams = {};
|
|
310
|
+
const extraQueryParams = { ...normalized.extraQueryParams };
|
|
99
311
|
if (resource) extraQueryParams.resource = resource;
|
|
100
|
-
if (normalized.directSignIn)
|
|
312
|
+
if (normalized.directSignIn) {
|
|
313
|
+
Object.assign(
|
|
314
|
+
extraQueryParams,
|
|
315
|
+
createLogtoDirectSignInRequest(normalized.directSignIn).extraQueryParams
|
|
316
|
+
);
|
|
317
|
+
}
|
|
101
318
|
return {
|
|
102
319
|
state: normalized.returnUrl ? { returnUrl: normalized.returnUrl } : void 0,
|
|
103
320
|
extraQueryParams: Object.keys(extraQueryParams).length ? extraQueryParams : void 0
|
|
@@ -189,151 +406,19 @@ async function getCurrentUser(config) {
|
|
|
189
406
|
}
|
|
190
407
|
|
|
191
408
|
// 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);
|
|
409
|
+
function experiencePasswordSignIn(input) {
|
|
410
|
+
const adapter = resolveLoginExperienceAdapter();
|
|
411
|
+
if (!adapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.passwordSignIn) || !adapter.experiencePasswordSignIn) {
|
|
412
|
+
return Promise.reject(new Error("Login experience does not support password sign-in"));
|
|
217
413
|
}
|
|
218
|
-
return
|
|
414
|
+
return adapter.experiencePasswordSignIn(input);
|
|
219
415
|
}
|
|
220
|
-
function
|
|
221
|
-
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
function sameOriginAuthorizeUrl(authorizeUrl, apiBase) {
|
|
226
|
-
const u = new URL(authorizeUrl);
|
|
227
|
-
const base = new URL(apiBase);
|
|
228
|
-
if (u.origin === base.origin) return authorizeUrl;
|
|
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}`);
|
|
416
|
+
function fetchSocialConnectors(input) {
|
|
417
|
+
const adapter = resolveLoginExperienceAdapter();
|
|
418
|
+
if (!adapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.socialConnectors) || !adapter.fetchSocialConnectors) {
|
|
419
|
+
return Promise.reject(new Error("Login experience does not support social connectors"));
|
|
318
420
|
}
|
|
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);
|
|
421
|
+
return adapter.fetchSocialConnectors(input);
|
|
337
422
|
}
|
|
338
423
|
|
|
339
424
|
// src/auth-gate.ts
|
|
@@ -596,6 +681,7 @@ function cx(...parts) {
|
|
|
596
681
|
}
|
|
597
682
|
function HeadlessLoginPanel({
|
|
598
683
|
config,
|
|
684
|
+
experienceAdapter: experienceAdapterProp,
|
|
599
685
|
productName,
|
|
600
686
|
logoSrc,
|
|
601
687
|
labels: labelsProp,
|
|
@@ -612,6 +698,10 @@ function HeadlessLoginPanel({
|
|
|
612
698
|
style,
|
|
613
699
|
themeColor = DEFAULT_LOGIN_THEME_COLOR
|
|
614
700
|
}) {
|
|
701
|
+
const experienceAdapter = resolveLoginExperienceAdapter(
|
|
702
|
+
experienceAdapterProp,
|
|
703
|
+
config.iamProvider
|
|
704
|
+
);
|
|
615
705
|
const labels = { ...defaults, ...labelsProp };
|
|
616
706
|
const configured = isIdpConfigured(config);
|
|
617
707
|
const [identifier, setIdentifier] = react.useState("");
|
|
@@ -624,7 +714,8 @@ function HeadlessLoginPanel({
|
|
|
624
714
|
const [loading, setLoading] = react.useState(null);
|
|
625
715
|
const [error, setError] = react.useState("");
|
|
626
716
|
const [connectors, setConnectors] = react.useState([]);
|
|
627
|
-
const socialEnabled = showSocialConnectors !== false && !(Array.isArray(socialProviders) && socialProviders.length === 0);
|
|
717
|
+
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";
|
|
718
|
+
const passwordEnabled = experienceAdapter.capabilities.includes(LOGIN_EXPERIENCE_CAPABILITIES.passwordSignIn) && typeof experienceAdapter.experiencePasswordSignIn === "function";
|
|
628
719
|
react.useEffect(() => {
|
|
629
720
|
const resetBusy = () => setLoading(null);
|
|
630
721
|
window.addEventListener("pageshow", resetBusy);
|
|
@@ -635,7 +726,8 @@ function HeadlessLoginPanel({
|
|
|
635
726
|
};
|
|
636
727
|
}, []);
|
|
637
728
|
react.useEffect(() => {
|
|
638
|
-
|
|
729
|
+
const fetchConnectors = experienceAdapter.fetchSocialConnectors;
|
|
730
|
+
if (!socialEnabled || !fetchConnectors) {
|
|
639
731
|
setConnectors([]);
|
|
640
732
|
return;
|
|
641
733
|
}
|
|
@@ -650,7 +742,7 @@ function HeadlessLoginPanel({
|
|
|
650
742
|
if (!cancelled) setConnectors([]);
|
|
651
743
|
return;
|
|
652
744
|
}
|
|
653
|
-
void
|
|
745
|
+
void fetchConnectors.call(experienceAdapter, { apiBase: sieBase, appId: config.clientId }).then((list) => {
|
|
654
746
|
apply(list);
|
|
655
747
|
}).catch(() => {
|
|
656
748
|
if (!cancelled) setConnectors([]);
|
|
@@ -658,21 +750,46 @@ function HeadlessLoginPanel({
|
|
|
658
750
|
return () => {
|
|
659
751
|
cancelled = true;
|
|
660
752
|
};
|
|
661
|
-
}, [
|
|
662
|
-
|
|
753
|
+
}, [
|
|
754
|
+
config.clientId,
|
|
755
|
+
config.experienceApiBase,
|
|
756
|
+
config.issuer,
|
|
757
|
+
experienceAdapter,
|
|
758
|
+
socialEnabled,
|
|
759
|
+
socialProviders
|
|
760
|
+
]);
|
|
761
|
+
const runOidc = async (experienceRequest) => {
|
|
663
762
|
if (!configured) throw new Error("IdP not configured");
|
|
664
763
|
if (mode === "redirect") {
|
|
665
|
-
await signInRedirect(config, { returnUrl,
|
|
764
|
+
await signInRedirect(config, { returnUrl, ...experienceRequest });
|
|
666
765
|
return;
|
|
667
766
|
}
|
|
668
|
-
const { session, returnUrl: next } = await signInPopup(config, {
|
|
767
|
+
const { session, returnUrl: next } = await signInPopup(config, {
|
|
768
|
+
returnUrl,
|
|
769
|
+
...experienceRequest
|
|
770
|
+
});
|
|
669
771
|
await onOidcSession?.(session, next);
|
|
670
772
|
};
|
|
671
773
|
const runSocial = async (target) => {
|
|
672
774
|
setError("");
|
|
673
775
|
setLoading(target);
|
|
674
776
|
try {
|
|
675
|
-
|
|
777
|
+
if (!socialEnabled || !experienceAdapter.capabilities.includes(
|
|
778
|
+
LOGIN_EXPERIENCE_CAPABILITIES.socialDirectSignIn
|
|
779
|
+
) || !experienceAdapter.createSocialSignInRequest) {
|
|
780
|
+
throw new Error("Login experience does not support direct social sign-in");
|
|
781
|
+
}
|
|
782
|
+
await runOidc(experienceAdapter.createSocialSignInRequest(target));
|
|
783
|
+
} catch (e) {
|
|
784
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
785
|
+
setLoading(null);
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
const runHostedOidc = async () => {
|
|
789
|
+
setError("");
|
|
790
|
+
setLoading("sso");
|
|
791
|
+
try {
|
|
792
|
+
await runOidc();
|
|
676
793
|
} catch (e) {
|
|
677
794
|
setError(e instanceof Error ? e.message : String(e));
|
|
678
795
|
setLoading(null);
|
|
@@ -695,7 +812,12 @@ function HeadlessLoginPanel({
|
|
|
695
812
|
if (!apiBase) {
|
|
696
813
|
throw new Error(labels.experienceUnavailable);
|
|
697
814
|
}
|
|
698
|
-
|
|
815
|
+
if (!passwordEnabled || !experienceAdapter.capabilities.includes(
|
|
816
|
+
LOGIN_EXPERIENCE_CAPABILITIES.passwordSignIn
|
|
817
|
+
) || !experienceAdapter.experiencePasswordSignIn) {
|
|
818
|
+
throw new Error(labels.experienceUnavailable);
|
|
819
|
+
}
|
|
820
|
+
const result = await experienceAdapter.experiencePasswordSignIn({
|
|
699
821
|
apiBase,
|
|
700
822
|
identifier: identifier.trim(),
|
|
701
823
|
password,
|
|
@@ -729,7 +851,7 @@ function HeadlessLoginPanel({
|
|
|
729
851
|
] }),
|
|
730
852
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: HeadlessLoginPanel_module_default.subtitle, children: labels.subtitle }),
|
|
731
853
|
configured ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
732
|
-
/* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: (e) => void runPassword(e), className: HeadlessLoginPanel_module_default.form, children: [
|
|
854
|
+
passwordEnabled ? /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: (e) => void runPassword(e), className: HeadlessLoginPanel_module_default.form, children: [
|
|
733
855
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
734
856
|
"input",
|
|
735
857
|
{
|
|
@@ -771,7 +893,16 @@ function HeadlessLoginPanel({
|
|
|
771
893
|
)
|
|
772
894
|
] }),
|
|
773
895
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", className: HeadlessLoginPanel_module_default.primaryBtn, disabled: busy || !identifier || !password, children: loading === "password" ? "\u2026" : labels.submitPassword })
|
|
774
|
-
] }),
|
|
896
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: HeadlessLoginPanel_module_default.form, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
897
|
+
"button",
|
|
898
|
+
{
|
|
899
|
+
type: "button",
|
|
900
|
+
className: HeadlessLoginPanel_module_default.primaryBtn,
|
|
901
|
+
disabled: busy,
|
|
902
|
+
onClick: () => void runHostedOidc(),
|
|
903
|
+
children: loading === "sso" ? "\u2026" : labels.submitSso
|
|
904
|
+
}
|
|
905
|
+
) }),
|
|
775
906
|
showHint ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: HeadlessLoginPanel_module_default.hint, children: labels.hint }) : null,
|
|
776
907
|
showSocial ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: HeadlessLoginPanel_module_default.divider, "aria-hidden": true, children: [
|
|
777
908
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: HeadlessLoginPanel_module_default.dividerLine }),
|
|
@@ -998,10 +1129,15 @@ function createPostLoginPathHelpers(options) {
|
|
|
998
1129
|
exports.AuthGateProvider = AuthGateProvider;
|
|
999
1130
|
exports.DEFAULT_LOGIN_THEME_COLOR = DEFAULT_LOGIN_THEME_COLOR;
|
|
1000
1131
|
exports.HeadlessLoginPanel = HeadlessLoginPanel;
|
|
1132
|
+
exports.HostedOidcExperienceAdapter = HostedOidcExperienceAdapter;
|
|
1133
|
+
exports.LOGIN_EXPERIENCE_CAPABILITIES = LOGIN_EXPERIENCE_CAPABILITIES;
|
|
1134
|
+
exports.LogtoExperienceAdapter = LogtoExperienceAdapter;
|
|
1001
1135
|
exports.LuminaryAuthProvider = LuminaryAuthProvider;
|
|
1002
1136
|
exports.ReauthOverlay = ReauthOverlay;
|
|
1003
1137
|
exports.authGate = authGate;
|
|
1004
1138
|
exports.clearStoredSession = clearStoredSession;
|
|
1139
|
+
exports.createLoginExperienceAdapter = createLoginExperienceAdapter;
|
|
1140
|
+
exports.createLogtoDirectSignInRequest = createLogtoDirectSignInRequest;
|
|
1005
1141
|
exports.createPostLoginPathHelpers = createPostLoginPathHelpers;
|
|
1006
1142
|
exports.createUserManager = createUserManager;
|
|
1007
1143
|
exports.experiencePasswordSignIn = experiencePasswordSignIn;
|
|
@@ -1017,6 +1153,8 @@ exports.readIdpConfigFromEnv = readIdpConfigFromEnv;
|
|
|
1017
1153
|
exports.resetUserManager = resetUserManager;
|
|
1018
1154
|
exports.resolveExperienceApiBase = resolveExperienceApiBase;
|
|
1019
1155
|
exports.resolveIssuer = resolveIssuer;
|
|
1156
|
+
exports.resolveLoginExperienceAdapter = resolveLoginExperienceAdapter;
|
|
1157
|
+
exports.resolveLoginExperienceProviderFromEnv = resolveLoginExperienceProviderFromEnv;
|
|
1020
1158
|
exports.sameOriginAuthorizeUrl = sameOriginAuthorizeUrl;
|
|
1021
1159
|
exports.signInPopup = signInPopup;
|
|
1022
1160
|
exports.signInRedirect = signInRedirect;
|