Authentication
This project uses Clerk for authentication across both web and mobile applications. The authentication is fully integrated with the tRPC API layer, ensuring type-safe authentication throughout the application.
Authentication Implementation
The authentication in this monorepo is implemented differently for each platform:
Next.js Implementation
The Next.js application uses the official Clerk SDK for Next.js:
// apps/nextjs/src/app/layout.tsx
import { ClerkProvider } from "@clerk/nextjs";
export default function RootLayout(props: { children: React.ReactNode }) {
return (
<ClerkProvider>
<html lang="en" suppressHydrationWarning>
{/* ... */}
</html>
</ClerkProvider>
);
}
Key components:
- ClerkProvider: Wraps the entire application to provide authentication context
- Middleware: Protects routes and API endpoints
- UI Components: Ready-to-use sign-in/sign-up buttons and user profile components
// apps/nextjs/src/middleware.ts
import { clerkMiddleware } from "@clerk/nextjs/server";
export default clerkMiddleware();
export const config = {
matcher: [
// Skip Next.js internals and all static files
"/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
// Always run for API routes
"/(api|trpc)(.*)",
],
};
Expo Implementation
The Expo application uses a custom authentication flow with web browser-based authentication:
// apps/expo/src/utils/auth.ts
import * as Linking from "expo-linking";
import * as Browser from "expo-web-browser";
export const signIn = async () => {
const signInUrl = `${getBaseUrl()}/api/auth/signin`;
const redirectTo = Linking.createURL("/login");
const result = await Browser.openAuthSessionAsync(
`${signInUrl}?expo-redirect=${encodeURIComponent(redirectTo)}`,
redirectTo,
);
if (result.type !== "success") return false;
const url = Linking.parse(result.url);
const sessionToken = String(url.queryParams?.session_token);
if (!sessionToken) throw new Error("No session token found");
setToken(sessionToken);
return true;
};
Key components:
- Web Browser Authentication: Opens Clerk authentication in a browser
- Token Management: Securely stores session tokens using
expo-secure-store
- React Hooks: Custom hooks for user state, sign-in, and sign-out
// Hooks for authentication
export const useUser = () => {
const { data: session } = useQuery(trpc.auth.getSession.queryOptions());
return session?.user ?? null;
};
export const useSignIn = () => {
// Implementation details
};
export const useSignOut = () => {
// Implementation details
};
tRPC Integration
The authentication is integrated with tRPC to provide protected API endpoints:
// packages/api/src/trpc.ts
export const createTRPCContext = async (opts: {
headers: Headers;
auth: Awaited<ReturnType<typeof auth>> | null;
}) => {
const authInfo = opts.auth ?? (await auth());
await dbConnect();
return {
auth: authInfo,
};
};
Protected procedures require authentication:
export const protectedProcedure = t.procedure.use(timingMiddleware).use(({ ctx, next }) => {
if (!ctx.auth.userId) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return next({
ctx,
});
});
Authentication Components
The Next.js application includes pre-built UI components for authentication:
// apps/nextjs/src/app/_components/auth-showcase.tsx
import {
SignedIn,
SignedOut,
SignInButton,
SignUpButton,
UserButton,
} from "@clerk/nextjs";
export function AuthShowcase() {
return (
<div className="flex flex-col items-center justify-center gap-4">
<SignedOut>
<SignInButton />
<SignUpButton />
</SignedOut>
<SignedIn>
<UserButton />
</SignedIn>
</div>
);
}
Environment Variables
The authentication requires the following environment variables:
# Server-side (private)
CLERK_SECRET_KEY=your_clerk_secret_key
# Client-side (public)
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your_clerk_publishable_key
These variables are validated using zod in apps/nextjs/src/env.ts
.
Authentication Flow
-
User Signs In:
- Web: Uses Clerk's UI components
- Mobile: Opens a web browser for authentication, then returns to the app
-
Authentication Token:
- Web: Managed by Clerk through cookies
- Mobile: Stored securely using
expo-secure-store
-
Protected API Access:
- The token is included in API requests
- tRPC middleware verifies the token for protected procedures
- If authentication fails, a
TRPCError
with codeUNAUTHORIZED
is thrown
-
User Signs Out:
- Tokens are invalidated and removed from storage
- Client state is reset
Clerk Dashboard
Most of the authentication configuration is managed through the Clerk Dashboard, including:
- User management
- Authentication methods (email/password, social logins, etc.)
- Security settings
- Branding and customization
This architecture provides a secure, type-safe authentication system that works seamlessly across both web and mobile platforms while leveraging the same API layer.