auth

Main authentication module for my projects
git clone git://git.finwo.net/app/auth
Log | Files | Refs

login.js (2623B)


      1 import { HttpError } from './http/http-error.js';
      2 import { findRealmByEmailDomain, openRealmDatabase } from './realm.js';
      3 import { getUserEmails, emailDomain } from './users.js';
      4 
      5 export function normalizeIdentifier(identifier) {
      6   const value = String(identifier || '').trim();
      7   if (!value) {
      8     throw new HttpError(400, 'invalid_request', 'An email address or username is required');
      9   }
     10   if (value.length > 254) {
     11     throw new HttpError(400, 'invalid_request', 'The identifier is too long');
     12   }
     13 
     14   const at = value.lastIndexOf('@');
     15   const isEmail = at > 0 && at < value.length - 1;
     16   const normalized = value.toLowerCase();
     17 
     18   return {
     19     value,
     20     normalized,
     21     isEmail,
     22     domain: isEmail ? normalized.slice(at + 1) : null,
     23   };
     24 }
     25 
     26 export async function getProviderForDomain(realm, domain) {
     27   const db = await openRealmDatabase(realm);
     28   return db.get(
     29     `SELECT p.slug, p.display_name, p.enabled
     30        FROM email_domain_providers d
     31        JOIN identity_providers p ON p.id = d.provider_id
     32       WHERE d.domain = {domain}
     33       LIMIT 1`,
     34     { domain }
     35   );
     36 }
     37 
     38 export async function resolveLogin({ identifier }) {
     39   const parsed = normalizeIdentifier(identifier);
     40   if (!parsed.isEmail) {
     41     return { next: 'password' };
     42   }
     43 
     44   const owner = await findRealmByEmailDomain(parsed.domain);
     45   if (!owner) {
     46     return { next: 'password' };
     47   }
     48 
     49   const provider = await getProviderForDomain(owner, parsed.domain);
     50   if (!provider) {
     51     return { next: 'password' };
     52   }
     53 
     54   if (!provider.enabled) {
     55     throw new HttpError(409, 'provider_disabled', 'The identity provider for this domain is unavailable');
     56   }
     57 
     58   return {
     59     next: 'oidc',
     60     provider: {
     61       slug: provider.slug,
     62       display_name: provider.display_name,
     63     },
     64   };
     65 }
     66 
     67 export async function findManagedProviderForUser(systemDb, user) {
     68   if (!user || !user.home_realm_id) return null;
     69 
     70   const homeRealm = await systemDb.get(
     71     'SELECT id, slug, db_file, owner_user_id FROM realms WHERE id = {id}',
     72     { id: user.home_realm_id }
     73   );
     74   if (!homeRealm) return null;
     75 
     76   const emails = await getUserEmails(systemDb, user.id);
     77   for (const entry of emails) {
     78     if (!entry.verified) continue;
     79     const domain = emailDomain(entry.email);
     80     if (!domain) continue;
     81 
     82     const owned = await systemDb.get(
     83       `SELECT domain
     84          FROM realm_email_domains
     85         WHERE domain = {domain} AND realm_id = {realmId} AND verified = 1`,
     86       { domain, realmId: homeRealm.id }
     87     );
     88     if (!owned) continue;
     89 
     90     const provider = await getProviderForDomain(homeRealm, domain);
     91     if (provider) return provider;
     92   }
     93 
     94   return null;
     95 }