Authentication
QuickDapp uses stateless JWT authentication with two provider options: email verification and OAuth. This page covers the authentication flows from the user's perspective. For backend implementation details (JWT internals, the @auth directive), see Backend > Authentication.
JWT Tokens
All authentication methods produce a JWT token signed with SESSION_ENCRYPTION_KEY. The token contains the user ID and expires after 24 hours. There's no session table—authentication state lives entirely in the token.
Email Authentication
Email verification uses encrypted, stateless codes:
- Client calls
sendEmailVerificationCodewith email address - Server generates a random code and encrypts it into a blob containing the code and expiration
- Server sends the code via email
- User enters the code they received
- Client calls
authenticateWithEmailwith email, code, and blob - Server decrypts the blob, verifies the code matches and hasn't expired
- Server finds or creates the user and returns a JWT
The blob approach means no database storage for pending verifications. Codes expire after a short window.
Email configuration requires MAILGUN_* environment variables for the email provider.
OAuth Authentication
Six OAuth providers are supported: Google, Facebook, GitHub, X (Twitter), TikTok, and LinkedIn. The flow:
- Client calls
getOAuthLoginUrlwith provider name and redirect URL - Server generates encrypted state containing PKCE challenge and redirect info
- User is redirected to the OAuth provider's authorization page
- After authorization, provider redirects to
/auth/callback/:provider - Server exchanges the authorization code for tokens
- Server fetches user info from the provider's API
- Server finds or creates the user based on provider user ID
- Server returns an HTML page that stores the JWT and redirects to the app
Each provider requires configuration:
OAUTH_GOOGLE_CLIENT_ID=...
OAUTH_GOOGLE_CLIENT_SECRET=...
OAUTH_GITHUB_CLIENT_ID=...
OAUTH_GITHUB_CLIENT_SECRET=...
# Similar for Facebook, X, TikTok, LinkedIn
The OAuth implementation uses the Arctic library and handles PKCE automatically for providers that support it.
Adding Custom Authentication Methods
To add a new authentication method, follow these steps:
- Add auth type constant — Define the new type in
src/shared/constants.ts - Create user lookup/creation — Add a function in
src/server/db/users.tsto find or create users by the new identifier - Add authentication logic — Implement verification in a new auth service method
- Create GraphQL mutation — Add the mutation to the schema and implement the resolver
- Update frontend — Create a login form that calls the new mutation
See Backend > Authentication for detailed implementation guidance.
Frontend Integration
The AuthContext exposes:
isAuthenticated— Whether the user is logged inisLoading— Loading state during auth operationserror— Error from failed auth attemptsauthToken— The current JWT (null if not authenticated)profile— The authenticated user's profileemail— The authenticated user's emaillogin(token, profile)— Store auth state after successful authenticationlogout()— Clear auth staterestoreAuth()— Attempt to restore auth from stored token on app load
Token Validation
Protected GraphQL operations use the @auth directive. The handler extracts the Bearer token, verifies it, loads the user, and checks the disabled flag. Invalid tokens return UNAUTHORIZED, disabled users return ACCOUNT_DISABLED. See Backend > Authentication for details.
Security
Encryption Key: The SESSION_ENCRYPTION_KEY must be 32+ characters and kept secret. It signs JWTs and encrypts OAuth state.
HTTPS: Always use HTTPS in production. Tokens sent over HTTP can be intercepted.
Token Storage: The frontend stores tokens in localStorage. For higher security requirements, consider httpOnly cookies with CSRF protection.
See src/server/auth/ for the complete authentication implementation.