react-native-nitro-auth 0.6.2 → 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,6 +1,20 @@
1
1
  # Changelog
2
2
 
3
- ## 0.6.2 - 2026-06-07
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
+
17
+ ## 0.6.3 - 2026-06-10
4
18
 
5
19
  ### Fixed
6
20
 
@@ -8,26 +22,20 @@
8
22
 
9
23
  ### Changed
10
24
 
11
- - Updated the Expo example SDK 56 patch dependencies so Expo Doctor passes cleanly.
25
+ - Updated README setup, provider examples, option tables, error codes, and typed API documentation to match the current package surface.
26
+ - Added stronger compile-time coverage for provider-specific login options used by `AuthService.login()` and `useAuth().login()`.
12
27
 
13
28
  ## 0.6.1 - 2026-05-21
14
29
 
15
30
  ### Changed
16
31
 
17
- - 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.
18
33
  - Raised the iOS deployment target to 16.4 for SDK 56 compatibility.
19
- - Added release preflight checks for Expo dependency validation, Expo Doctor, config introspection, package build, tests, C++ tests, and publish dry run.
20
- - Simplified the example app by keeping provider-specific advanced options collapsed by default.
21
- - Updated README badges, setup commands, release checks, and typed API examples to match the 0.6.1 package state.
22
34
  - Added compile-time coverage for provider-specific login option types.
23
- - Added CI setup and versioned tool detection for LLVM C++ coverage tools.
24
- - Added a CI-safe release preflight mode that skips unauthenticated npm publish dry runs while keeping local publish dry runs intact.
25
35
 
26
36
  ### Fixed
27
37
 
28
38
  - Retained the active iOS Apple Sign-In controller until completion to avoid premature native lifecycle cleanup.
29
- - Removed the example app's import-time native logging side effect.
30
- - Removed Turbo cache-output warnings from lint and typecheck tasks.
31
39
 
32
40
  ## 0.6.0 - 2026-05-14
33
41
 
@@ -37,7 +45,7 @@
37
45
  - Added Apple nonce and authorization-code/user-id result support.
38
46
  - Added Microsoft tenant and prompt option coverage across native and web flows.
39
47
  - Added `revokeAccess()` to the native/web auth API and `useAuth()` hook.
40
- - Added native logging hooks and platform-gated example controls for supported provider options only.
48
+ - Added native logging hooks.
41
49
  - Added provider-specific TypeScript option types for `AuthService.login()` and `useAuth().login()`.
42
50
 
43
51
  ### Changed
@@ -48,86 +56,36 @@
48
56
 
49
57
  ### Fixed
50
58
 
51
- - Fixed Android Metro watcher noise from transient Bun `node_modules/.old-*` directories in the example app.
52
59
  - Fixed Android Google cancellation handling so cancellations are not reported as unknown failures.
53
60
  - Fixed native session cleanup paths to reject pending work before clearing provider state.
54
61
 
55
62
  ## 0.5.12 - 2026-05-13
56
63
 
57
- ### Changed
58
-
59
- - Updated the Expo example to the current SDK 55 recommended `expo`, `expo-build-properties`, and `expo-system-ui` patch ranges.
60
-
61
64
  ### Fixed
62
65
 
63
- - Fixed the Android example launcher icon by adding an adaptive icon foreground and dark brand background.
64
66
  - Normalized web `SocialButton` and native login failures so presentation-anchor and missing-code errors surface as stable `AuthError` codes.
65
67
  - Shipped package-level Watchman ignores for Android CMake cache output so consumers avoid noisy native build watcher events.
66
68
 
67
69
  ## 0.5.11 - 2026-05-05
68
70
 
69
- ### Changed
70
-
71
- - Updated the Expo example to the Expo SDK 55 recommended `expo@~55.0.23` patch and Android API 36 target.
72
-
73
71
  ### Fixed
74
72
 
75
73
  - Wrapped synchronous native service failures in `AuthError` so public service errors keep a consistent code contract.
76
74
 
77
- ### Verified
78
-
79
- - `bun install --frozen-lockfile`
80
- - `bunx expo install --check --cwd apps/example`
81
- - `bunx expo-doctor@latest apps/example`
82
- - `bun run check:ci`
83
- - `bun run --cwd packages/react-native-nitro-auth test:coverage -- --runInBand`
84
- - `bun run --cwd packages/react-native-nitro-auth test:cpp:coverage`
85
- - `bun run example:prebuild`
86
- - `bun run publish-package:dry-run`
87
-
88
75
  ## 0.5.10 - 2026-04-27
89
76
 
90
77
  ### Fixed
91
78
 
92
79
  - Fixed iOS Microsoft sign-in so `ASWebAuthenticationSession` is retained until callback or cancellation and duplicate sessions fail with `operation_in_progress`.
93
- - Fixed the example app header so it displays the current package version.
94
-
95
- ### Verified
96
-
97
- - `bun run check:ci`
98
- - `bunx expo install --check --cwd apps/example`
99
- - `bunx expo-doctor@latest apps/example`
100
- - `bun run example:prebuild`
101
- - `bun run publish-package:dry-run`
102
80
 
103
81
  ## 0.5.9 - 2026-04-24
104
82
 
105
- ### Added
106
-
107
- - Added shared JS service factory coverage and logger behavior tests.
108
- - 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.
109
- - Added example-app smoke checks for the public auth API, provider support, platform-gated behavior, and session-dependent methods.
110
-
111
83
  ### Changed
112
84
 
113
- - 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.
114
86
  - Refactored native/web `AuthService` creation so native and web error mapping stay consistent.
115
87
  - Hardened web OAuth state, cache parsing, token refresh, and provider error handling.
116
- - Improved example app handling for unsupported providers and unavailable session actions.
117
- - Improved release validation to include JS and C++ coverage gates and a faster publish dry run path.
118
88
 
119
89
  ### Fixed
120
90
 
121
- - Fixed native runtime crashes in the example app when optional Nitro methods are not available on the installed native object.
122
- - Fixed startup `silentRestore()` errors in the example app so restore failures surface in status instead of becoming unhandled promises.
123
91
  - Excluded C++ test sources from the iOS pod target to avoid app-target duplicate `main` symbols.
124
-
125
- ### Verified
126
-
127
- - `bun run check:ci`
128
- - `bun run --cwd packages/react-native-nitro-auth test:coverage -- --runInBand`
129
- - `bun run --cwd packages/react-native-nitro-auth test:cpp:coverage`
130
- - `bun run publish-package:dry-run`
131
- - `bun run example:prebuild`
132
- - `bun run example:android`
133
- - `bun run example:ios`
package/README.md CHANGED
@@ -1,10 +1,13 @@
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/)
10
+ [![TypeScript](https://img.shields.io/badge/typescript-6.0-3178c6)](https://www.typescriptlang.org/)
8
11
 
9
12
  Google Sign-In, Apple Sign-In, and Microsoft Entra ID for React Native and
10
13
  Expo, powered by Nitro Modules.
@@ -113,15 +116,21 @@ path such as `contoso.onmicrosoft.com/B2C_1_signin`.
113
116
  ## Quick Start
114
117
 
115
118
  ```tsx
116
- import { AuthService, AuthProvider, useAuth } from "react-native-nitro-auth";
119
+ import {
120
+ AuthService,
121
+ useAuth,
122
+ type ProviderLoginOptions,
123
+ } from "react-native-nitro-auth";
117
124
 
118
125
  export function SignInButton() {
119
- const { user, login, logout, loading, error } = useAuth();
126
+ const { user, login, logout } = useAuth();
120
127
 
121
128
  async function signInWithGoogle() {
122
- await login(AuthProvider.Google, {
129
+ const options: ProviderLoginOptions<"google"> = {
123
130
  scopes: ["openid", "profile", "email"],
124
- });
131
+ };
132
+
133
+ await login("google", options);
125
134
  }
126
135
 
127
136
  if (user) {
@@ -131,8 +140,9 @@ export function SignInButton() {
131
140
  return <Button title="Continue with Google" onPress={signInWithGoogle} />;
132
141
  }
133
142
 
134
- await AuthService.login(AuthProvider.Microsoft, {
143
+ await AuthService.login("microsoft", {
135
144
  tenant: "organizations",
145
+ prompt: "select_account",
136
146
  });
137
147
  ```
138
148
 
@@ -155,12 +165,39 @@ Main exports:
155
165
  - `useAuth()` for React state, login, logout, refresh, and listeners.
156
166
  - `AuthService` for imperative login, refresh, logout, and user reads.
157
167
  - `SocialButton` for provider-aware UI.
158
- - `AuthProvider` for Google, Apple, and Microsoft provider names.
168
+ - `AuthProvider` for the `"google"`, `"apple"`, and `"microsoft"` provider names.
159
169
  - `AuthError` and `AuthErrorCode` for deterministic failures.
160
170
  - Provider option types for strongly typed login calls.
161
171
 
162
- Login options include `scopes`, `loginHint`, `accountId`, `forceRefresh`,
163
- `nonce`, `state`, `tenant`, `prompt`, and provider-specific fields.
172
+ Provider-aware login calls reject unsupported option fields at compile time:
173
+
174
+ ```ts
175
+ import type {
176
+ ProviderLoginOptions,
177
+ MicrosoftLoginOptions,
178
+ } from "react-native-nitro-auth";
179
+
180
+ const googleOptions: ProviderLoginOptions<"google"> = {
181
+ scopes: ["openid", "email"],
182
+ hostedDomain: "company.com",
183
+ forceAccountPicker: true,
184
+ };
185
+
186
+ const microsoftOptions: MicrosoftLoginOptions = {
187
+ tenant: "organizations",
188
+ prompt: "select_account",
189
+ };
190
+ ```
191
+
192
+ Supported login options:
193
+
194
+ | Provider | Options |
195
+ | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
196
+ | Google | `scopes`, `loginHint`, `nonce`, `forceAccountPicker`, `hostedDomain`, `useSheet`, `openIDRealm`, `useOneTap`, `filterByAuthorizedAccounts`, `useLegacyGoogleSignIn`, `forceCodeForRefreshToken`, `requestVerifiedPhoneNumber` |
197
+ | Apple | `scopes`, `nonce` |
198
+ | Microsoft | `scopes`, `loginHint`, `tenant`, `prompt` |
199
+
200
+ `prompt` is typed as `"login"`, `"consent"`, `"select_account"`, or `"none"`.
164
201
 
165
202
  ## Storage Model
166
203
 
@@ -174,8 +211,10 @@ server.
174
211
  Async public APIs throw `AuthError` with a stable `code`, `provider`, `platform`,
175
212
  and `message`. Use `instanceof AuthError` when branching in UI code.
176
213
 
177
- Common codes include `cancelled`, `configuration_error`, `network_error`,
178
- `provider_unavailable`, `token_refresh_failed`, and `unknown`.
214
+ Error codes are `cancelled`, `timeout`, `popup_blocked`, `network_error`,
215
+ `configuration_error`, `not_signed_in`, `operation_in_progress`,
216
+ `unsupported_provider`, `invalid_state`, `invalid_nonce`, `token_error`,
217
+ `no_id_token`, `parse_error`, `refresh_failed`, and `unknown`.
179
218
 
180
219
  ## Platform Support
181
220
 
@@ -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);