Clerk

clerkAuth38 lines

Drop-in authentication with prebuilt sign-in, MFA and user management.

Current — as published in v1.3.2. Written by hand for Clerk, not generated.

The rule

Reading auth state

tsx
const { isLoaded, isSignedIn, userId } = useAuth();
if (!isLoaded) return <Splash />;

Never treat "not loaded" as "signed out". Doing so flashes the sign-in screen at every launch for users who are already authenticated, and can redirect them out of a deep link.

Tokens

  • tokenCache must be backed by expo-secure-store. A session token is a credential; AsyncStorage is plaintext.
  • Send await getToken() as a bearer token to your backend.
  • The backend verifies the token. Never trust a user id from a request body or a query parameter.
  • The Clerk secret key is server-side only, never in the app.

User data

  • Store Clerk's userId on your own rows to join user data.
  • Handle the user.deleted webhook so removing a user removes their data. Orphaned personal data is a compliance problem, not just untidiness.

Environments

  • Separate Clerk applications for development and production. Sharing one puts test accounts in your production user pool.

Never

  • Never log a session token or JWT.
  • Never gate access purely on client state — the backend re-verifies.
  • Never render user-specific UI before isLoaded.

As each tool receives it

The same guidance, in the shape each editor reads. Cursor and Claude Code are written separately by hand; the rest are derived from the Cursor rule.

.cursor/rules/clerk.mdchand-written for this tool
.cursor/rules/clerk.mdc
---
description: Clerk authentication conventions
globs: ["src/features/auth/**", "src/services/**", "app/**"]
alwaysApply: false
---

# Clerk

## Reading auth state

```tsx
const { isLoaded, isSignedIn, userId } = useAuth();
if (!isLoaded) return <Splash />;
```

Never treat "not loaded" as "signed out". Doing so flashes the sign-in screen at
every launch for users who are already authenticated, and can redirect them out
of a deep link.

## Tokens

- `tokenCache` must be backed by `expo-secure-store`. A session token is a
  credential; `AsyncStorage` is plaintext.
- Send `await getToken()` as a bearer token to your backend.
- The backend **verifies** the token. Never trust a user id from a request body
  or a query parameter.
- The Clerk secret key is server-side only, never in the app.

## User data

- Store Clerk's `userId` on your own rows to join user data.
- Handle the `user.deleted` webhook so removing a user removes their data.
  Orphaned personal data is a compliance problem, not just untidiness.

## Environments

- Separate Clerk applications for development and production. Sharing one puts
  test accounts in your production user pool.

## Never

- Never log a session token or JWT.
- Never gate access purely on client state — the backend re-verifies.
- Never render user-specific UI before `isLoaded`.

What else this module writes

Selecting Clerk contributes more than a rule file — all of it merged with every other module you pick, with conflicts resolved rather than duplicated.

Environment
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEYrequiredPublishable key for this environment's Clerk instance.
CLERK_SECRET_KEYoptionalServer-side key for backend verification and the admin API. Never ship in the app.
CLERK_WEBHOOK_SECREToptionalVerifies incoming webhooks, including `user.deleted`. Server-side only.
Dependencies
@clerk/clerk-expo^2.19.0expo-secure-store^57.0.0
Folders
src/features/auth/screens/src/features/auth/components/src/hooks/auth/

Related

Put this in your repo

Adds Clerk to a project this tool generated, without starting over. Files you have hand-edited are left alone.