react-native-nitro-auth 0.5.12 → 0.6.1
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 +42 -0
- package/README.md +132 -32
- package/android/build.gradle +4 -4
- package/android/gradle.properties +2 -2
- package/android/src/main/cpp/PlatformAuth+Android.cpp +87 -4
- package/android/src/main/java/com/auth/AuthAdapter.kt +85 -48
- package/android/src/main/java/com/auth/GoogleSignInActivity.kt +12 -2
- package/cpp/HybridAuth.cpp +168 -18
- package/cpp/HybridAuth.hpp +7 -0
- package/cpp/PlatformAuth.hpp +1 -0
- package/ios/AuthAdapter.swift +101 -24
- package/ios/PlatformAuth+iOS.mm +37 -1
- package/lib/commonjs/Auth.web.js +74 -21
- package/lib/commonjs/Auth.web.js.map +1 -1
- package/lib/commonjs/create-auth-service.js +10 -0
- package/lib/commonjs/create-auth-service.js.map +1 -1
- package/lib/commonjs/index.js +12 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +12 -0
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/provider-options.js +6 -0
- package/lib/commonjs/provider-options.js.map +1 -0
- package/lib/commonjs/service.js.map +1 -1
- package/lib/commonjs/service.web.js.map +1 -1
- package/lib/commonjs/use-auth.js +21 -1
- package/lib/commonjs/use-auth.js.map +1 -1
- package/lib/module/Auth.web.js +74 -21
- package/lib/module/Auth.web.js.map +1 -1
- package/lib/module/create-auth-service.js +10 -0
- package/lib/module/create-auth-service.js.map +1 -1
- package/lib/module/global.d.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +1 -0
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/provider-options.js +4 -0
- package/lib/module/provider-options.js.map +1 -0
- package/lib/module/service.js.map +1 -1
- package/lib/module/service.web.js.map +1 -1
- package/lib/module/use-auth.js +21 -1
- package/lib/module/use-auth.js.map +1 -1
- package/lib/typescript/commonjs/Auth.nitro.d.ts +11 -0
- package/lib/typescript/commonjs/Auth.nitro.d.ts.map +1 -1
- package/lib/typescript/commonjs/Auth.web.d.ts +4 -0
- package/lib/typescript/commonjs/Auth.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/create-auth-service.d.ts +2 -1
- package/lib/typescript/commonjs/create-auth-service.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +1 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.web.d.ts +1 -0
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/provider-options.d.ts +23 -0
- package/lib/typescript/commonjs/provider-options.d.ts.map +1 -0
- package/lib/typescript/commonjs/service.d.ts +2 -2
- package/lib/typescript/commonjs/service.d.ts.map +1 -1
- package/lib/typescript/commonjs/service.web.d.ts +2 -2
- package/lib/typescript/commonjs/service.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/use-auth.d.ts +4 -2
- package/lib/typescript/commonjs/use-auth.d.ts.map +1 -1
- package/lib/typescript/module/Auth.nitro.d.ts +11 -0
- package/lib/typescript/module/Auth.nitro.d.ts.map +1 -1
- package/lib/typescript/module/Auth.web.d.ts +4 -0
- package/lib/typescript/module/Auth.web.d.ts.map +1 -1
- package/lib/typescript/module/create-auth-service.d.ts +2 -1
- package/lib/typescript/module/create-auth-service.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +1 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/index.web.d.ts +1 -0
- package/lib/typescript/module/index.web.d.ts.map +1 -1
- package/lib/typescript/module/provider-options.d.ts +23 -0
- package/lib/typescript/module/provider-options.d.ts.map +1 -0
- package/lib/typescript/module/service.d.ts +2 -2
- package/lib/typescript/module/service.d.ts.map +1 -1
- package/lib/typescript/module/service.web.d.ts +2 -2
- package/lib/typescript/module/service.web.d.ts.map +1 -1
- package/lib/typescript/module/use-auth.d.ts +4 -2
- package/lib/typescript/module/use-auth.d.ts.map +1 -1
- package/nitrogen/generated/shared/c++/AuthUser.hpp +17 -1
- package/nitrogen/generated/shared/c++/HybridAuthSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridAuthSpec.hpp +1 -0
- package/nitrogen/generated/shared/c++/LoginOptions.hpp +25 -1
- package/package.json +7 -7
- package/react-native-nitro-auth.podspec +1 -1
- package/src/Auth.nitro.ts +11 -0
- package/src/Auth.web.ts +99 -16
- package/src/create-auth-service.ts +19 -9
- package/src/global.d.ts +2 -1
- package/src/index.ts +1 -0
- package/src/index.web.ts +1 -0
- package/src/provider-options.ts +62 -0
- package/src/service.ts +2 -1
- package/src/service.web.ts +2 -2
- package/src/use-auth.ts +22 -8
package/src/Auth.web.ts
CHANGED
|
@@ -54,6 +54,7 @@ type WebStorageDriver = {
|
|
|
54
54
|
type AppleAuthResponse = {
|
|
55
55
|
authorization: {
|
|
56
56
|
id_token: string;
|
|
57
|
+
code?: string;
|
|
57
58
|
};
|
|
58
59
|
user?: {
|
|
59
60
|
email?: string;
|
|
@@ -222,6 +223,8 @@ class AuthWeb implements Auth {
|
|
|
222
223
|
private _refreshPromise: Promise<AuthTokens> | undefined;
|
|
223
224
|
private _pendingGoogleNonce: string | undefined;
|
|
224
225
|
private _loginInFlight: boolean = false;
|
|
226
|
+
private _sessionGeneration = 0;
|
|
227
|
+
private _disposed = false;
|
|
225
228
|
|
|
226
229
|
constructor() {
|
|
227
230
|
this._config = getConfig();
|
|
@@ -379,6 +382,7 @@ class AuthWeb implements Auth {
|
|
|
379
382
|
delete safeUser.accessToken;
|
|
380
383
|
delete safeUser.idToken;
|
|
381
384
|
delete safeUser.serverAuthCode;
|
|
385
|
+
delete safeUser.authorizationCode;
|
|
382
386
|
return safeUser;
|
|
383
387
|
}
|
|
384
388
|
|
|
@@ -415,6 +419,7 @@ class AuthWeb implements Auth {
|
|
|
415
419
|
delete safeUser.accessToken;
|
|
416
420
|
delete safeUser.idToken;
|
|
417
421
|
delete safeUser.serverAuthCode;
|
|
422
|
+
delete safeUser.authorizationCode;
|
|
418
423
|
this._currentUser = safeUser;
|
|
419
424
|
}
|
|
420
425
|
} catch (error) {
|
|
@@ -495,6 +500,9 @@ class AuthWeb implements Auth {
|
|
|
495
500
|
private async runLoginOperation(
|
|
496
501
|
operation: () => Promise<void>,
|
|
497
502
|
): Promise<void> {
|
|
503
|
+
if (this._disposed) {
|
|
504
|
+
throw new AuthWebError("cancelled", "Auth module disposed");
|
|
505
|
+
}
|
|
498
506
|
if (this._loginInFlight) {
|
|
499
507
|
throw new AuthWebError(
|
|
500
508
|
"operation_in_progress",
|
|
@@ -510,14 +518,21 @@ class AuthWeb implements Auth {
|
|
|
510
518
|
}
|
|
511
519
|
}
|
|
512
520
|
|
|
521
|
+
private assertActiveGeneration(generation: number): void {
|
|
522
|
+
if (this._disposed || this._sessionGeneration !== generation) {
|
|
523
|
+
throw new AuthWebError("cancelled", "Auth operation was cancelled");
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
513
527
|
async login(provider: AuthProvider, options?: LoginOptions): Promise<void> {
|
|
514
528
|
const loginHint = options?.loginHint;
|
|
529
|
+
const generation = this._sessionGeneration;
|
|
515
530
|
logger.log(`Starting login with ${provider}`, { scopes: options?.scopes });
|
|
516
531
|
try {
|
|
517
532
|
await this.runLoginOperation(async () => {
|
|
518
533
|
if (provider === "google") {
|
|
519
534
|
const scopes = options?.scopes ?? DEFAULT_SCOPES;
|
|
520
|
-
await this.loginGoogle(scopes, loginHint);
|
|
535
|
+
await this.loginGoogle(scopes, loginHint, options, generation);
|
|
521
536
|
return;
|
|
522
537
|
}
|
|
523
538
|
|
|
@@ -528,12 +543,13 @@ class AuthWeb implements Auth {
|
|
|
528
543
|
loginHint,
|
|
529
544
|
options?.tenant,
|
|
530
545
|
options?.prompt,
|
|
546
|
+
generation,
|
|
531
547
|
);
|
|
532
548
|
return;
|
|
533
549
|
}
|
|
534
550
|
|
|
535
551
|
if (provider === "apple") {
|
|
536
|
-
await this.loginApple();
|
|
552
|
+
await this.loginApple(options, generation);
|
|
537
553
|
return;
|
|
538
554
|
}
|
|
539
555
|
|
|
@@ -563,13 +579,20 @@ class AuthWeb implements Auth {
|
|
|
563
579
|
logger.log("Requesting additional scopes:", scopes);
|
|
564
580
|
const newScopes = [...new Set([...this._grantedScopes, ...scopes])];
|
|
565
581
|
try {
|
|
582
|
+
const generation = this._sessionGeneration;
|
|
566
583
|
await this.runLoginOperation(async () => {
|
|
567
584
|
if (provider === "google") {
|
|
568
|
-
await this.loginGoogle(newScopes);
|
|
585
|
+
await this.loginGoogle(newScopes, undefined, undefined, generation);
|
|
569
586
|
return;
|
|
570
587
|
}
|
|
571
588
|
|
|
572
|
-
await this.loginMicrosoft(
|
|
589
|
+
await this.loginMicrosoft(
|
|
590
|
+
newScopes,
|
|
591
|
+
undefined,
|
|
592
|
+
undefined,
|
|
593
|
+
undefined,
|
|
594
|
+
generation,
|
|
595
|
+
);
|
|
573
596
|
});
|
|
574
597
|
} catch (e) {
|
|
575
598
|
const error = this.mapError(e);
|
|
@@ -590,6 +613,10 @@ class AuthWeb implements Auth {
|
|
|
590
613
|
}
|
|
591
614
|
}
|
|
592
615
|
|
|
616
|
+
async revokeAccess(): Promise<void> {
|
|
617
|
+
this.logout();
|
|
618
|
+
}
|
|
619
|
+
|
|
593
620
|
async getAccessToken(): Promise<string | undefined> {
|
|
594
621
|
if (this._currentUser?.expirationTime) {
|
|
595
622
|
const now = Date.now();
|
|
@@ -606,7 +633,7 @@ class AuthWeb implements Auth {
|
|
|
606
633
|
return this._refreshPromise;
|
|
607
634
|
}
|
|
608
635
|
|
|
609
|
-
const refreshPromise = this.performRefreshToken();
|
|
636
|
+
const refreshPromise = this.performRefreshToken(this._sessionGeneration);
|
|
610
637
|
this._refreshPromise = refreshPromise;
|
|
611
638
|
try {
|
|
612
639
|
return await refreshPromise;
|
|
@@ -617,7 +644,7 @@ class AuthWeb implements Auth {
|
|
|
617
644
|
}
|
|
618
645
|
}
|
|
619
646
|
|
|
620
|
-
private async performRefreshToken(): Promise<AuthTokens> {
|
|
647
|
+
private async performRefreshToken(generation: number): Promise<AuthTokens> {
|
|
621
648
|
if (!this._currentUser) {
|
|
622
649
|
throw new Error("No user logged in");
|
|
623
650
|
}
|
|
@@ -656,6 +683,7 @@ class AuthWeb implements Auth {
|
|
|
656
683
|
});
|
|
657
684
|
|
|
658
685
|
const json = await this.parseResponseObject(response);
|
|
686
|
+
this.assertActiveGeneration(generation);
|
|
659
687
|
if (!response.ok) {
|
|
660
688
|
throw new AuthWebError(
|
|
661
689
|
"refresh_failed",
|
|
@@ -675,15 +703,19 @@ class AuthWeb implements Auth {
|
|
|
675
703
|
|
|
676
704
|
const expirationTime = this.getExpirationTime(json["expires_in"]);
|
|
677
705
|
|
|
678
|
-
const
|
|
706
|
+
const currentUser = this._currentUser;
|
|
707
|
+
if (!currentUser) {
|
|
708
|
+
throw new AuthWebError("cancelled", "Auth operation was cancelled");
|
|
709
|
+
}
|
|
710
|
+
const effectiveIdToken = idToken ?? currentUser.idToken;
|
|
679
711
|
const claims = effectiveIdToken
|
|
680
712
|
? this.decodeMicrosoftJwt(effectiveIdToken)
|
|
681
713
|
: {};
|
|
682
714
|
const user: AuthUser = {
|
|
683
|
-
...
|
|
715
|
+
...currentUser,
|
|
684
716
|
idToken: effectiveIdToken,
|
|
685
717
|
accessToken: accessToken ?? undefined,
|
|
686
|
-
refreshToken: newRefreshToken ??
|
|
718
|
+
refreshToken: newRefreshToken ?? currentUser.refreshToken,
|
|
687
719
|
expirationTime,
|
|
688
720
|
...claims,
|
|
689
721
|
};
|
|
@@ -706,9 +738,15 @@ class AuthWeb implements Auth {
|
|
|
706
738
|
}
|
|
707
739
|
|
|
708
740
|
logger.log("Refreshing tokens...");
|
|
709
|
-
await this.
|
|
710
|
-
this.
|
|
741
|
+
await this.runLoginOperation(() =>
|
|
742
|
+
this.loginGoogle(
|
|
743
|
+
this._grantedScopes.length > 0 ? this._grantedScopes : DEFAULT_SCOPES,
|
|
744
|
+
undefined,
|
|
745
|
+
undefined,
|
|
746
|
+
generation,
|
|
747
|
+
),
|
|
711
748
|
);
|
|
749
|
+
this.assertActiveGeneration(generation);
|
|
712
750
|
const tokens: AuthTokens = {
|
|
713
751
|
accessToken: this._currentUser.accessToken,
|
|
714
752
|
idToken: this._currentUser.idToken,
|
|
@@ -942,6 +980,8 @@ class AuthWeb implements Auth {
|
|
|
942
980
|
private async loginGoogle(
|
|
943
981
|
scopes: string[],
|
|
944
982
|
loginHint?: string,
|
|
983
|
+
options?: LoginOptions,
|
|
984
|
+
generation?: number,
|
|
945
985
|
): Promise<void> {
|
|
946
986
|
const clientId = this._config.googleWebClientId;
|
|
947
987
|
|
|
@@ -951,7 +991,7 @@ class AuthWeb implements Auth {
|
|
|
951
991
|
);
|
|
952
992
|
}
|
|
953
993
|
|
|
954
|
-
const nonce = crypto.randomUUID();
|
|
994
|
+
const nonce = options?.nonce ?? crypto.randomUUID();
|
|
955
995
|
this._pendingGoogleNonce = nonce;
|
|
956
996
|
return new Promise((resolve, reject) => {
|
|
957
997
|
const redirectUri = window.location.origin;
|
|
@@ -962,11 +1002,20 @@ class AuthWeb implements Auth {
|
|
|
962
1002
|
authUrl.searchParams.set("scope", scopes.join(" "));
|
|
963
1003
|
authUrl.searchParams.set("nonce", nonce);
|
|
964
1004
|
authUrl.searchParams.set("access_type", "offline");
|
|
965
|
-
authUrl.searchParams.set(
|
|
1005
|
+
authUrl.searchParams.set(
|
|
1006
|
+
"prompt",
|
|
1007
|
+
options?.forceAccountPicker ? "select_account consent" : "consent",
|
|
1008
|
+
);
|
|
966
1009
|
|
|
967
1010
|
if (loginHint) {
|
|
968
1011
|
authUrl.searchParams.set("login_hint", loginHint);
|
|
969
1012
|
}
|
|
1013
|
+
if (options?.hostedDomain) {
|
|
1014
|
+
authUrl.searchParams.set("hd", options.hostedDomain);
|
|
1015
|
+
}
|
|
1016
|
+
if (options?.openIDRealm) {
|
|
1017
|
+
authUrl.searchParams.set("openid.realm", options.openIDRealm);
|
|
1018
|
+
}
|
|
970
1019
|
|
|
971
1020
|
const width = 500;
|
|
972
1021
|
const height = 600;
|
|
@@ -1015,6 +1064,10 @@ class AuthWeb implements Auth {
|
|
|
1015
1064
|
}
|
|
1016
1065
|
this._pendingGoogleNonce = undefined;
|
|
1017
1066
|
|
|
1067
|
+
if (generation !== undefined) {
|
|
1068
|
+
this.assertActiveGeneration(generation);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1018
1071
|
this._grantedScopes = scopes;
|
|
1019
1072
|
this.saveValue(SCOPES_KEY, JSON.stringify(scopes));
|
|
1020
1073
|
|
|
@@ -1023,6 +1076,8 @@ class AuthWeb implements Auth {
|
|
|
1023
1076
|
idToken,
|
|
1024
1077
|
accessToken: accessToken ?? undefined,
|
|
1025
1078
|
serverAuthCode: code ?? undefined,
|
|
1079
|
+
userId: getOptionalString(decoded, "sub"),
|
|
1080
|
+
hostedDomain: getOptionalString(decoded, "hd"),
|
|
1026
1081
|
scopes,
|
|
1027
1082
|
expirationTime: this.getExpirationTime(expiresIn),
|
|
1028
1083
|
...this.decodeGoogleJwt(idToken),
|
|
@@ -1045,6 +1100,8 @@ class AuthWeb implements Auth {
|
|
|
1045
1100
|
email: getOptionalString(decoded, "email"),
|
|
1046
1101
|
name: getOptionalString(decoded, "name"),
|
|
1047
1102
|
photo: getOptionalString(decoded, "picture"),
|
|
1103
|
+
userId: getOptionalString(decoded, "sub"),
|
|
1104
|
+
hostedDomain: getOptionalString(decoded, "hd"),
|
|
1048
1105
|
};
|
|
1049
1106
|
} catch (error) {
|
|
1050
1107
|
logger.warn("Failed to decode Google ID token", { error: String(error) });
|
|
@@ -1057,6 +1114,7 @@ class AuthWeb implements Auth {
|
|
|
1057
1114
|
loginHint?: string,
|
|
1058
1115
|
tenant?: string,
|
|
1059
1116
|
prompt?: string,
|
|
1117
|
+
generation?: number,
|
|
1060
1118
|
): Promise<void> {
|
|
1061
1119
|
const clientId = this._config.microsoftClientId;
|
|
1062
1120
|
|
|
@@ -1156,6 +1214,7 @@ class AuthWeb implements Auth {
|
|
|
1156
1214
|
effectiveTenant,
|
|
1157
1215
|
nonce,
|
|
1158
1216
|
effectiveScopes,
|
|
1217
|
+
generation,
|
|
1159
1218
|
);
|
|
1160
1219
|
},
|
|
1161
1220
|
)
|
|
@@ -1194,6 +1253,7 @@ class AuthWeb implements Auth {
|
|
|
1194
1253
|
tenant: string,
|
|
1195
1254
|
expectedNonce: string,
|
|
1196
1255
|
scopes: string[],
|
|
1256
|
+
generation?: number,
|
|
1197
1257
|
): Promise<void> {
|
|
1198
1258
|
const authBaseUrl = this.getMicrosoftAuthBaseUrl(
|
|
1199
1259
|
tenant,
|
|
@@ -1218,6 +1278,9 @@ class AuthWeb implements Auth {
|
|
|
1218
1278
|
});
|
|
1219
1279
|
|
|
1220
1280
|
const json = await this.parseResponseObject(response);
|
|
1281
|
+
if (generation !== undefined) {
|
|
1282
|
+
this.assertActiveGeneration(generation);
|
|
1283
|
+
}
|
|
1221
1284
|
|
|
1222
1285
|
if (!response.ok) {
|
|
1223
1286
|
throw new AuthWebError(
|
|
@@ -1352,7 +1415,10 @@ class AuthWeb implements Auth {
|
|
|
1352
1415
|
return _appleSdkLoadPromise;
|
|
1353
1416
|
}
|
|
1354
1417
|
|
|
1355
|
-
private async loginApple(
|
|
1418
|
+
private async loginApple(
|
|
1419
|
+
options?: LoginOptions,
|
|
1420
|
+
generation?: number,
|
|
1421
|
+
): Promise<void> {
|
|
1356
1422
|
const clientId = this._config.appleWebClientId;
|
|
1357
1423
|
|
|
1358
1424
|
if (!clientId) {
|
|
@@ -1368,16 +1434,24 @@ class AuthWeb implements Auth {
|
|
|
1368
1434
|
|
|
1369
1435
|
window.AppleID.auth.init({
|
|
1370
1436
|
clientId,
|
|
1371
|
-
scope:
|
|
1437
|
+
scope: (options?.scopes?.length
|
|
1438
|
+
? options.scopes
|
|
1439
|
+
: ["name", "email"]
|
|
1440
|
+
).join(" "),
|
|
1372
1441
|
redirectURI: window.location.origin,
|
|
1442
|
+
nonce: options?.nonce,
|
|
1373
1443
|
usePopup: true,
|
|
1374
1444
|
});
|
|
1375
1445
|
|
|
1376
1446
|
try {
|
|
1377
1447
|
const response: AppleAuthResponse = await window.AppleID.auth.signIn();
|
|
1448
|
+
if (generation !== undefined) {
|
|
1449
|
+
this.assertActiveGeneration(generation);
|
|
1450
|
+
}
|
|
1378
1451
|
const user: AuthUser = {
|
|
1379
1452
|
provider: "apple",
|
|
1380
1453
|
idToken: response.authorization.id_token,
|
|
1454
|
+
authorizationCode: response.authorization.code,
|
|
1381
1455
|
email: response.user?.email,
|
|
1382
1456
|
name: response.user?.name
|
|
1383
1457
|
? `${response.user.name.firstName} ${response.user.name.lastName}`.trim()
|
|
@@ -1404,8 +1478,12 @@ class AuthWeb implements Auth {
|
|
|
1404
1478
|
}
|
|
1405
1479
|
|
|
1406
1480
|
logout(): void {
|
|
1481
|
+
this._sessionGeneration++;
|
|
1407
1482
|
this._currentUser = undefined;
|
|
1408
1483
|
this._grantedScopes = [];
|
|
1484
|
+
this._refreshPromise = undefined;
|
|
1485
|
+
this._pendingGoogleNonce = undefined;
|
|
1486
|
+
this._loginInFlight = false;
|
|
1409
1487
|
this.removeFromCache(CACHE_KEY);
|
|
1410
1488
|
this.removeFromCache(SCOPES_KEY);
|
|
1411
1489
|
this.removeFromCache(MS_REFRESH_TOKEN_KEY);
|
|
@@ -1433,7 +1511,12 @@ class AuthWeb implements Auth {
|
|
|
1433
1511
|
}
|
|
1434
1512
|
|
|
1435
1513
|
name = "Auth";
|
|
1436
|
-
dispose() {
|
|
1514
|
+
dispose() {
|
|
1515
|
+
this._disposed = true;
|
|
1516
|
+
this.logout();
|
|
1517
|
+
this._listeners = [];
|
|
1518
|
+
this._tokenListeners = [];
|
|
1519
|
+
}
|
|
1437
1520
|
equals(other: unknown) {
|
|
1438
1521
|
return other === this;
|
|
1439
1522
|
}
|
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
AuthProvider,
|
|
4
|
-
AuthTokens,
|
|
5
|
-
LoginOptions,
|
|
6
|
-
AuthUser,
|
|
7
|
-
} from "./Auth.nitro";
|
|
1
|
+
import type { Auth, AuthProvider, AuthTokens, AuthUser } from "./Auth.nitro";
|
|
2
|
+
import type { ProviderLoginOptions, TypedAuth } from "./provider-options";
|
|
8
3
|
import { AuthError } from "./utils/auth-error";
|
|
9
4
|
|
|
10
5
|
type AuthSource = () => Auth;
|
|
@@ -13,6 +8,7 @@ type AuthWithOptionalNativeMembers = Auth & {
|
|
|
13
8
|
callback: (user: AuthUser | undefined) => void,
|
|
14
9
|
) => () => void;
|
|
15
10
|
onTokensRefreshed?: (callback: (tokens: AuthTokens) => void) => () => void;
|
|
11
|
+
revokeAccess?: () => Promise<void>;
|
|
16
12
|
setLoggingEnabled?: (enabled: boolean) => void;
|
|
17
13
|
};
|
|
18
14
|
|
|
@@ -32,7 +28,7 @@ function wrapSyncAuthOperation<T>(operation: () => T): T {
|
|
|
32
28
|
}
|
|
33
29
|
}
|
|
34
30
|
|
|
35
|
-
export function createAuthService(getAuth: AuthSource):
|
|
31
|
+
export function createAuthService(getAuth: AuthSource): TypedAuth {
|
|
36
32
|
return {
|
|
37
33
|
get name() {
|
|
38
34
|
return wrapSyncAuthOperation(() => getAuth().name);
|
|
@@ -53,7 +49,10 @@ export function createAuthService(getAuth: AuthSource): Auth {
|
|
|
53
49
|
return wrapSyncAuthOperation(() => getAuth().hasPlayServices);
|
|
54
50
|
},
|
|
55
51
|
|
|
56
|
-
login
|
|
52
|
+
login<Provider extends AuthProvider>(
|
|
53
|
+
provider: Provider,
|
|
54
|
+
options?: ProviderLoginOptions<Provider>,
|
|
55
|
+
) {
|
|
57
56
|
return wrapAuthOperation(() => getAuth().login(provider, options));
|
|
58
57
|
},
|
|
59
58
|
|
|
@@ -65,6 +64,17 @@ export function createAuthService(getAuth: AuthSource): Auth {
|
|
|
65
64
|
return wrapAuthOperation(() => getAuth().revokeScopes(scopes));
|
|
66
65
|
},
|
|
67
66
|
|
|
67
|
+
revokeAccess() {
|
|
68
|
+
return wrapAuthOperation(async () => {
|
|
69
|
+
const auth = getAuth() as AuthWithOptionalNativeMembers;
|
|
70
|
+
if (auth.revokeAccess) {
|
|
71
|
+
await auth.revokeAccess();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
auth.logout();
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
|
|
68
78
|
getAccessToken() {
|
|
69
79
|
return wrapAuthOperation(() => getAuth().getAccessToken());
|
|
70
80
|
},
|
package/src/global.d.ts
CHANGED
|
@@ -6,10 +6,11 @@ declare global {
|
|
|
6
6
|
clientId: string;
|
|
7
7
|
scope: string;
|
|
8
8
|
redirectURI: string;
|
|
9
|
+
nonce?: string;
|
|
9
10
|
usePopup: boolean;
|
|
10
11
|
}) => void;
|
|
11
12
|
signIn: () => Promise<{
|
|
12
|
-
authorization: { id_token: string };
|
|
13
|
+
authorization: { id_token: string; code?: string };
|
|
13
14
|
user?: {
|
|
14
15
|
email?: string;
|
|
15
16
|
name?: {
|
package/src/index.ts
CHANGED
package/src/index.web.ts
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Auth, AuthProvider, LoginOptions } from "./Auth.nitro";
|
|
2
|
+
|
|
3
|
+
type StrictLoginOptions<AllowedKeys extends keyof LoginOptions> = Pick<
|
|
4
|
+
LoginOptions,
|
|
5
|
+
AllowedKeys
|
|
6
|
+
> &
|
|
7
|
+
Partial<Record<Exclude<keyof LoginOptions, AllowedKeys>, never>>;
|
|
8
|
+
|
|
9
|
+
type GoogleCommonKeys =
|
|
10
|
+
| "scopes"
|
|
11
|
+
| "loginHint"
|
|
12
|
+
| "nonce"
|
|
13
|
+
| "forceAccountPicker"
|
|
14
|
+
| "hostedDomain";
|
|
15
|
+
|
|
16
|
+
export type GoogleIOSLoginOptions = StrictLoginOptions<
|
|
17
|
+
GoogleCommonKeys | "useSheet" | "openIDRealm"
|
|
18
|
+
>;
|
|
19
|
+
|
|
20
|
+
export type GoogleAndroidLoginOptions = StrictLoginOptions<
|
|
21
|
+
| GoogleCommonKeys
|
|
22
|
+
| "useOneTap"
|
|
23
|
+
| "filterByAuthorizedAccounts"
|
|
24
|
+
| "useLegacyGoogleSignIn"
|
|
25
|
+
| "forceCodeForRefreshToken"
|
|
26
|
+
| "requestVerifiedPhoneNumber"
|
|
27
|
+
>;
|
|
28
|
+
|
|
29
|
+
export type GoogleWebLoginOptions = StrictLoginOptions<
|
|
30
|
+
GoogleCommonKeys | "openIDRealm"
|
|
31
|
+
>;
|
|
32
|
+
|
|
33
|
+
export type GoogleLoginOptions =
|
|
34
|
+
| GoogleIOSLoginOptions
|
|
35
|
+
| GoogleAndroidLoginOptions
|
|
36
|
+
| GoogleWebLoginOptions;
|
|
37
|
+
|
|
38
|
+
export type AppleIOSLoginOptions = StrictLoginOptions<"scopes" | "nonce">;
|
|
39
|
+
export type AppleWebLoginOptions = AppleIOSLoginOptions;
|
|
40
|
+
export type AppleLoginOptions = AppleIOSLoginOptions | AppleWebLoginOptions;
|
|
41
|
+
|
|
42
|
+
export type MicrosoftLoginOptions = StrictLoginOptions<
|
|
43
|
+
"scopes" | "loginHint" | "tenant" | "prompt"
|
|
44
|
+
>;
|
|
45
|
+
|
|
46
|
+
export type LoginOptionsByProvider = {
|
|
47
|
+
google: GoogleLoginOptions;
|
|
48
|
+
apple: AppleLoginOptions;
|
|
49
|
+
microsoft: MicrosoftLoginOptions;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type ProviderLoginOptions<Provider extends AuthProvider> =
|
|
53
|
+
LoginOptionsByProvider[Provider];
|
|
54
|
+
|
|
55
|
+
export type AuthLogin = <Provider extends AuthProvider>(
|
|
56
|
+
provider: Provider,
|
|
57
|
+
options?: ProviderLoginOptions<Provider>,
|
|
58
|
+
) => Promise<void>;
|
|
59
|
+
|
|
60
|
+
export type TypedAuth = Omit<Auth, "login"> & {
|
|
61
|
+
login: AuthLogin;
|
|
62
|
+
};
|
package/src/service.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NitroModules } from "react-native-nitro-modules";
|
|
2
2
|
import type { Auth } from "./Auth.nitro";
|
|
3
3
|
import { createAuthService } from "./create-auth-service";
|
|
4
|
+
import type { TypedAuth } from "./provider-options";
|
|
4
5
|
|
|
5
6
|
let nitroAuth: Auth | undefined;
|
|
6
7
|
|
|
@@ -9,4 +10,4 @@ function getNitroAuth(): Auth {
|
|
|
9
10
|
return nitroAuth;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
export const AuthService:
|
|
13
|
+
export const AuthService: TypedAuth = createAuthService(getNitroAuth);
|
package/src/service.web.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Auth } from "./Auth.nitro";
|
|
2
1
|
import { AuthModule } from "./Auth.web";
|
|
3
2
|
import { createAuthService } from "./create-auth-service";
|
|
3
|
+
import type { TypedAuth } from "./provider-options";
|
|
4
4
|
|
|
5
|
-
export const AuthService:
|
|
5
|
+
export const AuthService: TypedAuth = createAuthService(() => AuthModule);
|
package/src/use-auth.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { useState, useEffect, useCallback, useMemo } from "react";
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
AuthProvider,
|
|
5
|
-
LoginOptions,
|
|
6
|
-
AuthTokens,
|
|
7
|
-
} from "./Auth.nitro";
|
|
2
|
+
import type { AuthUser, AuthProvider, AuthTokens } from "./Auth.nitro";
|
|
3
|
+
import type { AuthLogin, ProviderLoginOptions } from "./provider-options";
|
|
8
4
|
import { AuthService } from "./service";
|
|
9
5
|
import { AuthError } from "./utils/auth-error";
|
|
10
6
|
|
|
@@ -45,10 +41,11 @@ const areScopesEqual = (left: string[], right: string[]): boolean => {
|
|
|
45
41
|
|
|
46
42
|
export type UseAuthReturn = AuthState & {
|
|
47
43
|
hasPlayServices: boolean;
|
|
48
|
-
login:
|
|
44
|
+
login: AuthLogin;
|
|
49
45
|
logout: () => void;
|
|
50
46
|
requestScopes: (scopes: string[]) => Promise<void>;
|
|
51
47
|
revokeScopes: (scopes: string[]) => Promise<void>;
|
|
48
|
+
revokeAccess: () => Promise<void>;
|
|
52
49
|
getAccessToken: () => Promise<string | undefined>;
|
|
53
50
|
refreshToken: () => Promise<AuthTokens>;
|
|
54
51
|
silentRestore: () => Promise<void>;
|
|
@@ -87,7 +84,10 @@ export function useAuth(): UseAuthReturn {
|
|
|
87
84
|
);
|
|
88
85
|
|
|
89
86
|
const login = useCallback(
|
|
90
|
-
async
|
|
87
|
+
async <Provider extends AuthProvider>(
|
|
88
|
+
provider: Provider,
|
|
89
|
+
options?: ProviderLoginOptions<Provider>,
|
|
90
|
+
) => {
|
|
91
91
|
setState((prev) => ({ ...prev, loading: true, error: undefined }));
|
|
92
92
|
try {
|
|
93
93
|
await AuthService.login(provider, options);
|
|
@@ -151,6 +151,18 @@ export function useAuth(): UseAuthReturn {
|
|
|
151
151
|
[syncStateFromService],
|
|
152
152
|
);
|
|
153
153
|
|
|
154
|
+
const revokeAccess = useCallback(async () => {
|
|
155
|
+
setState((prev) => ({ ...prev, loading: true, error: undefined }));
|
|
156
|
+
try {
|
|
157
|
+
await AuthService.revokeAccess();
|
|
158
|
+
syncStateFromService(false, undefined);
|
|
159
|
+
} catch (e) {
|
|
160
|
+
const error = AuthError.from(e);
|
|
161
|
+
setState((prev) => ({ ...prev, loading: false, error }));
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
}, [syncStateFromService]);
|
|
165
|
+
|
|
154
166
|
const getAccessToken = useCallback(() => AuthService.getAccessToken(), []);
|
|
155
167
|
|
|
156
168
|
const refreshToken = useCallback(async () => {
|
|
@@ -214,6 +226,7 @@ export function useAuth(): UseAuthReturn {
|
|
|
214
226
|
logout,
|
|
215
227
|
requestScopes,
|
|
216
228
|
revokeScopes,
|
|
229
|
+
revokeAccess,
|
|
217
230
|
getAccessToken,
|
|
218
231
|
refreshToken,
|
|
219
232
|
silentRestore,
|
|
@@ -224,6 +237,7 @@ export function useAuth(): UseAuthReturn {
|
|
|
224
237
|
logout,
|
|
225
238
|
requestScopes,
|
|
226
239
|
revokeScopes,
|
|
240
|
+
revokeAccess,
|
|
227
241
|
getAccessToken,
|
|
228
242
|
refreshToken,
|
|
229
243
|
silentRestore,
|