Add support for generic OAuth2 providers
Signed-off-by: Pedro Ferreira <pedro.ferreira@cern.ch>
This commit is contained in:
@@ -72,6 +72,12 @@ module.exports = {
|
||||
},
|
||||
s3bucket: undefined,
|
||||
// authentication
|
||||
oauth2: {
|
||||
authorizationURL: undefined,
|
||||
tokenURL: undefined,
|
||||
clientID: undefined,
|
||||
clientSecret: undefined
|
||||
},
|
||||
facebook: {
|
||||
clientID: undefined,
|
||||
clientSecret: undefined
|
||||
|
||||
@@ -66,6 +66,17 @@ module.exports = {
|
||||
clientID: process.env.HMD_MATTERMOST_CLIENTID,
|
||||
clientSecret: process.env.HMD_MATTERMOST_CLIENTSECRET
|
||||
},
|
||||
oauth2: {
|
||||
baseURL: process.env.HMD_OAUTH2_BASEURL,
|
||||
userProfileURL: process.env.HMD_OAUTH2_USER_PROFILE_URL,
|
||||
userProfileUsernameAttr: process.env.HMD_OAUTH2_USER_PROFILE_USERNAME_ATTR,
|
||||
userProfileDisplayNameAttr: process.env.HMD_OAUTH2_USER_PROFILE_DISPLAY_NAME_ATTR,
|
||||
userProfileEmailAttr: process.env.HMD_OAUTH2_USER_PROFILE_EMAIL_ATTR,
|
||||
tokenURL: process.env.HMD_OAUTH2_TOKEN_URL,
|
||||
authorizationURL: process.env.HMD_OAUTH2_AUTHORIZATION_URL,
|
||||
clientID: process.env.HMD_OAUTH2_CLIENT_ID,
|
||||
clientSecret: process.env.HMD_OAUTH2_CLIENT_SECRET
|
||||
},
|
||||
dropbox: {
|
||||
clientID: process.env.HMD_DROPBOX_CLIENTID,
|
||||
clientSecret: process.env.HMD_DROPBOX_CLIENTSECRET,
|
||||
|
||||
@@ -98,6 +98,7 @@ config.isGitLabEnable = config.gitlab.clientID && config.gitlab.clientSecret
|
||||
config.isMattermostEnable = config.mattermost.clientID && config.mattermost.clientSecret
|
||||
config.isLDAPEnable = config.ldap.url
|
||||
config.isSAMLEnable = config.saml.idpSsoUrl
|
||||
config.isOAuth2Enable = config.oauth2.clientID && config.oauth2.clientSecret
|
||||
config.isPDFExportEnable = config.allowPDFExport
|
||||
|
||||
// merge legacy values
|
||||
|
||||
@@ -70,6 +70,8 @@ function showIndex (req, res, next) {
|
||||
ldap: config.isLDAPEnable,
|
||||
ldapProviderName: config.ldap.providerName,
|
||||
saml: config.isSAMLEnable,
|
||||
oauth2: config.isOAuth2Enable,
|
||||
oauth2ProviderName: config.oauth2.providerName,
|
||||
email: config.isEmailEnable,
|
||||
allowEmailRegister: config.allowEmailRegister,
|
||||
allowPDFExport: config.allowPDFExport,
|
||||
@@ -104,7 +106,9 @@ function responseHackMD (res, note) {
|
||||
google: config.isGoogleEnable,
|
||||
ldap: config.isLDAPEnable,
|
||||
ldapProviderName: config.ldap.providerName,
|
||||
oauth2ProviderName: config.oauth2.providerName,
|
||||
saml: config.isSAMLEnable,
|
||||
oauth2: config.isOAuth2Enable,
|
||||
email: config.isEmailEnable,
|
||||
allowEmailRegister: config.allowEmailRegister,
|
||||
allowPDFExport: config.allowPDFExport
|
||||
|
||||
@@ -43,6 +43,7 @@ if (config.isDropboxEnable) authRouter.use(require('./dropbox'))
|
||||
if (config.isGoogleEnable) authRouter.use(require('./google'))
|
||||
if (config.isLDAPEnable) authRouter.use(require('./ldap'))
|
||||
if (config.isSAMLEnable) authRouter.use(require('./saml'))
|
||||
if (config.isOAuth2Enable) authRouter.use(require('./oauth2'))
|
||||
if (config.isEmailEnable) authRouter.use(require('./email'))
|
||||
|
||||
// logout
|
||||
|
||||
106
lib/web/auth/oauth2/index.js
Normal file
106
lib/web/auth/oauth2/index.js
Normal file
@@ -0,0 +1,106 @@
|
||||
'use strict'
|
||||
|
||||
const Router = require('express').Router
|
||||
const passport = require('passport')
|
||||
const OAuth2Strategy = require('passport-oauth2').Strategy
|
||||
const config = require('../../../config')
|
||||
const {setReturnToFromReferer, passportGeneralCallback} = require('../utils')
|
||||
|
||||
let oauth2Auth = module.exports = Router()
|
||||
|
||||
class OAuth2CustomStrategy extends OAuth2Strategy {
|
||||
constructor (options, verify) {
|
||||
options.customHeaders = options.customHeaders || {}
|
||||
super(options, verify)
|
||||
this.name = 'oauth2'
|
||||
this._userProfileURL = options.userProfileURL
|
||||
this._oauth2.useAuthorizationHeaderforGET(true)
|
||||
}
|
||||
|
||||
userProfile (accessToken, done) {
|
||||
this._oauth2.get(this._userProfileURL, accessToken, function (err, body, res) {
|
||||
var json
|
||||
|
||||
if (err) {
|
||||
return done(new passport.InternalOAuthError('Failed to fetch user profile', err))
|
||||
}
|
||||
|
||||
try {
|
||||
json = JSON.parse(body)
|
||||
} catch (ex) {
|
||||
return done(new Error('Failed to parse user profile'))
|
||||
}
|
||||
|
||||
let profile = parseProfile(json)
|
||||
profile.provider = 'oauth2'
|
||||
|
||||
done(null, profile)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function extractProfileAttribute (data, path) {
|
||||
// can handle stuff like `attrs[0].name`
|
||||
path = path.split('.')
|
||||
for (const segment of path) {
|
||||
const m = segment.match(/([\d\w]+)\[(.*)\]/)
|
||||
data = m ? data[m[1]][m[2]] : data[segment]
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
function parseProfile (data) {
|
||||
const username = extractProfileAttribute(data, config.oauth2.userProfileUsernameAttr)
|
||||
const displayName = extractProfileAttribute(data, config.oauth2.userProfileDisplayNameAttr)
|
||||
const email = extractProfileAttribute(data, config.oauth2.userProfileEmailAttr)
|
||||
|
||||
return {
|
||||
id: username,
|
||||
username: username,
|
||||
displayName: displayName,
|
||||
email: email
|
||||
}
|
||||
}
|
||||
|
||||
OAuth2CustomStrategy.prototype.userProfile = function (accessToken, done) {
|
||||
this._oauth2.get(this._userProfileURL, accessToken, function (err, body, res) {
|
||||
var json
|
||||
|
||||
if (err) {
|
||||
return done(new passport.InternalOAuthError('Failed to fetch user profile', err))
|
||||
}
|
||||
|
||||
try {
|
||||
json = JSON.parse(body)
|
||||
} catch (ex) {
|
||||
return done(new Error('Failed to parse user profile'))
|
||||
}
|
||||
|
||||
let profile = parseProfile(json)
|
||||
profile.provider = 'oauth2'
|
||||
|
||||
done(null, profile)
|
||||
})
|
||||
}
|
||||
|
||||
passport.use(new OAuth2CustomStrategy({
|
||||
authorizationURL: config.oauth2.authorizationURL,
|
||||
tokenURL: config.oauth2.tokenURL,
|
||||
clientID: config.oauth2.clientID,
|
||||
clientSecret: config.oauth2.clientSecret,
|
||||
callbackURL: config.serverURL + '/auth/oauth2/callback',
|
||||
userProfileURL: config.oauth2.userProfileURL
|
||||
}, passportGeneralCallback))
|
||||
|
||||
oauth2Auth.get('/auth/oauth2', function (req, res, next) {
|
||||
setReturnToFromReferer(req)
|
||||
passport.authenticate('oauth2')(req, res, next)
|
||||
})
|
||||
|
||||
// github auth callback
|
||||
oauth2Auth.get('/auth/oauth2/callback',
|
||||
passport.authenticate('oauth2', {
|
||||
successReturnToOrRedirect: config.serverurl + '/',
|
||||
failureRedirect: config.serverurl + '/'
|
||||
})
|
||||
)
|
||||
Reference in New Issue
Block a user