react-native-nitro-auth 0.6.1 → 0.6.3
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 +15 -0
- package/README.md +124 -436
- package/android/src/main/java/com/auth/AuthAdapter.kt +58 -7
- package/ios/AuthAdapter.swift +52 -6
- package/lib/commonjs/Auth.web.js +37 -6
- package/lib/commonjs/Auth.web.js.map +1 -1
- package/lib/commonjs/ui/social-button.js +39 -31
- package/lib/commonjs/ui/social-button.js.map +1 -1
- package/lib/module/Auth.web.js +37 -6
- package/lib/module/Auth.web.js.map +1 -1
- package/lib/module/ui/social-button.js +40 -31
- package/lib/module/ui/social-button.js.map +1 -1
- package/lib/typescript/commonjs/Auth.web.d.ts +2 -0
- package/lib/typescript/commonjs/Auth.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/social-button.d.ts +1 -1
- package/lib/typescript/commonjs/ui/social-button.d.ts.map +1 -1
- package/lib/typescript/module/Auth.web.d.ts +2 -0
- package/lib/typescript/module/Auth.web.d.ts.map +1 -1
- package/lib/typescript/module/ui/social-button.d.ts +1 -1
- package/lib/typescript/module/ui/social-button.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/Auth.web.ts +69 -6
- package/src/ui/social-button.tsx +46 -38
package/src/Auth.web.ts
CHANGED
|
@@ -24,6 +24,16 @@ const WEB_STORAGE_MODES = new Set([
|
|
|
24
24
|
STORAGE_MODE_LOCAL,
|
|
25
25
|
STORAGE_MODE_MEMORY,
|
|
26
26
|
] as const);
|
|
27
|
+
const MICROSOFT_TENANT_PATTERN =
|
|
28
|
+
/^(common|organizations|consumers|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|[A-Za-z0-9][A-Za-z0-9._-]{0,127})$/;
|
|
29
|
+
const MICROSOFT_B2C_TENANT_PATH_PATTERN =
|
|
30
|
+
/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|[A-Za-z0-9][A-Za-z0-9._-]{0,127})\/[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
31
|
+
const MICROSOFT_B2C_POLICY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
32
|
+
const MICROSOFT_B2CLOGIN_DOMAIN_SUFFIX = ".b2clogin.com";
|
|
33
|
+
const MICROSOFT_B2CLOGIN_TENANT_NAME_PATTERN =
|
|
34
|
+
/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$/;
|
|
35
|
+
const MICROSOFT_DOMAIN_PATTERN =
|
|
36
|
+
/^(?=.{1,253}$)(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z]{2,63}$/;
|
|
27
37
|
const WEB_AUTH_ERROR_CODES: ReadonlySet<string> = new Set<AuthErrorCode>([
|
|
28
38
|
"cancelled",
|
|
29
39
|
"timeout",
|
|
@@ -1328,15 +1338,68 @@ class AuthWeb implements Auth {
|
|
|
1328
1338
|
}
|
|
1329
1339
|
|
|
1330
1340
|
private getMicrosoftAuthBaseUrl(tenant: string, b2cDomain?: string): string {
|
|
1331
|
-
|
|
1332
|
-
|
|
1341
|
+
const trimmedTenant = tenant.trim();
|
|
1342
|
+
|
|
1343
|
+
const trimmedDomain = b2cDomain?.trim().toLowerCase();
|
|
1344
|
+
if (trimmedDomain) {
|
|
1345
|
+
if (!MICROSOFT_DOMAIN_PATTERN.test(trimmedDomain)) {
|
|
1346
|
+
throw new AuthWebError(
|
|
1347
|
+
"configuration_error",
|
|
1348
|
+
"Microsoft B2C domain must be a hostname.",
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1351
|
+
const b2cTenantPath = this.getMicrosoftB2cTenantPath(
|
|
1352
|
+
trimmedTenant,
|
|
1353
|
+
trimmedDomain,
|
|
1354
|
+
);
|
|
1355
|
+
return `https://${trimmedDomain}/${b2cTenantPath}/`;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
if (!MICROSOFT_TENANT_PATTERN.test(trimmedTenant)) {
|
|
1359
|
+
throw new AuthWebError(
|
|
1360
|
+
"configuration_error",
|
|
1361
|
+
"Microsoft tenant must be common, organizations, consumers, a tenant ID, or tenant domain.",
|
|
1362
|
+
);
|
|
1333
1363
|
}
|
|
1334
1364
|
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1365
|
+
return `https://login.microsoftonline.com/${trimmedTenant}/`;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
private getMicrosoftB2cTenantPath(tenant: string, domain: string): string {
|
|
1369
|
+
if (MICROSOFT_B2C_TENANT_PATH_PATTERN.test(tenant)) {
|
|
1370
|
+
return tenant;
|
|
1339
1371
|
}
|
|
1372
|
+
|
|
1373
|
+
if (!MICROSOFT_B2C_POLICY_PATTERN.test(tenant)) {
|
|
1374
|
+
throw new AuthWebError(
|
|
1375
|
+
"configuration_error",
|
|
1376
|
+
"Microsoft B2C tenant must be a policy or tenant/policy path.",
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
const tenantName = this.getMicrosoftB2cTenantName(domain);
|
|
1381
|
+
if (tenantName === undefined) {
|
|
1382
|
+
throw new AuthWebError(
|
|
1383
|
+
"configuration_error",
|
|
1384
|
+
"Microsoft B2C custom domains require microsoftTenant as a tenant/policy path.",
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
return `${tenantName}.onmicrosoft.com/${tenant}`;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
private getMicrosoftB2cTenantName(domain: string): string | undefined {
|
|
1392
|
+
if (!domain.endsWith(MICROSOFT_B2CLOGIN_DOMAIN_SUFFIX)) {
|
|
1393
|
+
return undefined;
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
const tenantName = domain.slice(
|
|
1397
|
+
0,
|
|
1398
|
+
-MICROSOFT_B2CLOGIN_DOMAIN_SUFFIX.length,
|
|
1399
|
+
);
|
|
1400
|
+
return MICROSOFT_B2CLOGIN_TENANT_NAME_PATTERN.test(tenantName)
|
|
1401
|
+
? tenantName
|
|
1402
|
+
: undefined;
|
|
1340
1403
|
}
|
|
1341
1404
|
|
|
1342
1405
|
private decodeMicrosoftJwt(token: string): Partial<AuthUser> {
|
package/src/ui/social-button.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
1
|
+
import React, { useCallback, useMemo, useState } from "react";
|
|
2
2
|
import type { ViewStyle, TextStyle } from "react-native";
|
|
3
3
|
import {
|
|
4
4
|
Pressable,
|
|
@@ -34,7 +34,7 @@ const PROVIDER_LABELS: Record<AuthProvider, string> = {
|
|
|
34
34
|
const PROVIDER_PRIMARY_BACKGROUND: Record<AuthProvider, string> = {
|
|
35
35
|
google: "#4285F4",
|
|
36
36
|
apple: "#000000",
|
|
37
|
-
microsoft: "#
|
|
37
|
+
microsoft: "#1f2937",
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
const getBackgroundColor = ({
|
|
@@ -46,21 +46,29 @@ const getBackgroundColor = ({
|
|
|
46
46
|
variant: SocialButtonVariant;
|
|
47
47
|
provider: AuthProvider;
|
|
48
48
|
}): string => {
|
|
49
|
-
if (disabled) return "#
|
|
49
|
+
if (disabled) return "#E2E8F0";
|
|
50
50
|
if (variant === "black") return "#000000";
|
|
51
51
|
if (variant === "white") return "#FFFFFF";
|
|
52
52
|
if (variant === "outline") return "transparent";
|
|
53
53
|
return PROVIDER_PRIMARY_BACKGROUND[provider];
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
-
const getTextColor = (
|
|
57
|
-
|
|
56
|
+
const getTextColor = ({
|
|
57
|
+
disabled,
|
|
58
|
+
variant,
|
|
59
|
+
}: {
|
|
60
|
+
disabled: boolean;
|
|
61
|
+
variant: SocialButtonVariant;
|
|
62
|
+
}): string => {
|
|
63
|
+
if (disabled) return "#64748B";
|
|
64
|
+
return variant === "white" || variant === "outline" ? "#111827" : "#FFFFFF";
|
|
65
|
+
};
|
|
58
66
|
|
|
59
67
|
async function performLogin(provider: AuthProvider): Promise<void> {
|
|
60
68
|
await AuthService.login(provider);
|
|
61
69
|
}
|
|
62
70
|
|
|
63
|
-
export const SocialButton = ({
|
|
71
|
+
export const SocialButton = React.memo(function SocialButton({
|
|
64
72
|
provider,
|
|
65
73
|
variant = "primary",
|
|
66
74
|
borderRadius = 8,
|
|
@@ -70,10 +78,11 @@ export const SocialButton = ({
|
|
|
70
78
|
onSuccess,
|
|
71
79
|
onError,
|
|
72
80
|
onPress,
|
|
73
|
-
}: SocialButtonProps)
|
|
81
|
+
}: SocialButtonProps) {
|
|
74
82
|
const [loading, setLoading] = useState(false);
|
|
83
|
+
const isDisabled = loading || disabled === true;
|
|
75
84
|
|
|
76
|
-
const handleLogin = async () => {
|
|
85
|
+
const handleLogin = useCallback(async () => {
|
|
77
86
|
if (loading || disabled) return;
|
|
78
87
|
if (onPress) {
|
|
79
88
|
onPress();
|
|
@@ -96,36 +105,41 @@ export const SocialButton = ({
|
|
|
96
105
|
} finally {
|
|
97
106
|
setLoading(false);
|
|
98
107
|
}
|
|
99
|
-
};
|
|
100
|
-
const isDisabled = loading || disabled === true;
|
|
108
|
+
}, [disabled, loading, onError, onPress, onSuccess, provider]);
|
|
101
109
|
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
110
|
+
const buttonStyle = useMemo(
|
|
111
|
+
() => ({
|
|
112
|
+
backgroundColor: getBackgroundColor({
|
|
113
|
+
disabled: isDisabled,
|
|
114
|
+
variant,
|
|
115
|
+
provider,
|
|
116
|
+
}),
|
|
117
|
+
borderRadius,
|
|
118
|
+
borderColor: variant === "outline" ? "#DDDDDD" : "transparent",
|
|
119
|
+
borderWidth: variant === "outline" ? 1 : 0,
|
|
120
|
+
}),
|
|
121
|
+
[borderRadius, isDisabled, provider, variant],
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const textColor = getTextColor({ disabled: isDisabled, variant });
|
|
125
|
+
const labelStyle = useMemo(
|
|
126
|
+
() => [styles.text, { color: textColor }, textStyle],
|
|
127
|
+
[textColor, textStyle],
|
|
128
|
+
);
|
|
129
|
+
const appleIconStyle = useMemo(
|
|
130
|
+
() => [styles.iconText, { color: textColor }],
|
|
131
|
+
[textColor],
|
|
132
|
+
);
|
|
106
133
|
|
|
107
134
|
return (
|
|
108
135
|
<Pressable
|
|
109
|
-
style={[
|
|
110
|
-
styles.button,
|
|
111
|
-
{
|
|
112
|
-
backgroundColor: getBackgroundColor({
|
|
113
|
-
disabled: isDisabled,
|
|
114
|
-
variant,
|
|
115
|
-
provider,
|
|
116
|
-
}),
|
|
117
|
-
borderRadius,
|
|
118
|
-
borderColor: getBorderColor(),
|
|
119
|
-
borderWidth: variant === "outline" ? 1 : 0,
|
|
120
|
-
},
|
|
121
|
-
style,
|
|
122
|
-
]}
|
|
136
|
+
style={[styles.button, buttonStyle, style]}
|
|
123
137
|
onPress={handleLogin}
|
|
124
138
|
disabled={isDisabled}
|
|
125
139
|
>
|
|
126
140
|
<View style={styles.content}>
|
|
127
141
|
{loading ? (
|
|
128
|
-
<ActivityIndicator size="small" color={
|
|
142
|
+
<ActivityIndicator size="small" color={textColor} />
|
|
129
143
|
) : (
|
|
130
144
|
<>
|
|
131
145
|
{provider === "google" && variant !== "primary" && (
|
|
@@ -135,11 +149,7 @@ export const SocialButton = ({
|
|
|
135
149
|
)}
|
|
136
150
|
{provider === "apple" && variant !== "primary" && (
|
|
137
151
|
<View style={styles.iconPlaceholder}>
|
|
138
|
-
<Text
|
|
139
|
-
style={[styles.iconText, { color: getTextColor(variant) }]}
|
|
140
|
-
>
|
|
141
|
-
|
|
142
|
-
</Text>
|
|
152
|
+
<Text style={appleIconStyle}></Text>
|
|
143
153
|
</View>
|
|
144
154
|
)}
|
|
145
155
|
{provider === "microsoft" && variant !== "primary" && (
|
|
@@ -147,9 +157,7 @@ export const SocialButton = ({
|
|
|
147
157
|
<Text style={styles.microsoftIconText}>⊞</Text>
|
|
148
158
|
</View>
|
|
149
159
|
)}
|
|
150
|
-
<Text
|
|
151
|
-
style={[styles.text, { color: getTextColor(variant) }, textStyle]}
|
|
152
|
-
>
|
|
160
|
+
<Text style={labelStyle}>
|
|
153
161
|
Sign in with {PROVIDER_LABELS[provider]}
|
|
154
162
|
</Text>
|
|
155
163
|
</>
|
|
@@ -157,7 +165,7 @@ export const SocialButton = ({
|
|
|
157
165
|
</View>
|
|
158
166
|
</Pressable>
|
|
159
167
|
);
|
|
160
|
-
};
|
|
168
|
+
});
|
|
161
169
|
|
|
162
170
|
const styles = StyleSheet.create({
|
|
163
171
|
button: {
|