UNPKG

@wepublish/oauth2

Version:
162 lines (139 loc) 4.22 kB
import {PrismaClient} from '@prisma/client' import {logger} from '@wepublish/api' import express, {Application, NextFunction} from 'express' import set from 'lodash/set' import {Provider} from 'oidc-provider' import path from 'path' import pino from 'pino' import pinoHttp from 'pino-http' import url from 'url' import {MongoDBAdapter as OAuth2DBAdapter} from './adapter' import {configuration} from './configuration' import {routes} from './routes' export interface OAuth2ServerOpts { readonly clientID: string readonly clientSecret: string readonly grantTypes: string[] readonly redirectUris: string[] readonly cookieKeys: string[] readonly jwksKeys: any readonly issuer: string readonly mongoUrlOauth2: string readonly viewPath?: string readonly debug?: boolean readonly logger?: pino.Logger } let serverLogger: pino.Logger const ONE_DAY_IN_MS = 1 * 24 * 60 * 60 * 1000 export class Oauth2Server { private readonly app: Application private readonly opts: OAuth2ServerOpts private readonly prisma: PrismaClient constructor(opts: OAuth2ServerOpts) { const app = express() this.opts = opts this.prisma = new PrismaClient() this.prisma.$connect() serverLogger = opts.logger ? opts.logger : pino({name: 'oauth2'}) /* const corsOptions = { origin: '*', allowedHeaders: [ 'authorization', 'content-type', 'content-length', 'accept', 'origin', 'user-agent' ], methods: ['POST', 'GET', 'OPTIONS'] } */ app.use( pinoHttp({ logger: serverLogger, useLevel: 'debug' }) ) app.use((err: any, req: Express.Request, res: Express.Response, next: NextFunction) => { logger('server').error(err) return next(err) }) if (opts.viewPath) { app.set('views', opts.viewPath) } else { app.set('views', path.join(__dirname, '..', 'views')) } app.set('view engine', 'ejs') this.app = app } async findAccount(ctx: any, id: any) { const user = await this.prisma.user.findUnique({ where: {id} }) if (user) { return { accountId: user.id, email: user.email, async claims(use: any, scope: any) { console.log('claims', use, scope) return {sub: user.email, email: user.email} } } } else { throw new Error('did not find user') } } async listen(port?: number, hostname?: string): Promise<void> { await OAuth2DBAdapter.initialize(this.opts.mongoUrlOauth2, 'en') await OAuth2DBAdapter.connect(this.opts.mongoUrlOauth2) const config = { adapter: OAuth2DBAdapter, clients: [ { client_id: this.opts.clientID, client_secret: this.opts.clientSecret, grant_types: this.opts.grantTypes, redirect_uris: this.opts.redirectUris } ], cookies: { long: {signed: true, maxAge: ONE_DAY_IN_MS}, short: {signed: true}, keys: this.opts.cookieKeys }, jwks: { keys: this.opts.jwksKeys }, findAccount: this.findAccount.bind(this), ...configuration } const provider = new Provider(this.opts.issuer, config) if (process.env.NODE_ENV === 'production') { this.app.enable('trust proxy') provider.proxy = true set(configuration, 'cookies.short.secure', true) set(configuration, 'cookies.long.secure', true) this.app.use((req, res, next) => { if (req.secure) { next() } else if (req.method === 'GET' || req.method === 'HEAD') { res.redirect( url.format({ protocol: 'https', host: req.get('host'), pathname: req.originalUrl }) ) } else { res.status(400).json({ error: 'invalid_request', error_description: 'do yourself a favor and only use https' }) } }) } routes(this.app, provider, this.prisma) this.app.use(provider.callback) console.log('views_path', path.join(__dirname, 'views')) this.app.listen(port ?? 4200, hostname ?? 'localhost') } }