react-native-nitro-auth 0.6.3 → 0.6.5
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 +25 -65
- package/README.md +8 -1
- package/android/src/main/java/com/auth/AuthAdapter.kt +5 -5
- package/app.plugin.js +31 -6
- package/ios/AuthAdapter.swift +18 -12
- package/lib/commonjs/Auth.web.js +75 -63
- package/lib/commonjs/Auth.web.js.map +1 -1
- package/lib/module/Auth.web.js +75 -63
- package/lib/module/Auth.web.js.map +1 -1
- package/lib/typescript/commonjs/Auth.web.d.ts.map +1 -1
- package/lib/typescript/module/Auth.web.d.ts.map +1 -1
- package/package.json +22 -2
- package/src/Auth.web.ts +143 -67
package/CHANGELOG.md
CHANGED
|
@@ -1,38 +1,48 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.5 - 2026-06-11
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Moved the Expo iOS Google Sign-In CocoaPods modular-header setup into the package config plugin so Expo/CNG consumers no longer need app-level `AppCheckCore`, `GoogleUtilities`, or `RecaptchaInterop` pod workarounds.
|
|
8
|
+
- Added the package plugin dependency needed to apply the iOS build-properties setup from the package.
|
|
9
|
+
|
|
10
|
+
## 0.6.4 - 2026-06-11
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- 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.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Strengthened the package TypeScript configuration (`exactOptionalPropertyTypes`, `noUncheckedIndexedAccess`, `noImplicitOverride`, `noImplicitReturns`, `noFallthroughCasesInSwitch`, `useUnknownInCatchVariables`) so editor and tooling diagnostics catch more mistakes at compile time.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Encoded iOS Microsoft token request bodies as form data so authorization codes, redirect URIs, and refresh tokens containing reserved characters are posted correctly.
|
|
23
|
+
|
|
3
24
|
## 0.6.3 - 2026-06-10
|
|
4
25
|
|
|
5
26
|
### Fixed
|
|
6
27
|
|
|
7
28
|
- 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
29
|
|
|
10
30
|
### Changed
|
|
11
31
|
|
|
12
|
-
- Updated
|
|
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.
|
|
32
|
+
- Updated README setup, provider examples, option tables, error codes, and typed API documentation to match the current package surface.
|
|
16
33
|
- Added stronger compile-time coverage for provider-specific login options used by `AuthService.login()` and `useAuth().login()`.
|
|
17
34
|
|
|
18
35
|
## 0.6.1 - 2026-05-21
|
|
19
36
|
|
|
20
37
|
### Changed
|
|
21
38
|
|
|
22
|
-
- Updated the package
|
|
39
|
+
- 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
40
|
- 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
41
|
- 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
42
|
|
|
31
43
|
### Fixed
|
|
32
44
|
|
|
33
45
|
- 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
46
|
|
|
37
47
|
## 0.6.0 - 2026-05-14
|
|
38
48
|
|
|
@@ -42,7 +52,7 @@
|
|
|
42
52
|
- Added Apple nonce and authorization-code/user-id result support.
|
|
43
53
|
- Added Microsoft tenant and prompt option coverage across native and web flows.
|
|
44
54
|
- Added `revokeAccess()` to the native/web auth API and `useAuth()` hook.
|
|
45
|
-
- Added native logging hooks
|
|
55
|
+
- Added native logging hooks.
|
|
46
56
|
- Added provider-specific TypeScript option types for `AuthService.login()` and `useAuth().login()`.
|
|
47
57
|
|
|
48
58
|
### Changed
|
|
@@ -53,86 +63,36 @@
|
|
|
53
63
|
|
|
54
64
|
### Fixed
|
|
55
65
|
|
|
56
|
-
- Fixed Android Metro watcher noise from transient Bun `node_modules/.old-*` directories in the example app.
|
|
57
66
|
- Fixed Android Google cancellation handling so cancellations are not reported as unknown failures.
|
|
58
67
|
- Fixed native session cleanup paths to reject pending work before clearing provider state.
|
|
59
68
|
|
|
60
69
|
## 0.5.12 - 2026-05-13
|
|
61
70
|
|
|
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
71
|
### Fixed
|
|
67
72
|
|
|
68
|
-
- Fixed the Android example launcher icon by adding an adaptive icon foreground and dark brand background.
|
|
69
73
|
- Normalized web `SocialButton` and native login failures so presentation-anchor and missing-code errors surface as stable `AuthError` codes.
|
|
70
74
|
- Shipped package-level Watchman ignores for Android CMake cache output so consumers avoid noisy native build watcher events.
|
|
71
75
|
|
|
72
76
|
## 0.5.11 - 2026-05-05
|
|
73
77
|
|
|
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
78
|
### Fixed
|
|
79
79
|
|
|
80
80
|
- Wrapped synchronous native service failures in `AuthError` so public service errors keep a consistent code contract.
|
|
81
81
|
|
|
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
82
|
## 0.5.10 - 2026-04-27
|
|
94
83
|
|
|
95
84
|
### Fixed
|
|
96
85
|
|
|
97
86
|
- 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
87
|
|
|
108
88
|
## 0.5.9 - 2026-04-24
|
|
109
89
|
|
|
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
90
|
### Changed
|
|
117
91
|
|
|
118
|
-
- Updated Expo SDK 55 patch dependencies, React Native 0.83.6, Nitro Modules 0.35.5
|
|
92
|
+
- Updated Expo SDK 55 patch dependencies, React Native 0.83.6, and Nitro Modules 0.35.5.
|
|
119
93
|
- Refactored native/web `AuthService` creation so native and web error mapping stay consistent.
|
|
120
94
|
- 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
95
|
|
|
124
96
|
### Fixed
|
|
125
97
|
|
|
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
98
|
- 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
|
[](https://www.npmjs.com/package/react-native-nitro-auth)
|
|
4
|
+
[](https://www.npmjs.com/package/react-native-nitro-auth)
|
|
5
|
+
[](https://github.com/JoaoPauloCMarra/react-native-nitro-auth/actions/workflows/ci.yml)
|
|
4
6
|
[](https://github.com/JoaoPauloCMarra/react-native-nitro-auth/blob/main/LICENSE)
|
|
5
7
|
[](https://reactnative.dev/)
|
|
6
|
-
[](https://expo.dev/)
|
|
8
|
+
[](https://docs.expo.dev/)
|
|
7
9
|
[](https://nitro.margelo.com/)
|
|
8
10
|
[](https://www.typescriptlang.org/)
|
|
9
11
|
|
|
@@ -104,6 +106,11 @@ Plugin options:
|
|
|
104
106
|
Web reads provider client IDs from `expo.extra`; native platforms read values
|
|
105
107
|
written by the plugin during prebuild.
|
|
106
108
|
|
|
109
|
+
On iOS, the plugin also applies the CocoaPods modular-header settings required
|
|
110
|
+
by the Google Sign-In dependency chain (`AppCheckCore`, `GoogleUtilities`, and
|
|
111
|
+
`RecaptchaInterop`). Expo apps should not add those pods manually through
|
|
112
|
+
`expo-build-properties`.
|
|
113
|
+
|
|
107
114
|
Microsoft tenant values are validated before opening the authorization URL. Use
|
|
108
115
|
`common`, `organizations`, `consumers`, a tenant ID, or a tenant domain for
|
|
109
116
|
standard Entra ID. For B2C, set `microsoftB2cDomain` to a hostname such as
|
|
@@ -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
|
-
|
|
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
|
-
|
|
739
|
+
GoogleSignIn.getClient(ctx, gso).also {
|
|
740
|
+
googleSignInClient = it
|
|
741
|
+
}
|
|
742
742
|
}
|
|
743
|
-
|
|
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))
|
package/app.plugin.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { withBuildProperties } = require("expo-build-properties");
|
|
1
2
|
const {
|
|
2
3
|
withInfoPlist,
|
|
3
4
|
withEntitlementsPlist,
|
|
@@ -8,10 +9,34 @@ const {
|
|
|
8
9
|
} = require("@expo/config-plugins");
|
|
9
10
|
const pkg = require("./package.json");
|
|
10
11
|
|
|
12
|
+
const googleSignInIosPods = [
|
|
13
|
+
{ name: "AppCheckCore", modular_headers: true },
|
|
14
|
+
{ name: "GoogleUtilities", modular_headers: true },
|
|
15
|
+
{ name: "RecaptchaInterop", modular_headers: true },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
function getNitroAuthIosExtraPods(extraPods = []) {
|
|
19
|
+
const pods = Array.isArray(extraPods) ? [...extraPods] : [];
|
|
20
|
+
const existingPodNames = new Set(pods.map((pod) => pod?.name));
|
|
21
|
+
|
|
22
|
+
for (const pod of googleSignInIosPods) {
|
|
23
|
+
if (!existingPodNames.has(pod.name)) {
|
|
24
|
+
pods.push(pod);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return pods;
|
|
29
|
+
}
|
|
30
|
+
|
|
11
31
|
const withNitroAuth = (config, props = {}) => {
|
|
12
32
|
const { ios = {}, android = {} } = props;
|
|
13
33
|
|
|
14
|
-
|
|
34
|
+
config = withBuildProperties(config, {
|
|
35
|
+
ios: {
|
|
36
|
+
extraPods: getNitroAuthIosExtraPods(config.ios?.extraPods),
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
15
40
|
config = withInfoPlist(config, (config) => {
|
|
16
41
|
if (ios.googleClientId) {
|
|
17
42
|
config.modResults.GIDClientID = ios.googleClientId;
|
|
@@ -34,10 +59,8 @@ const withNitroAuth = (config, props = {}) => {
|
|
|
34
59
|
];
|
|
35
60
|
}
|
|
36
61
|
}
|
|
37
|
-
// Microsoft configuration
|
|
38
62
|
if (ios.microsoftClientId) {
|
|
39
63
|
config.modResults.MSALClientID = ios.microsoftClientId;
|
|
40
|
-
// Add MSAL redirect URL scheme
|
|
41
64
|
const msalScheme = `msauth.${config.ios?.bundleIdentifier}`;
|
|
42
65
|
const existingSchemes = config.modResults.CFBundleURLTypes || [];
|
|
43
66
|
if (
|
|
@@ -62,7 +85,6 @@ const withNitroAuth = (config, props = {}) => {
|
|
|
62
85
|
return config;
|
|
63
86
|
});
|
|
64
87
|
|
|
65
|
-
// 2. iOS Entitlements
|
|
66
88
|
if (ios.appleSignIn === true) {
|
|
67
89
|
config = withEntitlementsPlist(config, (config) => {
|
|
68
90
|
config.modResults["com.apple.developer.applesignin"] = ["Default"];
|
|
@@ -70,7 +92,6 @@ const withNitroAuth = (config, props = {}) => {
|
|
|
70
92
|
});
|
|
71
93
|
}
|
|
72
94
|
|
|
73
|
-
// 3. Android Strings (for Google and Microsoft Client IDs)
|
|
74
95
|
config = withStringsXml(config, (config) => {
|
|
75
96
|
if (android.googleClientId) {
|
|
76
97
|
config.modResults = AndroidConfig.Strings.setStringItem(
|
|
@@ -119,7 +140,6 @@ const withNitroAuth = (config, props = {}) => {
|
|
|
119
140
|
return config;
|
|
120
141
|
});
|
|
121
142
|
|
|
122
|
-
// 4. Android Manifest for MSAL redirect
|
|
123
143
|
if (android.microsoftClientId) {
|
|
124
144
|
config = withAndroidManifest(config, (config) => {
|
|
125
145
|
const manifest = config.modResults.manifest;
|
|
@@ -170,3 +190,8 @@ const withNitroAuth = (config, props = {}) => {
|
|
|
170
190
|
};
|
|
171
191
|
|
|
172
192
|
module.exports = createRunOncePlugin(withNitroAuth, pkg.name, pkg.version);
|
|
193
|
+
module.exports.withNitroAuth = withNitroAuth;
|
|
194
|
+
module.exports._internal = {
|
|
195
|
+
getNitroAuthIosExtraPods,
|
|
196
|
+
googleSignInIosPods,
|
|
197
|
+
};
|
package/ios/AuthAdapter.swift
CHANGED
|
@@ -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 {
|
package/lib/commonjs/Auth.web.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
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
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
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
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
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
|
-
|
|
944
|
-
|
|
945
|
-
|
|
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
|
-
|
|
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);
|