Skip to content

Commit 40b3855

Browse files
committed
Add support for generic OAuth2 providers
Signed-off-by: Pedro Ferreira <pedro.ferreira@cern.ch>
1 parent 5d57a4b commit 40b3855

8 files changed

Lines changed: 138 additions & 4 deletions

File tree

lib/config/default.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ module.exports = {
7272
},
7373
s3bucket: undefined,
7474
// authentication
75+
oauth2: {
76+
authorizationURL: undefined,
77+
tokenURL: undefined,
78+
clientID: undefined,
79+
clientSecret: undefined
80+
},
7581
facebook: {
7682
clientID: undefined,
7783
clientSecret: undefined

lib/config/environment.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,17 @@ module.exports = {
6666
clientID: process.env.HMD_MATTERMOST_CLIENTID,
6767
clientSecret: process.env.HMD_MATTERMOST_CLIENTSECRET
6868
},
69+
oauth2: {
70+
baseURL: process.env.HMD_OAUTH2_BASEURL,
71+
userProfileURL: process.env.HMD_OAUTH2_USER_PROFILE_URL,
72+
userProfileUsernameAttr: process.env.HMD_OAUTH2_USER_PROFILE_USERNAME_ATTR,
73+
userProfileDisplayNameAttr: process.env.HMD_OAUTH2_USER_PROFILE_DISPLAY_NAME_ATTR,
74+
userProfileEmailAttr: process.env.HMD_OAUTH2_USER_PROFILE_EMAIL_ATTR,
75+
tokenURL: process.env.HMD_OAUTH2_TOKEN_URL,
76+
authorizationURL: process.env.HMD_OAUTH2_AUTHORIZATION_URL,
77+
clientID: process.env.HMD_OAUTH2_CLIENT_ID,
78+
clientSecret: process.env.HMD_OAUTH2_CLIENT_SECRET
79+
},
6980
dropbox: {
7081
clientID: process.env.HMD_DROPBOX_CLIENTID,
7182
clientSecret: process.env.HMD_DROPBOX_CLIENTSECRET,

lib/config/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ config.isGitLabEnable = config.gitlab.clientID && config.gitlab.clientSecret
9898
config.isMattermostEnable = config.mattermost.clientID && config.mattermost.clientSecret
9999
config.isLDAPEnable = config.ldap.url
100100
config.isSAMLEnable = config.saml.idpSsoUrl
101+
config.isOAuth2Enable = config.oauth2.clientID && config.oauth2.clientSecret
101102
config.isPDFExportEnable = config.allowPDFExport
102103

103104
// merge legacy values

lib/response.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ function showIndex (req, res, next) {
7070
ldap: config.isLDAPEnable,
7171
ldapProviderName: config.ldap.providerName,
7272
saml: config.isSAMLEnable,
73+
oauth2: config.isOAuth2Enable,
74+
oauth2ProviderName: config.oauth2.providerName,
7375
email: config.isEmailEnable,
7476
allowEmailRegister: config.allowEmailRegister,
7577
allowPDFExport: config.allowPDFExport,
@@ -104,7 +106,9 @@ function responseHackMD (res, note) {
104106
google: config.isGoogleEnable,
105107
ldap: config.isLDAPEnable,
106108
ldapProviderName: config.ldap.providerName,
109+
oauth2ProviderName: config.oauth2.providerName,
107110
saml: config.isSAMLEnable,
111+
oauth2: config.isOAuth2Enable,
108112
email: config.isEmailEnable,
109113
allowEmailRegister: config.allowEmailRegister,
110114
allowPDFExport: config.allowPDFExport

lib/web/auth/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ if (config.isDropboxEnable) authRouter.use(require('./dropbox'))
4343
if (config.isGoogleEnable) authRouter.use(require('./google'))
4444
if (config.isLDAPEnable) authRouter.use(require('./ldap'))
4545
if (config.isSAMLEnable) authRouter.use(require('./saml'))
46+
if (config.isOAuth2Enable) authRouter.use(require('./oauth2'))
4647
if (config.isEmailEnable) authRouter.use(require('./email'))
4748

4849
// logout

lib/web/auth/oauth2/index.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
'use strict'
2+
3+
const Router = require('express').Router
4+
const passport = require('passport')
5+
const OAuth2Strategy = require('passport-oauth2').Strategy
6+
const config = require('../../../config')
7+
const {setReturnToFromReferer, passportGeneralCallback} = require('../utils')
8+
9+
let oauth2Auth = module.exports = Router()
10+
11+
class OAuth2CustomStrategy extends OAuth2Strategy {
12+
constructor (options, verify) {
13+
options.customHeaders = options.customHeaders || {}
14+
super(options, verify)
15+
this.name = 'oauth2'
16+
this._userProfileURL = options.userProfileURL
17+
this._oauth2.useAuthorizationHeaderforGET(true)
18+
}
19+
20+
userProfile (accessToken, done) {
21+
this._oauth2.get(this._userProfileURL, accessToken, function (err, body, res) {
22+
var json
23+
24+
if (err) {
25+
return done(new passport.InternalOAuthError('Failed to fetch user profile', err))
26+
}
27+
28+
try {
29+
json = JSON.parse(body)
30+
} catch (ex) {
31+
return done(new Error('Failed to parse user profile'))
32+
}
33+
34+
let profile = parseProfile(json)
35+
profile.provider = 'oauth2'
36+
37+
done(null, profile)
38+
})
39+
}
40+
}
41+
42+
function extractProfileAttribute (data, path) {
43+
// can handle stuff like `attrs[0].name`
44+
path = path.split('.')
45+
for (const segment of path) {
46+
const m = segment.match(/([\d\w]+)\[(.*)\]/)
47+
data = m ? data[m[1]][m[2]] : data[segment]
48+
}
49+
return data
50+
}
51+
52+
function parseProfile (data) {
53+
const username = extractProfileAttribute(data, config.oauth2.userProfileUsernameAttr)
54+
const displayName = extractProfileAttribute(data, config.oauth2.userProfileDisplayNameAttr)
55+
const email = extractProfileAttribute(data, config.oauth2.userProfileEmailAttr)
56+
57+
return {
58+
id: username,
59+
username: username,
60+
displayName: displayName,
61+
email: email
62+
}
63+
}
64+
65+
OAuth2CustomStrategy.prototype.userProfile = function (accessToken, done) {
66+
this._oauth2.get(this._userProfileURL, accessToken, function (err, body, res) {
67+
var json
68+
69+
if (err) {
70+
return done(new passport.InternalOAuthError('Failed to fetch user profile', err))
71+
}
72+
73+
try {
74+
json = JSON.parse(body)
75+
} catch (ex) {
76+
return done(new Error('Failed to parse user profile'))
77+
}
78+
79+
let profile = parseProfile(json)
80+
profile.provider = 'oauth2'
81+
82+
done(null, profile)
83+
})
84+
}
85+
86+
passport.use(new OAuth2CustomStrategy({
87+
authorizationURL: config.oauth2.authorizationURL,
88+
tokenURL: config.oauth2.tokenURL,
89+
clientID: config.oauth2.clientID,
90+
clientSecret: config.oauth2.clientSecret,
91+
callbackURL: config.serverURL + '/auth/oauth2/callback',
92+
userProfileURL: config.oauth2.userProfileURL
93+
}, passportGeneralCallback))
94+
95+
oauth2Auth.get('/auth/oauth2', function (req, res, next) {
96+
setReturnToFromReferer(req)
97+
passport.authenticate('oauth2')(req, res, next)
98+
})
99+
100+
// github auth callback
101+
oauth2Auth.get('/auth/oauth2/callback',
102+
passport.authenticate('oauth2', {
103+
successReturnToOrRedirect: config.serverurl + '/',
104+
failureRedirect: config.serverurl + '/'
105+
})
106+
)

public/views/index/body.ejs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<% if(allowAnonymous) { %>
1616
<a type="button" href="<%- url %>/new" class="btn btn-sm btn-primary"><i class="fa fa-plus"></i> <%= __('New guest note') %></a>
1717
<% } %>
18-
<% if(facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || saml || email) { %>
18+
<% if(facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || saml || oauth2 || email) { %>
1919
<button class="btn btn-sm btn-success ui-signin" data-toggle="modal" data-target=".signin-modal"><%= __('Sign In') %></button>
2020
<% } %>
2121
</div>
@@ -49,7 +49,7 @@
4949
<% if (errorMessage && errorMessage.length > 0) { %>
5050
<div class="alert alert-danger" style="max-width: 400px; margin: 0 auto;"><%= errorMessage %></div>
5151
<% } %>
52-
<% if(facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || saml || email) { %>
52+
<% if(facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || saml || oauth2 || email) { %>
5353
<span class="ui-signin">
5454
<br>
5555
<a type="button" class="btn btn-lg btn-success ui-signin" data-toggle="modal" data-target=".signin-modal" style="min-width: 200px;"><%= __('Sign In') %></a>

public/views/shared/signin-modal.ejs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,12 @@
4848
<i class="fa fa-users"></i> <%= __('Sign in via %s', 'SAML') %>
4949
</a>
5050
<% } %>
51-
<% if((facebook || twitter || github || gitlab || mattermost || dropbox || google || saml) && ldap) { %>
51+
<% if(oauth2) { %>
52+
<a href="<%- url %>/auth/oauth2" class="btn btn-lg btn-block btn-social btn-soundcloud">
53+
<i class="fa fa-mail-forward"></i> <%= __('Sign in via %s', oauth2ProviderName || 'OAuth2') %>
54+
</a>
55+
<% } %>
56+
<% if((facebook || twitter || github || gitlab || mattermost || dropbox || google || saml || oauth2) && ldap) { %>
5257
<hr>
5358
<% }%>
5459
<% if(ldap) { %>
@@ -73,7 +78,7 @@
7378
</div>
7479
</form>
7580
<% } %>
76-
<% if((facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap) && email) { %>
81+
<% if((facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || oauth2) && email) { %>
7782
<hr>
7883
<% }%>
7984
<% if(email) { %>

0 commit comments

Comments
 (0)