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
|
'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')
let 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) {
var 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)
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 id = extractProfileAttribute(data, config.oauth2.userProfileIdAttr)
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: username,
displayName: displayName,
email: email
}
}
function checkAuthorization (data, done) {
const roles = extractProfileAttribute(data, config.oauth2.rolesClaim)
const username = extractProfileAttribute(data, config.oauth2.userProfileUsernameAttr)
if (config.oauth2.accessRole) {
if (!roles) {
logger.error('oauth2: "accessRole" configured, but user profile doesn\'t contain roles attribute. Permission denied')
return done('Permission denied', null)
}
if (!roles.includes(config.oauth2.accessRole)) {
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) {
var 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)
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,
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 + '/'
})
)
|