> ## Documentation Index
> Fetch the complete documentation index at: https://docs.soundchecklive.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> Clerk authentication integration in the Soundcheck mobile app

The Soundcheck mobile app uses **Clerk** for authentication, supporting email/password, Google OAuth, and Apple Sign In on supported platforms (not every SSO option appears on every platform).&#x20;

## Overview

Authentication is handled through:

* **Clerk SDK** for session management
* **JWT tokens** for API authentication
* **SecureStore** for token persistence
* **OAuth flows** for social login

***

## Authentication methods

### Email/password

Traditional email and password authentication with:

* Email verification
* Password reset flow
* Session management

### Google OAuth

"Continue with Google" runs `useGoogleSsoFlow` (`lib/hooks/useGoogleSsoFlow.ts`), which picks one of two flows:

| Flow | When |
| - | - |
| Native Google account sheet (`useSignInWithGoogle` from `@clerk/expo/google`, native module from `@clerk/expo-google-signin`) | A native build with the `ClerkGoogleSignIn` module linked and the variables below set. iOS needs the web and iOS client IDs plus a URL scheme that matches the reversed iOS client ID. Android needs the web client ID. |
| Browser OAuth (`useOAuth({ strategy: 'oauth_google' })`, redirect `soundcheck://oauth-callback`) | Expo Go, a build without the module, missing or mismatched variables, or any native failure other than a cancel or a Clerk API rejection |

A cancel in either flow returns quietly. Other failures show copy from `lib/errors/userFacingError.ts`, never Clerk's raw text. The native module ships only in a new store build; it cannot go out over the air.

#### Native Google sign-in configuration

The client IDs are public identifiers, not secrets. They come from the EAS environment (or a local `.env`), never from the repo. `app.config.js` copies them into `extra`, where Clerk's hook and config plugin read them, and appends `.apps.googleusercontent.com` to a client ID given without it. Without them the app uses the browser flow and never touches the native module.

| Variable | Where | Value |
| - | - | - |
| `EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID` | All EAS environments | The Google Cloud **Web application** client ID that Clerk's Google connection uses. Android uses it as the server client ID, and it is the ID token audience. |
| `EXPO_PUBLIC_CLERK_GOOGLE_IOS_CLIENT_ID` | All EAS environments | The Google Cloud **iOS** client ID for bundle `com.soundchecklive.app` |
| `EXPO_PUBLIC_CLERK_GOOGLE_IOS_URL_SCHEME` | All EAS environments | The reversed iOS client ID (`com.googleusercontent.apps.<id>`). `app.config.js` adds it to `CFBundleURLTypes`. iOS uses the native sheet only when it is set and matches; without it, iOS uses the browser. A mismatch logs a config warning instead of failing the build. |
| `GOOGLE_OAUTH_ANDROID_CLIENT_ID` | All EAS environments, build time only (no `EXPO_PUBLIC_` prefix) | The Android OAuth client. The app never reads it: Android's Credential Manager only takes the web client ID, and Google matches the Android client by package name and signing SHA-1. |

The URL scheme gate matters because GoogleSignIn on iOS raises a native exception, which JavaScript cannot catch, when the reversed client ID scheme is missing from the app.

**1. Google Cloud**

1. A **Web application** OAuth client. Its ID and secret are the custom credentials on Clerk's Google connection.
2. An **iOS** OAuth client for bundle ID `com.soundchecklive.app` (with the Apple Team ID).
3. An **Android** OAuth client for package `com.soundchecklive.app` with the SHA-1 of the EAS upload keystore (`eas credentials -p android`). Builds installed from Google Play are re-signed, so add a second Android client with the SHA-1 of the Play **app signing** key (Play Console → App integrity). A new Android client can take a few hours to start working; until then the app falls back to the browser.

**2. Clerk dashboard (each instance used by dev, qa and prod builds)**

1. **SSO connections → Google**: enabled, with **custom credentials** (the web client ID and secret from step 1).
2. **Native applications**: register the iOS app (Team ID + bundle ID) and the Android app (package name + SHA-256 fingerprints of the upload and Play signing keys).
3. Keep `soundcheck://oauth-callback` in the allowed redirect URLs so the browser fallback keeps working.

**3. EAS environment variables**

Set the variables in the table above (plain-text visibility), each matching the Clerk instance behind that environment's publishable key. Then make a new native build.

### Apple sign in

Apple Sign In is **not** offered on every platform:

| Platform | Experience |
| - | - |
| iOS | Native Apple Sign In prompt |
| Android | Google only on native (`SsoButtons.android.tsx`; no Apple button) |
| Web (Clerk) | Apple via standard OAuth popup |

On iOS and web, post-auth redirects use `/(tabs)/events`, the Expo Router path for the tab labeled **Gigs**:

```tsx theme={null}
const { startOAuthFlow } = useOAuth({ strategy: 'oauth_apple' });

const handleAppleLogin = async () => {
  const { createdSessionId, setActive } = await startOAuthFlow();
  if (createdSessionId && setActive) {
    await setActive({ session: createdSessionId });
    router.replace('/(tabs)/events'); // Gigs tab
  }
};
```

***

## Configuration

### iOS configuration

Apple Sign In requires iOS entitlements. These are configured in `app.config.js`:

```javascript theme={null}
ios: {
  entitlements: {
    "com.apple.developer.applesignin": ["Default"],
  },
}
```

The `expo-apple-authentication` plugin is included automatically.

### Clerk dashboard setup

To enable social authentication:

**1. Enable OAuth Providers**

1. Go to [Clerk Dashboard](https://dashboard.clerk.com/)
2. Navigate to **User & Authentication → Social Connections**
3. Enable Google and/or Apple

**2. Configure Google OAuth**

1. Create OAuth credentials in Google Cloud Console
2. Add credentials to Clerk Dashboard
3. Configure redirect URLs

**3. Configure Apple Sign In**

1. Enable Sign In with Apple in Apple Developer Console
2. Create Services ID for OAuth
3. Generate private key
4. Add credentials to Clerk Dashboard

> [!NOTE]
>
> Apple Sign In requires an Apple Developer account (\$99/year).

***

## Token management

### How tokens work

```
┌─────────────────┐
│   User Login    │
│  (via Clerk)    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ Clerk Session   │
│   Established   │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  AuthContext    │
│  sets up token  │
│  getter in      │
│  httpClient     │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  API Request    │
│  calls getToken()│
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Clerk returns  │
│  fresh JWT      │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Token added to │
│  Authorization  │
│  header         │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Request sent   │
│  to backend     │
└─────────────────┘
```

### Token lifecycle

1. **Generation**: Clerk creates a JWT when user signs in
2. **Caching**: Token is stored in SecureStore via `tokenCache`
3. **Retrieval**: `getToken()` returns the current valid token
4. **Refresh**: Clerk automatically refreshes expired tokens
5. **Expiration**: Tokens expire after 1 hour (configurable in Clerk)

### Token format

All authenticated requests include:

```
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Imluc18y...
```

***

## AuthContext

The `AuthContext` provides authentication state throughout the app:

```tsx theme={null}
// lib/context/AuthContext.tsx
interface AuthContextType {
  user: User | null;
  isSignedIn: boolean;
  isLoaded: boolean;
  isTokenReady: boolean;
  signOut: () => Promise<void>;
}

export function useAuth() {
  return useContext(AuthContext);
}
```

### Provider setup

The `AuthProvider` must be inside `ClerkProvider`:

```tsx theme={null}
// app/_layout.tsx
<ClerkProvider
  publishableKey={publishableKey}
  tokenCache={tokenCache}
  signInFallbackRedirectUrl="/(tabs)/events" // Gigs tab
  signUpFallbackRedirectUrl="/(tabs)/events" // Gigs tab
>
  <AuthProvider>
    {/* App content */}
  </AuthProvider>
</ClerkProvider>
```

***

## Error handling

### Common OAuth errors

> [accordion] User Cancelled OAuth Flow
>
> This is expected behavior when users cancel authentication:
>
> ```tsx theme={null}
> try {
>   await startOAuthFlow();
> } catch (error) {
>   if (error.code === 'ERR_REQUEST_CANCELED' ||
>       error.code === 'user_cancelled_oauth_flow') {
>     // Silent - user cancelled, no error message needed
>     return;
>   }
>   // Show error for other cases
>   showError('Authentication failed');
> }
> ```

> [accordion] Invalid Client Error
>
> The Services ID or configuration doesn't match between Apple and Clerk:
>
> 1. Verify Services ID in Clerk matches Apple Developer Console
> 2. Check redirect URLs match exactly
> 3. Ensure the Services ID is enabled for Sign In with Apple

> [accordion] Invalid Grant Error
>
> Private key or Team ID mismatch:
>
> 1. Re-enter the private key in Clerk Dashboard
> 2. Verify Team ID is correct (10 characters)
> 3. Verify Key ID is correct (10 characters)

> [accordion] App Doesn't Open After Auth
>
> OAuth redirect not working:
>
> 1. Verify `scheme: "soundcheck"` in `app.config.js`
> 2. Rebuild the app with `eas build`
> 3. Check Clerk Dashboard OAuth redirect configuration

***

## Security

### Best practices

1. **Never commit secrets** - Keep Clerk keys in environment variables
2. **Use SecureStore** - Tokens are stored securely in iOS Keychain/Android Keystore
3. **Short-lived tokens** - JWT tokens expire, limiting vulnerability window
4. **Automatic refresh** - Clerk handles token refresh automatically
5. **Revocable sessions** - Sessions can be revoked from Clerk Dashboard

### Environment variables

```env theme={null}
# Required
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...

# Not needed in mobile app (server-side only)
# CLERK_SECRET_KEY=sk_test_...
```

> [!WARNING]
>
> Never include `CLERK_SECRET_KEY` in mobile app code. It's only for server-side use.

***

## Testing authentication

### Debug checklist

1. **Check token is being sent**:
   * Open React Native debugger
   * Check Network tab
   * Verify `Authorization: Bearer ...` header

2. **Verify user state**:
   ```tsx theme={null}
   const { user, isSignedIn } = useAuth();
   console.log('Auth state:', { user, isSignedIn });
   ```

3. **Test with cURL**:
   ```bash theme={null}
   TOKEN="eyJhbGciOiJSUzI1NiIs..."
   curl -H "Authorization: Bearer $TOKEN" \
     http://localhost:4000/api/v1/users/me
   ```

***

## Resources

* [Clerk React Native Documentation](https://clerk.com/docs/quickstarts/react-native)
* [Clerk OAuth Documentation](https://clerk.com/docs/authentication/social-connections)
* [Apple Sign In Guidelines](https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.