Custom callback and user profile URLs for OAuth2
For parsing `oauth2_UsernameField` values like `profile.preferred_username`, this makes use of `eval()` which is generally Evil™, but I'm assuming that anyone with permission to edit config.json likely has permission to make changes to the fedwiki source code already anyway, so it's fragile rather than increasing a security attack surface. An alternative would be using a small function to look up properties of the `params` / `profile` objects using the same dotted-path notation.
This commit is contained in:
@ -1,4 +1,36 @@
|
|||||||
|
## 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_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
|
```JSON
|
||||||
{
|
{
|
||||||
@ -8,8 +40,21 @@
|
|||||||
"oauth2_clientSecret": "CLIENT SECRET",
|
"oauth2_clientSecret": "CLIENT SECRET",
|
||||||
"oauth2_AuthorizationURL": "https://auth.example.com/oauth2/authorize",
|
"oauth2_AuthorizationURL": "https://auth.example.com/oauth2/authorize",
|
||||||
"oauth2_TokenURL": "https://auth.example.com/oauth2/token",
|
"oauth2_TokenURL": "https://auth.example.com/oauth2/token",
|
||||||
"wikiDomains": {
|
}
|
||||||
"example.wiki": {}
|
```
|
||||||
}
|
|
||||||
|
#### 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_CallbackURL": "http://localhost:3000/auth/oauth2/callback",
|
||||||
|
"oauth2_UsernameField": "profile.preferred_username"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -201,23 +201,42 @@ module.exports = exports = (log, loga, argv) ->
|
|||||||
|
|
||||||
oauth2StrategyName = callbackHost + 'OAuth'
|
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({
|
passport.use(oauth2StrategyName, new OAuth2Strategy({
|
||||||
clientID: argv.oauth2_clientID
|
clientID: argv.oauth2_clientID
|
||||||
clientSecret: argv.oauth2_clientSecret
|
clientSecret: argv.oauth2_clientSecret
|
||||||
authorizationURL: argv.oauth2_AuthorizationURL
|
authorizationURL: argv.oauth2_AuthorizationURL
|
||||||
tokenURL: argv.oauth2_TokenURL
|
tokenURL: argv.oauth2_TokenURL,
|
||||||
# userURL: argv.oauth2_UserURL
|
# not all providers have a way of specifying the callback URL
|
||||||
# scope: 'user:emails'
|
callbackURL: argv.oauth2_CallbackURL,
|
||||||
# callbackURL is optional, and if it exists must match that given in
|
userInfoURL: argv.oauth2_UserInfoURL
|
||||||
# the OAuth application settings - so we don't specify it.
|
|
||||||
}, (accessToken, refreshToken, params, profile, cb) ->
|
}, (accessToken, refreshToken, params, profile, cb) ->
|
||||||
|
console.log("accessToken", accessToken)
|
||||||
|
console.log("refreshToken", refreshToken)
|
||||||
console.log("params", params)
|
console.log("params", params)
|
||||||
console.log("profile", profile)
|
console.log("profile", profile)
|
||||||
|
if argv.oauth2_UsernameField?
|
||||||
|
username_query = argv.oauth2_UsernameField
|
||||||
|
else
|
||||||
|
username_query = 'params.user_id'
|
||||||
user.oauth2 = {
|
user.oauth2 = {
|
||||||
id: params.user_id,
|
id: eval username_query,
|
||||||
username: params.user_id,
|
username: eval username_query
|
||||||
displayName: params.user_id,
|
displayName: eval username_query
|
||||||
}
|
}
|
||||||
|
console.log user.oauth2
|
||||||
cb(null, user)))
|
cb(null, user)))
|
||||||
|
|
||||||
# Github Strategy
|
# Github Strategy
|
||||||
|
Reference in New Issue
Block a user