2022-10-26 07:59:43 +00:00
|
|
|
const fs = require('fs')
|
2020-08-13 06:56:33 +00:00
|
|
|
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']
|
|
|
|
}
|
|
|
|
|
2022-10-26 07:59:43 +00:00
|
|
|
load_jwks(file) {
|
|
|
|
if ( fs.existsSync(file) ) {
|
|
|
|
const content = fs.readFileSync(file)
|
|
|
|
try {
|
|
|
|
return JSON.parse(content)
|
|
|
|
} catch (e) {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-13 06:56:33 +00:00
|
|
|
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)
|
|
|
|
|
2022-10-26 07:59:43 +00:00
|
|
|
const jwks_file = this.configs.get('oidc.jwks_file')
|
|
|
|
const jwks = this.load_jwks(jwks_file)
|
|
|
|
|
2020-08-13 06:56:33 +00:00
|
|
|
this.provider = new Provider(issuer, {
|
|
|
|
adapter: CoreIDAdapter,
|
|
|
|
clients: [],
|
2022-10-26 07:59:43 +00:00
|
|
|
jwks,
|
2020-08-13 06:56:33 +00:00
|
|
|
interactions: {
|
|
|
|
interactions,
|
2020-10-19 04:27:23 +00:00
|
|
|
url: (ctx, interaction) => `/openid/interaction/${ctx.oidc.uid.toLowerCase()}`,
|
2020-08-13 06:56:33 +00:00
|
|
|
},
|
|
|
|
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,
|
|
|
|
})
|
|
|
|
|
2021-12-03 08:45:02 +00:00
|
|
|
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)
|
|
|
|
|
2020-08-23 19:17:52 +00:00
|
|
|
if ( configuration.proxy ) this.provider.proxy = true
|
2020-08-13 06:56:33 +00:00
|
|
|
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
|