diff --git a/README.md b/README.md index c0de2d42..8cf13534 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ The _authorization_ section of the config file has three keys: `anonRead`, `vali If `anonRead` is true, then anyone who can access the wiki can read anything. If `anonRead` is false you need to authenticate also for reading and then the email of the user _must_ match at least one of the regular expressions provided via validMatches, which is a comma separated list. There is no "anonWrite", though. To edit a page the user must be authenticated. -`emptyEmailMatches` allows access when remote authentication providers do not provide an email address as part of user data. It defaults to `false`, but will usually need to be set to `true` for GitHub authentication (GitHub only returns email addresses that have been made public on users' GitHub accounts). +`emptyEmailMatches` allows access when remote authentication providers do not provide an email address as part of user data. It defaults to `false`, but will usually need to be set to `true` for GitHub authentication (GitHub only returns email addresses that have been made public on users' GitHub accounts). It must be set to `true` for Mastodon authentication. The authentication is mandatory to edit pages from the web interface, but jingo works on a git repository; that means that you could skip the authentication altogether and edit pages with your editor and push to the remote that jingo is serving. @@ -290,7 +290,24 @@ Configuration options reference Values required for GitHub OAuth2 authentication. Refer to a previous section of this document on how to set them up. -#### authentication.google.redirectUrl (string: /auth/github/callback) +#### authentication.github.redirectUrl (string: /auth/github/callback) + + Specifies a custom redirect URL for OAuth2 authentication instead of the default + +#### authentication.mastodon.enabled (boolean: false) + + Enable or disable authentication via Mastodon logins + +#### authentication.mastodon.clientId +#### authentication.mastodon.clientSecret + + Values required for Mastodon OAuth2 authentication. Refer to a previous section of this document on how to set them up. + +#### authentication.mastodon.domain + + Instance of Mastodon used to authenticate logins + +#### authentication.mastodon.redirectUrl (string: /auth/mastodon/callback) Specifies a custom redirect URL for OAuth2 authentication instead of the default diff --git a/lib/config.js b/lib/config.js index 23a97061..e1e88c11 100644 --- a/lib/config.js +++ b/lib/config.js @@ -76,6 +76,13 @@ module.exports = (function () { clientSecret: 'replace me with the real value', redirectURL: '' }, + mastodon: { + enabled: false, + clientId: 'replace me with the real value', + clientSecret: 'replace me with the real value', + domain: 'your.mastodon.instance', + redirectURL: '' + }, ldap: { enabled: false, url: 'ldap://example.org:389', @@ -165,6 +172,7 @@ module.exports = (function () { if (!config.authentication.google.enabled && !config.authentication.github.enabled && + !config.authentication.mastodon.enabled && !config.authentication.ldap.enabled && !config.authentication.alone.enabled && !config.authentication.local.enabled @@ -183,6 +191,11 @@ module.exports = (function () { return false } + if (config.authentication.mastodon.enabled && (!config.authentication.mastodon.clientId || !config.authentication.mastodon.clientSecret || !config.authentication.mastodon.domain)) { + error = 'Invalid or missing authentication credentials for Mastodon (clientId and/or clientSecret and/or domain).' + return false + } + if (config.authentication.ldap.enabled && (!config.authentication.ldap.url || !config.authentication.ldap.searchBase || !config.authentication.ldap.searchFilter)) { error = 'Invalid or missing config for LDAP (url and/or searchBase and/or searchFilter).' return false diff --git a/package.json b/package.json index cf96317a..6302c24f 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "passport-github": "^0.1.5", "passport-google-oauth": "^0.1.5", "passport-local": "^1.0.0", + "passport-mastodon": "^0.1.3", "pug": "^2.0.0-rc.4", "semver": "^5.3.0", "serve-favicon": "^2.1.7", diff --git a/routes/auth.js b/routes/auth.js index 7863f910..02a927bd 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -4,6 +4,7 @@ var _ = require('lodash') var passportLocal = require('passport-local') var passportGoogle = require('passport-google-oauth') var passportGithub = require('passport-github').Strategy +var passportMastodon = require('passport-mastodon').Strategy var tools = require('../lib/tools') var auth = app.locals.config.get('authentication') @@ -16,7 +17,6 @@ if (auth.ldap.enabled) { var passport = app.locals.passport var proxyPath = app.locals.config.getProxyPath() -var redirectURL router.get('/login', _getLogin) router.get('/logout', _getLogout) @@ -27,31 +27,17 @@ router.post('/login', passport.authenticate('local', { })) router.get('/auth/done', _getAuthDone) -router.get('/auth/google', passport.authenticate('google', { - scope: ['https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile'] -})) - -router.get('/oauth2callback', passport.authenticate('google', { - successRedirect: proxyPath + '/auth/done', - failureRedirect: proxyPath + '/login' -})) - -router.get('/auth/github', passport.authenticate('github')) -router.get('/auth/github/callback', passport.authenticate('github', { - successRedirect: proxyPath + '/auth/done', - failureRedirect: proxyPath + '/login' -})) +if (auth.google.enabled) { + router.get('/auth/google', passport.authenticate('google', { + scope: ['https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile'] + })) -if (auth.ldap.enabled) { - router.post('/auth/ldap', passport.authenticate('ldapauth', { + router.get('/oauth2callback', passport.authenticate('google', { successRedirect: proxyPath + '/auth/done', - failureRedirect: proxyPath + '/login', - failureFlash: true + failureRedirect: proxyPath + '/login' })) -} -if (auth.google.enabled) { - redirectURL = auth.google.redirectURL || app.locals.baseUrl + '/oauth2callback' + var redirectURL = auth.google.redirectURL || app.locals.baseUrl + '/oauth2callback' passport.use(new passportGoogle.OAuth2Strategy({ clientID: auth.google.clientId, clientSecret: auth.google.clientSecret, @@ -68,7 +54,13 @@ if (auth.google.enabled) { } if (auth.github.enabled) { - redirectURL = auth.github.redirectURL || app.locals.baseUrl + '/auth/github/callback' + router.get('/auth/github', passport.authenticate('github')) + router.get('/auth/github/callback', passport.authenticate('github', { + successRedirect: proxyPath + '/auth/done', + failureRedirect: proxyPath + '/login' + })) + + var redirectURL = auth.github.redirectURL || app.locals.baseUrl + '/auth/github/callback' // Register a new Application with Github https://github.com/settings/applications/new // Authorization callback URL /auth/github/callback @@ -84,7 +76,37 @@ if (auth.github.enabled) { )) } +if (auth.mastodon.enabled) { + router.get('/auth/mastodon', passport.authenticate('mastodon')) + router.get('/auth/mastodon/callback', passport.authenticate('mastodon', { + successRedirect: proxyPath + '/auth/done', + failureRedirect: proxyPath + '/login' + })) + + var redirectURL = auth.mastodon.redirectURL || app.locals.baseUrl + '/auth/mastodon/callback' + // Register a new Application with Mastodon + // https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#apps + // Authorization callback URL /auth/mastodon/callback + passport.use(new passportMastodon({ // eslint-disable-line new-cap + clientID: auth.mastodon.clientId, + clientSecret: auth.mastodon.clientSecret, + domain: auth.mastodon.domain, + callbackURL: redirectURL + }, + function (accessToken, refreshToken, profile, done) { + usedAuthentication('mastodon') + done(null, profile) + } + )) +} + if (auth.ldap.enabled) { + router.post('/auth/ldap', passport.authenticate('ldapauth', { + successRedirect: proxyPath + '/auth/done', + failureRedirect: proxyPath + '/login', + failureFlash: true + })) + passport.use(new passportLDAP(function(req, callback) { process.nextTick(function() { var bindDn = auth.ldap.bindDn.replace(/{{username}}/g, req.body.username) diff --git a/views/login.pug b/views/login.pug index 5512c3c0..218941fa 100644 --- a/views/login.pug +++ b/views/login.pug @@ -16,7 +16,11 @@ block content p +anchor('/auth/github', 'Github login').btn-auth.btn-auth-github - if (auth.google.enabled || auth.github.enabled) + if (auth.mastodon.enabled) + p + +anchor('/auth/mastodon', 'Mastodon login').btn-default + + if (auth.google.enabled || auth.github.enabled || auth.mastodon.enabled) p +anchor("/", 'Cancel')