Connect the React Native app to the admin backend so users and scans flow
into the panel and bans take effect on the device.
- Activate the bearer() plugin in lib/auth.ts so mobile clients can pass
the better-auth session token via Authorization: Bearer header
- Add requireMobileAuth() helper in lib/auth-guard.ts that resolves the
session, re-fetches the user from DB (banned flag is on User, not in
the Session payload) and returns 403 with banned/bannedReason for
banned accounts
- Extend CORS in middleware.ts to allow POST + Authorization header on
/api/mobile/* (preflight was failing before)
- New routes:
POST /api/mobile/auth/sync — passwordless mobile auth via
deterministic password derived from sha256(email + deviceId + pepper).
Tries signIn first, falls back to signUp on USER_NOT_FOUND. Returns
409 when the email exists with a different deviceId.
GET /api/mobile/auth/me — current user enriched with
banned/bannedReason/role/xp/level
POST /api/mobile/auth/sign-out — best-effort session revocation
POST /api/mobile/scans — create a scan, resolves diseaseSlug
to diseaseId, never accepts an imageUrl from the device (V1 keeps
photos local-only)
GET /api/mobile/scans — own scans, 50 most recent
Validated end-to-end via curl: signUp → me → repeat sync (idempotent) →
post scan → ban via DB → me reflects banned: true → POST scans returns
403 + banned/bannedReason.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
35 lines
859 B
TypeScript
35 lines
859 B
TypeScript
import { betterAuth } from "better-auth";
|
|
import { bearer } from "better-auth/plugins";
|
|
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
import { prisma } from "./prisma";
|
|
|
|
export const auth = betterAuth({
|
|
baseURL: process.env.BETTER_AUTH_URL || process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000",
|
|
database: prismaAdapter(prisma, {
|
|
provider: "postgresql",
|
|
}),
|
|
emailAndPassword: {
|
|
enabled: true,
|
|
minPasswordLength: 8,
|
|
maxPasswordLength: 64,
|
|
},
|
|
session: {
|
|
expiresIn: 60 * 60 * 24 * 7,
|
|
updateAge: 60 * 60 * 24,
|
|
},
|
|
user: {
|
|
additionalFields: {
|
|
role: {
|
|
type: "string",
|
|
defaultValue: "USER",
|
|
},
|
|
},
|
|
},
|
|
plugins: [bearer()],
|
|
trustedOrigins: [
|
|
process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000",
|
|
],
|
|
});
|
|
|
|
export type Session = typeof auth.$Infer.Session;
|