Skip to content

Conversation

@chriscanin
Copy link
Member

@chriscanin chriscanin commented Nov 12, 2025

Description

This PR implements native Google Sign-In support for Expo iOS and Android apps using the @react-native-google-signin/google-signin library. This provides a seamless native authentication experience for users signing in with their Google accounts.

What's New

  • New Hook: useSignInWithGoogle() - Provides native Google Sign-In functionality for iOS and Android
    • Platform-specific implementations for iOS (useSignInWithGoogle.ios.ts) and Android (useSignInWithGoogle.android.ts)
    • Automatic handling of sign-in/sign-up transfer flow
    • Graceful error handling including user cancellation detection
    • Support for custom unsafeMetadata during authentication

Key Features

  • Native UI: Uses platform-native Google Sign-In UI instead of web-based OAuth flow
  • Automatic Flow Detection: Seamlessly handles both new user sign-up and existing user sign-in
  • Error Handling: Comprehensive error handling for:
    • Missing credentials
    • User cancellation (codes SIGN_IN_CANCELLED and -5)
    • Play Services availability (Android)
  • Environment Variable Support: Reads credentials from both Expo config and environment variables
  • Full Test Coverage: Complete unit tests with 100% coverage for the new hook

Files Changed

  • packages/expo/src/hooks/useSignInWithGoogle.ts - Cross-platform stub with helpful error message
  • packages/expo/src/hooks/useSignInWithGoogle.ios.ts - iOS-specific implementation
  • packages/expo/src/hooks/useSignInWithGoogle.android.ts - Android-specific implementation
  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts - Comprehensive unit tests
  • packages/expo/src/hooks/index.ts - Export new hook
  • packages/expo/package.json - Add optional peer dependency
  • packages/expo/vitest.setup.mts - Add test mocks for expo-modules-core

Implementation Details

iOS Implementation:

  • Uses expo-apple-authentication for native Sign-In
  • Supports oauth_token_apple strategy
  • Handles identity token exchange

Android Implementation:

  • Uses @react-native-google-signin/google-signin library
  • Configures with Web Client ID and Android Client ID
  • Checks Google Play Services availability
  • Handles google_one_tap strategy with ID token

Both implementations:

  • Automatically handle transfer flow when user doesn't exist
  • Return consistent API: { createdSessionId, setActive, signIn, signUp }
  • Support optional unsafeMetadata parameter

Breaking Changes

None - this is a new feature.

Test Plan

  1. Unit tests pass (pnpm test --filter @clerk/clerk-expo)
  2. Build succeeds (pnpm build)
  3. All 9 test cases pass including:
    • Successful sign-in for existing users
    • Transfer flow for new users
    • User cancellation handling (both error codes)
    • Play Services availability check (Android)
    • Missing ID token validation
    • Missing credentials validation
    • Clerk not loaded handling

Documentation

Related documentation PR: [Link to clerk-docs PR]

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • JSDoc comments have been added for the new hook and all exported functions
  • Documentation has been created (see related PR in clerk-docs)

Type of change

  • 🌟 New feature

Summary by CodeRabbit

  • New Features

    • Native Google Sign-In (One Tap) support for iOS and Android: configure, sign-in, create account, explicit sign-in, sign-out; iOS plugin can auto-configure URL scheme.
    • Cross-platform useSignInWithGoogle hook with shared flow and platform-specific adapters.
    • Public JS types, helpers and Expo module metadata for One Tap integration.
  • Tests

    • Unit tests covering Android sign-in hook flows.
  • Chores

    • Package metadata, build config and packaging updates; CocoaPods spec and Android build config added; gitignore updated.

✏️ Tip: You can customize this high-level summary in your review settings.

@chriscanin chriscanin self-assigned this Nov 12, 2025
@changeset-bot
Copy link

changeset-bot bot commented Nov 12, 2025

🦋 Changeset detected

Latest commit: b192d31

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@clerk/expo Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Nov 12, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Dec 18, 2025 6:36pm
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 12, 2025

📝 Walkthrough

Walkthrough

Adds native Google Sign-In support for Expo across Android and iOS. Android: new Gradle build script, minimal AndroidManifest, and a Kotlin Expo module using Credential Manager. iOS: Swift Expo module and a CocoaPods podspec. Adds a TypeScript bridge (ClerkGoogleOneTapSignIn), shared and platform-specific useSignInWithGoogle hooks, types, and unit tests. Introduces an Expo config plugin to inject an iOS URL scheme, updates package.json exports/files/peer‑dev deps, and adds expo-module.config.json and related packaging files.

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 52.38% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective: implementing native Google Sign-In support for both Android and iOS platforms in the Expo package. It is specific, concise, and directly reflects the primary changes in the changeset.

📜 Recent review details

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3514955 and b192d31.

📒 Files selected for processing (1)
  • packages/expo/package.json (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/expo/package.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (27)
  • GitHub Check: Static analysis
  • GitHub Check: Publish with pkg-pr-new
  • GitHub Check: Unit Tests (**)
  • GitHub Check: Integration Tests (tanstack-react-start, chrome)
  • GitHub Check: Integration Tests (nextjs, chrome, 15)
  • GitHub Check: Integration Tests (custom, chrome)
  • GitHub Check: Integration Tests (nextjs, chrome, 16)
  • GitHub Check: Integration Tests (machine, chrome, RQ)
  • GitHub Check: Integration Tests (nextjs, chrome, 16, RQ)
  • GitHub Check: Integration Tests (quickstart, chrome, 15)
  • GitHub Check: Integration Tests (billing, chrome)
  • GitHub Check: Integration Tests (quickstart, chrome, 16)
  • GitHub Check: Integration Tests (react-router, chrome)
  • GitHub Check: Integration Tests (billing, chrome, RQ)
  • GitHub Check: Integration Tests (machine, chrome)
  • GitHub Check: Integration Tests (vue, chrome)
  • GitHub Check: Integration Tests (sessions, chrome)
  • GitHub Check: Integration Tests (astro, chrome)
  • GitHub Check: Integration Tests (localhost, chrome)
  • GitHub Check: Integration Tests (handshake, chrome)
  • GitHub Check: Integration Tests (nuxt, chrome)
  • GitHub Check: Integration Tests (generic, chrome)
  • GitHub Check: Integration Tests (handshake:staging, chrome)
  • GitHub Check: Integration Tests (express, chrome)
  • GitHub Check: Integration Tests (sessions:staging, chrome)
  • GitHub Check: Integration Tests (ap-flows, chrome)
  • GitHub Check: semgrep-cloud-platform/scan

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (15)
packages/expo/vitest.setup.mts (1)

6-18: Expo and __DEV__ mocks look good; consider matching RN/Expo dev semantics

The expo mock and guarded initialization are fine for tests. The only nuance is defaulting globalThis.__DEV__ to false, which makes the test runtime behave more like production than a typical React Native/Expo dev build. If any code under test gates behavior on __DEV__, you may want to consider defaulting it to true (or reading from an env flag) so tests better mirror the usual development environment.

packages/expo/android/build.gradle (1)

7-21: Gradle config is solid; consider aligning fallback SDK versions with Expo’s defaults

The use of safeExtGet and the overall Android/Kotlin configuration look good for an Expo module. To avoid divergence over time, you may want to keep the fallback compileSdk, targetSdk, and minSdk values in sync with whatever versions the root Expo setup uses (so that a misconfigured root project doesn’t silently build against an unexpected API level).

packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts (1)

31-43: Module mocks are minimal; consider partial mocks to reduce brittleness

Right now the react-native and expo-modules-core mocks return only the pieces you need (Platform and EventEmitter). If useSignInWithGoogle.android later imports additional exports from these modules, those will silently become undefined in tests. To make the suite more robust, consider using partial mocks that spread the real module and override just what you need, e.g. via vi.importActual('react-native') and overriding Platform.OS.

packages/expo/app.plugin.js (1)

9-44: Config plugin logic is sound; optionally derive plugin version from package.json

The plugin correctly short‑circuits when no iOS URL scheme is configured and safely appends a new CFBundleURLTypes entry only when needed, avoiding duplicates. As a small polish, you might consider wiring createRunOncePlugin’s version argument from package.json (similar to the podspec) to keep the plugin version in sync with the package, but that’s not required for correctness.

packages/expo/ios/ClerkGoogleSignIn.podspec (1)

5-21: Podspec looks correct; consider pinning a tag in s.source for reproducible builds

The podspec wiring (metadata, platform, Swift version, dependencies, and *.swift source file glob) looks appropriate for this module. If this spec is ever published to a CocoaPods spec repo or consumed via the :git source, you may want to add a :tag tied to package['version'] in s.source to make builds reproducible across releases.

packages/expo/src/google-one-tap/index.ts (1)

1-21: Barrel index.ts goes against “avoid barrel files” guideline (low risk here).

This file is a classic barrel re-export. It’s clean and tree‑shaking friendly, but it does conflict with the guideline to avoid index.ts barrels because of potential circular dependencies. Given it’s a leaf-ish submodule, the risk is low, but if this surface grows or starts being re-exported further up, consider switching callers to import directly from ./ClerkGoogleOneTapSignIn and ./types to keep dependency edges explicit.

packages/expo/src/hooks/useSignInWithGoogle.ios.ts (1)

8-20: Tighten typings and avoid duplicating public API types in the iOS-specific file.

  • SignUpUnsafeMetadata is Record<string, any> and StartGoogleAuthenticationFlowReturnType uses any for setActive, signIn, and signUp. Guidelines say to avoid any; you already have precise types in the shared stub (useSignInWithGoogle.ts).
  • You’re also re‑declaring StartGoogleAuthenticationFlowParams/ReturnType here, which risks drifting from the shared definition over time.

Consider instead:

  • Defining SignUpUnsafeMetadata once (e.g. in the shared stub) as Record<string, unknown>; and
  • Importing the shared types here via a type‑only import, reusing the same SetActive, SignInResource, and SignUpResource types.

For example:

-import { useSignIn, useSignUp } from '@clerk/clerk-react'; +import { useSignIn, useSignUp } from '@clerk/clerk-react'; +import type { SetActive, SignInResource, SignUpResource } from '@clerk/shared/types'; -// Type for unsafe metadata that can be attached to sign-ups -type SignUpUnsafeMetadata = Record<string, any>; +// Type for unsafe metadata that can be attached to sign-ups +type SignUpUnsafeMetadata = Record<string, unknown>; -export type StartGoogleAuthenticationFlowReturnType = { - createdSessionId: string | null; - setActive?: any; - signIn?: any; - signUp?: any; -}; +export type StartGoogleAuthenticationFlowReturnType = { + createdSessionId: string | null; + setActive?: SetActive; + signIn?: SignInResource; + signUp?: SignUpResource; +};

This keeps the iOS implementation aligned with the public TS surface and preserves type safety.

packages/expo/ios/ClerkGoogleSignInModule.swift (1)

4-165: Native iOS Google Sign-In flow looks correct; consider trimming or wiring unused fields.

  • Running all GoogleSignIn calls on the main queue and deriving the top-most presenting UIViewController is appropriate for Expo modules.
  • handleSignInResult correctly special-cases user cancellation via kGIDSignInErrorDomain + GIDSignInError.canceled and otherwise surfaces a structured success payload (type: "success", idToken, and user profile fields) that matches the JS bridge expectations.
  • Guarding on clientId and returning a NotConfiguredException from sign-in methods is a good explicit failure mode.

Minor cleanliness points:

  • hostedDomain and autoSelectEnabled in ConfigureParams are currently unused, as is NoSavedCredentialException. Either wire them into the GoogleSignIn configuration/flows or drop them to avoid confusion about behavior that doesn’t yet exist.

Also applies to: 170-182, 209-212

packages/expo/src/hooks/useSignInWithGoogle.android.ts (4)

2-2: Consider importing types from @clerk/shared/types.

As per coding guidelines, prefer importing types from @clerk/shared/types instead of the deprecated @clerk/types alias.


65-67: Add explicit return type annotation.

As per coding guidelines, always define explicit return types for functions, especially public APIs.

- async function startGoogleAuthenticationFlow( - startGoogleAuthenticationFlowParams?: StartGoogleAuthenticationFlowParams, - ): Promise<StartGoogleAuthenticationFlowReturnType> { + async function startGoogleAuthenticationFlow( + startGoogleAuthenticationFlowParams?: StartGoogleAuthenticationFlowParams, + ): Promise<StartGoogleAuthenticationFlowReturnType> {

The return type is already specified - this looks correct. No change needed.


150-165: Extract Clerk error type guard for improved readability.

The inline type assertions are verbose and repeated. Consider extracting a type guard function to improve readability and maintainability, following the coding guideline to implement type guards for unknown types.

+function isClerkErrorWithCode(error: unknown, code: string): boolean { + if ( + error && + typeof error === 'object' && + 'clerkError' in error && + (error as { clerkError: boolean }).clerkError === true && + 'errors' in error && + Array.isArray((error as { errors: unknown[] }).errors) + ) { + return (error as { errors: Array<{ code: string }> }).errors.some( + err => err.code === code, + ); + } + return false; +} + // In the catch block: - const isClerkError = - signInError && - typeof signInError === 'object' && - 'clerkError' in signInError && - (signInError as { clerkError: boolean }).clerkError === true; - - const hasExternalAccountNotFoundError = - signInError && - typeof signInError === 'object' && - 'errors' in signInError && - Array.isArray((signInError as { errors: unknown[] }).errors) && - (signInError as { errors: Array<{ code: string }> }).errors.some( - err => err.code === 'external_account_not_found', - ); - - if (isClerkError && hasExternalAccountNotFoundError) { + if (isClerkErrorWithCode(signInError, 'external_account_not_found')) {

118-123: Add null check for signIn before calling create.

If isSignInLoaded is true but signIn is unexpectedly undefined, calling signIn.create() will throw. Consider adding a defensive null check.

+ if (!signIn || !signUp) { + return { + createdSessionId: null, + setActive, + signIn, + signUp, + }; + } + try { // Try to sign in with the Google One Tap strategy await signIn.create({
packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts (1)

65-91: JSDoc @platform annotation may be misleading.

The documentation states @platform Android, but this file serves as a cross-platform bridge (no .android.ts suffix). The native module handles platform-specific logic. Consider updating the docs to reflect cross-platform support or adding a note about iOS availability.

packages/expo/android/src/main/java/expo/modules/clerk/googlesignin/ClerkGoogleSignInModule.kt (2)

74-75: Consider caching the CredentialManager instance.

The credentialManager getter creates a new instance on each access. While CredentialManager.create() is lightweight, caching the instance could improve efficiency for repeated sign-in attempts.

+ private var _credentialManager: CredentialManager? = null + private val credentialManager: CredentialManager - get() = CredentialManager.create(context) + get() = _credentialManager ?: CredentialManager.create(context).also { _credentialManager = it }

26-36: Empty string default for webClientId may pass validation.

ConfigureParams.webClientId defaults to an empty string. The configure function stores this value directly without validation, which means an empty webClientId could be set. While the sign-in will fail later with a less clear error, consider validating early.

 // Configure the module Function("configure") { params: ConfigureParams -> + if (params.webClientId.isBlank()) { + throw GoogleSignInException("webClientId is required and cannot be empty") + } webClientId = params.webClientId hostedDomain = params.hostedDomain autoSelectEnabled = params.autoSelectEnabled ?: false }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b944ff3 and 6811d5f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • packages/expo/android/build.gradle (1 hunks)
  • packages/expo/android/src/main/AndroidManifest.xml (1 hunks)
  • packages/expo/android/src/main/java/expo/modules/clerk/googlesignin/ClerkGoogleSignInModule.kt (1 hunks)
  • packages/expo/app.plugin.js (1 hunks)
  • packages/expo/expo-module.config.json (1 hunks)
  • packages/expo/ios/ClerkGoogleSignIn.podspec (1 hunks)
  • packages/expo/ios/ClerkGoogleSignInModule.swift (1 hunks)
  • packages/expo/package.json (4 hunks)
  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts (1 hunks)
  • packages/expo/src/google-one-tap/index.ts (1 hunks)
  • packages/expo/src/google-one-tap/types.ts (1 hunks)
  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts (1 hunks)
  • packages/expo/src/hooks/index.ts (1 hunks)
  • packages/expo/src/hooks/useSignInWithGoogle.android.ts (1 hunks)
  • packages/expo/src/hooks/useSignInWithGoogle.ios.ts (1 hunks)
  • packages/expo/src/hooks/useSignInWithGoogle.ts (1 hunks)
  • packages/expo/vitest.setup.mts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (12)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

All code must pass ESLint checks with the project's configuration

Files:

  • packages/expo/src/hooks/useSignInWithGoogle.ts
  • packages/expo/src/hooks/useSignInWithGoogle.android.ts
  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
  • packages/expo/app.plugin.js
  • packages/expo/src/hooks/useSignInWithGoogle.ios.ts
  • packages/expo/src/google-one-tap/types.ts
  • packages/expo/src/google-one-tap/index.ts
  • packages/expo/src/hooks/index.ts
  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
**/*.{js,jsx,ts,tsx,json,md,yml,yaml}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use Prettier for consistent code formatting

Files:

  • packages/expo/src/hooks/useSignInWithGoogle.ts
  • packages/expo/src/hooks/useSignInWithGoogle.android.ts
  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
  • packages/expo/app.plugin.js
  • packages/expo/package.json
  • packages/expo/src/hooks/useSignInWithGoogle.ios.ts
  • packages/expo/src/google-one-tap/types.ts
  • packages/expo/src/google-one-tap/index.ts
  • packages/expo/expo-module.config.json
  • packages/expo/src/hooks/index.ts
  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
packages/**/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

TypeScript is required for all packages

Files:

  • packages/expo/src/hooks/useSignInWithGoogle.ts
  • packages/expo/src/hooks/useSignInWithGoogle.android.ts
  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
  • packages/expo/src/hooks/useSignInWithGoogle.ios.ts
  • packages/expo/src/google-one-tap/types.ts
  • packages/expo/src/google-one-tap/index.ts
  • packages/expo/src/hooks/index.ts
  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Follow established naming conventions (PascalCase for components, camelCase for variables)

Prefer importing types from @clerk/shared/types instead of the deprecated @clerk/types alias

Files:

  • packages/expo/src/hooks/useSignInWithGoogle.ts
  • packages/expo/src/hooks/useSignInWithGoogle.android.ts
  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
  • packages/expo/app.plugin.js
  • packages/expo/src/hooks/useSignInWithGoogle.ios.ts
  • packages/expo/src/google-one-tap/types.ts
  • packages/expo/src/google-one-tap/index.ts
  • packages/expo/src/hooks/index.ts
  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
packages/**/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

packages/**/src/**/*.{ts,tsx,js,jsx}: Maintain comprehensive JSDoc comments for public APIs
Use tree-shaking friendly exports
Validate all inputs and sanitize outputs
All public APIs must be documented with JSDoc
Use dynamic imports for optional features
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Implement proper logging with different levels

Files:

  • packages/expo/src/hooks/useSignInWithGoogle.ts
  • packages/expo/src/hooks/useSignInWithGoogle.android.ts
  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
  • packages/expo/src/hooks/useSignInWithGoogle.ios.ts
  • packages/expo/src/google-one-tap/types.ts
  • packages/expo/src/google-one-tap/index.ts
  • packages/expo/src/hooks/index.ts
  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
**/*.ts?(x)

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use proper TypeScript error types

Files:

  • packages/expo/src/hooks/useSignInWithGoogle.ts
  • packages/expo/src/hooks/useSignInWithGoogle.android.ts
  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
  • packages/expo/src/hooks/useSignInWithGoogle.ios.ts
  • packages/expo/src/google-one-tap/types.ts
  • packages/expo/src/google-one-tap/index.ts
  • packages/expo/src/hooks/index.ts
  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Implement type guards for unknown types using the pattern function isType(value: unknown): value is Type
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details in classes
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Use mixins for shared behavior across unrelated classes in TypeScript
Use generic constraints with bounded type parameters like <T extends { id: string }>
Use utility types like Omit, Partial, and Pick for data transformation instead of manual type construction
Use discriminated unions instead of boolean flags for state management and API responses
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation at the type level
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Document functions with JSDoc comments including @param, @returns, @throws, and @example tags
Create custom error classes that extend Error for specific error types
Use the Result pattern for error handling instead of throwing exceptions
Use optional chaining (?.) and nullish coalescing (??) operators for safe property access
Let TypeScript infer obvious types to reduce verbosity
Use const assertions with as const for literal types
Use satisfies operator for type checking without widening types
Declare readonly arrays and objects for immutable data structures
Use spread operator and array spread for immutable updates instead of mutations
Use lazy loading for large types...

Files:

  • packages/expo/src/hooks/useSignInWithGoogle.ts
  • packages/expo/src/hooks/useSignInWithGoogle.android.ts
  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
  • packages/expo/src/hooks/useSignInWithGoogle.ios.ts
  • packages/expo/src/google-one-tap/types.ts
  • packages/expo/src/google-one-tap/index.ts
  • packages/expo/src/hooks/index.ts
  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

**/*.{test,spec}.{ts,tsx,js,jsx}: Unit tests are required for all new functionality
Verify proper error handling and edge cases
Include tests for all new features

Files:

  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
**/*.{test,spec,e2e}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use real Clerk instances for integration tests

Files:

  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
packages/*/package.json

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

packages/*/package.json: Packages should export TypeScript types alongside runtime code
Follow semantic versioning for all packages

All packages must be published under @clerk namespace

Files:

  • packages/expo/package.json
**/package.json

📄 CodeRabbit inference engine (.cursor/rules/global.mdc)

Use pnpm as the package manager for this monorepo

Files:

  • packages/expo/package.json
**/index.ts

📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)

Avoid barrel files (index.ts re-exports) as they can cause circular dependencies

Files:

  • packages/expo/src/google-one-tap/index.ts
  • packages/expo/src/hooks/index.ts
🧬 Code graph analysis (3)
packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts (2)
packages/expo/src/hooks/useSignInWithGoogle.ios.ts (1)
  • useSignInWithGoogle (63-217)
packages/expo/src/hooks/useSignInWithGoogle.android.ts (1)
  • useSignInWithGoogle (61-210)
packages/expo/ios/ClerkGoogleSignInModule.swift (2)
packages/expo/android/src/main/java/expo/modules/clerk/googlesignin/ClerkGoogleSignInModule.kt (1)
  • message (62-62)
packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts (1)
  • signIn (117-131)
packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts (2)
packages/expo/src/google-one-tap/types.ts (8)
  • ConfigureParams (4-31)
  • SignInParams (36-49)
  • OneTapResponse (145-145)
  • CreateAccountParams (54-60)
  • ExplicitSignInParams (65-71)
  • CancelledResponse (129-132)
  • NoSavedCredentialFound (137-140)
  • OneTapSuccessResponse (111-124)
packages/expo/src/google-one-tap/index.ts (13)
  • ConfigureParams (10-10)
  • SignInParams (11-11)
  • OneTapResponse (14-14)
  • CreateAccountParams (12-12)
  • ExplicitSignInParams (13-13)
  • isCancelledResponse (3-3)
  • CancelledResponse (16-16)
  • isNoSavedCredentialFoundResponse (4-4)
  • NoSavedCredentialFound (17-17)
  • isSuccessResponse (5-5)
  • OneTapSuccessResponse (15-15)
  • isErrorWithCode (6-6)
  • ClerkGoogleOneTapSignIn (2-2)
🪛 detekt (1.23.8)
packages/expo/android/src/main/java/expo/modules/clerk/googlesignin/ClerkGoogleSignInModule.kt

[warning] 115-115: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)


[warning] 117-117: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)


[warning] 154-154: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)


[warning] 156-156: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)


[warning] 192-192: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

🔇 Additional comments (13)
packages/expo/android/src/main/AndroidManifest.xml (1)

1-2: Minimal library manifest is acceptable

A bare manifest with just the Android namespace is fine here since the namespace is configured in Gradle and the module doesn’t declare its own components. No issues from a packaging perspective.

packages/expo/src/hooks/index.ts (1)

14-18: New useSignInWithGoogle export aligns with existing hook surface

Re‑exporting useSignInWithGoogle from the hooks index keeps the Expo hook API consistent with Apple/SSO/OAuth exports and remains tree‑shaking friendly. Just ensure useSignInWithGoogle doesn’t itself import from this barrel to avoid potential circular references.

packages/expo/expo-module.config.json (1)

1-9: Expo module wiring looks consistent with native module names

The config structure and module identifiers for Android and iOS look correct for Expo modules and line up with the described ClerkGoogleSignInModule implementations. I don’t see any structural issues here.

packages/expo/src/hooks/useSignInWithGoogle.ios.ts (1)

63-211: Core iOS Google sign-in flow, transfer handling, and cancellation behavior look solid.

  • You correctly gate on isSignInLoaded / isSignUpLoaded and still return { setActive, signIn, signUp }, matching other Clerk hooks.
  • Env handling (EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID / EXPO_PUBLIC_CLERK_GOOGLE_IOS_CLIENT_ID) plus ClerkGoogleOneTapSignIn.configure aligns with the native module signature.
  • Nonce generation + SHA‑256 hashing via expo-crypto and passing the hashed nonce into presentExplicitSignIn is the right pattern.
  • Transfer flow is covered twice: first via signIn.firstFactorVerification.status === 'transferable', then via the external_account_not_found Clerk error path, which is a good safety net.
  • Cancellation from native (SIGN_IN_CANCELLED) and non‑success One‑Tap responses both gracefully resolve with createdSessionId: null and the Clerk handles, which gives callers a uniform “no session created” signal without throwing.

No changes needed here from a logic standpoint.

packages/expo/package.json (1)

55-65: Exports and Expo module wiring look consistent; just confirm peer version ranges.

  • Exporting ./google-one-tap with both types and default matches the rest of the package and keeps the TS surface available.
  • Including android, ios, expo-module.config.json, and app.plugin.js in files ensures the native module and config plugin ship correctly.
  • Adding expo-constants / expo-modules-core as both devDependencies and peerDependencies (with expo-constants optional) matches how other Expo modules are usually modeled.

Please just double‑check that the >=12 / >=3.0.0 peer ranges align with the minimum Expo SDK versions you intend to support.

Also applies to: 69-81, 104-116, 117-150

packages/expo/src/hooks/useSignInWithGoogle.ts (1)

16-69: Stub hook behavior and typing are good; just ensure the shared surface stays in sync with platform files.

  • The JSDoc clearly communicates that this is a stub for non‑iOS/Android platforms and nudges devs toward useSSO with strategy: "oauth_google", which is very helpful.
  • The explicit return type on useSignInWithGoogle and the StartGoogleAuthenticationFlowReturnType structure (session id + optional setActive, signIn, signUp) matches what the platform-specific hooks are returning at runtime.
  • Using errorThrower.throw to produce a consistent runtime error for unsupported platforms keeps the API predictable.

Once the SignUpUnsafeMetadata type is added here and reused in the .ios / .android variants, the TS surface across platforms will be nicely aligned.

packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts (3)

32-63: Well-implemented type guards.

The type guard functions follow best practices with clear naming, proper response is Type return signatures, and concise implementations. Good use of discriminated unions.


21-30: Lazy-loading pattern prevents initialization crashes.

Good approach to lazily load the native module using require(). This prevents app crashes when the module isn't available (e.g., in Expo Go or web).


194-204: GoogleUser is re-exported but not imported.

The GoogleUser type is re-exported at line 203 but isn't included in the imports at lines 1-10. This will cause a TypeScript error.

 import type { ConfigureParams, SignInParams, CreateAccountParams, ExplicitSignInParams, OneTapResponse, OneTapSuccessResponse, CancelledResponse, NoSavedCredentialFound, + GoogleUser, } from './types';

Likely an incorrect or invalid review comment.

packages/expo/src/google-one-tap/types.ts (2)

111-145: Well-designed discriminated union types.

Good use of the discriminated union pattern with a type field for OneTapResponse. This enables exhaustive type checking and clean type narrowing with the type guards in the bridge module.


159-161: Consider using interface vs type for error interface.

The GoogleSignInError interface extending Error follows the coding guideline to use interface for object shapes that might be extended. Good choice.

packages/expo/android/src/main/java/expo/modules/clerk/googlesignin/ClerkGoogleSignInModule.kt (2)

115-123: Exception swallowing is intentional and appropriate here.

The static analysis tool flags these as "swallowed exceptions," but this is intentional behavior - the exceptions are being transformed into typed error responses (GoogleSignInCancelledException, GoogleSignInNoCredentialException). The original exception details are captured in the custom exception messages where relevant.


227-228: User email is derived from id field.

Note that googleIdTokenCredential.id returns the user's email address (used for credential matching), not the unique Google user ID (sub claim). This is consistent with how the type is documented but worth being aware of - if you need the actual sub claim, you'd need to decode the ID token.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 25, 2025

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7208 

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7208 

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7208 

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7208 

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7208 

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7208 

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7208 

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7208 

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7208 

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7208 

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7208 

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7208 

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7208 

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7208 

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7208 

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7208 

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7208 

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7208 

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7208 

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7208 

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7208 

commit: b192d31

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
packages/expo/src/hooks/useSignInWithGoogle.android.ts (1)

95-105: Nonce handling issue was previously identified.

The nonce is hashed before being passed to Google, but Google's Credential Manager API expects the plain nonce value. This was flagged in a previous review. The hashedNonce should be replaced with the plain nonce.

packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts (1)

119-127: Test assertions may pass incorrectly due to mock mismatch.

Given that mocks.GoogleSignin.hasPlayServices and mocks.GoogleSignin.signIn are never called by the actual hook (which uses ClerkGoogleOneTapSignIn), these assertions will fail or pass based on incorrect assumptions.

This is a consequence of the mock mismatch noted above. The tests need to be updated to mock the correct module (ClerkGoogleOneTapSignIn via expo-modules-core).

🧹 Nitpick comments (5)
packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts (2)

117-158: Consider extracting shared error-handling logic.

The signIn and createAccount methods have identical catch blocks. This duplication could be reduced by extracting a helper function.

// Helper function to map errors to responses function mapErrorToResponse(error: unknown): OneTapResponse | null { if (isErrorWithCode(error)) { if (error.code === 'SIGN_IN_CANCELLED') { return { type: 'cancelled', data: null }; } if (error.code === 'NO_SAVED_CREDENTIAL_FOUND') { return { type: 'noSavedCredentialFound', data: null }; } } return null; }

90-91: Documentation indicates Android-only, but module is cross-platform.

The JSDoc states @platform Android, but per the PR summary this module also supports iOS. Consider updating to @platform Android, iOS or removing the platform annotation.

packages/expo/android/src/main/java/expo/modules/clerk/googlesignin/ClerkGoogleSignInModule.kt (2)

75-76: CredentialManager is recreated on every access.

CredentialManager.create(context) is called each time the credentialManager property is accessed. While this is likely lightweight, consider caching the instance for consistency and minor performance improvement.

+ private var _credentialManager: CredentialManager? = null + private val credentialManager: CredentialManager - get() = CredentialManager.create(context) + get() = _credentialManager ?: CredentialManager.create(context).also { _credentialManager = it }

70-70: Consider cancelling the coroutine scope on module cleanup.

The mainScope is never cancelled, which could lead to coroutine leaks if the module is destroyed while operations are in-flight. Consider implementing OnDestroy to cancel pending coroutines.

-class ClerkGoogleSignInModule : Module() { +class ClerkGoogleSignInModule : Module() { private var webClientId: String? = null private var hostedDomain: String? = null private var autoSelectEnabled: Boolean = false - private val mainScope = CoroutineScope(Dispatchers.Main) + private val mainScope = CoroutineScope(Dispatchers.Main + SupervisorJob()) + + override fun definition() = ModuleDefinition { + Name("ClerkGoogleSignIn") + + OnDestroy { + mainScope.cancel() + }
packages/expo/src/hooks/useSignInWithGoogle.android.ts (1)

154-167: Consider extracting Clerk error type checking into a helper.

The isClerkError and hasExternalAccountNotFoundError checks are verbose. Consider creating a reusable type guard function for Clerk errors.

// In a shared utils file function isClerkErrorWithCode(error: unknown, code: string): boolean { if (!error || typeof error !== 'object') return false; if (!('clerkError' in error) || (error as { clerkError: boolean }).clerkError !== true) return false; if (!('errors' in error) || !Array.isArray((error as { errors: unknown[] }).errors)) return false; return (error as { errors: Array<{ code: string }> }).errors.some(err => err.code === code); } // Usage if (isClerkErrorWithCode(signInError, 'external_account_not_found')) { // ... }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 14a5ae6 and 7a9f7de.

📒 Files selected for processing (6)
  • packages/expo/android/src/main/java/expo/modules/clerk/googlesignin/ClerkGoogleSignInModule.kt (1 hunks)
  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts (1 hunks)
  • packages/expo/src/google-one-tap/types.ts (1 hunks)
  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts (1 hunks)
  • packages/expo/src/hooks/useSignInWithGoogle.android.ts (1 hunks)
  • packages/expo/src/hooks/useSignInWithGoogle.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/expo/src/google-one-tap/types.ts
  • packages/expo/src/hooks/useSignInWithGoogle.ts
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

All code must pass ESLint checks with the project's configuration

Files:

  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
  • packages/expo/src/hooks/useSignInWithGoogle.android.ts
  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
**/*.{js,jsx,ts,tsx,json,md,yml,yaml}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use Prettier for consistent code formatting

Files:

  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
  • packages/expo/src/hooks/useSignInWithGoogle.android.ts
  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
packages/**/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

TypeScript is required for all packages

Files:

  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
  • packages/expo/src/hooks/useSignInWithGoogle.android.ts
  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Follow established naming conventions (PascalCase for components, camelCase for variables)

Prefer importing types from @clerk/shared/types instead of the deprecated @clerk/types alias

Files:

  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
  • packages/expo/src/hooks/useSignInWithGoogle.android.ts
  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
packages/**/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

packages/**/src/**/*.{ts,tsx,js,jsx}: Maintain comprehensive JSDoc comments for public APIs
Use tree-shaking friendly exports
Validate all inputs and sanitize outputs
All public APIs must be documented with JSDoc
Use dynamic imports for optional features
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Implement proper logging with different levels

Files:

  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
  • packages/expo/src/hooks/useSignInWithGoogle.android.ts
  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

**/*.{test,spec}.{ts,tsx,js,jsx}: Unit tests are required for all new functionality
Verify proper error handling and edge cases
Include tests for all new features

Files:

  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
**/*.ts?(x)

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use proper TypeScript error types

Files:

  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
  • packages/expo/src/hooks/useSignInWithGoogle.android.ts
  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
**/*.{test,spec,e2e}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use real Clerk instances for integration tests

Files:

  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Implement type guards for unknown types using the pattern function isType(value: unknown): value is Type
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details in classes
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Use mixins for shared behavior across unrelated classes in TypeScript
Use generic constraints with bounded type parameters like <T extends { id: string }>
Use utility types like Omit, Partial, and Pick for data transformation instead of manual type construction
Use discriminated unions instead of boolean flags for state management and API responses
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation at the type level
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Document functions with JSDoc comments including @param, @returns, @throws, and @example tags
Create custom error classes that extend Error for specific error types
Use the Result pattern for error handling instead of throwing exceptions
Use optional chaining (?.) and nullish coalescing (??) operators for safe property access
Let TypeScript infer obvious types to reduce verbosity
Use const assertions with as const for literal types
Use satisfies operator for type checking without widening types
Declare readonly arrays and objects for immutable data structures
Use spread operator and array spread for immutable updates instead of mutations
Use lazy loading for large types...

Files:

  • packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
  • packages/expo/src/hooks/useSignInWithGoogle.android.ts
  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
🧬 Code graph analysis (2)
packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts (1)
packages/expo/src/hooks/useSignInWithGoogle.android.ts (1)
  • useSignInWithGoogle (63-212)
packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts (1)
packages/expo/src/google-one-tap/types.ts (8)
  • ConfigureParams (4-31)
  • SignInParams (36-49)
  • OneTapResponse (146-146)
  • CreateAccountParams (54-60)
  • ExplicitSignInParams (65-71)
  • CancelledResponse (130-133)
  • NoSavedCredentialFound (138-141)
  • OneTapSuccessResponse (112-125)
🪛 detekt (1.23.8)
packages/expo/android/src/main/java/expo/modules/clerk/googlesignin/ClerkGoogleSignInModule.kt

[warning] 121-121: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)


[warning] 123-123: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)


[warning] 165-165: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)


[warning] 167-167: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)


[warning] 208-208: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Build Packages
  • GitHub Check: Formatting | Dedupe | Changeset
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (7)
packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts (2)

21-30: Lazy-loading pattern is well-implemented.

The lazy-load approach prevents crashes when the native module is unavailable (e.g., on web or in test environments). The module is cached after first load, which is efficient.


56-63: Well-implemented type guard with proper narrowing.

The isErrorWithCode function correctly narrows unknown to a typed error object using safe property checks. This follows TypeScript best practices for type guards.

packages/expo/android/src/main/java/expo/modules/clerk/googlesignin/ClerkGoogleSignInModule.kt (2)

121-129: Exception handling is appropriate despite static analysis warning.

The static analysis flags "swallowed exceptions," but these are correctly converted to typed promise rejections with meaningful error codes (SIGN_IN_CANCELLED, NO_SAVED_CREDENTIAL_FOUND). The user-facing error semantics are preserved. For enhanced debugging, you could optionally log the original exception before rejecting.


242-244: The review comment's core premise is incorrect, but the underlying concern is valid.

Per Google's official documentation, GoogleIdTokenCredential.id is the email address, not a separate identifier. The review comment incorrectly states "The Google ID may differ from the email address."

However, there is a legitimate semantic issue: using the email address for both the id and email fields is not ideal. The id field should contain the user's unique Google identifier (the sub claim from the JWT), while email should contain the email address. The idToken is available and could be decoded to extract the proper unique identifier.

"user" to mapOf( "id" to googleIdTokenCredential.id, "email" to googleIdTokenCredential.id, 

Consider extracting the unique user ID from the idToken JWT claims for the id field, rather than using the email address.

Likely an incorrect or invalid review comment.

packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts (1)

242-291: Good coverage of the external_account_not_found fallback path.

This test addresses the previously noted missing coverage. It properly validates that when signIn.create throws an external_account_not_found error, the hook falls back to signUp.create with the token and unsafeMetadata. However, ensure the mocks are corrected so this test actually exercises the real code path.

packages/expo/src/hooks/useSignInWithGoogle.android.ts (2)

22-62: Comprehensive JSDoc with clear example.

The documentation clearly explains the hook's purpose, features, and usage pattern. The example demonstrates proper error handling and session activation.


209-212: Clean hook API design.

The hook follows React conventions by returning a stable object with the startGoogleAuthenticationFlow function. The return type is consistent across all execution paths.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts (3)

14-31: Lazy native-module loading pattern looks good; consider clarifying the comment.

The ClerkGoogleSignInNativeModule interface and getNativeModule lazy initializer are well-typed and align with Expo’s requireNativeModule usage. The comment “avoid crashes when not available” can be a bit misleading though — you’re primarily avoiding an import‑time crash on platforms where the native module doesn’t exist, but a call to getNativeModule() will still throw if the module is missing.

You might tweak the comment to something like “Lazy‑load the native module to avoid crashes at import time on unsupported platforms” to better set expectations, but the implementation itself is solid.


57-64: Tighten isErrorWithCode guard or relax the return type to avoid lying about message.

Right now the guard promises error is { code: string; message: string } but only validates the code property. That means callers could safely access error.message as a string even when it’s missing or non‑string at runtime.

Either of these adjustments would make the guard accurate:

  • Option A – Validate message as well (keeps current type):
-export function isErrorWithCode(error: unknown): error is { code: string; message: string } { +export function isErrorWithCode(error: unknown): error is { code: string; message: string } { return ( error !== null && typeof error === 'object' && - 'code' in error && - typeof (error as { code: unknown }).code === 'string' + 'code' in error && + typeof (error as { code: unknown }).code === 'string' && + 'message' in error && + typeof (error as { message: unknown }).message === 'string' ); }
  • Option B – Relax the type so only code is guaranteed (simpler, since you don’t use message):
-export function isErrorWithCode(error: unknown): error is { code: string; message: string } { +export function isErrorWithCode(error: unknown): error is { code: string } { return ( error !== null && typeof error === 'object' && 'code' in error && typeof (error as { code: unknown }).code === 'string' ); }

Given current usage only checks error.code, Option B is probably the minimal change.


66-193: Core API shape and error mapping look solid; consider a small helper to dedupe error-to-response mapping.

The ClerkGoogleOneTapSignIn methods are well-typed, documented, and provide a clean Promise‑based API. params?: … plus params ?? {} is a nice ergonomic choice for callers, and the mapping of known error codes (SIGN_IN_CANCELLED, NO_SAVED_CREDENTIAL_FOUND) to structured OneTapResponse shapes is clear.

There is some duplication in how you translate error.code into { type: 'cancelled' | 'noSavedCredentialFound', data: null } across signIn, createAccount, and presentExplicitSignIn. A tiny internal helper would reduce drift if more codes are added later; for example:

+function mapErrorToOneTapResponse(error: unknown): OneTapResponse | null { + if (!isErrorWithCode(error)) { + return null; + } + + if (error.code === 'SIGN_IN_CANCELLED') { + return { type: 'cancelled', data: null }; + } + + if (error.code === 'NO_SAVED_CREDENTIAL_FOUND') { + return { type: 'noSavedCredentialFound', data: null }; + } + + return null; +} + export const ClerkGoogleOneTapSignIn = { ... async signIn(params?: SignInParams): Promise<OneTapResponse> { try { return await getNativeModule().signIn(params ?? {}); } catch (error) { - if (isErrorWithCode(error)) { - if (error.code === 'SIGN_IN_CANCELLED') { - return { type: 'cancelled', data: null }; - } - if (error.code === 'NO_SAVED_CREDENTIAL_FOUND') { - return { type: 'noSavedCredentialFound', data: null }; - } - } + const mapped = mapErrorToOneTapResponse(error); + if (mapped) return mapped; throw error; } }, ... async createAccount(params?: CreateAccountParams): Promise<OneTapResponse> { try { return await getNativeModule().createAccount(params ?? {}); } catch (error) { - if (isErrorWithCode(error)) { - if (error.code === 'SIGN_IN_CANCELLED') { - return { type: 'cancelled', data: null }; - } - if (error.code === 'NO_SAVED_CREDENTIAL_FOUND') { - return { type: 'noSavedCredentialFound', data: null }; - } - } + const mapped = mapErrorToOneTapResponse(error); + if (mapped) return mapped; throw error; } }, ... async presentExplicitSignIn(params?: ExplicitSignInParams): Promise<OneTapResponse> { try { return await getNativeModule().presentExplicitSignIn(params ?? {}); } catch (error) { - if (isErrorWithCode(error)) { - if (error.code === 'SIGN_IN_CANCELLED') { - return { type: 'cancelled', data: null }; - } - } + const mapped = mapErrorToOneTapResponse(error); + if (mapped) return mapped; throw error; } }, ... };

Not mandatory, but it keeps the behavior consistent as you evolve the error surface.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7724619 and bc11a78.

📒 Files selected for processing (2)
  • .changeset/brave-clouds-swim.md (1 hunks)
  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

All code must pass ESLint checks with the project's configuration

Files:

  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
**/*.{js,jsx,ts,tsx,json,md,yml,yaml}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use Prettier for consistent code formatting

Files:

  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
packages/**/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

TypeScript is required for all packages

Files:

  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Follow established naming conventions (PascalCase for components, camelCase for variables)

Prefer importing types from @clerk/shared/types instead of the deprecated @clerk/types alias

Files:

  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
packages/**/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

packages/**/src/**/*.{ts,tsx,js,jsx}: Maintain comprehensive JSDoc comments for public APIs
Use tree-shaking friendly exports
Validate all inputs and sanitize outputs
All public APIs must be documented with JSDoc
Use dynamic imports for optional features
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Implement proper logging with different levels

Files:

  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
**/*.ts?(x)

📄 CodeRabbit inference engine (.cursor/rules/development.mdc)

Use proper TypeScript error types

Files:

  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Implement type guards for unknown types using the pattern function isType(value: unknown): value is Type
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details in classes
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Use mixins for shared behavior across unrelated classes in TypeScript
Use generic constraints with bounded type parameters like <T extends { id: string }>
Use utility types like Omit, Partial, and Pick for data transformation instead of manual type construction
Use discriminated unions instead of boolean flags for state management and API responses
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation at the type level
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Document functions with JSDoc comments including @param, @returns, @throws, and @example tags
Create custom error classes that extend Error for specific error types
Use the Result pattern for error handling instead of throwing exceptions
Use optional chaining (?.) and nullish coalescing (??) operators for safe property access
Let TypeScript infer obvious types to reduce verbosity
Use const assertions with as const for literal types
Use satisfies operator for type checking without widening types
Declare readonly arrays and objects for immutable data structures
Use spread operator and array spread for immutable updates instead of mutations
Use lazy loading for large types...

Files:

  • packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (34)
  • GitHub Check: Integration Tests (nextjs, chrome, 15)
  • GitHub Check: Integration Tests (machine, chrome)
  • GitHub Check: Integration Tests (custom, chrome)
  • GitHub Check: Integration Tests (nextjs, chrome, 16)
  • GitHub Check: Integration Tests (nextjs, chrome, 15, RQ)
  • GitHub Check: Integration Tests (quickstart, chrome, 16)
  • GitHub Check: Integration Tests (quickstart, chrome, 15)
  • GitHub Check: Integration Tests (machine, chrome, RQ)
  • GitHub Check: Integration Tests (nextjs, chrome, 14)
  • GitHub Check: Integration Tests (sessions, chrome)
  • GitHub Check: Integration Tests (elements, chrome)
  • GitHub Check: Integration Tests (vue, chrome)
  • GitHub Check: Integration Tests (billing, chrome, RQ)
  • GitHub Check: Integration Tests (handshake, chrome)
  • GitHub Check: Integration Tests (express, chrome)
  • GitHub Check: Integration Tests (tanstack-react-start, chrome)
  • GitHub Check: Integration Tests (nuxt, chrome)
  • GitHub Check: Integration Tests (react-router, chrome)
  • GitHub Check: Integration Tests (generic, chrome)
  • GitHub Check: Integration Tests (ap-flows, chrome)
  • GitHub Check: Integration Tests (billing, chrome)
  • GitHub Check: Integration Tests (sessions:staging, chrome)
  • GitHub Check: Integration Tests (localhost, chrome)
  • GitHub Check: Integration Tests (astro, chrome)
  • GitHub Check: Integration Tests (handshake:staging, chrome)
  • GitHub Check: Integration Tests (expo-web, chrome)
  • GitHub Check: Unit Tests (22, shared, clerk-js, RQ)
  • GitHub Check: Publish with pkg-pr-new
  • GitHub Check: Unit Tests (22, **)
  • GitHub Check: Static analysis
  • GitHub Check: Formatting | Dedupe | Changeset
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (3)
.changeset/brave-clouds-swim.md (1)

1-5: Changeset entry is well-formed and correctly categorized.

The file follows the standard changeset format with the appropriate minor version bump for @clerk/clerk-expo and provides a clear, concise description of the feature addition. The version level correctly reflects the non-breaking nature of the new native Google Sign-In support.

packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts (2)

33-52: Response discriminant type guards are concise and correct.

The three guards (isCancelledResponse, isNoSavedCredentialFoundResponse, isSuccessResponse) cleanly narrow OneTapResponse based on the type discriminant and should work well with a discriminated union in ./types. This is a nice, ergonomic API surface for consumers.


195-205: Type re-exports are clean and tree-shaking–friendly.

Re-exporting the public types from ./types in a grouped export type { … } keeps the surface area discoverable and maintains good tree-shaking characteristics, while also aligning with the JSDoc’d public API.

@chriscanin chriscanin changed the title feat(expo): Implement Google Sign-In support for Android and iOS feat(component): Implement Google Sign-In support for Android and iOS Nov 26, 2025
@chriscanin chriscanin changed the title feat(component): Implement Google Sign-In support for Android and iOS feat(clerk-expo): Implement Google Sign-In support for Android and iOS Dec 1, 2025
Copy link

@mikepitre mikepitre left a comment

Choose a reason for hiding this comment

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

Swift code looks fine to me!

implementation project(':expo-modules-core')

// Credential Manager for Google Sign-In with nonce support
implementation "androidx.credentials:credentials:1.3.0"
Copy link

Choose a reason for hiding this comment

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

unrelated but you can use a version catalog (https://docs.gradle.org/current/userguide/version_catalogs.html) and then setup automated updates for these dependencies

@chriscanin
Copy link
Member Author

!snapshot

@chriscanin
Copy link
Member Author

!snapshot

@clerk-cookie
Copy link
Collaborator

Hey @chriscanin - the snapshot version command generated the following package versions:

Package Version
@clerk/agent-toolkit 0.2.9-snapshot.v20251217165918
@clerk/astro 3.0.0-snapshot.v20251217165918
@clerk/backend 3.0.0-snapshot.v20251217165918
@clerk/chrome-extension 3.0.0-snapshot.v20251217165918
@clerk/clerk-js 6.0.0-snapshot.v20251217165918
@clerk/dev-cli 1.0.0-snapshot.v20251217165918
@clerk/expo 3.0.0-snapshot.v20251217165918
@clerk/expo-passkeys 1.0.0-snapshot.v20251217165918
@clerk/express 2.0.0-snapshot.v20251217165918
@clerk/fastify 2.6.9-snapshot.v20251217165918
@clerk/localizations 4.0.0-snapshot.v20251217165918
@clerk/nextjs 7.0.0-snapshot.v20251217165918
@clerk/nuxt 2.0.0-snapshot.v20251217165918
@clerk/react 6.0.0-snapshot.v20251217165918
@clerk/react-router 3.0.0-snapshot.v20251217165918
@clerk/shared 4.0.0-snapshot.v20251217165918
@clerk/tanstack-react-start 1.0.0-snapshot.v20251217165918
@clerk/testing 2.0.0-snapshot.v20251217165918
@clerk/ui 1.0.0-snapshot.v20251217165918
@clerk/upgrade 2.0.0-snapshot.v20251217165918
@clerk/vue 2.0.0-snapshot.v20251217165918

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/agent-toolkit

npm i @clerk/agent-toolkit@0.2.9-snapshot.v20251217165918 --save-exact

@clerk/astro

npm i @clerk/astro@3.0.0-snapshot.v20251217165918 --save-exact

@clerk/backend

npm i @clerk/backend@3.0.0-snapshot.v20251217165918 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.0.0-snapshot.v20251217165918 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.0.0-snapshot.v20251217165918 --save-exact

@clerk/dev-cli

npm i @clerk/dev-cli@1.0.0-snapshot.v20251217165918 --save-exact

@clerk/expo

npm i @clerk/expo@3.0.0-snapshot.v20251217165918 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.0.0-snapshot.v20251217165918 --save-exact

@clerk/express

npm i @clerk/express@2.0.0-snapshot.v20251217165918 --save-exact

@clerk/fastify

npm i @clerk/fastify@2.6.9-snapshot.v20251217165918 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.0.0-snapshot.v20251217165918 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.0.0-snapshot.v20251217165918 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.0.0-snapshot.v20251217165918 --save-exact

@clerk/react

npm i @clerk/react@6.0.0-snapshot.v20251217165918 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.0.0-snapshot.v20251217165918 --save-exact

@clerk/shared

npm i @clerk/shared@4.0.0-snapshot.v20251217165918 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.0.0-snapshot.v20251217165918 --save-exact

@clerk/testing

npm i @clerk/testing@2.0.0-snapshot.v20251217165918 --save-exact

@clerk/ui

npm i @clerk/ui@1.0.0-snapshot.v20251217165918 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.0-snapshot.v20251217165918 --save-exact

@clerk/vue

npm i @clerk/vue@2.0.0-snapshot.v20251217165918 --save-exact
@chriscanin chriscanin requested a review from dstaley December 17, 2025 18:52
- Changed the export path in app.plugin.js to point to the new distribution directory. - Removed the build:plugin script and unnecessary entries from package.json, streamlining the configuration. These changes are in effort to continue using tsup. Resolves: https://github.com/clerk/javascript/pull/7208/changes#r2628292077 and https://github.com/clerk/javascript/pull/7208/changes#r2628293763
@chriscanin
Copy link
Member Author

!snapshot

@clerk-cookie
Copy link
Collaborator

Hey @chriscanin - the snapshot version command generated the following package versions:

Package Version
@clerk/agent-toolkit 0.2.9-snapshot.v20251217194100
@clerk/astro 3.0.0-snapshot.v20251217194100
@clerk/backend 3.0.0-snapshot.v20251217194100
@clerk/chrome-extension 3.0.0-snapshot.v20251217194100
@clerk/clerk-js 6.0.0-snapshot.v20251217194100
@clerk/dev-cli 1.0.0-snapshot.v20251217194100
@clerk/expo 3.0.0-snapshot.v20251217194100
@clerk/expo-passkeys 1.0.0-snapshot.v20251217194100
@clerk/express 2.0.0-snapshot.v20251217194100
@clerk/fastify 2.6.9-snapshot.v20251217194100
@clerk/localizations 4.0.0-snapshot.v20251217194100
@clerk/nextjs 7.0.0-snapshot.v20251217194100
@clerk/nuxt 2.0.0-snapshot.v20251217194100
@clerk/react 6.0.0-snapshot.v20251217194100
@clerk/react-router 3.0.0-snapshot.v20251217194100
@clerk/shared 4.0.0-snapshot.v20251217194100
@clerk/tanstack-react-start 1.0.0-snapshot.v20251217194100
@clerk/testing 2.0.0-snapshot.v20251217194100
@clerk/ui 1.0.0-snapshot.v20251217194100
@clerk/upgrade 2.0.0-snapshot.v20251217194100
@clerk/vue 2.0.0-snapshot.v20251217194100

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/agent-toolkit

npm i @clerk/agent-toolkit@0.2.9-snapshot.v20251217194100 --save-exact

@clerk/astro

npm i @clerk/astro@3.0.0-snapshot.v20251217194100 --save-exact

@clerk/backend

npm i @clerk/backend@3.0.0-snapshot.v20251217194100 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.0.0-snapshot.v20251217194100 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.0.0-snapshot.v20251217194100 --save-exact

@clerk/dev-cli

npm i @clerk/dev-cli@1.0.0-snapshot.v20251217194100 --save-exact

@clerk/expo

npm i @clerk/expo@3.0.0-snapshot.v20251217194100 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.0.0-snapshot.v20251217194100 --save-exact

@clerk/express

npm i @clerk/express@2.0.0-snapshot.v20251217194100 --save-exact

@clerk/fastify

npm i @clerk/fastify@2.6.9-snapshot.v20251217194100 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.0.0-snapshot.v20251217194100 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.0.0-snapshot.v20251217194100 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.0.0-snapshot.v20251217194100 --save-exact

@clerk/react

npm i @clerk/react@6.0.0-snapshot.v20251217194100 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.0.0-snapshot.v20251217194100 --save-exact

@clerk/shared

npm i @clerk/shared@4.0.0-snapshot.v20251217194100 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.0.0-snapshot.v20251217194100 --save-exact

@clerk/testing

npm i @clerk/testing@2.0.0-snapshot.v20251217194100 --save-exact

@clerk/ui

npm i @clerk/ui@1.0.0-snapshot.v20251217194100 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.0-snapshot.v20251217194100 --save-exact

@clerk/vue

npm i @clerk/vue@2.0.0-snapshot.v20251217194100 --save-exact
…Expo plugin - Changed the import of package.json from require to ES module syntax in order to fix the no "require" imports allowed eslint warning.
Copy link
Member

@dstaley dstaley left a comment

Choose a reason for hiding this comment

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

one note about SignUpUnsafeMetadata, but otherwise LGTM!

@chriscanin
Copy link
Member Author

!snapshot

@clerk-cookie
Copy link
Collaborator

Hey @chriscanin - the snapshot version command generated the following package versions:

Package Version
@clerk/agent-toolkit 0.2.9-snapshot.v20251218165926
@clerk/astro 3.0.0-snapshot.v20251218165926
@clerk/backend 3.0.0-snapshot.v20251218165926
@clerk/chrome-extension 3.0.0-snapshot.v20251218165926
@clerk/clerk-js 6.0.0-snapshot.v20251218165926
@clerk/dev-cli 1.0.0-snapshot.v20251218165926
@clerk/expo 3.0.0-snapshot.v20251218165926
@clerk/expo-passkeys 1.0.0-snapshot.v20251218165926
@clerk/express 2.0.0-snapshot.v20251218165926
@clerk/fastify 2.6.9-snapshot.v20251218165926
@clerk/localizations 4.0.0-snapshot.v20251218165926
@clerk/nextjs 7.0.0-snapshot.v20251218165926
@clerk/nuxt 2.0.0-snapshot.v20251218165926
@clerk/react 6.0.0-snapshot.v20251218165926
@clerk/react-router 3.0.0-snapshot.v20251218165926
@clerk/shared 4.0.0-snapshot.v20251218165926
@clerk/tanstack-react-start 1.0.0-snapshot.v20251218165926
@clerk/testing 2.0.0-snapshot.v20251218165926
@clerk/ui 1.0.0-snapshot.v20251218165926
@clerk/upgrade 2.0.0-snapshot.v20251218165926
@clerk/vue 2.0.0-snapshot.v20251218165926

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/agent-toolkit

npm i @clerk/agent-toolkit@0.2.9-snapshot.v20251218165926 --save-exact

@clerk/astro

npm i @clerk/astro@3.0.0-snapshot.v20251218165926 --save-exact

@clerk/backend

npm i @clerk/backend@3.0.0-snapshot.v20251218165926 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.0.0-snapshot.v20251218165926 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.0.0-snapshot.v20251218165926 --save-exact

@clerk/dev-cli

npm i @clerk/dev-cli@1.0.0-snapshot.v20251218165926 --save-exact

@clerk/expo

npm i @clerk/expo@3.0.0-snapshot.v20251218165926 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.0.0-snapshot.v20251218165926 --save-exact

@clerk/express

npm i @clerk/express@2.0.0-snapshot.v20251218165926 --save-exact

@clerk/fastify

npm i @clerk/fastify@2.6.9-snapshot.v20251218165926 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.0.0-snapshot.v20251218165926 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.0.0-snapshot.v20251218165926 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.0.0-snapshot.v20251218165926 --save-exact

@clerk/react

npm i @clerk/react@6.0.0-snapshot.v20251218165926 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.0.0-snapshot.v20251218165926 --save-exact

@clerk/shared

npm i @clerk/shared@4.0.0-snapshot.v20251218165926 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.0.0-snapshot.v20251218165926 --save-exact

@clerk/testing

npm i @clerk/testing@2.0.0-snapshot.v20251218165926 --save-exact

@clerk/ui

npm i @clerk/ui@1.0.0-snapshot.v20251218165926 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.0-snapshot.v20251218165926 --save-exact

@clerk/vue

npm i @clerk/vue@2.0.0-snapshot.v20251218165926 --save-exact
@chriscanin
Copy link
Member Author

!snapshot

@clerk-cookie
Copy link
Collaborator

Hey @chriscanin - the snapshot version command generated the following package versions:

Package Version
@clerk/agent-toolkit 0.2.9-snapshot.v20251218183643
@clerk/astro 3.0.0-snapshot.v20251218183643
@clerk/backend 3.0.0-snapshot.v20251218183643
@clerk/chrome-extension 3.0.0-snapshot.v20251218183643
@clerk/clerk-js 6.0.0-snapshot.v20251218183643
@clerk/dev-cli 1.0.0-snapshot.v20251218183643
@clerk/expo 3.0.0-snapshot.v20251218183643
@clerk/expo-passkeys 1.0.0-snapshot.v20251218183643
@clerk/express 2.0.0-snapshot.v20251218183643
@clerk/fastify 2.6.9-snapshot.v20251218183643
@clerk/localizations 4.0.0-snapshot.v20251218183643
@clerk/nextjs 7.0.0-snapshot.v20251218183643
@clerk/nuxt 2.0.0-snapshot.v20251218183643
@clerk/react 6.0.0-snapshot.v20251218183643
@clerk/react-router 3.0.0-snapshot.v20251218183643
@clerk/shared 4.0.0-snapshot.v20251218183643
@clerk/tanstack-react-start 1.0.0-snapshot.v20251218183643
@clerk/testing 2.0.0-snapshot.v20251218183643
@clerk/ui 1.0.0-snapshot.v20251218183643
@clerk/upgrade 2.0.0-snapshot.v20251218183643
@clerk/vue 2.0.0-snapshot.v20251218183643

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/agent-toolkit

npm i @clerk/agent-toolkit@0.2.9-snapshot.v20251218183643 --save-exact

@clerk/astro

npm i @clerk/astro@3.0.0-snapshot.v20251218183643 --save-exact

@clerk/backend

npm i @clerk/backend@3.0.0-snapshot.v20251218183643 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.0.0-snapshot.v20251218183643 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.0.0-snapshot.v20251218183643 --save-exact

@clerk/dev-cli

npm i @clerk/dev-cli@1.0.0-snapshot.v20251218183643 --save-exact

@clerk/expo

npm i @clerk/expo@3.0.0-snapshot.v20251218183643 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.0.0-snapshot.v20251218183643 --save-exact

@clerk/express

npm i @clerk/express@2.0.0-snapshot.v20251218183643 --save-exact

@clerk/fastify

npm i @clerk/fastify@2.6.9-snapshot.v20251218183643 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.0.0-snapshot.v20251218183643 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.0.0-snapshot.v20251218183643 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.0.0-snapshot.v20251218183643 --save-exact

@clerk/react

npm i @clerk/react@6.0.0-snapshot.v20251218183643 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.0.0-snapshot.v20251218183643 --save-exact

@clerk/shared

npm i @clerk/shared@4.0.0-snapshot.v20251218183643 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.0.0-snapshot.v20251218183643 --save-exact

@clerk/testing

npm i @clerk/testing@2.0.0-snapshot.v20251218183643 --save-exact

@clerk/ui

npm i @clerk/ui@1.0.0-snapshot.v20251218183643 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.0-snapshot.v20251218183643 --save-exact

@clerk/vue

npm i @clerk/vue@2.0.0-snapshot.v20251218183643 --save-exact
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment