Project ROUNDTABLE Docs
API reference

Authentication API

NextAuth credentials endpoint and session behavior

Authentication is the one API area that is owned by NextAuth rather than a custom JSON route. It turns an email/password credential pair into a short-lived JWT session, then refreshes authorization attributes from Prisma on later requests.

GET and POST /api/auth/[...nextauth]

Authentication: This is the sign-in boundary; the credentials callback itself does not require an existing session. NextAuth also serves its own CSRF, session, and provider endpoints under this catch-all path.

The configured credentials fields are:

FieldTypeRequired?Constraints
emailemail stringYesTrimmed, lowercased, looked up by unique User.email.
passwordstringYesCompared with the stored bcrypt hash.

Example browser-oriented sign-in uses NextAuth's provider flow:

const result = await signIn("credentials", {
  email: "poc.ai@navy.mil",
  password: "Roundtable2026!Demo",
  redirect: false,
});

The response is NextAuth-managed rather than the application's { data } envelope. A successful sign-in establishes a session containing user ID, email, display name, role, command ID, and organization ID. A failed credential returns an unsuccessful sign-in result and does not disclose whether the email or password was wrong.

Session rules

  • JWT strategy.
  • Maximum age: 15 minutes.
  • Sign-in page: /login.
  • Existing users are looked up case-insensitively after trimming.
  • Bcrypt failures for an existing user create authentication_failure.
  • Success creates authentication_success.
  • The JWT callback re-reads role, command, and organization from Prisma on every request.
  • The JWT callback also checks the database sessionVersion; a token with a stale version is revoked by removing its authorization attributes on refresh.

Error cases

NextAuth handles malformed callback, CSRF, and provider errors. Application routes should not inspect a client-provided role; they use getSession, requireGovSession, or requireIndustrySession and the database-backed session attributes.

Login debugging sequence

  1. Confirm the request reaches the NextAuth callback path rather than a page-level redirect.
  2. Confirm the submitted email is lowercased and trimmed, and that the account exists with the expected role.
  3. Confirm bcrypt compares the submitted password with passwordHash; the database never stores the plaintext demo password.
  4. Check the audit rows for authentication_success or authentication_failure.
  5. Inspect cookie flags and the NEXTAUTH_URL origin if the callback succeeds but the browser is not signed in.
  6. Make a fresh protected request and verify that role, command, and organization attributes match current Prisma rows.

Do not debug an authentication failure by printing credentials or token contents. Use account email, event type, HTTP status, and safe configuration metadata.

Route helper choice

CallerHelperResult when wrong audience
Public login callbackNextAuth providernull for invalid credentials.
Government APIrequireGovSession401 without session, 403 for industry role.
Industry APIrequireIndustrySession401 without session, 403 for government role.
Shared authenticated APIgetSessionCaller performs its own user-specific policy.

The helper is an initial gate, not a substitute for checking the requested record's organization or command scope.