auth.js (2205B)
1 // src/conductor/routes/auth.js - logging in and out 2 // 3 // Only meaningful for built-in accounts. When OIDC is in use the provider 4 // issues the token and these endpoints report that, rather than pretending 5 // to accept a password. 6 7 import { issueToken, sessionCookie, clearedCookie } from '../../lib/auth/index.js'; 8 9 export default async function authRoutes(fastify, { cfg, auth, users }) { 10 const secure = cfg.server.public_url.startsWith('https://'); 11 12 fastify.get('/mode', async (req, reply) => reply.send({ 13 mode: auth.mode, 14 local_login: auth.localLogin, 15 issuer: cfg.auth.oidc.issuer ?? null, 16 })); 17 18 fastify.post('/login', async (req, reply) => { 19 if (!auth.localLogin) { 20 return reply.code(400).send({ 21 error: 'this conductor authenticates through OIDC; obtain a token from the provider', 22 issuer: cfg.auth.oidc.issuer, 23 }); 24 } 25 26 const { username, password } = req.body ?? {}; 27 if (typeof username !== 'string' || typeof password !== 'string') { 28 return reply.code(400).send({ error: 'username and password are required' }); 29 } 30 31 const user = await users.authenticate(username, password); 32 // Deliberately the same answer for an unknown user and a wrong password. 33 if (!user) return reply.code(401).send({ error: 'invalid username or password' }); 34 35 const ttl = cfg.auth.session_ttl; 36 const token = issueToken(cfg.auth.session_secret, { 37 sub: user.id, 38 name: user.username, 39 role: user.role, 40 }, { ttl }); 41 42 reply.header('set-cookie', sessionCookie(token, { ttl, secure })); 43 return reply.send({ 44 token, 45 expires_in: ttl, 46 user: { id: user.id, username: user.username, role: user.role }, 47 }); 48 }); 49 50 fastify.post('/logout', async (req, reply) => { 51 // Tokens are stateless, so this clears the cookie and nothing more. A 52 // token already in hand stays valid until it expires. 53 reply.header('set-cookie', clearedCookie()); 54 return reply.send({ ok: true }); 55 }); 56 57 fastify.get('/me', async (req, reply) => { 58 const user = await auth.identify(req); 59 if (!user) return reply.code(401).send({ error: 'not authenticated' }); 60 return reply.send({ user }); 61 }); 62 }