Merge #32 'Add generic OAuth support'
This commit is contained in:
59
docs/config-oauth2.md
Normal file
59
docs/config-oauth2.md
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
## Generic OAuth 2
|
||||||
|
|
||||||
|
### Login provider set-up
|
||||||
|
|
||||||
|
Like the other PassportJS login providers, we'll need a separate "OAuth2 Client"
|
||||||
|
(others call it an "app", a "product" etc.) for our Federated Wiki instance.
|
||||||
|
|
||||||
|
How to do this varies slightly for each provider.
|
||||||
|
|
||||||
|
### `config.json`
|
||||||
|
|
||||||
|
In general, you will need to specify:
|
||||||
|
* `oauth2_clientID` -- some systems generate this for you, others allow you to
|
||||||
|
specify it
|
||||||
|
* `oauth2_clientSecret` -- secure key (keep this secret!)
|
||||||
|
* `oauth2_AuthorizationURL` and `oauth2_TokenURL` -- from your login provider's documentation
|
||||||
|
|
||||||
|
You will also need to specify a callback URL. For some providers, you can add
|
||||||
|
this when making a new "OAuth Client" for your wiki, for others you will need to
|
||||||
|
specify it with `oauth2_CallbackURL`.
|
||||||
|
|
||||||
|
You might also need to tell Federated Wiki how to look up usernames:
|
||||||
|
* `oauth2_UserInfoURL` -- from login provider's documentation
|
||||||
|
* `oauth2_IdField`, `oauth2_DisplayNameField`, `oauth2_UsernameField` -- starting with
|
||||||
|
* `params` for information returned in the original token request, or
|
||||||
|
* `profile` for data returned from `oauth2_UserInfoURL`, if you provided it.
|
||||||
|
|
||||||
|
Sometimes, you'll be able to look up the URLs by visiting your provider's
|
||||||
|
`/.well-known/openid-configuration` URL in a web browser.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
#### Nextcloud
|
||||||
|
|
||||||
|
```JSON
|
||||||
|
{
|
||||||
|
"farm": true,
|
||||||
|
"security_type": "passportjs",
|
||||||
|
"oauth2_clientID": "CLIENT ID",
|
||||||
|
"oauth2_clientSecret": "CLIENT SECRET",
|
||||||
|
"oauth2_AuthorizationURL": "https://auth.example.com/oauth2/authorize",
|
||||||
|
"oauth2_TokenURL": "https://auth.example.com/oauth2/token",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Keycloak
|
||||||
|
|
||||||
|
```JSON
|
||||||
|
{
|
||||||
|
"farm": true,
|
||||||
|
"security_type": "passportjs",
|
||||||
|
"oauth2_clientID": "CLIENT ID",
|
||||||
|
"oauth2_clientSecret": "CLIENT SECRET",
|
||||||
|
"oauth2_AuthorizationURL": "https://auth.example.com/auth/realms/Wiki.Cafe/protocol/openid-connect/auth",
|
||||||
|
"oauth2_TokenURL": "https://auth.example.com/auth/realms/Wiki.Cafe/protocol/openid-connect/token",
|
||||||
|
"oauth2_UserInfoURL": "https://auth.example.com/auth/realms/Wiki.Cafe/protocol/openid-connect/userinfo",
|
||||||
|
"oauth2_UsernameField": "profile.preferred_username"
|
||||||
|
}
|
||||||
|
```
|
@ -17,3 +17,4 @@ See, depending on which identity provider you choose to use:
|
|||||||
* [GitHub](./config-github.md)
|
* [GitHub](./config-github.md)
|
||||||
* [Google](./config-google.md)
|
* [Google](./config-google.md)
|
||||||
* [Twitter](./config-twitter.md)
|
* [Twitter](./config-twitter.md)
|
||||||
|
* [Generic OAuth](./config-oauth2.md)
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"passport": "^0.3.2",
|
"passport": "^0.3.2",
|
||||||
"passport-github": "github:FedWiki/passport-github#70fe99ba7e66422092a19d89f4b1ab1a23eac644",
|
"passport-github": "github:FedWiki/passport-github#70fe99ba7e66422092a19d89f4b1ab1a23eac644",
|
||||||
"passport-google-oauth20": "^2.0.0",
|
"passport-google-oauth20": "^2.0.0",
|
||||||
|
"passport-oauth2": "^1.6.1",
|
||||||
"passport-twitter": "^1.0.4",
|
"passport-twitter": "^1.0.4",
|
||||||
"persona-pass": "^0.2.1",
|
"persona-pass": "^0.2.1",
|
||||||
"qs": "^6.7.0",
|
"qs": "^6.7.0",
|
||||||
|
@ -135,7 +135,7 @@ module.exports = exports = (log, loga, argv) ->
|
|||||||
idProvider = _.head(_.keys(req.session.passport.user))
|
idProvider = _.head(_.keys(req.session.passport.user))
|
||||||
console.log 'idProvider: ', idProvider
|
console.log 'idProvider: ', idProvider
|
||||||
switch idProvider
|
switch idProvider
|
||||||
when 'github', 'google', 'twitter'
|
when 'github', 'google', 'twitter', 'oauth2'
|
||||||
if _.isEqual(owner[idProvider].id, req.session.passport.user[idProvider].id)
|
if _.isEqual(owner[idProvider].id, req.session.passport.user[idProvider].id)
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
@ -165,7 +165,7 @@ module.exports = exports = (log, loga, argv) ->
|
|||||||
return false
|
return false
|
||||||
|
|
||||||
switch idProvider
|
switch idProvider
|
||||||
when "github", "google", "twitter"
|
when "github", "google", "twitter", 'oauth2'
|
||||||
if _.isEqual(admin[idProvider], req.session.passport.user[idProvider].id)
|
if _.isEqual(admin[idProvider], req.session.passport.user[idProvider].id)
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
@ -194,6 +194,75 @@ module.exports = exports = (log, loga, argv) ->
|
|||||||
passport.deserializeUser = (obj, req, done) ->
|
passport.deserializeUser = (obj, req, done) ->
|
||||||
done(null, obj)
|
done(null, obj)
|
||||||
|
|
||||||
|
# OAuth Strategy
|
||||||
|
if argv.oauth2_clientID? and argv.oauth2_clientSecret?
|
||||||
|
ids.push('oauth2')
|
||||||
|
OAuth2Strategy = require('passport-oauth2').Strategy
|
||||||
|
|
||||||
|
oauth2StrategyName = callbackHost + 'OAuth'
|
||||||
|
|
||||||
|
if argv.oauth2_UserInfoURL?
|
||||||
|
OAuth2Strategy::userProfile = (accesstoken, done) ->
|
||||||
|
console.log "hello"
|
||||||
|
console.log accesstoken
|
||||||
|
@_oauth2._request "GET", argv.oauth2_UserInfoURL, null, null, accesstoken, (err, data) ->
|
||||||
|
if err
|
||||||
|
return done err
|
||||||
|
try
|
||||||
|
data = JSON.parse data
|
||||||
|
catch e
|
||||||
|
return done e
|
||||||
|
done(null, data)
|
||||||
|
|
||||||
|
passport.use(oauth2StrategyName, new OAuth2Strategy({
|
||||||
|
clientID: argv.oauth2_clientID
|
||||||
|
clientSecret: argv.oauth2_clientSecret
|
||||||
|
authorizationURL: argv.oauth2_AuthorizationURL
|
||||||
|
tokenURL: argv.oauth2_TokenURL,
|
||||||
|
# not all providers have a way of specifying the callback URL
|
||||||
|
callbackURL: callbackProtocol + '//' + callbackHost + '/auth/oauth2/callback',
|
||||||
|
userInfoURL: argv.oauth2_UserInfoURL
|
||||||
|
}, (accessToken, refreshToken, params, profile, cb) ->
|
||||||
|
|
||||||
|
extractUserInfo = (uiParam, uiDef) ->
|
||||||
|
uiPath = ''
|
||||||
|
if typeof uiParam == 'undefined' then (uiPath = uiDef) else (uiPath = uiParam)
|
||||||
|
console.log('extractUI', uiParam, uiDef, uiPath)
|
||||||
|
sParts = uiPath.split('.')
|
||||||
|
sFrom = sParts.shift()
|
||||||
|
switch sFrom
|
||||||
|
when "params"
|
||||||
|
obj = params
|
||||||
|
when "profile"
|
||||||
|
obj = profile
|
||||||
|
else
|
||||||
|
console.error('*** source of user info not recognised', uiPath)
|
||||||
|
obj = {}
|
||||||
|
|
||||||
|
while (sParts.length)
|
||||||
|
obj = obj[sParts.shift()]
|
||||||
|
return obj
|
||||||
|
|
||||||
|
console.log("accessToken", accessToken)
|
||||||
|
console.log("refreshToken", refreshToken)
|
||||||
|
console.log("params", params)
|
||||||
|
console.log("profile", profile)
|
||||||
|
if argv.oauth2_UsernameField?
|
||||||
|
username_query = argv.oauth2_UsernameField
|
||||||
|
else
|
||||||
|
username_query = 'params.user_id'
|
||||||
|
|
||||||
|
try
|
||||||
|
user.oauth2 = {
|
||||||
|
id: extractUserInfo(argv.oauth2_IdField, 'params.user_id')
|
||||||
|
username: extractUserInfo(argv.oauth2_UsernameField, 'params.user_id')
|
||||||
|
displayName: extractUserInfo(argv.oauth2_DisplayNameField, 'params.user_id')
|
||||||
|
}
|
||||||
|
catch e
|
||||||
|
console.error('*** Error extracting user info:', e)
|
||||||
|
console.log user.oauth2
|
||||||
|
cb(null, user)))
|
||||||
|
|
||||||
# Github Strategy
|
# Github Strategy
|
||||||
if argv.github_clientID? and argv.github_clientSecret?
|
if argv.github_clientID? and argv.github_clientSecret?
|
||||||
ids.push('github')
|
ids.push('github')
|
||||||
@ -275,6 +344,11 @@ module.exports = exports = (log, loga, argv) ->
|
|||||||
app.use(passport.initialize())
|
app.use(passport.initialize())
|
||||||
app.use(passport.session())
|
app.use(passport.session())
|
||||||
|
|
||||||
|
# OAuth2
|
||||||
|
app.get('/auth/oauth2', passport.authenticate(oauth2StrategyName), (req, res) -> )
|
||||||
|
app.get('/auth/oauth2/callback',
|
||||||
|
passport.authenticate(oauth2StrategyName, { successRedirect: '/auth/loginDone', failureRedirect: '/auth/loginDialog'}))
|
||||||
|
|
||||||
# Github
|
# Github
|
||||||
app.get('/auth/github', passport.authenticate(githubStrategyName, {scope: 'user:email'}), (req, res) -> )
|
app.get('/auth/github', passport.authenticate(githubStrategyName, {scope: 'user:email'}), (req, res) -> )
|
||||||
app.get('/auth/github/callback',
|
app.get('/auth/github/callback',
|
||||||
@ -317,6 +391,7 @@ module.exports = exports = (log, loga, argv) ->
|
|||||||
schemeButtons = []
|
schemeButtons = []
|
||||||
_(ids).forEach (scheme) ->
|
_(ids).forEach (scheme) ->
|
||||||
switch scheme
|
switch scheme
|
||||||
|
when "oauth2" then schemeButtons.push({button: "<a href='/auth/oauth2' class='scheme-button oauth2-button'><span>OAuth2</span></a>"})
|
||||||
when "twitter" then schemeButtons.push({button: "<a href='/auth/twitter' class='scheme-button twitter-button'><span>Twitter</span></a>"})
|
when "twitter" then schemeButtons.push({button: "<a href='/auth/twitter' class='scheme-button twitter-button'><span>Twitter</span></a>"})
|
||||||
when "github" then schemeButtons.push({button: "<a href='/auth/github' class='scheme-button github-button'><span>Github</span></a>"})
|
when "github" then schemeButtons.push({button: "<a href='/auth/github' class='scheme-button github-button'><span>Github</span></a>"})
|
||||||
when "google"
|
when "google"
|
||||||
@ -504,6 +579,13 @@ module.exports = exports = (log, loga, argv) ->
|
|||||||
userIds = {}
|
userIds = {}
|
||||||
idProviders.forEach (idProvider) ->
|
idProviders.forEach (idProvider) ->
|
||||||
id = switch idProvider
|
id = switch idProvider
|
||||||
|
when "oauth2" then {
|
||||||
|
name: user.oauth2.displayName
|
||||||
|
oauth2: {
|
||||||
|
id: user.oauth2.id
|
||||||
|
username: user.oauth2.username
|
||||||
|
}
|
||||||
|
}
|
||||||
when "twitter" then {
|
when "twitter" then {
|
||||||
name: user.twitter.displayName
|
name: user.twitter.displayName
|
||||||
twitter: {
|
twitter: {
|
||||||
@ -580,6 +662,13 @@ module.exports = exports = (log, loga, argv) ->
|
|||||||
id = {}
|
id = {}
|
||||||
idProviders.forEach (idProvider) ->
|
idProviders.forEach (idProvider) ->
|
||||||
id = switch idProvider
|
id = switch idProvider
|
||||||
|
when "oauth2" then {
|
||||||
|
name: user.oauth2.displayName
|
||||||
|
oauth2: {
|
||||||
|
id: user.oauth2.id
|
||||||
|
username: user.oauth2.username
|
||||||
|
}
|
||||||
|
}
|
||||||
when "twitter" then {
|
when "twitter" then {
|
||||||
name: user.twitter.displayName
|
name: user.twitter.displayName
|
||||||
twitter: {
|
twitter: {
|
||||||
|
Reference in New Issue
Block a user