-
Notifications
You must be signed in to change notification settings - Fork 3
/
oauth-index.js
136 lines (116 loc) · 4.29 KB
/
oauth-index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
'use strict'
const Router = require('express').Router
const passport = require('passport')
const { Strategy, InternalOAuthError } = require('passport-oauth2')
const config = require('../../../config')
const logger = require('../../../logger')
const { passportGeneralCallback } = require('../utils')
const oauth2Auth = module.exports = Router()
class OAuth2CustomStrategy extends Strategy {
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) {
let json
if (err) {
return done(new InternalOAuthError('Failed to fetch user profile', err))
}
try {
json = JSON.parse(body)
} catch (ex) {
return done(new Error('Failed to parse user profile'))
}
checkAuthorization(json, done)
const 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) {
// only try to parse the id if a claim is configured
const id = config.oauth2.userProfileIdAttr ? extractProfileAttribute(data, config.oauth2.userProfileIdAttr) : undefined
const username = extractProfileAttribute(data, config.oauth2.userProfileUsernameAttr)
const displayName = extractProfileAttribute(data, config.oauth2.userProfileDisplayNameAttr)
const email = extractProfileAttribute(data, config.oauth2.userProfileEmailAttr)
return {
id: id || username,
username,
displayName,
emails: email ? [email] : []
}
}
function checkAuthorization (data, done) {
// a role the user must have is set in the config
if (config.oauth2.accessRole) {
// check if we know which claim contains the list of groups a user is in
if (!config.oauth2.rolesClaim) {
// log error, but accept all logins
logger.error('oauth2: "accessRole" is configured, but "rolesClaim" is missing from the config. Can\'t check group membership!')
} else {
// parse and check role data
const roles = data[config.oauth2.rolesClaim]
const acceptedRoles = config.oauth2.accessRole.split(';')
if (!roles) {
logger.error('oauth2: "accessRole" is configured, but user profile doesn\'t contain roles attribute. Permission denied')
return done('Permission denied', null)
}
if (!roles.some(r => acceptedRoles.includes(`${r[0]},${r[1]}`))) {
const username = extractProfileAttribute(data, config.oauth2.userProfileUsernameAttr)
logger.debug(`oauth2: user "${username}" doesn't have the required role. Permission denied`)
return done('Permission denied', null)
}
}
}
}
OAuth2CustomStrategy.prototype.userProfile = function (accessToken, done) {
this._oauth2.get(this._userProfileURL, accessToken, function (err, body, res) {
let json
if (err) {
return done(new InternalOAuthError('Failed to fetch user profile', err))
}
try {
json = JSON.parse(body)
} catch (ex) {
return done(new Error('Failed to parse user profile'))
}
checkAuthorization(json, done)
const 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,
scope: config.oauth2.scope,
state: true
}, passportGeneralCallback))
oauth2Auth.get('/auth/oauth2', function (req, res, next) {
passport.authenticate('oauth2')(req, res, next)
})
// github auth callback
oauth2Auth.get('/auth/oauth2/callback',
passport.authenticate('oauth2', {
successReturnToOrRedirect: config.serverURL + '/',
failureRedirect: config.serverURL + '/'
})
)