mirror of
https://github.com/wasp-lang/open-saas.git
synced 2025-11-22 22:07:35 +01:00
* Refactor file upload and toast notifications Replaces react-hot-toast with a custom toast system using @radix-ui/react-toast, updating all usages and adding new UI components for toast and dialog. Refactors file upload to use a two-step process: first generating an S3 upload URL, then adding the file to the database, and adds file deletion support with confirmation dialog and S3 cleanup. Updates Prisma schema, removes unused fields, and cleans up navigation and admin settings page. * Enforce file upload limit and update dependencies on opensaas-sh Added a check to restrict users to 2 file uploads in the demo, with a new helper function and error handling. Updated navigation items, improved landing page content, and removed unused dependencies (react-hot-toast). Added @radix-ui/react-toast, updated testimonials, and made minor content and code improvements. * update tests * Improve file deletion error handling and cleanup Refactors file deletion to delete the database record before attempting S3 deletion, ensuring the file is removed from the app even if S3 deletion fails. Adds error logging for failed S3 deletions to aid in manual cleanup. Also simplifies error handling in the file upload page and removes unused imports in the demo app page. * Add credit check and S3 file existence validation Added logic to decrement user credits or throw an error if out of credits in the AI demo app. Updated file upload operations to validate file existence in S3 before adding to the database, and implemented S3 file existence check utility. Minor UI and code improvements included. * Update s3Utils.ts * update app_diff * fix diff * Update deletions * Improve toast UI, error handling, and credit messaging Updated toast action hover style and icon spacing for better UI consistency. Enhanced error handling in file deletion to display specific error messages. Refined credit/subscription error message in GPT response operation for clarity and removed redundant credit decrement logic. * Refactor file upload validation and error handling Replaces error state management with toast notifications for file upload errors and success. Refactors file type validation to use a new ALLOWED_FILE_TYPES_CONST and type AllowedFileTypes. Updates validation logic to throw errors instead of returning error objects, and simplifies type handling across file upload modules. * Refactor file upload to use s3Key and add cleanup job Replaces the 'key' field with 's3Key' for file storage references throughout the codebase and database schema. Updates all related logic, types, and API contracts to use 's3Key'. Adds a scheduled job to delete old files from S3 and the database. Cleans up file type validation constants and improves consistency in file upload and download operations. * add orphaned file clean up * remove s3 cleanup job from template removed but added suggestion to docs. * Update SettingsPage.tsx * prettier format * Update UI, remove unused files Updated README with deployment and demo details. Removed unused App.tsx and package-lock.json files. Modified Main.css, NavBar constants, file uploading logic, file upload operations, and landing page content sections for improved UI and functionality. * remove pricing page from isMarketingPage
170 lines
8.6 KiB
Plaintext
170 lines
8.6 KiB
Plaintext
---
|
|
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 <div>Loading...</div>;
|
|
// 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 <div>Please log in to access this page.</div>;
|
|
}
|
|
|
|
// User is authenticated, render the page content
|
|
// Use helpers like getEmail(user) or getUsername(user) if needed
|
|
return <div>Welcome back!</div>; // 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.
|