@clerk/expo-google-signin 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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/android/build.gradle +39 -0
  3. package/android/src/main/AndroidManifest.xml +1 -0
  4. package/android/src/main/java/expo/modules/clerk/googlesignin/ClerkGoogleSignInModule.kt +257 -0
  5. package/app.plugin.d.ts +5 -0
  6. package/app.plugin.js +32 -0
  7. package/dist/google-one-tap/ClerkGoogleOneTapSignIn.d.ts +86 -0
  8. package/dist/google-one-tap/ClerkGoogleOneTapSignIn.d.ts.map +1 -0
  9. package/dist/google-one-tap/ClerkGoogleOneTapSignIn.js +165 -0
  10. package/dist/google-one-tap/ClerkGoogleOneTapSignIn.js.map +1 -0
  11. package/dist/google-one-tap/index.d.ts +3 -0
  12. package/dist/google-one-tap/index.d.ts.map +1 -0
  13. package/dist/google-one-tap/index.js +37 -0
  14. package/dist/google-one-tap/index.js.map +1 -0
  15. package/dist/google-one-tap/types.d.ts +161 -0
  16. package/dist/google-one-tap/types.d.ts.map +1 -0
  17. package/dist/google-one-tap/types.js +17 -0
  18. package/dist/google-one-tap/types.js.map +1 -0
  19. package/dist/index.d.ts +3 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +29 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/specs/NativeClerkGoogleSignIn.android.d.ts +11 -0
  24. package/dist/specs/NativeClerkGoogleSignIn.android.d.ts.map +1 -0
  25. package/dist/specs/NativeClerkGoogleSignIn.android.js +26 -0
  26. package/dist/specs/NativeClerkGoogleSignIn.android.js.map +1 -0
  27. package/dist/specs/NativeClerkGoogleSignIn.d.ts +11 -0
  28. package/dist/specs/NativeClerkGoogleSignIn.d.ts.map +1 -0
  29. package/dist/specs/NativeClerkGoogleSignIn.js +26 -0
  30. package/dist/specs/NativeClerkGoogleSignIn.js.map +1 -0
  31. package/dist/useSignInWithGoogle.android.d.ts +46 -0
  32. package/dist/useSignInWithGoogle.android.d.ts.map +1 -0
  33. package/dist/useSignInWithGoogle.android.js +30 -0
  34. package/dist/useSignInWithGoogle.android.js.map +1 -0
  35. package/dist/useSignInWithGoogle.d.ts +52 -0
  36. package/dist/useSignInWithGoogle.d.ts.map +1 -0
  37. package/dist/useSignInWithGoogle.ios.d.ts +46 -0
  38. package/dist/useSignInWithGoogle.ios.d.ts.map +1 -0
  39. package/dist/useSignInWithGoogle.ios.js +30 -0
  40. package/dist/useSignInWithGoogle.ios.js.map +1 -0
  41. package/dist/useSignInWithGoogle.js +40 -0
  42. package/dist/useSignInWithGoogle.js.map +1 -0
  43. package/dist/useSignInWithGoogle.shared.d.ts +29 -0
  44. package/dist/useSignInWithGoogle.shared.d.ts.map +1 -0
  45. package/dist/useSignInWithGoogle.shared.js +167 -0
  46. package/dist/useSignInWithGoogle.shared.js.map +1 -0
  47. package/dist/useSignInWithGoogle.types.d.ts +13 -0
  48. package/dist/useSignInWithGoogle.types.d.ts.map +1 -0
  49. package/dist/useSignInWithGoogle.types.js +17 -0
  50. package/dist/useSignInWithGoogle.types.js.map +1 -0
  51. package/expo-module.config.json +9 -0
  52. package/ios/ClerkGoogleSignIn.podspec +27 -0
  53. package/ios/ClerkGoogleSignInModule.swift +199 -0
  54. package/package.json +73 -0
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var useSignInWithGoogle_exports = {};
20
+ __export(useSignInWithGoogle_exports, {
21
+ useSignInWithGoogle: () => useSignInWithGoogle
22
+ });
23
+ module.exports = __toCommonJS(useSignInWithGoogle_exports);
24
+ var import_error = require("@clerk/shared/error");
25
+ const errorThrower = (0, import_error.buildErrorThrower)({ packageName: "@clerk/expo-google-signin" });
26
+ function useSignInWithGoogle() {
27
+ function startGoogleAuthenticationFlow(_startGoogleAuthenticationFlowParams) {
28
+ return errorThrower.throw(
29
+ 'Native Google Authentication is only available on iOS and Android. For web and other platforms, please use the OAuth-based flow with useSSO and strategy: "oauth_google".'
30
+ );
31
+ }
32
+ return {
33
+ startGoogleAuthenticationFlow
34
+ };
35
+ }
36
+ // Annotate the CommonJS export names for ESM import in node:
37
+ 0 && (module.exports = {
38
+ useSignInWithGoogle
39
+ });
40
+ //# sourceMappingURL=useSignInWithGoogle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/useSignInWithGoogle.ts"],"sourcesContent":["import { buildErrorThrower } from '@clerk/shared/error';\nimport type { SetActive, SignInResource, SignUpResource } from '@clerk/shared/types';\n\nconst errorThrower = buildErrorThrower({ packageName: PACKAGE_NAME });\n\ntype SignUpUnsafeMetadata = Record<string, unknown>;\n\nexport type StartGoogleAuthenticationFlowParams = {\n unsafeMetadata?: SignUpUnsafeMetadata;\n};\n\nexport type StartGoogleAuthenticationFlowReturnType = {\n createdSessionId: string | null;\n setActive?: SetActive;\n signIn?: SignInResource;\n signUp?: SignUpResource;\n};\n\n/**\n * Stub for Google Authentication hook on unsupported platforms.\n *\n * Native Google Authentication is only available on iOS and Android.\n * For web platforms, use the OAuth-based Google Sign-In flow instead via useSSO.\n *\n * @example\n * ```tsx\n * import { useSSO } from '@clerk/expo';\n * import { Button } from 'react-native';\n *\n * function GoogleSignInButton() {\n * const { startSSOFlow } = useSSO();\n *\n * const onPress = async () => {\n * try {\n * const { createdSessionId, setActive } = await startSSOFlow({\n * strategy: 'oauth_google'\n * });\n *\n * if (createdSessionId && setActive) {\n * await setActive({ session: createdSessionId });\n * }\n * } catch (err) {\n * console.error('Google Authentication error:', err);\n * }\n * };\n *\n * return <Button title=\"Sign in with Google\" onPress={onPress} />;\n * }\n * ```\n *\n * @platform iOS, Android - This hook only works on iOS and Android. On other platforms, it will throw an error.\n *\n * @returns An object containing the `startGoogleAuthenticationFlow` function that throws an error\n */\nexport function useSignInWithGoogle(): {\n startGoogleAuthenticationFlow: (\n startGoogleAuthenticationFlowParams?: StartGoogleAuthenticationFlowParams,\n ) => Promise<StartGoogleAuthenticationFlowReturnType>;\n} {\n function startGoogleAuthenticationFlow(\n _startGoogleAuthenticationFlowParams?: StartGoogleAuthenticationFlowParams,\n ): Promise<StartGoogleAuthenticationFlowReturnType> {\n return errorThrower.throw(\n 'Native Google Authentication is only available on iOS and Android. ' +\n 'For web and other platforms, please use the OAuth-based flow with useSSO and strategy: \"oauth_google\".',\n );\n }\n\n return {\n startGoogleAuthenticationFlow,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkC;AAGlC,MAAM,mBAAe,gCAAkB,EAAE,aAAa,4BAAa,CAAC;AAmD7D,SAAS,sBAId;AACA,WAAS,8BACP,sCACkD;AAClD,WAAO,aAAa;AAAA,MAClB;AAAA,IAEF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,29 @@
1
+ import type { ClientResource, SetActive } from '@clerk/shared/types';
2
+ import type { StartGoogleAuthenticationFlowParams, StartGoogleAuthenticationFlowReturnType } from './useSignInWithGoogle.types';
3
+ export type GoogleClientIds = {
4
+ webClientId: string;
5
+ iosClientId?: string;
6
+ };
7
+ export type GoogleAuthenticationFlowContext = {
8
+ client: ClientResource;
9
+ setActive: SetActive;
10
+ };
11
+ type PlatformConfig = {
12
+ requiresIosClientId: boolean;
13
+ };
14
+ /**
15
+ * Factory function to create the useSignInWithGoogle hook with platform-specific configuration.
16
+ *
17
+ * @internal
18
+ */
19
+ export declare function createUseSignInWithGoogle(platformConfig: PlatformConfig): () => {
20
+ startGoogleAuthenticationFlow: (startGoogleAuthenticationFlowParams?: StartGoogleAuthenticationFlowParams) => Promise<StartGoogleAuthenticationFlowReturnType>;
21
+ };
22
+ /**
23
+ * Core implementation of Google Authentication flow shared between iOS and Android.
24
+ *
25
+ * @internal
26
+ */
27
+ export declare function executeGoogleAuthenticationFlow(context: GoogleAuthenticationFlowContext, clientIds: GoogleClientIds, params?: StartGoogleAuthenticationFlowParams): Promise<StartGoogleAuthenticationFlowReturnType>;
28
+ export {};
29
+ //# sourceMappingURL=useSignInWithGoogle.shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSignInWithGoogle.shared.d.ts","sourceRoot":"","sources":["../src/useSignInWithGoogle.shared.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGrE,OAAO,KAAK,EACV,mCAAmC,EACnC,uCAAuC,EACxC,MAAM,6BAA6B,CAAC;AAIrC,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC5C,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,SAAS,CAAC;CACtB,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,mBAAmB,EAAE,OAAO,CAAC;CAC9B,CAAC;AA+BF;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,cAAc,EAAE,cAAc;0EAO5B,mCAAmC,KACxE,OAAO,CAAC,uCAAuC,CAAC;EAoCtD;AAED;;;;GAIG;AACH,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,+BAA+B,EACxC,SAAS,EAAE,eAAe,EAC1B,MAAM,CAAC,EAAE,mCAAmC,GAC3C,OAAO,CAAC,uCAAuC,CAAC,CAmGlD"}
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var useSignInWithGoogle_shared_exports = {};
30
+ __export(useSignInWithGoogle_shared_exports, {
31
+ createUseSignInWithGoogle: () => createUseSignInWithGoogle,
32
+ executeGoogleAuthenticationFlow: () => executeGoogleAuthenticationFlow
33
+ });
34
+ module.exports = __toCommonJS(useSignInWithGoogle_shared_exports);
35
+ var import_react = require("@clerk/react");
36
+ var import_error = require("@clerk/shared/error");
37
+ var import_telemetry = require("@clerk/shared/telemetry");
38
+ var import_google_one_tap = require("./google-one-tap");
39
+ const errorThrower = (0, import_error.buildErrorThrower)({ packageName: "@clerk/expo-google-signin" });
40
+ async function getGoogleClientIds() {
41
+ var _a, _b, _c, _d;
42
+ let webClientId;
43
+ let iosClientId;
44
+ try {
45
+ const ConstantsModule = await import("expo-constants");
46
+ const Constants = ConstantsModule.default;
47
+ webClientId = ((_b = (_a = Constants == null ? void 0 : Constants.expoConfig) == null ? void 0 : _a.extra) == null ? void 0 : _b.EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID) || process.env.EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID;
48
+ iosClientId = ((_d = (_c = Constants == null ? void 0 : Constants.expoConfig) == null ? void 0 : _c.extra) == null ? void 0 : _d.EXPO_PUBLIC_CLERK_GOOGLE_IOS_CLIENT_ID) || process.env.EXPO_PUBLIC_CLERK_GOOGLE_IOS_CLIENT_ID;
49
+ } catch {
50
+ webClientId = process.env.EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID;
51
+ iosClientId = process.env.EXPO_PUBLIC_CLERK_GOOGLE_IOS_CLIENT_ID;
52
+ }
53
+ return { webClientId, iosClientId };
54
+ }
55
+ function createUseSignInWithGoogle(platformConfig) {
56
+ return function useSignInWithGoogle() {
57
+ var _a;
58
+ const clerk = (0, import_react.useClerk)();
59
+ (_a = clerk.telemetry) == null ? void 0 : _a.record((0, import_telemetry.eventMethodCalled)("useSignInWithGoogle"));
60
+ async function startGoogleAuthenticationFlow(startGoogleAuthenticationFlowParams) {
61
+ const { client, loaded, setActive } = clerk;
62
+ if (!loaded || !client) {
63
+ return {
64
+ createdSessionId: null,
65
+ setActive
66
+ };
67
+ }
68
+ const { webClientId, iosClientId } = await getGoogleClientIds();
69
+ if (!webClientId) {
70
+ return errorThrower.throw(
71
+ "Google Sign-In credentials not found. Please set EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID in your .env file."
72
+ );
73
+ }
74
+ if (platformConfig.requiresIosClientId && !iosClientId) {
75
+ return errorThrower.throw(
76
+ "Google Sign-In credentials not found. Please set EXPO_PUBLIC_CLERK_GOOGLE_IOS_CLIENT_ID in your .env file."
77
+ );
78
+ }
79
+ return executeGoogleAuthenticationFlow(
80
+ { client, setActive },
81
+ { webClientId, iosClientId },
82
+ startGoogleAuthenticationFlowParams
83
+ );
84
+ }
85
+ return {
86
+ startGoogleAuthenticationFlow
87
+ };
88
+ };
89
+ }
90
+ async function executeGoogleAuthenticationFlow(context, clientIds, params) {
91
+ var _a;
92
+ const { client, setActive } = context;
93
+ const { signIn, signUp } = client;
94
+ import_google_one_tap.ClerkGoogleOneTapSignIn.configure(clientIds);
95
+ const { randomUUID } = await import("expo-crypto");
96
+ const nonce = randomUUID();
97
+ try {
98
+ const response = await import_google_one_tap.ClerkGoogleOneTapSignIn.presentExplicitSignIn({
99
+ nonce
100
+ });
101
+ if (!(0, import_google_one_tap.isSuccessResponse)(response)) {
102
+ return {
103
+ createdSessionId: null,
104
+ setActive,
105
+ signIn,
106
+ signUp
107
+ };
108
+ }
109
+ const { idToken } = response.data;
110
+ try {
111
+ await signIn.create({
112
+ strategy: "google_one_tap",
113
+ token: idToken
114
+ });
115
+ const userNeedsToBeCreated = signIn.firstFactorVerification.status === "transferable";
116
+ if (userNeedsToBeCreated) {
117
+ await signUp.create({
118
+ transfer: true,
119
+ unsafeMetadata: params == null ? void 0 : params.unsafeMetadata
120
+ });
121
+ return {
122
+ createdSessionId: signUp.createdSessionId,
123
+ setActive,
124
+ signIn,
125
+ signUp
126
+ };
127
+ }
128
+ return {
129
+ createdSessionId: signIn.createdSessionId,
130
+ setActive,
131
+ signIn,
132
+ signUp
133
+ };
134
+ } catch (signInError) {
135
+ if ((0, import_error.isClerkAPIResponseError)(signInError) && ((_a = signInError.errors) == null ? void 0 : _a.some((err) => err.code === "external_account_not_found"))) {
136
+ await signUp.create({
137
+ strategy: "google_one_tap",
138
+ token: idToken,
139
+ unsafeMetadata: params == null ? void 0 : params.unsafeMetadata
140
+ });
141
+ return {
142
+ createdSessionId: signUp.createdSessionId,
143
+ setActive,
144
+ signIn,
145
+ signUp
146
+ };
147
+ }
148
+ throw signInError;
149
+ }
150
+ } catch (error) {
151
+ if ((0, import_google_one_tap.isErrorWithCode)(error) && error.code === "SIGN_IN_CANCELLED") {
152
+ return {
153
+ createdSessionId: null,
154
+ setActive,
155
+ signIn,
156
+ signUp
157
+ };
158
+ }
159
+ throw error;
160
+ }
161
+ }
162
+ // Annotate the CommonJS export names for ESM import in node:
163
+ 0 && (module.exports = {
164
+ createUseSignInWithGoogle,
165
+ executeGoogleAuthenticationFlow
166
+ });
167
+ //# sourceMappingURL=useSignInWithGoogle.shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/useSignInWithGoogle.shared.ts"],"sourcesContent":["import { useClerk } from '@clerk/react';\nimport { buildErrorThrower, isClerkAPIResponseError } from '@clerk/shared/error';\nimport { eventMethodCalled } from '@clerk/shared/telemetry';\nimport type { ClientResource, SetActive } from '@clerk/shared/types';\n\nimport { ClerkGoogleOneTapSignIn, isErrorWithCode, isSuccessResponse } from './google-one-tap';\nimport type {\n StartGoogleAuthenticationFlowParams,\n StartGoogleAuthenticationFlowReturnType,\n} from './useSignInWithGoogle.types';\n\nconst errorThrower = buildErrorThrower({ packageName: PACKAGE_NAME });\n\nexport type GoogleClientIds = {\n webClientId: string;\n iosClientId?: string;\n};\n\nexport type GoogleAuthenticationFlowContext = {\n client: ClientResource;\n setActive: SetActive;\n};\n\ntype PlatformConfig = {\n requiresIosClientId: boolean;\n};\n\n/**\n * Helper to get Google client IDs from expo-constants or process.env.\n * Dynamically imports expo-constants to keep it optional.\n */\nasync function getGoogleClientIds(): Promise<{ webClientId?: string; iosClientId?: string }> {\n let webClientId: string | undefined;\n let iosClientId: string | undefined;\n\n // Try to get values from expo-constants first\n try {\n const ConstantsModule = await import('expo-constants');\n const Constants = ConstantsModule.default as {\n expoConfig?: { extra?: Record<string, string> };\n };\n webClientId =\n Constants?.expoConfig?.extra?.EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID ||\n process.env.EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID;\n iosClientId =\n Constants?.expoConfig?.extra?.EXPO_PUBLIC_CLERK_GOOGLE_IOS_CLIENT_ID ||\n process.env.EXPO_PUBLIC_CLERK_GOOGLE_IOS_CLIENT_ID;\n } catch {\n // expo-constants not available, fall back to process.env only\n webClientId = process.env.EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID;\n iosClientId = process.env.EXPO_PUBLIC_CLERK_GOOGLE_IOS_CLIENT_ID;\n }\n\n return { webClientId, iosClientId };\n}\n\n/**\n * Factory function to create the useSignInWithGoogle hook with platform-specific configuration.\n *\n * @internal\n */\nexport function createUseSignInWithGoogle(platformConfig: PlatformConfig) {\n return function useSignInWithGoogle() {\n const clerk = useClerk();\n\n clerk.telemetry?.record(eventMethodCalled('useSignInWithGoogle'));\n\n async function startGoogleAuthenticationFlow(\n startGoogleAuthenticationFlowParams?: StartGoogleAuthenticationFlowParams,\n ): Promise<StartGoogleAuthenticationFlowReturnType> {\n const { client, loaded, setActive } = clerk;\n\n if (!loaded || !client) {\n return {\n createdSessionId: null,\n setActive,\n };\n }\n\n // Get environment variables from expo-constants or process.env\n const { webClientId, iosClientId } = await getGoogleClientIds();\n\n if (!webClientId) {\n return errorThrower.throw(\n 'Google Sign-In credentials not found. Please set EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID in your .env file.',\n );\n }\n\n if (platformConfig.requiresIosClientId && !iosClientId) {\n return errorThrower.throw(\n 'Google Sign-In credentials not found. Please set EXPO_PUBLIC_CLERK_GOOGLE_IOS_CLIENT_ID in your .env file.',\n );\n }\n\n return executeGoogleAuthenticationFlow(\n { client, setActive },\n { webClientId, iosClientId },\n startGoogleAuthenticationFlowParams,\n );\n }\n\n return {\n startGoogleAuthenticationFlow,\n };\n };\n}\n\n/**\n * Core implementation of Google Authentication flow shared between iOS and Android.\n *\n * @internal\n */\nexport async function executeGoogleAuthenticationFlow(\n context: GoogleAuthenticationFlowContext,\n clientIds: GoogleClientIds,\n params?: StartGoogleAuthenticationFlowParams,\n): Promise<StartGoogleAuthenticationFlowReturnType> {\n const { client, setActive } = context;\n const { signIn, signUp } = client;\n\n // Configure Google Sign-In with client IDs\n ClerkGoogleOneTapSignIn.configure(clientIds);\n\n // Generate a cryptographic nonce for replay attack protection\n const { randomUUID } = await import('expo-crypto');\n const nonce = randomUUID();\n\n try {\n // Present Google Sign-In UI with nonce\n const response = await ClerkGoogleOneTapSignIn.presentExplicitSignIn({\n nonce,\n });\n\n // User cancelled\n if (!isSuccessResponse(response)) {\n return {\n createdSessionId: null,\n setActive,\n signIn,\n signUp,\n };\n }\n\n const { idToken } = response.data;\n\n try {\n // Try to sign in with the Google One Tap strategy\n await signIn.create({\n strategy: 'google_one_tap',\n token: idToken,\n });\n\n // Check if we need to transfer to SignUp (user doesn't exist yet)\n const userNeedsToBeCreated = signIn.firstFactorVerification.status === 'transferable';\n\n if (userNeedsToBeCreated) {\n // User doesn't exist - create a new SignUp with transfer\n await signUp.create({\n transfer: true,\n unsafeMetadata: params?.unsafeMetadata,\n });\n\n return {\n createdSessionId: signUp.createdSessionId,\n setActive,\n signIn,\n signUp,\n };\n }\n\n // User exists - return the SignIn session\n return {\n createdSessionId: signIn.createdSessionId,\n setActive,\n signIn,\n signUp,\n };\n } catch (signInError: unknown) {\n // Handle the case where the user doesn't exist (external_account_not_found)\n if (\n isClerkAPIResponseError(signInError) &&\n signInError.errors?.some(err => err.code === 'external_account_not_found')\n ) {\n // User doesn't exist - create a new SignUp with the token\n await signUp.create({\n strategy: 'google_one_tap',\n token: idToken,\n unsafeMetadata: params?.unsafeMetadata,\n });\n\n return {\n createdSessionId: signUp.createdSessionId,\n setActive,\n signIn,\n signUp,\n };\n }\n\n // Re-throw if it's a different error\n throw signInError;\n }\n } catch (error: unknown) {\n // Handle Google Sign-In cancellation errors\n if (isErrorWithCode(error) && error.code === 'SIGN_IN_CANCELLED') {\n return {\n createdSessionId: null,\n setActive,\n signIn,\n signUp,\n };\n }\n\n // Re-throw other errors\n throw error;\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,13 @@
1
+ import type { SetActive, SignInResource, SignUpResource } from '@clerk/shared/types';
2
+ type SignUpUnsafeMetadata = Record<string, unknown>;
3
+ export type StartGoogleAuthenticationFlowParams = {
4
+ unsafeMetadata?: SignUpUnsafeMetadata;
5
+ };
6
+ export type StartGoogleAuthenticationFlowReturnType = {
7
+ createdSessionId: string | null;
8
+ setActive?: SetActive;
9
+ signIn?: SignInResource;
10
+ signUp?: SignUpResource;
11
+ };
12
+ export {};
13
+ //# sourceMappingURL=useSignInWithGoogle.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSignInWithGoogle.types.d.ts","sourceRoot":"","sources":["../src/useSignInWithGoogle.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErF,KAAK,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpD,MAAM,MAAM,mCAAmC,GAAG;IAChD,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,uCAAuC,GAAG;IACpD,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,MAAM,CAAC,EAAE,cAAc,CAAC;CACzB,CAAC"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+ var useSignInWithGoogle_types_exports = {};
16
+ module.exports = __toCommonJS(useSignInWithGoogle_types_exports);
17
+ //# sourceMappingURL=useSignInWithGoogle.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/useSignInWithGoogle.types.ts"],"sourcesContent":["import type { SetActive, SignInResource, SignUpResource } from '@clerk/shared/types';\n\ntype SignUpUnsafeMetadata = Record<string, unknown>;\n\nexport type StartGoogleAuthenticationFlowParams = {\n unsafeMetadata?: SignUpUnsafeMetadata;\n};\n\nexport type StartGoogleAuthenticationFlowReturnType = {\n createdSessionId: string | null;\n setActive?: SetActive;\n signIn?: SignInResource;\n signUp?: SignUpResource;\n};\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
@@ -0,0 +1,9 @@
1
+ {
2
+ "platforms": ["apple", "android"],
3
+ "apple": {
4
+ "modules": ["ClerkGoogleSignInModule"]
5
+ },
6
+ "android": {
7
+ "modules": ["expo.modules.clerk.googlesignin.ClerkGoogleSignInModule"]
8
+ }
9
+ }
@@ -0,0 +1,27 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'ClerkGoogleSignIn'
7
+ s.version = package['version']
8
+ s.summary = 'Native Google Sign-In module for Clerk Expo'
9
+ s.description = 'Native Google Sign-In functionality using Google Sign-In SDK with nonce support for Clerk authentication'
10
+ s.license = package['license']
11
+ s.author = package['author']
12
+ s.homepage = package['homepage']
13
+ s.platforms = { :ios => '13.4' }
14
+ s.swift_version = '5.4'
15
+ s.source = { :git => 'https://github.com/clerk/javascript.git' }
16
+ s.static_framework = true
17
+
18
+ s.dependency 'ExpoModulesCore'
19
+ s.dependency 'GoogleSignIn', '~> 9.0'
20
+ s.dependency 'GoogleUtilities'
21
+ s.dependency 'RecaptchaInterop'
22
+
23
+ # Only include the Google Sign-In module files
24
+ s.source_files = 'ClerkGoogleSignInModule.swift'
25
+
26
+ install_modules_dependencies(s)
27
+ end
@@ -0,0 +1,199 @@
1
+ import ExpoModulesCore
2
+ import GoogleSignIn
3
+
4
+ public class ClerkGoogleSignInModule: Module {
5
+ private var clientId: String?
6
+ private var hostedDomain: String?
7
+
8
+ public func definition() -> ModuleDefinition {
9
+ Name("ClerkGoogleSignIn")
10
+
11
+ Function("configure") { (params: [String: Any?]) in
12
+ self.configure(params)
13
+ }
14
+
15
+ AsyncFunction("signIn") { (params: [String: Any?]?, promise: Promise) in
16
+ self.signIn(params, promise: promise)
17
+ }.runOnQueue(.main)
18
+
19
+ AsyncFunction("createAccount") { (params: [String: Any?]?, promise: Promise) in
20
+ self.createAccount(params, promise: promise)
21
+ }.runOnQueue(.main)
22
+
23
+ AsyncFunction("presentExplicitSignIn") { (params: [String: Any?]?, promise: Promise) in
24
+ self.presentExplicitSignIn(params, promise: promise)
25
+ }.runOnQueue(.main)
26
+
27
+ AsyncFunction("signOut") { (promise: Promise) in
28
+ self.signOut(promise: promise)
29
+ }.runOnQueue(.main)
30
+ }
31
+
32
+ // MARK: - configure
33
+
34
+ private func configure(_ params: [String: Any?]) {
35
+ let webClientId = self.nonEmptyClientId(params["webClientId"] as? String)
36
+ let iosClientId = self.nonEmptyClientId(params["iosClientId"] as? String)
37
+ self.clientId = iosClientId ?? webClientId
38
+ self.hostedDomain = params["hostedDomain"] as? String
39
+
40
+ if let clientId = self.clientId {
41
+ let config = GIDConfiguration(
42
+ clientID: clientId,
43
+ serverClientID: webClientId
44
+ )
45
+ GIDSignIn.sharedInstance.configuration = config
46
+ }
47
+ }
48
+
49
+ // MARK: - signIn
50
+
51
+ private func signIn(_ params: [String: Any?]?, promise: Promise) {
52
+ guard self.clientId != nil else {
53
+ promise.reject("NOT_CONFIGURED", "Google Sign-In is not configured. Call configure() first.")
54
+ return
55
+ }
56
+
57
+ guard let presentingVC = self.getPresentingViewController() else {
58
+ promise.reject("GOOGLE_SIGN_IN_ERROR", "No presenting view controller available")
59
+ return
60
+ }
61
+
62
+ let hint = params?["hint"] as? String
63
+ let nonce = params?["nonce"] as? String
64
+
65
+ GIDSignIn.sharedInstance.signIn(
66
+ withPresenting: presentingVC,
67
+ hint: hint,
68
+ additionalScopes: nil,
69
+ nonce: nonce,
70
+ completion: { result, error in
71
+ self.handleSignInResult(result: result, error: error, promise: promise)
72
+ }
73
+ )
74
+ }
75
+
76
+ // MARK: - createAccount
77
+
78
+ private func createAccount(_ params: [String: Any?]?, promise: Promise) {
79
+ guard self.clientId != nil else {
80
+ promise.reject("NOT_CONFIGURED", "Google Sign-In is not configured. Call configure() first.")
81
+ return
82
+ }
83
+
84
+ guard let presentingVC = self.getPresentingViewController() else {
85
+ promise.reject("GOOGLE_SIGN_IN_ERROR", "No presenting view controller available")
86
+ return
87
+ }
88
+
89
+ let nonce = params?["nonce"] as? String
90
+
91
+ GIDSignIn.sharedInstance.signIn(
92
+ withPresenting: presentingVC,
93
+ hint: nil,
94
+ additionalScopes: nil,
95
+ nonce: nonce,
96
+ completion: { result, error in
97
+ self.handleSignInResult(result: result, error: error, promise: promise)
98
+ }
99
+ )
100
+ }
101
+
102
+ // MARK: - presentExplicitSignIn
103
+
104
+ private func presentExplicitSignIn(_ params: [String: Any?]?, promise: Promise) {
105
+ guard self.clientId != nil else {
106
+ promise.reject("NOT_CONFIGURED", "Google Sign-In is not configured. Call configure() first.")
107
+ return
108
+ }
109
+
110
+ guard let presentingVC = self.getPresentingViewController() else {
111
+ promise.reject("GOOGLE_SIGN_IN_ERROR", "No presenting view controller available")
112
+ return
113
+ }
114
+
115
+ let nonce = params?["nonce"] as? String
116
+
117
+ GIDSignIn.sharedInstance.signIn(
118
+ withPresenting: presentingVC,
119
+ hint: nil,
120
+ additionalScopes: nil,
121
+ nonce: nonce,
122
+ completion: { result, error in
123
+ self.handleSignInResult(result: result, error: error, promise: promise)
124
+ }
125
+ )
126
+ }
127
+
128
+ // MARK: - signOut
129
+
130
+ private func signOut(promise: Promise) {
131
+ GIDSignIn.sharedInstance.signOut()
132
+ promise.resolve()
133
+ }
134
+
135
+ // MARK: - Helpers
136
+
137
+ private func nonEmptyClientId(_ value: String?) -> String? {
138
+ guard let value = value?.trimmingCharacters(in: .whitespacesAndNewlines) else {
139
+ return nil
140
+ }
141
+
142
+ return value.isEmpty ? nil : value
143
+ }
144
+
145
+ private func getPresentingViewController() -> UIViewController? {
146
+ guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
147
+ let window = scene.windows.first,
148
+ let rootVC = window.rootViewController else {
149
+ return nil
150
+ }
151
+
152
+ var topVC = rootVC
153
+ while let presentedVC = topVC.presentedViewController {
154
+ topVC = presentedVC
155
+ }
156
+ return topVC
157
+ }
158
+
159
+ private func handleSignInResult(result: GIDSignInResult?, error: Error?, promise: Promise) {
160
+ if let error = error {
161
+ let nsError = error as NSError
162
+
163
+ // Check for user cancellation
164
+ if nsError.domain == kGIDSignInErrorDomain && nsError.code == GIDSignInError.canceled.rawValue {
165
+ promise.reject("SIGN_IN_CANCELLED", "User cancelled the sign-in flow")
166
+ return
167
+ }
168
+
169
+ promise.reject("GOOGLE_SIGN_IN_ERROR", error.localizedDescription)
170
+ return
171
+ }
172
+
173
+ guard let result = result,
174
+ let idToken = result.user.idToken?.tokenString else {
175
+ promise.reject("GOOGLE_SIGN_IN_ERROR", "No ID token received")
176
+ return
177
+ }
178
+
179
+ let user = result.user
180
+ let profile = user.profile
181
+
182
+ let response: [String: Any] = [
183
+ "type": "success",
184
+ "data": [
185
+ "idToken": idToken,
186
+ "user": [
187
+ "id": user.userID ?? "",
188
+ "email": profile?.email ?? "",
189
+ "name": profile?.name ?? "",
190
+ "givenName": profile?.givenName ?? "",
191
+ "familyName": profile?.familyName ?? "",
192
+ "photo": profile?.imageURL(withDimension: 200)?.absoluteString ?? NSNull()
193
+ ] as [String: Any]
194
+ ] as [String: Any]
195
+ ]
196
+
197
+ promise.resolve(response)
198
+ }
199
+ }
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@clerk/expo-google-signin",
3
+ "version": "0.1.0",
4
+ "description": "Native Google Sign-In library to be used with Clerk for Expo",
5
+ "keywords": [
6
+ "react-native",
7
+ "expo",
8
+ "google-signin",
9
+ "clerk"
10
+ ],
11
+ "homepage": "https://clerk.com/",
12
+ "bugs": {
13
+ "url": "https://github.com/clerk/javascript/issues"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/clerk/javascript.git",
18
+ "directory": "packages/expo-google-signin"
19
+ },
20
+ "license": "MIT",
21
+ "author": "Clerk",
22
+ "main": "dist/index.js",
23
+ "types": "dist/index.d.ts",
24
+ "files": [
25
+ "dist",
26
+ "android",
27
+ "ios",
28
+ "expo-module.config.json",
29
+ "app.plugin.js",
30
+ "app.plugin.d.ts"
31
+ ],
32
+ "dependencies": {
33
+ "tslib": "2.8.1",
34
+ "@clerk/react": "^6.11.3",
35
+ "@clerk/shared": "^4.23.0"
36
+ },
37
+ "devDependencies": {
38
+ "@expo/config-plugins": "^54.0.4",
39
+ "@types/react": "18.3.28",
40
+ "expo": "~54.0.34",
41
+ "expo-constants": "^18.0.13",
42
+ "expo-crypto": "^15.0.9",
43
+ "react": "18.3.1",
44
+ "react-native": "^0.85.2",
45
+ "tsup": "8.5.1"
46
+ },
47
+ "peerDependencies": {
48
+ "expo": ">=53 <57",
49
+ "expo-constants": ">=12",
50
+ "expo-crypto": ">=12",
51
+ "react": "^18.0.0 || ^19.0.0",
52
+ "react-native": ">=0.75"
53
+ },
54
+ "peerDependenciesMeta": {
55
+ "expo-constants": {
56
+ "optional": true
57
+ }
58
+ },
59
+ "publishConfig": {
60
+ "access": "public"
61
+ },
62
+ "scripts": {
63
+ "build": "tsup",
64
+ "build:declarations": "tsc -p tsconfig.declarations.json",
65
+ "clean": "rimraf ./dist",
66
+ "dev": "tsup --watch",
67
+ "dev:pub": "pnpm dev -- --env.publish",
68
+ "format": "node ../../scripts/format-package.mjs",
69
+ "format:check": "node ../../scripts/format-package.mjs --check",
70
+ "lint": "eslint src",
71
+ "test": "vitest run"
72
+ }
73
+ }