@micha.bigler/ui-core-micha 1.2.1 → 1.2.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/dist/auth/authApi.js
CHANGED
|
@@ -23,11 +23,14 @@ function getCsrfToken() {
|
|
|
23
23
|
return match ? match[1] : null;
|
|
24
24
|
}
|
|
25
25
|
const hasJsonWebAuthn = typeof window !== 'undefined' &&
|
|
26
|
-
window.PublicKeyCredential &&
|
|
26
|
+
typeof window.PublicKeyCredential !== 'undefined' &&
|
|
27
27
|
typeof window.PublicKeyCredential.parseCreationOptionsFromJSON === 'function' &&
|
|
28
28
|
typeof window.PublicKeyCredential.parseRequestOptionsFromJSON === 'function';
|
|
29
29
|
function ensureWebAuthnSupport() {
|
|
30
|
-
if (
|
|
30
|
+
if (typeof window === 'undefined' ||
|
|
31
|
+
typeof navigator === 'undefined' ||
|
|
32
|
+
!window.PublicKeyCredential ||
|
|
33
|
+
!navigator.credentials) {
|
|
31
34
|
throw new Error('Passkeys are not supported in this browser.');
|
|
32
35
|
}
|
|
33
36
|
}
|
|
@@ -141,6 +144,10 @@ export async function logoutSession() {
|
|
|
141
144
|
* 2. Uses the correct path '/providers/{provider}/login'.
|
|
142
145
|
*/
|
|
143
146
|
export function startSocialLogin(provider) {
|
|
147
|
+
// Basic safety check to avoid runtime errors in non-browser environments
|
|
148
|
+
if (typeof window === 'undefined') {
|
|
149
|
+
throw new Error('Social login is only available in a browser environment.');
|
|
150
|
+
}
|
|
144
151
|
window.location.href = `/accounts/${provider}/login/?process=login`;
|
|
145
152
|
}
|
|
146
153
|
/**
|
|
@@ -201,14 +208,24 @@ async function registerPasskeyComplete(credentialJson, name = 'Passkey') {
|
|
|
201
208
|
// High-level Helper, den das UI aufruft
|
|
202
209
|
export async function registerPasskey(name = 'Passkey') {
|
|
203
210
|
ensureWebAuthnSupport();
|
|
204
|
-
const optionsJson = await registerPasskeyStart({ passwordless: true });
|
|
205
211
|
if (!hasJsonWebAuthn) {
|
|
206
|
-
throw new Error('Passkey JSON helpers not available in this browser.');
|
|
212
|
+
throw new Error('Passkey JSON helpers are not available in this browser.');
|
|
213
|
+
}
|
|
214
|
+
const optionsJson = await registerPasskeyStart({ passwordless: true });
|
|
215
|
+
let credential;
|
|
216
|
+
try {
|
|
217
|
+
const publicKeyOptions = window.PublicKeyCredential.parseCreationOptionsFromJSON(optionsJson);
|
|
218
|
+
credential = await navigator.credentials.create({
|
|
219
|
+
publicKey: publicKeyOptions,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
// Typischer Fall: User klickt "Abbrechen"
|
|
224
|
+
if (err && err.name === 'NotAllowedError') {
|
|
225
|
+
throw new Error('Passkey creation was cancelled by the user.');
|
|
226
|
+
}
|
|
227
|
+
throw err;
|
|
207
228
|
}
|
|
208
|
-
const publicKeyOptions = window.PublicKeyCredential.parseCreationOptionsFromJSON(optionsJson);
|
|
209
|
-
const credential = await navigator.credentials.create({
|
|
210
|
-
publicKey: publicKeyOptions,
|
|
211
|
-
});
|
|
212
229
|
if (!credential) {
|
|
213
230
|
throw new Error('Passkey creation was cancelled.');
|
|
214
231
|
}
|
|
@@ -227,23 +244,47 @@ async function loginWithPasskeyComplete(credentialJson) {
|
|
|
227
244
|
// Public API, die du im UI verwendest
|
|
228
245
|
export async function loginWithPasskey() {
|
|
229
246
|
ensureWebAuthnSupport();
|
|
230
|
-
const optionsJson = await loginWithPasskeyStart();
|
|
231
247
|
if (!hasJsonWebAuthn) {
|
|
232
|
-
throw new Error('Passkey JSON helpers not available in this browser.');
|
|
248
|
+
throw new Error('Passkey JSON helpers are not available in this browser.');
|
|
249
|
+
}
|
|
250
|
+
const optionsJson = await loginWithPasskeyStart();
|
|
251
|
+
let assertion;
|
|
252
|
+
try {
|
|
253
|
+
const publicKeyOptions = window.PublicKeyCredential.parseRequestOptionsFromJSON(optionsJson);
|
|
254
|
+
assertion = await navigator.credentials.get({
|
|
255
|
+
publicKey: publicKeyOptions,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
catch (err) {
|
|
259
|
+
if (err && err.name === 'NotAllowedError') {
|
|
260
|
+
// User hat abgebrochen oder Timeout
|
|
261
|
+
throw new Error('Passkey authentication was cancelled by the user.');
|
|
262
|
+
}
|
|
263
|
+
throw err;
|
|
233
264
|
}
|
|
234
|
-
const publicKeyOptions = window.PublicKeyCredential.parseRequestOptionsFromJSON(optionsJson);
|
|
235
|
-
const assertion = await navigator.credentials.get({
|
|
236
|
-
publicKey: publicKeyOptions,
|
|
237
|
-
});
|
|
238
265
|
if (!assertion) {
|
|
239
266
|
throw new Error('Passkey authentication was cancelled.');
|
|
240
267
|
}
|
|
241
268
|
const credentialJson = assertion.toJSON();
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
269
|
+
let postError = null;
|
|
270
|
+
try {
|
|
271
|
+
await loginWithPasskeyComplete(credentialJson);
|
|
272
|
+
}
|
|
273
|
+
catch (err) {
|
|
274
|
+
// z.B. 401, obwohl das Credential eigentlich ok war
|
|
275
|
+
postError = err;
|
|
276
|
+
}
|
|
277
|
+
// Versuchen, die aktuelle Session zu laden
|
|
278
|
+
try {
|
|
279
|
+
const user = await fetchCurrentUser();
|
|
280
|
+
return { user }; // <--- wie loginWithPassword
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
if (postError) {
|
|
284
|
+
throw new Error(extractErrorMessage(postError));
|
|
285
|
+
}
|
|
286
|
+
throw new Error(extractErrorMessage(err));
|
|
287
|
+
}
|
|
247
288
|
}
|
|
248
289
|
export const authApi = {
|
|
249
290
|
fetchCurrentUser,
|
|
@@ -4,15 +4,16 @@ import { Box, Typography, Divider, Button, Stack, Alert, } from '@mui/material';
|
|
|
4
4
|
import PasswordChangeForm from './PasswordChangeForm';
|
|
5
5
|
import SocialLoginButtons from './SocialLoginButtons';
|
|
6
6
|
import { authApi } from '../auth/authApi';
|
|
7
|
+
import { FEATURES } from '../auth/authConfig';
|
|
7
8
|
const SecurityComponent = () => {
|
|
8
9
|
const [message, setMessage] = useState('');
|
|
9
10
|
const [error, setError] = useState('');
|
|
10
|
-
const handleSocialClick =
|
|
11
|
+
const handleSocialClick = (provider) => {
|
|
11
12
|
setMessage('');
|
|
12
13
|
setError('');
|
|
13
14
|
try {
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
authApi.startSocialLogin(provider);
|
|
16
|
+
// Ab hier verlässt der Browser normalerweise die Seite
|
|
16
17
|
}
|
|
17
18
|
catch (e) {
|
|
18
19
|
setError(e.message || 'Social login could not be started.');
|
package/dist/pages/LoginPage.js
CHANGED
package/package.json
CHANGED
package/src/auth/authApi.jsx
CHANGED
|
@@ -25,12 +25,17 @@ function getCsrfToken() {
|
|
|
25
25
|
|
|
26
26
|
const hasJsonWebAuthn =
|
|
27
27
|
typeof window !== 'undefined' &&
|
|
28
|
-
window.PublicKeyCredential &&
|
|
28
|
+
typeof window.PublicKeyCredential !== 'undefined' &&
|
|
29
29
|
typeof window.PublicKeyCredential.parseCreationOptionsFromJSON === 'function' &&
|
|
30
30
|
typeof window.PublicKeyCredential.parseRequestOptionsFromJSON === 'function';
|
|
31
31
|
|
|
32
32
|
function ensureWebAuthnSupport() {
|
|
33
|
-
if (
|
|
33
|
+
if (
|
|
34
|
+
typeof window === 'undefined' ||
|
|
35
|
+
typeof navigator === 'undefined' ||
|
|
36
|
+
!window.PublicKeyCredential ||
|
|
37
|
+
!navigator.credentials
|
|
38
|
+
) {
|
|
34
39
|
throw new Error('Passkeys are not supported in this browser.');
|
|
35
40
|
}
|
|
36
41
|
}
|
|
@@ -167,6 +172,11 @@ export async function logoutSession() {
|
|
|
167
172
|
* 2. Uses the correct path '/providers/{provider}/login'.
|
|
168
173
|
*/
|
|
169
174
|
export function startSocialLogin(provider) {
|
|
175
|
+
// Basic safety check to avoid runtime errors in non-browser environments
|
|
176
|
+
if (typeof window === 'undefined') {
|
|
177
|
+
throw new Error('Social login is only available in a browser environment.');
|
|
178
|
+
}
|
|
179
|
+
|
|
170
180
|
window.location.href = `/accounts/${provider}/login/?process=login`;
|
|
171
181
|
}
|
|
172
182
|
|
|
@@ -263,18 +273,27 @@ async function registerPasskeyComplete(credentialJson, name = 'Passkey') {
|
|
|
263
273
|
export async function registerPasskey(name = 'Passkey') {
|
|
264
274
|
ensureWebAuthnSupport();
|
|
265
275
|
|
|
266
|
-
const optionsJson = await registerPasskeyStart({ passwordless: true });
|
|
267
|
-
|
|
268
276
|
if (!hasJsonWebAuthn) {
|
|
269
|
-
throw new Error('Passkey JSON helpers not available in this browser.');
|
|
277
|
+
throw new Error('Passkey JSON helpers are not available in this browser.');
|
|
270
278
|
}
|
|
271
279
|
|
|
272
|
-
const
|
|
273
|
-
window.PublicKeyCredential.parseCreationOptionsFromJSON(optionsJson);
|
|
280
|
+
const optionsJson = await registerPasskeyStart({ passwordless: true });
|
|
274
281
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
282
|
+
let credential;
|
|
283
|
+
try {
|
|
284
|
+
const publicKeyOptions =
|
|
285
|
+
window.PublicKeyCredential.parseCreationOptionsFromJSON(optionsJson);
|
|
286
|
+
|
|
287
|
+
credential = await navigator.credentials.create({
|
|
288
|
+
publicKey: publicKeyOptions,
|
|
289
|
+
});
|
|
290
|
+
} catch (err) {
|
|
291
|
+
// Typischer Fall: User klickt "Abbrechen"
|
|
292
|
+
if (err && err.name === 'NotAllowedError') {
|
|
293
|
+
throw new Error('Passkey creation was cancelled by the user.');
|
|
294
|
+
}
|
|
295
|
+
throw err;
|
|
296
|
+
}
|
|
278
297
|
|
|
279
298
|
if (!credential) {
|
|
280
299
|
throw new Error('Passkey creation was cancelled.');
|
|
@@ -307,18 +326,27 @@ async function loginWithPasskeyComplete(credentialJson) {
|
|
|
307
326
|
export async function loginWithPasskey() {
|
|
308
327
|
ensureWebAuthnSupport();
|
|
309
328
|
|
|
310
|
-
const optionsJson = await loginWithPasskeyStart();
|
|
311
|
-
|
|
312
329
|
if (!hasJsonWebAuthn) {
|
|
313
|
-
throw new Error('Passkey JSON helpers not available in this browser.');
|
|
330
|
+
throw new Error('Passkey JSON helpers are not available in this browser.');
|
|
314
331
|
}
|
|
315
332
|
|
|
316
|
-
const
|
|
317
|
-
window.PublicKeyCredential.parseRequestOptionsFromJSON(optionsJson);
|
|
333
|
+
const optionsJson = await loginWithPasskeyStart();
|
|
318
334
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
335
|
+
let assertion;
|
|
336
|
+
try {
|
|
337
|
+
const publicKeyOptions =
|
|
338
|
+
window.PublicKeyCredential.parseRequestOptionsFromJSON(optionsJson);
|
|
339
|
+
|
|
340
|
+
assertion = await navigator.credentials.get({
|
|
341
|
+
publicKey: publicKeyOptions,
|
|
342
|
+
});
|
|
343
|
+
} catch (err) {
|
|
344
|
+
if (err && err.name === 'NotAllowedError') {
|
|
345
|
+
// User hat abgebrochen oder Timeout
|
|
346
|
+
throw new Error('Passkey authentication was cancelled by the user.');
|
|
347
|
+
}
|
|
348
|
+
throw err;
|
|
349
|
+
}
|
|
322
350
|
|
|
323
351
|
if (!assertion) {
|
|
324
352
|
throw new Error('Passkey authentication was cancelled.');
|
|
@@ -326,12 +354,24 @@ export async function loginWithPasskey() {
|
|
|
326
354
|
|
|
327
355
|
const credentialJson = assertion.toJSON();
|
|
328
356
|
|
|
329
|
-
|
|
330
|
-
|
|
357
|
+
let postError = null;
|
|
358
|
+
try {
|
|
359
|
+
await loginWithPasskeyComplete(credentialJson);
|
|
360
|
+
} catch (err) {
|
|
361
|
+
// z.B. 401, obwohl das Credential eigentlich ok war
|
|
362
|
+
postError = err;
|
|
363
|
+
}
|
|
331
364
|
|
|
332
|
-
//
|
|
333
|
-
|
|
334
|
-
|
|
365
|
+
// Versuchen, die aktuelle Session zu laden
|
|
366
|
+
try {
|
|
367
|
+
const user = await fetchCurrentUser();
|
|
368
|
+
return { user }; // <--- wie loginWithPassword
|
|
369
|
+
} catch (err) {
|
|
370
|
+
if (postError) {
|
|
371
|
+
throw new Error(extractErrorMessage(postError));
|
|
372
|
+
}
|
|
373
|
+
throw new Error(extractErrorMessage(err));
|
|
374
|
+
}
|
|
335
375
|
}
|
|
336
376
|
|
|
337
377
|
|
|
@@ -10,17 +10,18 @@ import {
|
|
|
10
10
|
import PasswordChangeForm from './PasswordChangeForm';
|
|
11
11
|
import SocialLoginButtons from './SocialLoginButtons';
|
|
12
12
|
import { authApi } from '../auth/authApi';
|
|
13
|
+
import { FEATURES } from '../auth/authConfig';
|
|
13
14
|
|
|
14
15
|
const SecurityComponent = () => {
|
|
15
16
|
const [message, setMessage] = useState('');
|
|
16
17
|
const [error, setError] = useState('');
|
|
17
18
|
|
|
18
|
-
const handleSocialClick =
|
|
19
|
+
const handleSocialClick = (provider) => {
|
|
19
20
|
setMessage('');
|
|
20
21
|
setError('');
|
|
21
22
|
try {
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
authApi.startSocialLogin(provider);
|
|
24
|
+
// Ab hier verlässt der Browser normalerweise die Seite
|
|
24
25
|
} catch (e) {
|
|
25
26
|
setError(e.message || 'Social login could not be started.');
|
|
26
27
|
}
|
package/src/pages/LoginPage.jsx
CHANGED