- Introduction
- Pitch
- Hogwarts
- Live Demo
- MVP
- Roadmap
- Launch Sprint
- PRD
- Get Started
- Localhost
- Architecture
- Structure
- Pattern
- Page
- Layout
- Content
- Types
- Config
- Actions
- Queries
- Authorization
- Validation
- Form
- Table
- Detail
- Card
- Util
- Hooks
- List Params
- Views
- README.md
- ISSUE.md
- Technology Stack
- Database
- File
- CDN Assets
- Entry Points
- Dashboard
- Authentication
- Credentials
- OAuth
- Flow Diagrams
- Multi-Tenancy
- Offline
- Onboarding
- Onboarding Videos
- Add Values
- Admission
- Application
- Attendance
- Compliance
- Profile
- Exams
- Exam Wizard
- Timetable
- Classrooms
- Notifications
- Conference
- LMS (Lumos)
- Finance
- Fee Management
- Invoice
- Wallet
- Salary
- Payroll
- Timesheet
- Expenses
- Budget
- Receipt
- Accounts
- Banking
- Reports
- Dashboard
- Permissions
- Messages
- Integration Flow
- Provision
- AI Document Processing
- Document Intelligence
- Internationalization
- Translation
- Translation Guide
- Icons
- Docs Factory
- Inspiration
- Listings
- Teachers
- Students
- Catalog
- Library
- Contributing
- Code of conduct
- GitHub Workflow
- Database Seeds
- Database Safety
- Test Accounts
- Playwright
- Prettier
- Block Rebound
Authentication runs on Next.js App Router with Auth.js (NextAuth v5). Users authenticate via OAuth (Google, Facebook) or credentials, with optional 2FA, email verification, and password reset. Sessions are JWTs scoped via cookies on .databayt.org for cross-subdomain SSO.
For provider-specific detail see credentials, oauth, and flow diagrams.
Entry points
| # | Entry | Trigger | Post-login redirect |
|---|---|---|---|
| 1 | Marketing header | Login icon on ed.databayt.org | DEVELOPER → /dashboard, school user → {school}.databayt.org/dashboard, USER → / |
| 2 | Get Started | Marketing CTA | /login?callbackUrl=/onboarding then 15-step wizard |
| 3 | School subdomain | {school}.databayt.org/login | Same as #1, scoped to school |
| 4 | Protected route | Direct URL | /login?callbackUrl={original_url}, returns after auth |
USER role redirects to /{locale} (homepage), not /onboarding. Onboarding is reachable only via explicit callbackUrl.
Redirect priority
Decision chain in src/auth.ts redirect callback and src/components/auth/login/action.ts:
- Callback URL — cookie / URL params / baseUrl params (preserved across OAuth via httpOnly cookie)
- Explicit relative path from
signIn({ redirectTo }) - Subdomain detection — on a school subdomain → that school's dashboard
- Smart redirect — decode JWT, look up school, redirect to school dashboard
- Fallback — main domain dashboard
Session and JWT
// JWT (jwt callback)
token = {
id: string
role: UserRole
schoolId: string | null
provider: string
providerAccountId: string
sessionToken: string
updatedAt: number
hash: string
exp: number
}
// Session (session callback)
session.user = {
id: string
email: string
role: UserRole // Effective role — preview if active
schoolId: string | null
isPreviewMode: boolean
}Session config: strategy: "jwt", maxAge: 24 * 60 * 60 (24h), updateAge: 5 * 60 (5 min in production).
Tenant context
import { getTenantContext } from "@/lib/tenant-context"
const { schoolId, requestId, role, isPlatformAdmin } = await getTenantContext()
// Priority: impersonation cookie → x-subdomain header → sessionTwo-tier subdomain → schoolId cache: Upstash Redis (5 min, shared) → in-memory Map (1 min, max 100 entries, per-instance fallback). Redis optional. Full detail in multi-tenancy.
Roles
enum UserRole {
DEVELOPER // 8 - platform admin (cross-school)
ADMIN // 7 - school administrator
TEACHER // 6 - teaching staff
ACCOUNTANT // 5 - finance staff
STAFF // 4 - general staff
GUARDIAN // 3 - parent / guardian
STUDENT // 2 - enrolled student
USER // 1 - default
}Eight roles total. Hierarchy in src/lib/school-access.ts; must stay in sync with roleRoutes in src/routes.ts. See authorization for feature-level permission checks.
Route protection
Edge middleware decodes JWT, checks roleRoutes, detects subdomain (custom domain → Redis lookup), sets x-subdomain header for downstream consumers.
// Server component
import { auth } from "@/auth"
export default async function ProtectedPage() {
const session = await auth()
if (!session) redirect("/login")
return <Dashboard schoolId={session.user.schoolId} />
}"use client"
import { RoleGate } from "@/components/auth/role-gate"
import { useCurrentRole } from "@/components/auth/use-current-role"
import { useCurrentUser } from "@/components/auth/use-current-user"
;<RoleGate allowedRole={UserRole.ADMIN}>
<AdminPanel />
</RoleGate>Registration paths
| Option | Method | Status |
|---|---|---|
| Self-registration | OAuth (Google/Facebook) | Working |
| Bulk creation | Admin imports CSV with pre-assigned roles | Working |
| Join codes | 6-char codes (ABCDEFGHJKMNPQRSTUVWXYZ2345679) — 481M combinations | Working |
| Invitations | Email invite links with role | Partial |
Self-enrollment toggle: SchoolBranding.allowSelfEnrollment — enabled gives instant join, disabled creates a pending MembershipRequest.
Preview mode
Lets admins see the UI as another role without changing their actual role. Uses preview-mode and preview-role cookies; session callback overrides role and sets isPreviewMode: true.
| Hook | Returns |
|---|---|
useEffectiveRole | UserRole — preview if active, else actual |
useIsPreviewMode | boolean |
useRoleInfo | { actual, preview, effective, isPreviewMode } |
Impersonation
DEVELOPERs can impersonate school contexts for debugging. Cookie impersonate_schoolId is httpOnly, secure, 1-hour maxAge. IMPERSONATION_STARTED and IMPERSONATION_STOPPED events are audit-logged. Impersonation takes priority over all other tenant resolution methods.
See also
- Credentials
- OAuth — providers, multi-tenant adapter, cross-subdomain cookies
- Flow diagrams
- Onboarding
- Multi-tenancy