conductor

CI task system
git clone git://git.finwo.net/app/conductor
Log | Files | Refs | README | LICENSE

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 }