const Unit = require('libflitter/Unit') const { Provider, interactionPolicy: { Prompt, base: policy } } = require('oidc-provider') const uuid = require('uuid').v4 const CoreIDAdapter = require('../classes/oidc/CoreIDAdapter') const RequestLocalizationHelper = require('flitter-i18n/src/RequestLocalizationHelper') const ResponseSystemMiddleware = require('libflitter/routing/ResponseSystemMiddleware') class OpenIDConnectUnit extends Unit { static get name() { return 'openid_connect' } static get services() { return [...super.services, 'output', 'configs', 'models'] } async go(app) { this.Vue = this.app.di().get('Vue') const issuer = this.configs.get('app.url') const configuration = this.configs.get('oidc.provider') const interactions = policy() const User = this.models.get('auth:User') CoreIDAdapter.connect(app) this.provider = new Provider(issuer, { adapter: CoreIDAdapter, clients: [], interactions: { interactions, url: (ctx, interaction) => `/openid/interaction/${ctx.oidc.uid.toLowerCase()}`, }, cookies: { long: { signed: true, maxAge: 24 * 60 * 60 * 1000 }, // 1 day, ms short: { signed: true }, keys: [this.configs.get('server.session.secret') || uuid()] }, claims: { email: ['email'], profile: [ 'first_name', 'last_name', 'picture', 'tagline', 'username', ], }, features: { devInteractions: { enabled: false }, deviceFlow: { enabled: true }, introspection: { enabled: true }, revocation: { enabled: true }, }, ttl: { AccessToken: 60 * 60, // 1 hour in seconds AuthorizationCode: 10 * 60, // 10 minutes in seconds IdToken: 60 * 60, // 1 hour in seconds DeviceCode: 10 * 60, // 10 minutes in seconds RefreshToken: 24 * 60 * 60, // 1 day in seconds }, findAccount: (...args) => User.findAccount(...args), ...configuration, }) const reportError = ({ headers: { authorization }, oidc: { body, client } }, err) => { this.output.error('OpenIDConnect authorization error!') this.output.error(err) } this.provider.on('grant.error', reportError) this.provider.on('introspection.error', reportError) this.provider.on('revocation.error', reportError) if ( configuration.proxy ) this.provider.proxy = true app.express.use('/oidc', this.wrap(this.provider.callback)) } wrap(callback) { return async (req, res, next) => { const client_id = req?.query?.client_id // Provide some basic Flitter niceties in the request req.i18n = new RequestLocalizationHelper(req, res) new ResponseSystemMiddleware(this.app, res, req) // If we got a client ID, make sure the current user has access to it if ( req?.session?.auth?.user_id && client_id ) { const User = this.models.get('auth:User') const Application = this.models.get('Application') const Policy = this.models.get('iam:Policy') const user = await User.findById(req.session.auth.user_id) const application = await Application.findOne({ openid_client_ids: client_id }) if ( !application ) { return this.Vue.auth_message(res, { message: req.T('saml.no_access').replace('APP_NAME', 'this application'), next_destination: '/dash', }) } else if ( !(await Policy.check_user_access(user, application.id)) ) { return this.Vue.auth_message(res, { message: req.T('saml.no_access').replace('APP_NAME', application.name), next_destination: '/dash', }) } } return callback(req, res, next) } } } module.exports = exports = OpenIDConnectUnit