Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .changeset/expo-google-signin-package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
'@clerk/expo': major
'@clerk/expo-google-signin': minor
Comment thread
wobsoriano marked this conversation as resolved.
---

Native Google Sign-In has moved out of `@clerk/expo` into a new optional package, `@clerk/expo-google-signin`. Apps that don't use `useSignInWithGoogle` no longer pull in the native Google Sign-In dependencies during prebuild.

If you use native Google Sign-In, install the new package:

```sh
npx expo install @clerk/expo-google-signin
```

add its config plugin alongside `@clerk/expo` in your app config:

```json
{
"expo": {
"plugins": ["@clerk/expo", "@clerk/expo-google-signin"]
}
}
```

then rebuild your native app. The `@clerk/expo/google` import path still re-exports `useSignInWithGoogle`, but it now requires `@clerk/expo-google-signin` to be installed.
11 changes: 8 additions & 3 deletions .github/workflows/expo-native-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:
- 'integration/templates/expo-native/**'
- 'integration/tests/expo-native/**'
- 'packages/expo/**'
- 'packages/expo-google-signin/**'
workflow_dispatch:

permissions:
Expand Down Expand Up @@ -74,11 +75,13 @@ jobs:
- name: Install monorepo dependencies
run: pnpm install --frozen-lockfile

- name: Build and pack @clerk/expo
- name: Build and pack Clerk packages
run: |
pnpm --filter @clerk/expo... build
pnpm --filter @clerk/expo-google-signin build
mkdir -p "$SDK_PACK_DIR"
pnpm --filter @clerk/expo pack --pack-destination "$SDK_PACK_DIR"
pnpm --filter @clerk/expo-google-signin pack --pack-destination "$SDK_PACK_DIR"

- name: Install fixture dependencies
working-directory: ${{ env.FIXTURE_DIR }}
Expand All @@ -88,8 +91,10 @@ jobs:
run: |
cp "package.sdk-$EXPO_SDK.json" package.json
pnpm install --no-frozen-lockfile
SDK_TARBALL="$(ls "$SDK_PACK_DIR"/clerk-expo-*.tgz)"
pnpm add "$SDK_TARBALL" -w
# [0-9] keeps this glob off the clerk-expo-google-signin tarball.
SDK_TARBALL="$(ls "$SDK_PACK_DIR"/clerk-expo-[0-9]*.tgz)"
GOOGLE_SIGNIN_TARBALL="$(ls "$SDK_PACK_DIR"/clerk-expo-google-signin-*.tgz)"
pnpm add "$SDK_TARBALL" "$GOOGLE_SIGNIN_TARBALL" -w
# expo-dev-client makes even release builds boot into the dev
# launcher (unreachable Metro in CI), which stalls every Maestro
# flow on a blank screen. Skip it on e2e jobs only.
Expand Down
1 change: 1 addition & 0 deletions break-check.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"packages/chrome-extension",
"packages/clerk-js",
"packages/expo",
"packages/expo-google-signin",
"packages/expo-passkeys",
"packages/express",
"packages/fastify",
Expand Down
16 changes: 16 additions & 0 deletions integration/templates/expo-native/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ClerkProvider, useAuth, useUser } from '@clerk/expo';
import { useSignInWithGoogle } from '@clerk/expo/google';
import { AuthView, UserButton } from '@clerk/expo/native';
import { tokenCache } from '@clerk/expo/token-cache';
import { useState } from 'react';
Expand All @@ -13,7 +14,9 @@ if (!publishableKey) {
function NativeBuildFixture() {
const { isLoaded, isSignedIn, signOut } = useAuth({ treatPendingAsSignedOut: false });
const { user } = useUser();
const { startGoogleAuthenticationFlow } = useSignInWithGoogle();
const [isAuthOpen, setIsAuthOpen] = useState(false);
const [googleResult, setGoogleResult] = useState<string | null>(null);

return (
<View style={styles.container}>
Expand All @@ -29,6 +32,19 @@ function NativeBuildFixture() {
title='Open native AuthView'
onPress={() => setIsAuthOpen(true)}
/>
{!isSignedIn && (
<Button
testID='google-sign-in-button'
title='Sign in with Google'
onPress={() => {
void startGoogleAuthenticationFlow().catch((error: unknown) => {
const message = error instanceof Error ? error.message : String(error);
setGoogleResult(message.replace(/\s+/g, ' '));
});
}}
/>
)}
{googleResult && <Text testID='google-result'>{googleResult}</Text>}
{isSignedIn && (
<Button
testID='sign-out-button'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
appId: com.clerk.exponativebuildfixture
name: useSignInWithGoogle surfaces the missing-credentials error
---
- runFlow: subflows/open-app.yaml
- tapOn:
id: 'google-sign-in-button'
- extendedWaitUntil:
visible: '.*Google Sign-In credentials not found.*'
timeout: 15000
39 changes: 39 additions & 0 deletions packages/expo-google-signin/android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'maven-publish'

group = 'expo.modules.clerk.googlesignin'
version = '1.0.0'

def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
apply from: expoModulesCorePlugin
applyKotlinExpoModulesCorePlugin()
useCoreDependencies()
useExpoPublishing()

buildscript {
ext.safeExtGet = { prop, fallback ->
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
}

android {
namespace "expo.modules.clerk.googlesignin"

compileSdkVersion safeExtGet("compileSdkVersion", 36)

defaultConfig {
minSdkVersion safeExtGet("minSdkVersion", 24)
targetSdkVersion safeExtGet("targetSdkVersion", 36)
versionCode 1
versionName "1.0.0"
}
}

dependencies {
implementation project(':expo-modules-core')
implementation "androidx.credentials:credentials:1.3.0"
implementation "androidx.credentials:credentials-play-services-auth:1.3.0"
implementation "com.google.android.libraries.identity.googleid:googleid:1.1.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" />
5 changes: 5 additions & 0 deletions packages/expo-google-signin/app.plugin.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { ConfigPlugin } from '@expo/config-plugins';

declare const withClerkExpoGoogleSignIn: ConfigPlugin;

export = withClerkExpoGoogleSignIn;
32 changes: 32 additions & 0 deletions packages/expo-google-signin/app.plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const { withInfoPlist, createRunOncePlugin } = require('@expo/config-plugins');
const pkg = require('./package.json');

const withClerkExpoGoogleSignIn = config => {
const iosUrlScheme =
process.env.EXPO_PUBLIC_CLERK_GOOGLE_IOS_URL_SCHEME ||
(config.extra && config.extra.EXPO_PUBLIC_CLERK_GOOGLE_IOS_URL_SCHEME);

if (!iosUrlScheme) {
return config;
}

return withInfoPlist(config, modConfig => {
if (!Array.isArray(modConfig.modResults.CFBundleURLTypes)) {
modConfig.modResults.CFBundleURLTypes = [];
}

const schemeExists = modConfig.modResults.CFBundleURLTypes.some(urlType =>
urlType.CFBundleURLSchemes?.includes(iosUrlScheme),
);

if (!schemeExists) {
modConfig.modResults.CFBundleURLTypes.push({
CFBundleURLSchemes: [iosUrlScheme],
});
}

return modConfig;
});
};

module.exports = createRunOncePlugin(withClerkExpoGoogleSignIn, pkg.name, pkg.version);
9 changes: 9 additions & 0 deletions packages/expo-google-signin/expo-module.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"platforms": ["apple", "android"],
"apple": {
"modules": ["ClerkGoogleSignInModule"]
},
"android": {
"modules": ["expo.modules.clerk.googlesignin.ClerkGoogleSignInModule"]
}
}
73 changes: 73 additions & 0 deletions packages/expo-google-signin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"name": "@clerk/expo-google-signin",
Comment thread
wobsoriano marked this conversation as resolved.
"version": "0.0.0",
"description": "Native Google Sign-In library to be used with Clerk for Expo",
"keywords": [
"react-native",
"expo",
"google-signin",
"clerk"
],
"homepage": "https://clerk.com/",
"bugs": {
"url": "https://github.com/clerk/javascript/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/clerk/javascript.git",
"directory": "packages/expo-google-signin"
},
"license": "MIT",
"author": "Clerk",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist",
"android",
"ios",
"expo-module.config.json",
"app.plugin.js",
"app.plugin.d.ts"
],
"scripts": {
"build": "tsup",
"build:declarations": "tsc -p tsconfig.declarations.json",
"clean": "rimraf ./dist",
"dev": "tsup --watch",
"dev:pub": "pnpm dev -- --env.publish",
"format": "node ../../scripts/format-package.mjs",
"format:check": "node ../../scripts/format-package.mjs --check",
"lint": "eslint src",
"test": "vitest run"
},
"dependencies": {
"@clerk/react": "workspace:^",
"@clerk/shared": "workspace:^",
"tslib": "catalog:repo"
},
"devDependencies": {
"@expo/config-plugins": "^54.0.4",
"@types/react": "catalog:react",
"expo": "~54.0.34",
"expo-constants": "^18.0.13",
"expo-crypto": "^15.0.9",
"react": "catalog:react",
"react-native": "^0.86.0",
"tsup": "catalog:repo"
},
"peerDependencies": {
"expo": ">=53 <58",
"expo-constants": ">=12",
"expo-crypto": ">=12",
"react": "^18.0.0 || ^19.0.0",
"react-native": ">=0.75"
Comment on lines +51 to +63

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Which React Native version does Expo SDK 54 require, and is react-native 0.85 compatible with expo 54?

💡 Result:

Expo SDK 54 requires React Native 0.81 [1][2]. It is not compatible with React Native 0.85 [3][4]. Each version of Expo SDK is designed to work with a specific React Native version, and using mismatched versions is generally not supported [3]. React Native 0.85 was introduced as the target version for Expo SDK 56 [3][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the package manifest and nearby workspace context.
sed -n '1,220p' packages/expo-google-signin/package.json
printf '\n---\n'
git ls-files 'packages/expo-google-signin/*' | sed -n '1,120p'

Repository: clerk/javascript

Length of output: 3499


Align the Expo devDependency versions. Expo SDK 54 ships with React Native 0.81, so react-native@^0.85.2 is out of sync here and can break local installs or builds for this package.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/expo-google-signin/package.json` around lines 51 - 63, The
devDependency versions in the package.json for expo-google-signin are misaligned
with the Expo SDK being targeted, specifically the react-native version is too
new for SDK 54. Update the expo-related devDependencies in this package to match
the Expo 54-compatible versions, and keep the peerDependencies range broad
enough to support the intended SDKs; use the existing package.json dependency
block and peerDependencies block as the place to align the versions.

},
"peerDependenciesMeta": {
"expo-constants": {
"optional": true
}
},
"publishConfig": {
"access": "public"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ vi.mock('@clerk/shared/error', async importOriginal => {
};
});

vi.mock('../../google-one-tap', async importOriginal => {
vi.mock('../google-one-tap', async importOriginal => {
const actual = await importOriginal();
return {
...actual,
Expand All @@ -46,17 +46,7 @@ vi.mock('react-native', () => {
};
});

vi.mock('../../specs/NativeClerkModule', () => {
return {
default: {
configure: vi.fn(),
getClientToken: vi.fn(),
syncClientStateFromJs: vi.fn(),
},
};
});

vi.mock('../../specs/NativeClerkGoogleSignIn', () => {
vi.mock('../specs/NativeClerkGoogleSignIn', () => {
return {
default: {
configure: vi.fn(),
Expand Down Expand Up @@ -127,25 +117,6 @@ describe('useSignInWithGoogle', () => {
});

describe('startGoogleAuthenticationFlow', () => {
test('should warn once in development about the upcoming package split', () => {
const originalDev = globalThis.__DEV__;
globalThis.__DEV__ = true;
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);

try {
renderHook(() => useSignInWithGoogle());
renderHook(() => useSignInWithGoogle());

expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
expect(consoleWarnSpy).toHaveBeenCalledWith(
'Clerk: In the next major version, native Google Sign-In will require installing @clerk/expo-google-signin and adding its Expo config plugin. The @clerk/expo/google import path will continue to work.',
);
} finally {
consoleWarnSpy.mockRestore();
globalThis.__DEV__ = originalDev;
}
});

test('should return the hook with startGoogleAuthenticationFlow function', () => {
const { result } = renderHook(() => useSignInWithGoogle());

Expand Down
1 change: 1 addition & 0 deletions packages/expo-google-signin/src/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare const PACKAGE_NAME: string;
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function getNativeModule(): NonNullable<typeof NativeClerkGoogleSignIn> {
if (!NativeClerkGoogleSignIn) {
throw new Error(
'ClerkGoogleSignIn native module is not available. ' +
'Ensure the @clerk/expo plugin is added to your app.json and you have run a development build.',
'Ensure the @clerk/expo-google-signin plugin is added to your app.json and you have run a development build.',
);
}
return NativeClerkGoogleSignIn;
Expand Down
5 changes: 5 additions & 0 deletions packages/expo-google-signin/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { useSignInWithGoogle } from './useSignInWithGoogle';
export type {
StartGoogleAuthenticationFlowParams,
StartGoogleAuthenticationFlowReturnType,
} from './useSignInWithGoogle.types';
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@ export type {
* - Built-in nonce support for replay attack protection
* - No additional dependencies required
*
* In the next major version, apps using native Google Sign-In will need to install
* `@clerk/expo-google-signin` alongside `@clerk/expo` and add its Expo config plugin.
*
* @example
* ```tsx
* import { useSignInWithGoogle } from '@clerk/expo';
* import { useSignInWithGoogle } from '@clerk/expo-google-signin';
* import { Button } from 'react-native';
*
* function GoogleSignInButton() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@ export type {
* - Built-in nonce support for replay attack protection
* - No additional dependencies required
*
* In the next major version, apps using native Google Sign-In will need to install
* `@clerk/expo-google-signin` alongside `@clerk/expo` and add its Expo config plugin.
*
* @example
* ```tsx
* import { useSignInWithGoogle } from '@clerk/expo';
* import { useSignInWithGoogle } from '@clerk/expo-google-signin';
* import { Button } from 'react-native';
*
* function GoogleSigninButton() {
Expand Down
Loading
Loading