0%
balqalam Logo
balqalam
FeaturesCommunityPricingDocumentation
Login
  • 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
  • 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
Sales & GTM
  • Marketing Brief
  • Sales
  • Go-to-market
  • Marketing
  • Admission — Feature Spotlight
  • Pilot Program
  • Leads
  • Proposal
  • Outreach Templates
  • Case Study
  • Competitors
  • Landing-page teardown
  • Competitor FAQ
  • Business model
  • Shared economy
  • Traction
Fundraising & Ecosystem
  • Get Support
  • Investor Deck
  • Data Room
  • Investors
  • Accelerators
  • Incubators
  • Grants
  • Sponsors
  • Partners
  • Competitions & Hackathons
  • Universities & Training Centers

Credentials

PreviousNext

Email/password flows — registration, verification, login with 2FA, and password reset.

Credentials authentication covers four flows on top of Auth.js: registration, email verification, login (with 2FA branch), and password reset. Tokens, emails, and audit logs are shared infrastructure.

For OAuth, multi-tenant redirect logic, and SSO see authentication and oauth.

Registration

Source: src/components/auth/join/{form,action}.ts.

  1. Client posts { email, password, username } to register().
  2. Validate RegisterSchema (email format, password ≥ 6, username ≥ 1).
  3. getUserByEmail(email) — findMany, prefers user with password set.
  4. Duplicate → "Email already in use!".
  5. bcrypt.hash(password, 10) → db.user.create() with role: USER, no schoolId.
  6. generateVerificationToken(email) (UUID, 24h TTL).
  7. sendVerificationEmail() → link /{locale}/new-verification?token={uuid}.
  8. Return "Confirmation email sent!".
ResultType
Invalid fields!error
Email already in use!error
Confirmation email sent!success

Email verification

Source: src/components/auth/verification/{form,action,verificiation-token}.ts.

  1. User opens /new-verification?token={uuid}.
  2. NewVerificationForm calls newVerification(token) on mount.
  3. getVerificationTokenByToken(token) — if missing, fall back to checking already-verified state.
  4. Check expires < now.
  5. getUserByEmail(token.email). If already verified → "Email verified!" (idempotent).
  6. Update user.emailVerified = new Date() and user.email = token.email.
  7. Delete the token.
  8. UI shows success + "Back to login" link (no auto-redirect).
ResultType
Token does not exist!error
Token has expired!error
Email does not exist!error
Email verified!success
Email already verified!success

Login

10-step flow with a 2FA branch. Source: src/components/auth/login/{form,action}.ts, src/auth.config.ts.

  1. Validate — LoginSchema.safeParse({ email, password, code? }).
  2. Brute force check — isBruteForceBlocked(email) blocks at ≥ 5 failures in last 15 min.
  3. User lookup — getUserByEmail(email) (multi-tenant findMany, prefers user with password).
  4. Exists — missing user/email/password logs USER_NOT_FOUND → "Email does not exist!".
  5. Email verified? — if not, regenerate verification token, send email, return "Confirmation email sent!".
  6. 2FA branch (if isTwoFactorEnabled):
    • No code → generate 6-digit token (5 min TTL), email it, return { twoFactor: true }.
    • Code submitted → validate exists/matches/unexpired, delete token, replace TwoFactorConfirmation.
  7. Build redirect URL — context-aware (callbackUrl, role, subdomain) — see authentication.
  8. Sign in — signIn("credentials", { email, password, redirectTo }).
  9. Authorize (auth.config.ts) — re-parse, getUserByEmail, bcrypt.compare.
  10. Errors — AuthError.CredentialsSignin → "Invalid credentials!"; NEXT_REDIRECT re-thrown.
ResultType
Invalid fields!error
Too many failed attempts. Please try again in 15 minutes.error
Email does not exist!error
Confirmation email sent!success
Invalid code!error
Code expired!error
Invalid credentials!error
Something went wrong!error
An unexpected error occurred. Please try again.error

Password reset

Two steps — request, then set new password.

Request

Source: src/components/auth/reset/{form,action}.ts.

  1. Submit { email } to reset().
  2. ResetSchema validates email.
  3. getUserByEmail(email) — missing → "Email not found!".
  4. generatePasswordResetToken(email) (UUID, 1h TTL).
  5. sendPasswordResetEmail() → /{locale}/new-password?token={uuid}.
  6. Return "Reset email sent!".

Set new password

Source: src/components/auth/password/{form,action,token}.ts.

  1. Submit { password } + token from URL.
  2. No token → "Missing token!".
  3. NewPasswordSchema validates password ≥ 6.
  4. getPasswordResetTokenByToken(token) — missing → "Invalid token!"; expired → "Token has expired!".
  5. getUserByEmail(token.email) — missing → "Email does not exist!".
  6. bcrypt.hash(password, 10) → db.user.update.
  7. Delete token.
  8. Return "Password updated!".

2FA

Integrated into login when user.isTwoFactorEnabled.

// src/components/auth/tokens.ts
const token = crypto.randomInt(100_000, 1_000_000).toString() // 6-digit
const expires = new Date(Date.now() + 5 * 60 * 1000) // 5 minutes

Lifecycle:

  1. Login detects isTwoFactorEnabled + no code → generate token, email it, return { twoFactor: true }.
  2. Client shows code input.
  3. Resubmit with code → validate (exact match + not expired).
  4. Delete TwoFactorToken.
  5. Replace TwoFactorConfirmation for the user.
  6. Continue with normal signIn("credentials").

Email format (no link, code inline):

LocaleSubjectBody
en2FA CodeYour 2FA code: {token}
arرمز التحققرمز التحقق الخاص بك: {token}

Source: src/components/auth/{tokens,verification/2f-token,verification/2f-confirmation}.ts.

Token system

All tokens use delete-before-create — only one active token per email at a time.

TokenFormatTTLGeneratorModel
VerificationUUID v424hgenerateVerificationTokenVerificationToken
Password resetUUID v41hgeneratePasswordResetTokenPasswordResetToken
2FA6-digit numeric5 mingenerateTwoFactorTokenTwoFactorToken

Source: src/components/auth/tokens.ts.

Email service

SettingValue
ProviderResend (RESEND_API_KEY)
Sendernoreply@databayt.org
DomainNEXT_PUBLIC_APP_URL
LanguagesEnglish + Arabic (dir="rtl")
ErrorsSilent — try/catch with console.error
FunctionSubject (en)Subject (ar)Link
sendVerificationEmailConfirm your emailتأكيد بريدك الإلكتروني/{locale}/new-verification?token={uuid}
sendPasswordResetEmailReset your passwordإعادة تعيين كلمة المرور/{locale}/new-password?token={uuid}
sendTwoFactorTokenEmail2FA Codeرمز التحققNone — code inline

Source: src/components/auth/mail.ts.

Brute force protection

Source: src/lib/audit-log.ts.

// isBruteForceBlocked(email, ip?) — email-only, ip stored but not used in query
const recentFailures = await db.loginAttempt.count({
  where: {
    email,
    success: false,
    timestamp: { gte: fifteenMinutesAgo },
  },
})
return recentFailures >= 5

logLoginAttempt({ email, success, failureReason, ip, userAgent, schoolId }) records every attempt for auditing.

Validation schemas

Two tiers exist: legacy (server-side, English) and i18n factories (client-side, dictionary-translated).

Legacy schemaFieldsConstraints
LoginSchemaemail, password, code?email format, password ≥ 1
RegisterSchemaemail, password, usernameemail format, password ≥ 6, username ≥ 1
ResetSchemaemailemail format
NewPasswordSchemapassword≥ 6 chars
SettingsSchemaname?, email?, role, password?, newPassword?, isTwoFactorEnabled?newPassword ↔ password coupling

Factories: createLoginSchema(dictionary), createRegisterSchema(dictionary), createResetSchema(dictionary), createNewPasswordSchema(dictionary), createSettingsSchema(dictionary). Note: password/form.tsx still uses the legacy NewPasswordSchema.

Source: src/components/auth/validation.ts.

Models

User, VerificationToken, PasswordResetToken, TwoFactorToken, TwoFactorConfirmation, LoginAttempt. The User model uses @@unique([email, schoolId]) rather than @unique email — see the multi-tenant adapter section in oauth for the implications. Schemas live under prisma/models/auth.prisma. See database and accounts for full field reference.

See also

  • Authentication
  • OAuth — multi-tenant adapter
  • Accounts
  • Database
AuthenticationOAuth

On This Page

RegistrationEmail verificationLoginPassword resetRequestSet new password2FAToken systemEmail serviceBrute force protectionValidation schemasModelsSee also

Built by Databayt ·

Welcome to balqalam.

A great journey is about to begin.