0015: MIDOT signs in TREDIS users outside Auth
MIDOT verifies legacy TREDIS credentials and issues its own stateless session
- Status: Accepted
- Date: 2026-10-13
- Decision maker: Trevor Gerhardt
Context
PRD 0008 requires sign-in for Michigan TREDIS users.
Their identities and passwords live in the legacy tredis_global_dynamic database, not in Concourse.
ADR 0002 makes @concourse/auth the owner of identity and sessions.
Auth has no TREDIS identities, and the PRD excludes a mapping between the two systems.
MIDOT runs as a Start SPA with a Node server route on Vercel, as ADR 0014 records. The legacy database has no session store, reset token table, or lockout data.
Decision
MIDOT owns sign-in for TREDIS users. This is an exception to ADR 0002 for MIDOT only. Concourse apps continue to use Auth.
Identity. The TREDIS Global Dynamic service in @concourse/services owns all legacy data access, under ADR 0011.
It verifies passwords inside MySQL, reads account eligibility, changes passwords, and writes the session log.
No password or password ciphertext leaves MySQL.
Session. The MIDOT server route issues an encrypted and signed cookie. The cookie is HttpOnly, Secure, and SameSite=Lax. It holds the user ID, the username, the display name, the login access key, and the issue time. It is valid for 8 hours from sign-in. MIDOT does not renew it and does not check the database again during the session.
Reset links. A reset link carries a stateless token. The token is an HMAC over the user ID, the expiry time, and a password fingerprint. MySQL computes the fingerprint as a hash of the stored encrypted password. A password change makes every earlier link fail.
Keys. One server variable, MIDOT_SESSION_SECRET, supplies the key material. HKDF derives separate keys for the cookie and for reset tokens.
Boundaries. apps/midot owns cookies, tokens, pages, and the browser redirect.
It implements the session port of @concourse/api/midot, which serves the sign-in routes under
ADR 0017, and the reset token port of the Services sign-in
workflows. The workflows return session claims, and the route seals them into the cookie.
@concourse/email delivers reset email.
A session middleware runs before each protected route reads its request body.
Consequences
- MIDOT needs no new storage, and the legacy schema does not change.
- A session stays valid until it expires. A disabled account, a changed password, or a sign-out on another device does not end it.
- A change of
MIDOT_SESSION_SECRETends all sessions and makes all open reset links fail. - A reset link is single-use only through the password fingerprint. A link stays valid for 30 minutes if the user does not change the password.
- MIDOT writes legacy
tbluserlogrows withProductset toMIDOT. Legacy reads that filter onTREDISdo not see them. - Sessions that expire without a sign-out have no end time in
tbluserlog. - The browser gate is only navigation. The server route must refuse every protected API request without a valid cookie.
- When sign-in launches, the deployment protection statement in ADR 0014 changes in the same release.