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 }