react-native-nitro-auth 0.6.3 → 0.6.4

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/CHANGELOG.md CHANGED
@@ -1,38 +1,41 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.4 - 2026-06-11
4
+
5
+ ### Added
6
+
7
+ - Added a modern `exports` map with `react-native`, `browser`, `import`, and `require` conditions plus explicit `./app.plugin`, `./app.plugin.js`, and `./package.json` subpaths, so bundlers and Node resolve the package deterministically.
8
+
9
+ ### Changed
10
+
11
+ - Strengthened the package TypeScript configuration (`exactOptionalPropertyTypes`, `noUncheckedIndexedAccess`, `noImplicitOverride`, `noImplicitReturns`, `noFallthroughCasesInSwitch`, `useUnknownInCatchVariables`) so editor and tooling diagnostics catch more mistakes at compile time.
12
+
13
+ ### Fixed
14
+
15
+ - Encoded iOS Microsoft token request bodies as form data so authorization codes, redirect URIs, and refresh tokens containing reserved characters are posted correctly.
16
+
3
17
  ## 0.6.3 - 2026-06-10
4
18
 
5
19
  ### Fixed
6
20
 
7
21
  - Hardened Microsoft authority URL construction to reject absolute tenant URLs and invalid B2C domains while building valid B2C tenant/policy authority paths.
8
- - Kept the Expo example iOS build on source-built Expo modules until precompiled module linking is supported by the current native dependency set.
9
22
 
10
23
  ### Changed
11
24
 
12
- - Updated the Expo example SDK 56 patch dependencies so Expo Doctor passes cleanly.
13
- - Polished the Expo example app UI for more consistent provider cards, controls, smoke-test status, and action states.
14
- - Reduced unnecessary example-app React work by memoizing repeated demo rows, smoke-test UI, social buttons, and stable action handlers.
15
- - Updated README setup, provider examples, option tables, badge links, error codes, and typed API documentation to match the current package surface.
25
+ - Updated README setup, provider examples, option tables, error codes, and typed API documentation to match the current package surface.
16
26
  - Added stronger compile-time coverage for provider-specific login options used by `AuthService.login()` and `useAuth().login()`.
17
27
 
18
28
  ## 0.6.1 - 2026-05-21
19
29
 
20
30
  ### Changed
21
31
 
22
- - Updated the package and Expo example baseline to Expo SDK 56, React Native 0.85.3, React 19.2.3, TypeScript 6.0.3, Nitro Modules 0.35.7, and nitrogen 0.35.7.
32
+ - Updated the package baseline to Expo SDK 56, React Native 0.85.3, React 19.2.3, TypeScript 6.0.3, Nitro Modules 0.35.7, and nitrogen 0.35.7.
23
33
  - Raised the iOS deployment target to 16.4 for SDK 56 compatibility.
24
- - Added release preflight checks for Expo dependency validation, Expo Doctor, config introspection, package build, tests, C++ tests, and publish dry run.
25
- - Simplified the example app by keeping provider-specific advanced options collapsed by default.
26
- - Updated README badges, setup commands, release checks, and typed API examples to match the 0.6.1 package state.
27
34
  - Added compile-time coverage for provider-specific login option types.
28
- - Added CI setup and versioned tool detection for LLVM C++ coverage tools.
29
- - Added a CI-safe release preflight mode that skips unauthenticated npm publish dry runs while keeping local publish dry runs intact.
30
35
 
31
36
  ### Fixed
32
37
 
33
38
  - Retained the active iOS Apple Sign-In controller until completion to avoid premature native lifecycle cleanup.
34
- - Removed the example app's import-time native logging side effect.
35
- - Removed Turbo cache-output warnings from lint and typecheck tasks.
36
39
 
37
40
  ## 0.6.0 - 2026-05-14
38
41
 
@@ -42,7 +45,7 @@
42
45
  - Added Apple nonce and authorization-code/user-id result support.
43
46
  - Added Microsoft tenant and prompt option coverage across native and web flows.
44
47
  - Added `revokeAccess()` to the native/web auth API and `useAuth()` hook.
45
- - Added native logging hooks and platform-gated example controls for supported provider options only.
48
+ - Added native logging hooks.
46
49
  - Added provider-specific TypeScript option types for `AuthService.login()` and `useAuth().login()`.
47
50
 
48
51
  ### Changed
@@ -53,86 +56,36 @@
53
56
 
54
57
  ### Fixed
55
58
 
56
- - Fixed Android Metro watcher noise from transient Bun `node_modules/.old-*` directories in the example app.
57
59
  - Fixed Android Google cancellation handling so cancellations are not reported as unknown failures.
58
60
  - Fixed native session cleanup paths to reject pending work before clearing provider state.
59
61
 
60
62
  ## 0.5.12 - 2026-05-13
61
63
 
62
- ### Changed
63
-
64
- - Updated the Expo example to the current SDK 55 recommended `expo`, `expo-build-properties`, and `expo-system-ui` patch ranges.
65
-
66
64
  ### Fixed
67
65
 
68
- - Fixed the Android example launcher icon by adding an adaptive icon foreground and dark brand background.
69
66
  - Normalized web `SocialButton` and native login failures so presentation-anchor and missing-code errors surface as stable `AuthError` codes.
70
67
  - Shipped package-level Watchman ignores for Android CMake cache output so consumers avoid noisy native build watcher events.
71
68
 
72
69
  ## 0.5.11 - 2026-05-05
73
70
 
74
- ### Changed
75
-
76
- - Updated the Expo example to the Expo SDK 55 recommended `expo@~55.0.23` patch and Android API 36 target.
77
-
78
71
  ### Fixed
79
72
 
80
73
  - Wrapped synchronous native service failures in `AuthError` so public service errors keep a consistent code contract.
81
74
 
82
- ### Verified
83
-
84
- - `bun install --frozen-lockfile`
85
- - `bunx expo install --check --cwd apps/example`
86
- - `bunx expo-doctor@latest apps/example`
87
- - `bun run check:ci`
88
- - `bun run --cwd packages/react-native-nitro-auth test:coverage -- --runInBand`
89
- - `bun run --cwd packages/react-native-nitro-auth test:cpp:coverage`
90
- - `bun run example:prebuild`
91
- - `bun run publish-package:dry-run`
92
-
93
75
  ## 0.5.10 - 2026-04-27
94
76
 
95
77
  ### Fixed
96
78
 
97
79
  - Fixed iOS Microsoft sign-in so `ASWebAuthenticationSession` is retained until callback or cancellation and duplicate sessions fail with `operation_in_progress`.
98
- - Fixed the example app header so it displays the current package version.
99
-
100
- ### Verified
101
-
102
- - `bun run check:ci`
103
- - `bunx expo install --check --cwd apps/example`
104
- - `bunx expo-doctor@latest apps/example`
105
- - `bun run example:prebuild`
106
- - `bun run publish-package:dry-run`
107
80
 
108
81
  ## 0.5.9 - 2026-04-24
109
82
 
110
- ### Added
111
-
112
- - Added shared JS service factory coverage and logger behavior tests.
113
- - Added C++ coverage support with `test:cpp:coverage` and expanded native tests for session restore, token refresh, access-token fallback, scope updates, listener isolation, logout cancellation, and serializer behavior.
114
- - Added example-app smoke checks for the public auth API, provider support, platform-gated behavior, and session-dependent methods.
115
-
116
83
  ### Changed
117
84
 
118
- - Updated Expo SDK 55 patch dependencies, React Native 0.83.6, Nitro Modules 0.35.5, and related build tooling.
85
+ - Updated Expo SDK 55 patch dependencies, React Native 0.83.6, and Nitro Modules 0.35.5.
119
86
  - Refactored native/web `AuthService` creation so native and web error mapping stay consistent.
120
87
  - Hardened web OAuth state, cache parsing, token refresh, and provider error handling.
121
- - Improved example app handling for unsupported providers and unavailable session actions.
122
- - Improved release validation to include JS and C++ coverage gates and a faster publish dry run path.
123
88
 
124
89
  ### Fixed
125
90
 
126
- - Fixed native runtime crashes in the example app when optional Nitro methods are not available on the installed native object.
127
- - Fixed startup `silentRestore()` errors in the example app so restore failures surface in status instead of becoming unhandled promises.
128
91
  - Excluded C++ test sources from the iOS pod target to avoid app-target duplicate `main` symbols.
129
-
130
- ### Verified
131
-
132
- - `bun run check:ci`
133
- - `bun run --cwd packages/react-native-nitro-auth test:coverage -- --runInBand`
134
- - `bun run --cwd packages/react-native-nitro-auth test:cpp:coverage`
135
- - `bun run publish-package:dry-run`
136
- - `bun run example:prebuild`
137
- - `bun run example:android`
138
- - `bun run example:ios`
package/README.md CHANGED
@@ -1,9 +1,11 @@
1
1
  # react-native-nitro-auth
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/react-native-nitro-auth?color=f97316&label=npm)](https://www.npmjs.com/package/react-native-nitro-auth)
4
+ [![npm downloads](https://img.shields.io/npm/dm/react-native-nitro-auth?color=22c55e&label=downloads)](https://www.npmjs.com/package/react-native-nitro-auth)
5
+ [![CI](https://github.com/JoaoPauloCMarra/react-native-nitro-auth/actions/workflows/ci.yml/badge.svg)](https://github.com/JoaoPauloCMarra/react-native-nitro-auth/actions/workflows/ci.yml)
4
6
  [![license](https://img.shields.io/npm/l/react-native-nitro-auth?color=007ec6)](https://github.com/JoaoPauloCMarra/react-native-nitro-auth/blob/main/LICENSE)
5
7
  [![React Native](https://img.shields.io/badge/react--native-%3E%3D0.75-61dafb)](https://reactnative.dev/)
6
- [![Expo](https://img.shields.io/badge/expo-SDK%2056-000020)](https://expo.dev/)
8
+ [![Expo](https://img.shields.io/badge/expo-SDK%2056-000020)](https://docs.expo.dev/)
7
9
  [![Nitro Modules](https://img.shields.io/badge/nitro--modules-%3E%3D0.35.7-black)](https://nitro.margelo.com/)
8
10
  [![TypeScript](https://img.shields.io/badge/typescript-6.0-3178c6)](https://www.typescriptlang.org/)
9
11
 
@@ -47,7 +47,6 @@ object AuthAdapter {
47
47
  private var currentActivity: Activity? = null
48
48
  private var googleSignInClient: GoogleSignInClient? = null
49
49
  private var lifecycleCallbacks: Application.ActivityLifecycleCallbacks? = null
50
- private var pendingScopes: List<String> = emptyList()
51
50
  private var pendingMicrosoftScopes: List<String> = emptyList()
52
51
 
53
52
  @Volatile
@@ -212,7 +211,6 @@ object AuthAdapter {
212
211
  }
213
212
 
214
213
  val requestedScopes = scopes?.toList() ?: listOf("email", "profile")
215
- pendingScopes = requestedScopes
216
214
 
217
215
  if (useLegacyGoogleSignIn || forceAccountPicker) {
218
216
  loginLegacy(context, clientId, requestedScopes, loginHint, forceAccountPicker, forceCodeForRefreshToken, hostedDomain, "login")
@@ -727,7 +725,7 @@ object AuthAdapter {
727
725
  val ctx = appContext ?: context.applicationContext
728
726
  val account = GoogleSignIn.getLastSignedInAccount(ctx)
729
727
  if (account != null) {
730
- if (googleSignInClient == null) {
728
+ val client = googleSignInClient ?: run {
731
729
  val clientId = getClientIdFromResources(ctx)
732
730
  if (clientId == null) {
733
731
  nativeOnRefreshError("configuration_error", "Google Client ID not configured")
@@ -738,9 +736,11 @@ object AuthAdapter {
738
736
  .requestServerAuthCode(clientId)
739
737
  .requestEmail()
740
738
  .build()
741
- googleSignInClient = GoogleSignIn.getClient(ctx, gso)
739
+ GoogleSignIn.getClient(ctx, gso).also {
740
+ googleSignInClient = it
741
+ }
742
742
  }
743
- googleSignInClient!!.silentSignIn().addOnCompleteListener { task ->
743
+ client.silentSignIn().addOnCompleteListener { task ->
744
744
  if (task.isSuccessful) {
745
745
  val acc = task.result
746
746
  nativeOnRefreshSuccess(acc?.idToken, null, getJwtExpirationTimeMs(acc?.idToken))
@@ -292,6 +292,21 @@ public class AuthAdapter: NSObject {
292
292
  .replacingOccurrences(of: "/", with: "_")
293
293
  .replacingOccurrences(of: "=", with: "")
294
294
  }
295
+
296
+ private static let formUrlEncodedAllowedCharacters = CharacterSet(
297
+ charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"
298
+ )
299
+
300
+ private static func formUrlEncodedBody(_ params: [String: String]) -> Data? {
301
+ params
302
+ .map { key, value in
303
+ let encodedKey = key.addingPercentEncoding(withAllowedCharacters: formUrlEncodedAllowedCharacters) ?? key
304
+ let encodedValue = value.addingPercentEncoding(withAllowedCharacters: formUrlEncodedAllowedCharacters) ?? value
305
+ return "\(encodedKey)=\(encodedValue)"
306
+ }
307
+ .joined(separator: "&")
308
+ .data(using: .utf8)
309
+ }
295
310
 
296
311
  private static func exchangeCodeForTokens(
297
312
  code: String,
@@ -322,10 +337,7 @@ public class AuthAdapter: NSObject {
322
337
  "code_verifier": codeVerifier
323
338
  ]
324
339
 
325
- request.httpBody = bodyParams
326
- .map { "\($0.key)=\($0.value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? $0.value)" }
327
- .joined(separator: "&")
328
- .data(using: .utf8)
340
+ request.httpBody = formUrlEncodedBody(bodyParams)
329
341
 
330
342
  URLSession.shared.dataTask(with: request) { data, response, error in
331
343
  DispatchQueue.main.async {
@@ -606,10 +618,7 @@ public class AuthAdapter: NSObject {
606
618
  "refresh_token": refreshToken
607
619
  ]
608
620
 
609
- request.httpBody = bodyParams
610
- .map { "\($0.key)=\($0.value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? $0.value)" }
611
- .joined(separator: "&")
612
- .data(using: .utf8)
621
+ request.httpBody = formUrlEncodedBody(bodyParams)
613
622
 
614
623
  URLSession.shared.dataTask(with: request) { data, response, error in
615
624
  DispatchQueue.main.async {
@@ -692,10 +701,7 @@ public class AuthAdapter: NSObject {
692
701
  "grant_type": "refresh_token",
693
702
  "refresh_token": refreshToken
694
703
  ]
695
- request.httpBody = bodyParams
696
- .map { "\($0.key)=\($0.value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? $0.value)" }
697
- .joined(separator: "&")
698
- .data(using: .utf8)
704
+ request.httpBody = formUrlEncodedBody(bodyParams)
699
705
  URLSession.shared.dataTask(with: request) { data, response, error in
700
706
  DispatchQueue.main.async {
701
707
  if error != nil {
@@ -43,6 +43,18 @@ const getOptionalNumber = (source, key) => {
43
43
  const candidate = source[key];
44
44
  return typeof candidate === "number" && Number.isFinite(candidate) ? candidate : undefined;
45
45
  };
46
+ function setIfDefined(target, key, value) {
47
+ if (value !== undefined) {
48
+ target[key] = value;
49
+ }
50
+ }
51
+ function setOrDelete(target, key, value) {
52
+ if (value === undefined) {
53
+ delete target[key];
54
+ return;
55
+ }
56
+ target[key] = value;
57
+ }
46
58
  const parseExpiresInSeconds = value => {
47
59
  const candidate = typeof value === "number" ? value : typeof value === "string" && value.trim().length > 0 ? Number(value) : undefined;
48
60
  if (typeof candidate !== "number" || !Number.isFinite(candidate) || candidate < 0) {
@@ -56,19 +68,20 @@ const parseAuthUser = value => {
56
68
  }
57
69
  const scopesCandidate = value.scopes;
58
70
  const scopes = Array.isArray(scopesCandidate) ? scopesCandidate.filter(scope => typeof scope === "string") : undefined;
59
- return {
60
- provider: value.provider,
61
- email: getOptionalString(value, "email"),
62
- name: getOptionalString(value, "name"),
63
- photo: getOptionalString(value, "photo"),
64
- idToken: getOptionalString(value, "idToken"),
65
- accessToken: getOptionalString(value, "accessToken"),
66
- refreshToken: getOptionalString(value, "refreshToken"),
67
- serverAuthCode: getOptionalString(value, "serverAuthCode"),
68
- scopes,
69
- expirationTime: getOptionalNumber(value, "expirationTime"),
70
- underlyingError: getOptionalString(value, "underlyingError")
71
+ const user = {
72
+ provider: value.provider
71
73
  };
74
+ setIfDefined(user, "email", getOptionalString(value, "email"));
75
+ setIfDefined(user, "name", getOptionalString(value, "name"));
76
+ setIfDefined(user, "photo", getOptionalString(value, "photo"));
77
+ setIfDefined(user, "idToken", getOptionalString(value, "idToken"));
78
+ setIfDefined(user, "accessToken", getOptionalString(value, "accessToken"));
79
+ setIfDefined(user, "refreshToken", getOptionalString(value, "refreshToken"));
80
+ setIfDefined(user, "serverAuthCode", getOptionalString(value, "serverAuthCode"));
81
+ setIfDefined(user, "scopes", scopes);
82
+ setIfDefined(user, "expirationTime", getOptionalNumber(value, "expirationTime"));
83
+ setIfDefined(user, "underlyingError", getOptionalString(value, "underlyingError"));
84
+ return user;
72
85
  };
73
86
  const parseScopes = value => {
74
87
  if (!Array.isArray(value)) {
@@ -83,15 +96,15 @@ const parseAuthWebExtraConfig = value => {
83
96
  }
84
97
  const nitroAuthWebStorageCandidate = value.nitroAuthWebStorage;
85
98
  const nitroAuthWebStorage = nitroAuthWebStorageCandidate === STORAGE_MODE_SESSION || nitroAuthWebStorageCandidate === STORAGE_MODE_LOCAL || nitroAuthWebStorageCandidate === STORAGE_MODE_MEMORY ? nitroAuthWebStorageCandidate : undefined;
86
- return {
87
- googleWebClientId: getOptionalString(value, "googleWebClientId"),
88
- microsoftClientId: getOptionalString(value, "microsoftClientId"),
89
- microsoftTenant: getOptionalString(value, "microsoftTenant"),
90
- microsoftB2cDomain: getOptionalString(value, "microsoftB2cDomain"),
91
- appleWebClientId: getOptionalString(value, "appleWebClientId"),
92
- nitroAuthWebStorage,
93
- nitroAuthPersistTokensOnWeb: typeof value.nitroAuthPersistTokensOnWeb === "boolean" ? value.nitroAuthPersistTokensOnWeb : undefined
94
- };
99
+ const config = {};
100
+ setIfDefined(config, "googleWebClientId", getOptionalString(value, "googleWebClientId"));
101
+ setIfDefined(config, "microsoftClientId", getOptionalString(value, "microsoftClientId"));
102
+ setIfDefined(config, "microsoftTenant", getOptionalString(value, "microsoftTenant"));
103
+ setIfDefined(config, "microsoftB2cDomain", getOptionalString(value, "microsoftB2cDomain"));
104
+ setIfDefined(config, "appleWebClientId", getOptionalString(value, "appleWebClientId"));
105
+ setIfDefined(config, "nitroAuthWebStorage", nitroAuthWebStorage);
106
+ setIfDefined(config, "nitroAuthPersistTokensOnWeb", typeof value.nitroAuthPersistTokensOnWeb === "boolean" ? value.nitroAuthPersistTokensOnWeb : undefined);
107
+ return config;
95
108
  };
96
109
  const getConfig = () => {
97
110
  try {
@@ -507,19 +520,18 @@ class AuthWeb {
507
520
  const claims = effectiveIdToken ? this.decodeMicrosoftJwt(effectiveIdToken) : {};
508
521
  const user = {
509
522
  ...currentUser,
510
- idToken: effectiveIdToken,
511
- accessToken: accessToken ?? undefined,
512
- refreshToken: newRefreshToken ?? currentUser.refreshToken,
513
- expirationTime,
514
523
  ...claims
515
524
  };
525
+ setOrDelete(user, "idToken", effectiveIdToken);
526
+ setOrDelete(user, "accessToken", accessToken);
527
+ setOrDelete(user, "refreshToken", newRefreshToken ?? currentUser.refreshToken);
528
+ setOrDelete(user, "expirationTime", expirationTime);
516
529
  this.updateUser(user);
517
- const tokens = {
518
- accessToken: accessToken ?? undefined,
519
- idToken: effectiveIdToken,
520
- refreshToken: newRefreshToken ?? undefined,
521
- expirationTime
522
- };
530
+ const tokens = {};
531
+ setIfDefined(tokens, "accessToken", accessToken);
532
+ setIfDefined(tokens, "idToken", effectiveIdToken);
533
+ setIfDefined(tokens, "refreshToken", newRefreshToken);
534
+ setIfDefined(tokens, "expirationTime", expirationTime);
523
535
  this.notifyTokenListeners(tokens);
524
536
  return tokens;
525
537
  }
@@ -529,12 +541,11 @@ class AuthWeb {
529
541
  _logger.logger.log("Refreshing tokens...");
530
542
  await this.runLoginOperation(() => this.loginGoogle(this._grantedScopes.length > 0 ? this._grantedScopes : DEFAULT_SCOPES, undefined, undefined, generation));
531
543
  this.assertActiveGeneration(generation);
532
- const tokens = {
533
- accessToken: this._currentUser.accessToken,
534
- idToken: this._currentUser.idToken,
535
- refreshToken: this._currentUser.refreshToken,
536
- expirationTime: this._currentUser.expirationTime
537
- };
544
+ const tokens = {};
545
+ setIfDefined(tokens, "accessToken", this._currentUser.accessToken);
546
+ setIfDefined(tokens, "idToken", this._currentUser.idToken);
547
+ setIfDefined(tokens, "refreshToken", this._currentUser.refreshToken);
548
+ setIfDefined(tokens, "expirationTime", this._currentUser.expirationTime);
538
549
  this.notifyTokenListeners(tokens);
539
550
  return tokens;
540
551
  }
@@ -742,14 +753,14 @@ class AuthWeb {
742
753
  const user = {
743
754
  provider: "google",
744
755
  idToken,
745
- accessToken: accessToken ?? undefined,
746
- serverAuthCode: code ?? undefined,
747
- userId: getOptionalString(decoded, "sub"),
748
- hostedDomain: getOptionalString(decoded, "hd"),
749
756
  scopes,
750
- expirationTime: this.getExpirationTime(expiresIn),
751
757
  ...this.decodeGoogleJwt(idToken)
752
758
  };
759
+ setIfDefined(user, "accessToken", accessToken ?? undefined);
760
+ setIfDefined(user, "serverAuthCode", code ?? undefined);
761
+ setIfDefined(user, "userId", getOptionalString(decoded, "sub"));
762
+ setIfDefined(user, "hostedDomain", getOptionalString(decoded, "hd"));
763
+ setIfDefined(user, "expirationTime", this.getExpirationTime(expiresIn));
753
764
  this.updateUser(user);
754
765
  }).then(() => {
755
766
  resolve();
@@ -761,13 +772,13 @@ class AuthWeb {
761
772
  decodeGoogleJwt(token) {
762
773
  try {
763
774
  const decoded = this.parseJwtPayload(token);
764
- return {
765
- email: getOptionalString(decoded, "email"),
766
- name: getOptionalString(decoded, "name"),
767
- photo: getOptionalString(decoded, "picture"),
768
- userId: getOptionalString(decoded, "sub"),
769
- hostedDomain: getOptionalString(decoded, "hd")
770
- };
775
+ const user = {};
776
+ setIfDefined(user, "email", getOptionalString(decoded, "email"));
777
+ setIfDefined(user, "name", getOptionalString(decoded, "name"));
778
+ setIfDefined(user, "photo", getOptionalString(decoded, "picture"));
779
+ setIfDefined(user, "userId", getOptionalString(decoded, "sub"));
780
+ setIfDefined(user, "hostedDomain", getOptionalString(decoded, "hd"));
781
+ return user;
771
782
  } catch (error) {
772
783
  _logger.logger.warn("Failed to decode Google ID token", {
773
784
  error: String(error)
@@ -894,12 +905,12 @@ class AuthWeb {
894
905
  const user = {
895
906
  provider: "microsoft",
896
907
  idToken,
897
- accessToken: accessToken ?? undefined,
898
- refreshToken: refreshToken ?? undefined,
899
908
  scopes,
900
- expirationTime: this.getExpirationTime(json["expires_in"]),
901
909
  ...claims
902
910
  };
911
+ setIfDefined(user, "accessToken", accessToken);
912
+ setIfDefined(user, "refreshToken", refreshToken);
913
+ setIfDefined(user, "expirationTime", this.getExpirationTime(json["expires_in"]));
903
914
  this.updateUser(user);
904
915
  }
905
916
  getMicrosoftAuthBaseUrl(tenant, b2cDomain) {
@@ -940,10 +951,10 @@ class AuthWeb {
940
951
  decodeMicrosoftJwt(token) {
941
952
  try {
942
953
  const decoded = this.parseJwtPayload(token);
943
- return {
944
- email: getOptionalString(decoded, "preferred_username") ?? getOptionalString(decoded, "email"),
945
- name: getOptionalString(decoded, "name")
946
- };
954
+ const user = {};
955
+ setIfDefined(user, "email", getOptionalString(decoded, "preferred_username") ?? getOptionalString(decoded, "email"));
956
+ setIfDefined(user, "name", getOptionalString(decoded, "name"));
957
+ return user;
947
958
  } catch (error) {
948
959
  _logger.logger.warn("Failed to decode Microsoft ID token", {
949
960
  error: String(error)
@@ -1003,13 +1014,14 @@ class AuthWeb {
1003
1014
  if (!window.AppleID) {
1004
1015
  throw new Error("Apple SDK not loaded");
1005
1016
  }
1006
- window.AppleID.auth.init({
1017
+ const appleAuthConfig = {
1007
1018
  clientId,
1008
1019
  scope: (options?.scopes?.length ? options.scopes : ["name", "email"]).join(" "),
1009
1020
  redirectURI: window.location.origin,
1010
- nonce: options?.nonce,
1011
1021
  usePopup: true
1012
- });
1022
+ };
1023
+ setIfDefined(appleAuthConfig, "nonce", options?.nonce);
1024
+ window.AppleID.auth.init(appleAuthConfig);
1013
1025
  try {
1014
1026
  const response = await window.AppleID.auth.signIn();
1015
1027
  if (generation !== undefined) {
@@ -1017,11 +1029,11 @@ class AuthWeb {
1017
1029
  }
1018
1030
  const user = {
1019
1031
  provider: "apple",
1020
- idToken: response.authorization.id_token,
1021
- authorizationCode: response.authorization.code,
1022
- email: response.user?.email,
1023
- name: response.user?.name ? `${response.user.name.firstName} ${response.user.name.lastName}`.trim() : undefined
1032
+ idToken: response.authorization.id_token
1024
1033
  };
1034
+ setIfDefined(user, "authorizationCode", response.authorization.code);
1035
+ setIfDefined(user, "email", response.user?.email);
1036
+ setIfDefined(user, "name", response.user?.name ? `${response.user.name.firstName} ${response.user.name.lastName}`.trim() : undefined);
1025
1037
  this.updateUser(user);
1026
1038
  } catch (error) {
1027
1039
  throw this.mapError(error);