@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.
@@ -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 (!window.PublicKeyCredential || !navigator.credentials) {
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
- // Allauth kann hier 200 (eingeloggt) oder 401 (z.B. Email noch nicht verifiziert) liefern :contentReference[oaicite:5]{index=5}
243
- await loginWithPasskeyComplete(credentialJson);
244
- // Danach wie bei Passwort-Login aktuellen User laden
245
- const user = await fetchCurrentUser();
246
- return user;
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 = async (provider) => {
11
+ const handleSocialClick = (provider) => {
11
12
  setMessage('');
12
13
  setError('');
13
14
  try {
14
- // FIX: Await the async function to catch network errors
15
- await authApi.startSocialLogin(provider);
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.');
@@ -40,7 +40,6 @@ export function LoginPage() {
40
40
  authApi.startSocialLogin(providerKey);
41
41
  }
42
42
  catch (err) {
43
- // eslint-disable-next-line no-console
44
43
  console.error('Social login init failed', err);
45
44
  setError('Could not start social login.');
46
45
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@micha.bigler/ui-core-micha",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "private": false,
@@ -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 (!window.PublicKeyCredential || !navigator.credentials) {
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 publicKeyOptions =
273
- window.PublicKeyCredential.parseCreationOptionsFromJSON(optionsJson);
280
+ const optionsJson = await registerPasskeyStart({ passwordless: true });
274
281
 
275
- const credential = await navigator.credentials.create({
276
- publicKey: publicKeyOptions,
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 publicKeyOptions =
317
- window.PublicKeyCredential.parseRequestOptionsFromJSON(optionsJson);
333
+ const optionsJson = await loginWithPasskeyStart();
318
334
 
319
- const assertion = await navigator.credentials.get({
320
- publicKey: publicKeyOptions,
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
- // Allauth kann hier 200 (eingeloggt) oder 401 (z.B. Email noch nicht verifiziert) liefern :contentReference[oaicite:5]{index=5}
330
- await loginWithPasskeyComplete(credentialJson);
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
- // Danach wie bei Passwort-Login aktuellen User laden
333
- const user = await fetchCurrentUser();
334
- return user;
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 = async (provider) => {
19
+ const handleSocialClick = (provider) => {
19
20
  setMessage('');
20
21
  setError('');
21
22
  try {
22
- // FIX: Await the async function to catch network errors
23
- await authApi.startSocialLogin(provider);
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
  }
@@ -41,7 +41,6 @@ export function LoginPage() {
41
41
  try {
42
42
  authApi.startSocialLogin(providerKey);
43
43
  } catch (err) {
44
- // eslint-disable-next-line no-console
45
44
  console.error('Social login init failed', err);
46
45
  setError('Could not start social login.');
47
46
  }