summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoph (Sheogorath) Kern2018-06-04 15:54:47 +0200
committerGitHub2018-06-04 15:54:47 +0200
commit551840ad57125fa168247c21613f9d6de6cd5d99 (patch)
tree51dda6767b42acc445fb169ea8ea6b4fe19050ac
parent3f7a33df646c4db3de449bd486a7dfddc9ba6e6b (diff)
parent34df7ccce83bebec1db3b6995eb25cb6fe4fdbd4 (diff)
Merge pull request #784 from pferreir/add-oauth2-support
Add "generic" OAuth2 support
-rw-r--r--lib/config/default.js6
-rw-r--r--lib/config/environment.js11
-rw-r--r--lib/config/index.js1
-rw-r--r--lib/migrations/20180326103000-use-text-in-tokens.js23
-rw-r--r--lib/models/user.js4
-rw-r--r--lib/response.js4
-rw-r--r--lib/web/auth/index.js1
-rw-r--r--lib/web/auth/oauth2/index.js106
-rw-r--r--public/views/index/body.ejs4
-rw-r--r--public/views/shared/signin-modal.ejs9
10 files changed, 163 insertions, 6 deletions
diff --git a/lib/config/default.js b/lib/config/default.js
index 30ce2090..1b124b3e 100644
--- a/lib/config/default.js
+++ b/lib/config/default.js
@@ -79,6 +79,12 @@ module.exports = {
container: undefined
},
// authentication
+ oauth2: {
+ authorizationURL: undefined,
+ tokenURL: undefined,
+ clientID: undefined,
+ clientSecret: undefined
+ },
facebook: {
clientID: undefined,
clientSecret: undefined
diff --git a/lib/config/environment.js b/lib/config/environment.js
index 810cb225..e1c11569 100644
--- a/lib/config/environment.js
+++ b/lib/config/environment.js
@@ -72,6 +72,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,
diff --git a/lib/config/index.js b/lib/config/index.js
index f10eadb8..484301c4 100644
--- a/lib/config/index.js
+++ b/lib/config/index.js
@@ -99,6 +99,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
diff --git a/lib/migrations/20180326103000-use-text-in-tokens.js b/lib/migrations/20180326103000-use-text-in-tokens.js
new file mode 100644
index 00000000..f9507747
--- /dev/null
+++ b/lib/migrations/20180326103000-use-text-in-tokens.js
@@ -0,0 +1,23 @@
+'use strict'
+
+module.exports = {
+ up: function (queryInterface, Sequelize) {
+ return queryInterface.changeColumn('Users', 'accessToken', {
+ type: Sequelize.TEXT
+ }).then(function () {
+ return queryInterface.changeColumn('Users', 'refreshToken', {
+ type: Sequelize.TEXT
+ })
+ })
+ },
+
+ down: function (queryInterface, Sequelize) {
+ return queryInterface.changeColumn('Users', 'accessToken', {
+ type: Sequelize.STRING
+ }).then(function () {
+ return queryInterface.changeColumn('Users', 'refreshToken', {
+ type: Sequelize.STRING
+ })
+ })
+ }
+}
diff --git a/lib/models/user.js b/lib/models/user.js
index 4c823355..0f6acd65 100644
--- a/lib/models/user.js
+++ b/lib/models/user.js
@@ -26,10 +26,10 @@ module.exports = function (sequelize, DataTypes) {
type: DataTypes.TEXT
},
accessToken: {
- type: DataTypes.STRING
+ type: DataTypes.TEXT
},
refreshToken: {
- type: DataTypes.STRING
+ type: DataTypes.TEXT
},
email: {
type: Sequelize.TEXT,
diff --git a/lib/response.js b/lib/response.js
index d6fb3b42..89ade7a7 100644
--- a/lib/response.js
+++ b/lib/response.js
@@ -76,6 +76,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,
@@ -110,7 +112,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
diff --git a/lib/web/auth/index.js b/lib/web/auth/index.js
index eb42fb36..61e7c3f9 100644
--- a/lib/web/auth/index.js
+++ b/lib/web/auth/index.js
@@ -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
diff --git a/lib/web/auth/oauth2/index.js b/lib/web/auth/oauth2/index.js
new file mode 100644
index 00000000..f2a3132d
--- /dev/null
+++ b/lib/web/auth/oauth2/index.js
@@ -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 + '/'
+ })
+)
diff --git a/public/views/index/body.ejs b/public/views/index/body.ejs
index d8766fec..bf7988b7 100644
--- a/public/views/index/body.ejs
+++ b/public/views/index/body.ejs
@@ -15,7 +15,7 @@
<% if(allowAnonymous) { %>
<a type="button" href="<%- url %>/new" class="btn btn-sm btn-primary"><i class="fa fa-plus"></i> <%= __('New guest note') %></a>
<% } %>
- <% if(facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || saml || email) { %>
+ <% if(facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || saml || oauth2 || email) { %>
<button class="btn btn-sm btn-success ui-signin" data-toggle="modal" data-target=".signin-modal"><%= __('Sign In') %></button>
<% } %>
</div>
@@ -49,7 +49,7 @@
<% if (errorMessage && errorMessage.length > 0) { %>
<div class="alert alert-danger" style="max-width: 400px; margin: 0 auto;"><%= errorMessage %></div>
<% } %>
- <% if(facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || saml || email) { %>
+ <% if(facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || saml || oauth2 || email) { %>
<span class="ui-signin">
<br>
<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>
diff --git a/public/views/shared/signin-modal.ejs b/public/views/shared/signin-modal.ejs
index 82b5cf1f..f0cffad9 100644
--- a/public/views/shared/signin-modal.ejs
+++ b/public/views/shared/signin-modal.ejs
@@ -48,7 +48,12 @@
<i class="fa fa-users"></i> <%= __('Sign in via %s', 'SAML') %>
</a>
<% } %>
- <% if((facebook || twitter || github || gitlab || mattermost || dropbox || google || saml) && ldap) { %>
+ <% if(oauth2) { %>
+ <a href="<%- url %>/auth/oauth2" class="btn btn-lg btn-block btn-social btn-soundcloud">
+ <i class="fa fa-mail-forward"></i> <%= __('Sign in via %s', oauth2ProviderName || 'OAuth2') %>
+ </a>
+ <% } %>
+ <% if((facebook || twitter || github || gitlab || mattermost || dropbox || google || saml || oauth2) && ldap) { %>
<hr>
<% }%>
<% if(ldap) { %>
@@ -73,7 +78,7 @@
</div>
</form>
<% } %>
- <% if((facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap) && email) { %>
+ <% if((facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || oauth2) && email) { %>
<hr>
<% }%>
<% if(email) { %>