import {Unit} from '../lifecycle/Unit' import {Injectable, Inject, StaticInstantiable} from '../di' import {Logging} from '../service/Logging' import {Middlewares} from '../service/Middlewares' import {CanonicalResolver} from '../service/Canonical' import {Middleware} from '../http/routing/Middleware' import {AuthRequiredMiddleware} from './middleware/AuthRequiredMiddleware' import {GuestRequiredMiddleware} from './middleware/GuestRequiredMiddleware' import {SessionAuthMiddleware} from './middleware/SessionAuthMiddleware' import {ViewEngine} from '../views/ViewEngine' import {SecurityContext} from './context/SecurityContext' import {LoginProvider, LoginProviderConfig} from './provider/LoginProvider' import {Config} from '../service/Config' import {ErrorWithContext, hasOwnProperty} from '../util' import {Route} from '../http/routing/Route' @Injectable() export class Authentication extends Unit { @Inject() protected readonly logging!: Logging @Inject() protected readonly middleware!: Middlewares @Inject() protected readonly config!: Config protected providers: {[name: string]: LoginProvider} = {} getProvider(name: string): LoginProvider { const provider = this.providers[name] if ( !provider ) { throw new ErrorWithContext('Invalid auth provider name: ' + name, { name }) } return provider } async up(): Promise { this.middleware.registerNamespace('@auth', this.getMiddlewareResolver()) this.container().onResolve(ViewEngine) .then((engine: ViewEngine) => { engine.registerGlobalFactory('user', req => { return () => req?.make(SecurityContext)?.getUser() }) }) const config = this.config.get('auth.providers', {}) const middleware = this.config.get('auth.middleware', SessionAuthMiddleware) if ( !(middleware?.prototype instanceof Middleware) ) { throw new ErrorWithContext('Auth middleware must extend Middleware base class', { providedValue: middleware, configKey: 'auth.middleware', }) } for ( const name in config ) { if ( !hasOwnProperty(config, name) ) { continue } if ( this.providers[name] ) { this.logging.warn(`Registering duplicate authentication provider: ${name}`) } this.logging.verbose(`Registered authentication provider: ${name}`) this.providers[name] = this.make(config[name].driver, name, config[name].config) Route.group(`/auth/${name}`, () => { this.providers[name].routes() }).pre(middleware) } } protected getMiddlewareResolver(): CanonicalResolver> { return (key: string) => { return ({ required: AuthRequiredMiddleware, guest: GuestRequiredMiddleware, web: SessionAuthMiddleware, })[key] } } }