--- description: globs: alwaysApply: true --- # 4. Authentication This document gives a quick rundown on how authentication is configured and used within the Wasp application. See the Wasp Auth docs for available methods and complete guides [wasp-overview.mdc](mdc:template/app/.cursor/rules/wasp-overview.mdc) ## Wasp Auth Setup - Wasp provides built-in authentication with minimal configuration via the Wasp config file. - Wasp generates all necessary auth routes, middleware, and UI components based on the configuration. - Example auth configuration in [main.wasp](mdc:main.wasp): ```wasp app myApp { // ... other config auth: { // Links Wasp auth to your User model in @schema.prisma userEntity: User, methods: { // Enable username/password login usernameAndPassword: {}, // Enable Google OAuth login // Requires setting GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET env vars google: {}, // Enable email/password login with verification email: { // Set up an email sender (Dummy prints to console) // See https://wasp-lang.com/docs/auth/email-auth#email-sending fromField: { name: "Budgeting Vibe", email: "noreply@budgetingvibe.com" }, emailVerification: { clientRoute: EmailVerificationRoute }, passwordReset: { clientRoute: PasswordResetRoute } } }, // Route to redirect to if auth fails onAuthFailedRedirectTo: "/login", // Optional: Route after successful signup/login // onAuthSucceededRedirectTo: "/dashboard" } emailSender: { provider: Dummy // Use Dummy for local dev (prints emails to console) // provider: SMTP // For production, configure SMTP } } // Define the routes needed by email auth methods route EmailVerificationRoute { path: "/auth/verify-email", to: EmailVerificationPage } page EmailVerificationPage { component: import { EmailVerification } from "@src/features/auth/EmailVerificationPage.tsx" } route PasswordResetRoute { path: "/auth/reset-password", to: PasswordResetPage } page PasswordResetPage { component: import { PasswordReset } from "@src/features/auth/PasswordResetPage.tsx" } ``` - **Dummy Email Provider Note:** When `emailSender: { provider: Dummy }` is configured in [main.wasp](mdc:main.wasp), Wasp does not send actual emails. Instead, the content of verification/password reset emails, including the clickable link, will be printed directly to the server console where `wasp start` is running. ## Wasp Auth Rules - **User Model ( [schema.prisma](mdc:schema.prisma) ):** - Wasp Auth methods handle essential identity fields (like `email`, `password hash`, `provider IDs`, `isVerified`) internally. These are stored in separate Prisma models managed by Wasp (`AuthProvider`, `AuthProviderData`). - Your Prisma `User` model (specified in [main.wasp](mdc:main.wasp) as `auth.userEntity`) typically **only needs the `id` field** for Wasp to link the auth identity. ```prisma // Minimal User model in @schema.prisma model User { id Int @id @default(autoincrement()) // Add other *non-auth* related fields as needed // e.g., profile info, preferences, relations to other models // profileImageUrl String? // timeZone String? @default("UTC") } ``` - **Avoid adding** `email`, `emailVerified`, `password`, `username`, or provider-specific ID fields directly to *your* `User` model in [schema.prisma](mdc:schema.prisma) unless you have very specific customization needs that require overriding Wasp's default behavior and managing these fields manually. - If you need frequent access to an identity field like `email` or `username` for *any* user (not just the logged-in one), see the **Recommendation** in the "Wasp Auth User Fields" section below. - **Auth Pages:** - When initially creating Auth pages (Login, Signup), use the pre-built components provided by Wasp for simplicity: - `import { LoginForm, SignupForm } from 'wasp/client/auth';` - These components work with the configured auth methods in [main.wasp](mdc:main.wasp). - You can customize their appearance or build completely custom forms if needed. - **Protected Routes/Pages:** - Use the `useAuth` hook from `wasp/client/auth` to access the current user's data and check authentication status. - Redirect or show alternative content if the user is not authenticated. ```typescript import { useAuth } from 'wasp/client/auth'; import { useNavigate } from 'react-router-dom'; const MyProtectedPage = () => { const { data: user, isLoading, error } = useAuth(); // Returns AuthUser | null const navigate = useNavigate(); if (isLoading) return
Loading...
; // If error, it likely means the auth session is invalid/expired if (error || !user) { // Redirect to login page defined in main.wasp (auth.onAuthFailedRedirectTo) // or use the navigate hook from react-router-dom for more fine-grained control navigate('/some-other-path'); return
Please log in to access this page.
; } // User is authenticated, render the page content // Use helpers like getEmail(user) or getUsername(user) if needed return
Welcome back!
; // Access user.id if needed }; ``` ## Wasp Auth User Fields (`AuthUser`) - The `user` object returned by `useAuth()` hook on the client, or accessed via `context.user` in server operations/APIs, is an `AuthUser` object (type imported from `wasp/auth`). - **Auth-specific fields** (email, username, verification status, provider IDs) live under the nested `identities` property based on the auth method used. - e.g., `user.identities.email?.email` - e.g., `user.identities.username?.username` - e.g., `user.identities.google?.providerUserId` - **Always check for `null` or `undefined`** before accessing these nested properties, as a user might not have used all configured auth methods. - **Helpers:** Wasp provides helper functions from `wasp/auth` for easier access to common identity fields on the `AuthUser` object: - `import { getEmail, getUsername } from 'wasp/auth';` - `const email = getEmail(user); // Returns string | null` - `const username = getUsername(user); // Returns string | null` - **Standard User Entities:** Remember that standard `User` entities fetched via `context.entities.User.findMany()` or similar in server code **DO NOT** automatically include these auth identity fields (`email`, `username`, etc.) by default. They only contain the fields defined directly in your [schema.prisma](mdc:schema.prisma) `User` model. - **Recommendation:** - If you need *frequent* access to an identity field like `email` or `username` for *any* user (not just the currently logged-in one accessed via `context.user` or `useAuth`) and want to query it easily via `context.entities.User`, consider this approach: 1. **Add the field directly** to your `User` model in [schema.prisma](mdc:schema.prisma). ```prisma model User { id Int @id @default(autoincrement()) email String? @unique // Add if needed frequently // other fields... } ``` 2. **Ensure this field is populated correctly** when the user signs up or updates their profile. You can do this through the `userSignupFields` property in the wasp config file for each auth method. ```wasp //main.wasp auth: { userEntity: User, methods: { email: { //... userSignupFields: import { getEmailUserFields } from "@src/auth/userSignupFields" }, } } ``` ```ts //userSignupFields.ts import { defineUserSignupFields } from 'wasp/auth/providers/types'; const userDataSchema = z.object({ email: z.string(), }); export const getEmailUserFields = defineUserSignupFields({ email: (data) => { const userData = userDataSchema.parse(data); return userData.email; } }) ``` 3. This makes the field (`email` in this example) a standard, queryable field on your `User` entity, accessible via `context.entities.User`, separate from the `AuthUser`'s identity structure. - **Common Issue:** If auth isn't working, first verify the `auth` configuration in [main.wasp](mdc:main.wasp) is correct and matches your intent (correct `userEntity`, enabled `methods`, `onAuthFailedRedirectTo`). Ensure environment variables for social providers are set if applicable. Check the Wasp server logs for errors.