@thoughtbot/react-native-social-auth 0.1.0
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/LICENSE +20 -0
- package/README.md +316 -0
- package/ReactNativeSocialAuth.podspec +22 -0
- package/android/build.gradle +70 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/thoughtbot/reactnativesocialauth/GoogleSignInModule.kt +188 -0
- package/android/src/main/java/com/thoughtbot/reactnativesocialauth/ReactNativeSocialAuthPackage.kt +31 -0
- package/ios/GoogleSignIn.h +7 -0
- package/ios/GoogleSignIn.mm +213 -0
- package/lib/module/google/GoogleLogo.js +41 -0
- package/lib/module/google/GoogleLogo.js.map +1 -0
- package/lib/module/google/GoogleSignIn.js +124 -0
- package/lib/module/google/GoogleSignIn.js.map +1 -0
- package/lib/module/google/GoogleSignInButton.js +176 -0
- package/lib/module/google/GoogleSignInButton.js.map +1 -0
- package/lib/module/google/NativeGoogleSignIn.js +5 -0
- package/lib/module/google/NativeGoogleSignIn.js.map +1 -0
- package/lib/module/google/errors.js +69 -0
- package/lib/module/google/errors.js.map +1 -0
- package/lib/module/google/index.js +6 -0
- package/lib/module/google/index.js.map +1 -0
- package/lib/module/google/types.js +2 -0
- package/lib/module/google/types.js.map +1 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/google/GoogleLogo.d.ts +6 -0
- package/lib/typescript/src/google/GoogleLogo.d.ts.map +1 -0
- package/lib/typescript/src/google/GoogleSignIn.d.ts +89 -0
- package/lib/typescript/src/google/GoogleSignIn.d.ts.map +1 -0
- package/lib/typescript/src/google/GoogleSignInButton.d.ts +74 -0
- package/lib/typescript/src/google/GoogleSignInButton.d.ts.map +1 -0
- package/lib/typescript/src/google/NativeGoogleSignIn.d.ts +12 -0
- package/lib/typescript/src/google/NativeGoogleSignIn.d.ts.map +1 -0
- package/lib/typescript/src/google/errors.d.ts +57 -0
- package/lib/typescript/src/google/errors.d.ts.map +1 -0
- package/lib/typescript/src/google/index.d.ts +6 -0
- package/lib/typescript/src/google/index.d.ts.map +1 -0
- package/lib/typescript/src/google/types.d.ts +93 -0
- package/lib/typescript/src/google/types.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +3 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +180 -0
- package/src/google/GoogleLogo.tsx +35 -0
- package/src/google/GoogleSignIn.ts +131 -0
- package/src/google/GoogleSignInButton.tsx +224 -0
- package/src/google/NativeGoogleSignIn.ts +12 -0
- package/src/google/errors.ts +72 -0
- package/src/google/index.ts +19 -0
- package/src/google/types.ts +100 -0
- package/src/index.tsx +18 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
GoogleAuthCredential,
|
|
3
|
+
GoogleSignInConfig,
|
|
4
|
+
GoogleUser,
|
|
5
|
+
} from './types';
|
|
6
|
+
import { GoogleSignInError, GoogleSignInErrorCode } from './errors';
|
|
7
|
+
import NativeGoogleSignIn from './NativeGoogleSignIn';
|
|
8
|
+
|
|
9
|
+
let _configured = false;
|
|
10
|
+
|
|
11
|
+
function ensureConfigured(): void {
|
|
12
|
+
if (!_configured) {
|
|
13
|
+
throw new GoogleSignInError(
|
|
14
|
+
GoogleSignInErrorCode.NOT_CONFIGURED,
|
|
15
|
+
'GoogleSignIn.configure() must be called before using other methods.'
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Initialize the native Google Sign-In SDK. Must be called once at app startup
|
|
22
|
+
* (or before any other method) — subsequent calls overwrite the prior config.
|
|
23
|
+
*
|
|
24
|
+
* @param config - See {@link GoogleSignInConfig}.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* GoogleSignIn.configure({
|
|
29
|
+
* webClientId: 'WEB_CLIENT_ID.apps.googleusercontent.com',
|
|
30
|
+
* iosClientId: 'IOS_CLIENT_ID.apps.googleusercontent.com',
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
function configure(config: GoogleSignInConfig): void {
|
|
35
|
+
NativeGoogleSignIn.configure(config);
|
|
36
|
+
_configured = true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Trigger the Google sign-in flow.
|
|
41
|
+
*
|
|
42
|
+
* @remarks
|
|
43
|
+
* On both platforms, the SDK attempts silent restore of a previously authorized
|
|
44
|
+
* account first, then falls back to the interactive bottom sheet / system sheet
|
|
45
|
+
* if no authorized account is found.
|
|
46
|
+
*
|
|
47
|
+
* @returns A {@link GoogleAuthCredential} containing the ID token, optional
|
|
48
|
+
* access token, and user profile.
|
|
49
|
+
*
|
|
50
|
+
* @throws A {@link GoogleSignInError} with one of the
|
|
51
|
+
* {@link GoogleSignInErrorCode} values — most commonly `SIGN_IN_CANCELLED`
|
|
52
|
+
* when the user dismisses the sheet, or `NOT_CONFIGURED` when called before
|
|
53
|
+
* {@link configure}.
|
|
54
|
+
*/
|
|
55
|
+
async function signIn(): Promise<GoogleAuthCredential> {
|
|
56
|
+
ensureConfigured();
|
|
57
|
+
const result = await NativeGoogleSignIn.signIn();
|
|
58
|
+
return result as unknown as GoogleAuthCredential;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Clear the local credential state so the next {@link signIn} call requires
|
|
63
|
+
* fresh user interaction. The user remains signed into Google itself.
|
|
64
|
+
*
|
|
65
|
+
* @throws A {@link GoogleSignInError} with code `NOT_CONFIGURED` when called
|
|
66
|
+
* before {@link configure}.
|
|
67
|
+
*/
|
|
68
|
+
async function signOut(): Promise<void> {
|
|
69
|
+
ensureConfigured();
|
|
70
|
+
await NativeGoogleSignIn.signOut();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Return the in-memory authenticated user, or `null` if nobody has signed in
|
|
75
|
+
* since app launch.
|
|
76
|
+
*
|
|
77
|
+
* @returns The current {@link GoogleUser}, or `null`.
|
|
78
|
+
*
|
|
79
|
+
* @throws A {@link GoogleSignInError} with code `NOT_CONFIGURED` when called
|
|
80
|
+
* before {@link configure}.
|
|
81
|
+
*/
|
|
82
|
+
async function getCurrentUser(): Promise<GoogleUser | null> {
|
|
83
|
+
ensureConfigured();
|
|
84
|
+
const result = await NativeGoogleSignIn.getCurrentUser();
|
|
85
|
+
return result as unknown as GoogleUser | null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Revoke the app's access to the user's Google account. The user will see a
|
|
90
|
+
* fresh consent screen on the next {@link signIn}.
|
|
91
|
+
*
|
|
92
|
+
* @throws A {@link GoogleSignInError} with code `NOT_CONFIGURED` when called
|
|
93
|
+
* before {@link configure}.
|
|
94
|
+
*/
|
|
95
|
+
async function revokeAccess(): Promise<void> {
|
|
96
|
+
ensureConfigured();
|
|
97
|
+
await NativeGoogleSignIn.revokeAccess();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Synchronous check for whether a user is currently signed in (in memory).
|
|
102
|
+
*
|
|
103
|
+
* @returns `true` when {@link getCurrentUser} would resolve to a non-null user.
|
|
104
|
+
*
|
|
105
|
+
* @throws A {@link GoogleSignInError} with code `NOT_CONFIGURED` when called
|
|
106
|
+
* before {@link configure}.
|
|
107
|
+
*/
|
|
108
|
+
function isSignedIn(): boolean {
|
|
109
|
+
ensureConfigured();
|
|
110
|
+
return NativeGoogleSignIn.isSignedIn();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The Google Sign-In runtime API. Call {@link GoogleSignIn.configure} once at
|
|
115
|
+
* app startup, then drive the flow with the other methods.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* GoogleSignIn.configure({ webClientId: '...' });
|
|
120
|
+
* const credential = await GoogleSignIn.signIn();
|
|
121
|
+
* console.log(credential.user.email);
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export const GoogleSignIn = {
|
|
125
|
+
configure,
|
|
126
|
+
signIn,
|
|
127
|
+
signOut,
|
|
128
|
+
getCurrentUser,
|
|
129
|
+
revokeAccess,
|
|
130
|
+
isSignedIn,
|
|
131
|
+
} as const;
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Pressable,
|
|
3
|
+
View,
|
|
4
|
+
Text,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
type StyleProp,
|
|
7
|
+
type ViewStyle,
|
|
8
|
+
} from 'react-native';
|
|
9
|
+
import { GoogleLogo } from './GoogleLogo';
|
|
10
|
+
|
|
11
|
+
// Layout dimensions
|
|
12
|
+
const BUTTON_HEIGHT = 40;
|
|
13
|
+
const ICON_BUTTON_SIZE = 40;
|
|
14
|
+
const BORDER_RADIUS_ROUNDED = 20;
|
|
15
|
+
const BORDER_RADIUS_SQUARE = 4;
|
|
16
|
+
const GOOGLE_LOGO_SIZE = 20;
|
|
17
|
+
|
|
18
|
+
// Spacing
|
|
19
|
+
const LOGO_MARGIN_START = 12;
|
|
20
|
+
const LOGO_MARGIN_END = 10;
|
|
21
|
+
|
|
22
|
+
// Typography
|
|
23
|
+
const LABEL_FONT_SIZE = 14;
|
|
24
|
+
const LABEL_LINE_HEIGHT = 20;
|
|
25
|
+
const LABEL_FONT_WEIGHT = '500';
|
|
26
|
+
|
|
27
|
+
// Effects
|
|
28
|
+
const DISABLED_OPACITY = 0.38;
|
|
29
|
+
const BORDER_WIDTH_THEMED = 1;
|
|
30
|
+
const BORDER_WIDTH_NEUTRAL = 0;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* One of the three button themes defined by Google's branding guidelines.
|
|
34
|
+
*
|
|
35
|
+
* - `light` — white background, dark text, gray border
|
|
36
|
+
* - `dark` — near-black background, light text, gray border
|
|
37
|
+
* - `neutral` — light gray background, dark text, no border
|
|
38
|
+
*/
|
|
39
|
+
export type GoogleSignInButtonTheme = 'light' | 'dark' | 'neutral';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Button corner radius:
|
|
43
|
+
*
|
|
44
|
+
* - `rounded` — pill shape (`borderRadius: 20`)
|
|
45
|
+
* - `square` — slightly rounded rectangle (`borderRadius: 4`)
|
|
46
|
+
*/
|
|
47
|
+
export type GoogleSignInButtonShape = 'rounded' | 'square';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The call-to-action label. Only these three strings are permitted by Google.
|
|
51
|
+
*
|
|
52
|
+
* - `signin` — "Sign in with Google"
|
|
53
|
+
* - `signup` — "Sign up with Google"
|
|
54
|
+
* - `continue` — "Continue with Google"
|
|
55
|
+
*/
|
|
56
|
+
export type GoogleSignInButtonText = 'signin' | 'signup' | 'continue';
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Button size:
|
|
60
|
+
*
|
|
61
|
+
* - `standard` — full button with logo + text
|
|
62
|
+
* - `icon` — 40×40 logo-only button for compact layouts
|
|
63
|
+
*/
|
|
64
|
+
export type GoogleSignInButtonSize = 'standard' | 'icon';
|
|
65
|
+
|
|
66
|
+
/** Props for {@link GoogleSignInButton}. */
|
|
67
|
+
export interface GoogleSignInButtonProps {
|
|
68
|
+
/** {@inheritDoc GoogleSignInButtonTheme} @defaultValue `'light'` */
|
|
69
|
+
theme?: GoogleSignInButtonTheme;
|
|
70
|
+
/** {@inheritDoc GoogleSignInButtonShape} @defaultValue `'rounded'` */
|
|
71
|
+
shape?: GoogleSignInButtonShape;
|
|
72
|
+
/** {@inheritDoc GoogleSignInButtonText} @defaultValue `'signin'` */
|
|
73
|
+
text?: GoogleSignInButtonText;
|
|
74
|
+
/** {@inheritDoc GoogleSignInButtonSize} @defaultValue `'standard'` */
|
|
75
|
+
size?: GoogleSignInButtonSize;
|
|
76
|
+
/** Tap handler. Typically wired to {@link GoogleSignIn.signIn}. */
|
|
77
|
+
onPress?: () => void;
|
|
78
|
+
/**
|
|
79
|
+
* When `true`, renders at 0.38 opacity and disables taps.
|
|
80
|
+
* @defaultValue false
|
|
81
|
+
*/
|
|
82
|
+
disabled?: boolean;
|
|
83
|
+
/** Additional container styles (margin, alignment, etc.). */
|
|
84
|
+
style?: StyleProp<ViewStyle>;
|
|
85
|
+
/** Testing identifier. */
|
|
86
|
+
testID?: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const BUTTON_LABELS: Record<GoogleSignInButtonText, string> = {
|
|
90
|
+
signin: 'Sign in with Google',
|
|
91
|
+
signup: 'Sign up with Google',
|
|
92
|
+
continue: 'Continue with Google',
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const THEME_STYLES: Record<
|
|
96
|
+
GoogleSignInButtonTheme,
|
|
97
|
+
{
|
|
98
|
+
backgroundColor: string;
|
|
99
|
+
borderColor: string;
|
|
100
|
+
borderWidth: number;
|
|
101
|
+
textColor: string;
|
|
102
|
+
}
|
|
103
|
+
> = {
|
|
104
|
+
light: {
|
|
105
|
+
backgroundColor: '#FFFFFF',
|
|
106
|
+
borderColor: '#747775',
|
|
107
|
+
borderWidth: BORDER_WIDTH_THEMED,
|
|
108
|
+
textColor: '#1F1F1F',
|
|
109
|
+
},
|
|
110
|
+
dark: {
|
|
111
|
+
backgroundColor: '#131314',
|
|
112
|
+
borderColor: '#8E918F',
|
|
113
|
+
borderWidth: BORDER_WIDTH_THEMED,
|
|
114
|
+
textColor: '#E3E3E3',
|
|
115
|
+
},
|
|
116
|
+
neutral: {
|
|
117
|
+
backgroundColor: '#F2F2F2',
|
|
118
|
+
borderColor: 'transparent',
|
|
119
|
+
borderWidth: BORDER_WIDTH_NEUTRAL,
|
|
120
|
+
textColor: '#1F1F1F',
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* A pre-built button conforming to Google's official branding guidelines.
|
|
126
|
+
*
|
|
127
|
+
* @remarks
|
|
128
|
+
* The Google "G" is rendered via `react-native-svg` so the button stays crisp
|
|
129
|
+
* at any density without bundled raster assets. Colors, typography, padding,
|
|
130
|
+
* and the permitted CTA strings all follow the official spec — do not
|
|
131
|
+
* restyle the logo or override theme colors, or Google may reject your app
|
|
132
|
+
* during brand review.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```tsx
|
|
136
|
+
* <GoogleSignInButton
|
|
137
|
+
* theme="dark"
|
|
138
|
+
* text="continue"
|
|
139
|
+
* onPress={() => GoogleSignIn.signIn()}
|
|
140
|
+
* />
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export function GoogleSignInButton({
|
|
144
|
+
theme = 'light',
|
|
145
|
+
shape = 'rounded',
|
|
146
|
+
text = 'signin',
|
|
147
|
+
size = 'standard',
|
|
148
|
+
onPress,
|
|
149
|
+
disabled = false,
|
|
150
|
+
style,
|
|
151
|
+
testID,
|
|
152
|
+
}: GoogleSignInButtonProps) {
|
|
153
|
+
const themeStyle = THEME_STYLES[theme];
|
|
154
|
+
const isIcon = size === 'icon';
|
|
155
|
+
const borderRadius =
|
|
156
|
+
shape === 'rounded' ? BORDER_RADIUS_ROUNDED : BORDER_RADIUS_SQUARE;
|
|
157
|
+
const label = BUTTON_LABELS[text];
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<Pressable
|
|
161
|
+
style={[
|
|
162
|
+
styles.button,
|
|
163
|
+
{
|
|
164
|
+
backgroundColor: themeStyle.backgroundColor,
|
|
165
|
+
borderColor: themeStyle.borderColor,
|
|
166
|
+
borderWidth: themeStyle.borderWidth,
|
|
167
|
+
borderRadius,
|
|
168
|
+
},
|
|
169
|
+
isIcon && styles.iconButton,
|
|
170
|
+
disabled && styles.disabled,
|
|
171
|
+
style,
|
|
172
|
+
]}
|
|
173
|
+
onPress={onPress}
|
|
174
|
+
disabled={disabled}
|
|
175
|
+
accessibilityRole="button"
|
|
176
|
+
accessibilityLabel={label}
|
|
177
|
+
testID={testID}
|
|
178
|
+
>
|
|
179
|
+
<View style={isIcon ? styles.iconLogoContainer : styles.logoContainer}>
|
|
180
|
+
<GoogleLogo size={GOOGLE_LOGO_SIZE} />
|
|
181
|
+
</View>
|
|
182
|
+
{!isIcon && (
|
|
183
|
+
<Text
|
|
184
|
+
style={[styles.label, { color: themeStyle.textColor }]}
|
|
185
|
+
numberOfLines={1}
|
|
186
|
+
>
|
|
187
|
+
{label}
|
|
188
|
+
</Text>
|
|
189
|
+
)}
|
|
190
|
+
</Pressable>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const styles = StyleSheet.create({
|
|
195
|
+
button: {
|
|
196
|
+
flexDirection: 'row',
|
|
197
|
+
alignItems: 'center',
|
|
198
|
+
height: BUTTON_HEIGHT,
|
|
199
|
+
alignSelf: 'flex-start',
|
|
200
|
+
},
|
|
201
|
+
iconButton: {
|
|
202
|
+
width: ICON_BUTTON_SIZE,
|
|
203
|
+
justifyContent: 'center',
|
|
204
|
+
},
|
|
205
|
+
logoContainer: {
|
|
206
|
+
marginStart: LOGO_MARGIN_START,
|
|
207
|
+
marginEnd: LOGO_MARGIN_END,
|
|
208
|
+
},
|
|
209
|
+
iconLogoContainer: {
|
|
210
|
+
alignItems: 'center',
|
|
211
|
+
justifyContent: 'center',
|
|
212
|
+
flex: 1,
|
|
213
|
+
},
|
|
214
|
+
label: {
|
|
215
|
+
fontFamily: 'Roboto-Medium',
|
|
216
|
+
fontSize: LABEL_FONT_SIZE,
|
|
217
|
+
lineHeight: LABEL_LINE_HEIGHT,
|
|
218
|
+
fontWeight: LABEL_FONT_WEIGHT,
|
|
219
|
+
marginEnd: LOGO_MARGIN_END,
|
|
220
|
+
},
|
|
221
|
+
disabled: {
|
|
222
|
+
opacity: DISABLED_OPACITY,
|
|
223
|
+
},
|
|
224
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { TurboModuleRegistry, type TurboModule } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export interface Spec extends TurboModule {
|
|
4
|
+
configure(config: Object): void;
|
|
5
|
+
signIn(): Promise<Object>;
|
|
6
|
+
signOut(): Promise<void>;
|
|
7
|
+
getCurrentUser(): Promise<Object | null>;
|
|
8
|
+
revokeAccess(): Promise<void>;
|
|
9
|
+
isSignedIn(): boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default TurboModuleRegistry.getEnforcing<Spec>('GoogleSignIn');
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discriminator for {@link GoogleSignInError.code}. Identical on Android and
|
|
3
|
+
* iOS so error-handling code is platform-agnostic.
|
|
4
|
+
*/
|
|
5
|
+
export enum GoogleSignInErrorCode {
|
|
6
|
+
/** The user dismissed the sign-in sheet. Don't surface an error. */
|
|
7
|
+
SIGN_IN_CANCELLED = 'SIGN_IN_CANCELLED',
|
|
8
|
+
/** A generic native failure; inspect `message` and `nativeErrorCode` for details. */
|
|
9
|
+
SIGN_IN_FAILED = 'SIGN_IN_FAILED',
|
|
10
|
+
/** No Google accounts are available on the device. */
|
|
11
|
+
NO_CREDENTIALS = 'NO_CREDENTIALS',
|
|
12
|
+
/** Google Play Services is missing or out of date (Android only). */
|
|
13
|
+
PLAY_SERVICES_NOT_AVAILABLE = 'PLAY_SERVICES_NOT_AVAILABLE',
|
|
14
|
+
/** The device could not reach Google's auth servers. */
|
|
15
|
+
NETWORK_ERROR = 'NETWORK_ERROR',
|
|
16
|
+
/** A method was called before {@link GoogleSignIn.configure}. */
|
|
17
|
+
NOT_CONFIGURED = 'NOT_CONFIGURED',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Typed error thrown / rejected by every {@link GoogleSignIn} method.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* try {
|
|
26
|
+
* await GoogleSignIn.signIn();
|
|
27
|
+
* } catch (error) {
|
|
28
|
+
* if (isGoogleSignInError(error) && error.code === GoogleSignInErrorCode.SIGN_IN_CANCELLED) {
|
|
29
|
+
* return;
|
|
30
|
+
* }
|
|
31
|
+
* throw error;
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export class GoogleSignInError extends Error {
|
|
36
|
+
/** The platform-agnostic error code. */
|
|
37
|
+
readonly code: GoogleSignInErrorCode;
|
|
38
|
+
/**
|
|
39
|
+
* The underlying native error code (e.g. Android `28444`, iOS
|
|
40
|
+
* `kGIDSignInErrorCode...`). Useful for telemetry; do not branch on it from
|
|
41
|
+
* application code — branch on {@link code} instead.
|
|
42
|
+
*/
|
|
43
|
+
readonly nativeErrorCode: string | undefined;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @param code - A {@link GoogleSignInErrorCode} value.
|
|
47
|
+
* @param message - Human-readable description, suitable for logs.
|
|
48
|
+
* @param nativeErrorCode - Optional native-side code for diagnostics.
|
|
49
|
+
*/
|
|
50
|
+
constructor(
|
|
51
|
+
code: GoogleSignInErrorCode,
|
|
52
|
+
message: string,
|
|
53
|
+
nativeErrorCode?: string
|
|
54
|
+
) {
|
|
55
|
+
super(message);
|
|
56
|
+
this.name = 'GoogleSignInError';
|
|
57
|
+
this.code = code;
|
|
58
|
+
this.nativeErrorCode = nativeErrorCode;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Type guard that narrows `unknown` to {@link GoogleSignInError}.
|
|
64
|
+
*
|
|
65
|
+
* @param error - Any caught value.
|
|
66
|
+
* @returns `true` when `error` is a {@link GoogleSignInError} instance.
|
|
67
|
+
*/
|
|
68
|
+
export function isGoogleSignInError(
|
|
69
|
+
error: unknown
|
|
70
|
+
): error is GoogleSignInError {
|
|
71
|
+
return error instanceof GoogleSignInError;
|
|
72
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { GoogleSignIn } from './GoogleSignIn';
|
|
2
|
+
export {
|
|
3
|
+
GoogleSignInError,
|
|
4
|
+
GoogleSignInErrorCode,
|
|
5
|
+
isGoogleSignInError,
|
|
6
|
+
} from './errors';
|
|
7
|
+
export { GoogleSignInButton } from './GoogleSignInButton';
|
|
8
|
+
export type {
|
|
9
|
+
GoogleSignInButtonProps,
|
|
10
|
+
GoogleSignInButtonTheme,
|
|
11
|
+
GoogleSignInButtonShape,
|
|
12
|
+
GoogleSignInButtonText,
|
|
13
|
+
GoogleSignInButtonSize,
|
|
14
|
+
} from './GoogleSignInButton';
|
|
15
|
+
export type {
|
|
16
|
+
GoogleSignInConfig,
|
|
17
|
+
GoogleUser,
|
|
18
|
+
GoogleAuthCredential,
|
|
19
|
+
} from './types';
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration accepted by {@link GoogleSignIn.configure}.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* `webClientId` is required on every platform — it sets the audience (`aud`)
|
|
6
|
+
* of the issued ID token so a single backend verification path works across
|
|
7
|
+
* iOS, Android, and web.
|
|
8
|
+
*/
|
|
9
|
+
export interface GoogleSignInConfig {
|
|
10
|
+
/**
|
|
11
|
+
* The OAuth 2.0 **Web application** client ID from Google Cloud Console.
|
|
12
|
+
* Used as the `serverClientID` on iOS and the `serverClientId` on Android,
|
|
13
|
+
* which becomes the `aud` claim of the issued ID token.
|
|
14
|
+
*/
|
|
15
|
+
webClientId: string;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The OAuth 2.0 **iOS application** client ID. Required when running on iOS;
|
|
19
|
+
* ignored on Android.
|
|
20
|
+
*/
|
|
21
|
+
iosClientId?: string;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* When `true`, request a server auth code in addition to the ID token so a
|
|
25
|
+
* backend can exchange it for refresh tokens. Not yet exposed end-to-end.
|
|
26
|
+
*
|
|
27
|
+
* @defaultValue false
|
|
28
|
+
*/
|
|
29
|
+
offlineAccess?: boolean;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Additional OAuth scopes to request beyond the default `openid email profile`.
|
|
33
|
+
*/
|
|
34
|
+
scopes?: string[];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Restrict sign-in to accounts on a specific Google Workspace domain (e.g.
|
|
38
|
+
* `"example.com"`).
|
|
39
|
+
*/
|
|
40
|
+
hostedDomain?: string;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* When `true`, returning users sign in silently with the most recently used
|
|
44
|
+
* account without showing the bottom sheet.
|
|
45
|
+
*
|
|
46
|
+
* @defaultValue false
|
|
47
|
+
*/
|
|
48
|
+
autoSelect?: boolean;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A unique value bound into the ID token. Recommended when verifying tokens
|
|
52
|
+
* server-side to prevent replay attacks.
|
|
53
|
+
*
|
|
54
|
+
* @remarks
|
|
55
|
+
* Honored on Android. On iOS the GoogleSignIn 7.x public API does not expose
|
|
56
|
+
* a nonce parameter; nonce verification must be enforced server-side instead.
|
|
57
|
+
*/
|
|
58
|
+
nonce?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The authenticated Google account's profile, returned by
|
|
63
|
+
* {@link GoogleSignIn.signIn} and {@link GoogleSignIn.getCurrentUser}.
|
|
64
|
+
*/
|
|
65
|
+
export interface GoogleUser {
|
|
66
|
+
/** The user's stable Google account ID (`sub` claim in the ID token). */
|
|
67
|
+
id: string;
|
|
68
|
+
/** The user's primary email address. */
|
|
69
|
+
email: string;
|
|
70
|
+
/** The user's full display name, if provided. */
|
|
71
|
+
displayName: string | null;
|
|
72
|
+
/** The user's given (first) name, if provided. */
|
|
73
|
+
givenName: string | null;
|
|
74
|
+
/** The user's family (last) name, if provided. */
|
|
75
|
+
familyName: string | null;
|
|
76
|
+
/** A URL to the user's profile photo (200×200), if provided. */
|
|
77
|
+
photoUrl: string | null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The result of a successful {@link GoogleSignIn.signIn} call.
|
|
82
|
+
*
|
|
83
|
+
* @remarks
|
|
84
|
+
* Pass `idToken` to your backend and verify it with Google's public keys. The
|
|
85
|
+
* `aud` claim will equal the {@link GoogleSignInConfig.webClientId} you passed
|
|
86
|
+
* to {@link GoogleSignIn.configure}.
|
|
87
|
+
*/
|
|
88
|
+
export interface GoogleAuthCredential {
|
|
89
|
+
/** The Google-issued ID token. JWT format; verify on your backend. */
|
|
90
|
+
idToken: string;
|
|
91
|
+
/** OAuth access token for calling Google APIs; `null` when not requested. */
|
|
92
|
+
accessToken: string | null;
|
|
93
|
+
/**
|
|
94
|
+
* One-time code your backend can exchange for refresh tokens. `null` unless
|
|
95
|
+
* {@link GoogleSignInConfig.offlineAccess} was set.
|
|
96
|
+
*/
|
|
97
|
+
serverAuthCode: string | null;
|
|
98
|
+
/** The authenticated user's profile. */
|
|
99
|
+
user: GoogleUser;
|
|
100
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export {
|
|
2
|
+
GoogleSignIn,
|
|
3
|
+
GoogleSignInButton,
|
|
4
|
+
GoogleSignInError,
|
|
5
|
+
GoogleSignInErrorCode,
|
|
6
|
+
isGoogleSignInError,
|
|
7
|
+
} from './google';
|
|
8
|
+
|
|
9
|
+
export type {
|
|
10
|
+
GoogleSignInButtonProps,
|
|
11
|
+
GoogleSignInButtonTheme,
|
|
12
|
+
GoogleSignInButtonShape,
|
|
13
|
+
GoogleSignInButtonText,
|
|
14
|
+
GoogleSignInButtonSize,
|
|
15
|
+
GoogleSignInConfig,
|
|
16
|
+
GoogleUser,
|
|
17
|
+
GoogleAuthCredential,
|
|
18
|
+
} from './google';
|