2020-04-13 09:45:29 -04:00
|
|
|
import { InternalOAuthError, Strategy as OAuth2Strategy } from 'passport-oauth2'
|
|
|
|
import { config } from '../../../config'
|
2020-06-06 13:59:53 -04:00
|
|
|
import { PassportProfile, ProviderEnum } from '../utils'
|
2020-06-17 09:09:47 -04:00
|
|
|
import { logger } from '../../../logger'
|
2020-04-13 09:45:29 -04:00
|
|
|
|
2020-05-24 12:38:24 -04:00
|
|
|
function extractProfileAttribute (data, path: string): string {
|
2020-04-13 09:45:29 -04:00
|
|
|
// can handle stuff like `attrs[0].name`
|
|
|
|
const pathArray = path.split('.')
|
|
|
|
for (const segment of pathArray) {
|
|
|
|
const regex = /([\d\w]+)\[(.*)\]/
|
|
|
|
const m = regex.exec(segment)
|
|
|
|
data = m ? data[m[1]][m[2]] : data[segment]
|
|
|
|
}
|
|
|
|
return data
|
|
|
|
}
|
|
|
|
|
2020-06-04 15:25:42 -04:00
|
|
|
function parseProfile (data): Partial<PassportProfile> {
|
2020-04-13 09:45:29 -04:00
|
|
|
const username = extractProfileAttribute(data, config.oauth2.userProfileUsernameAttr)
|
2020-06-17 09:09:47 -04:00
|
|
|
let displayName: string | undefined
|
|
|
|
try {
|
|
|
|
// This may fail if the config.oauth2.userProfileDisplayNameAttr is undefined,
|
|
|
|
// or it is foo.bar and data["foo"] is undefined.
|
|
|
|
displayName = extractProfileAttribute(data, config.oauth2.userProfileDisplayNameAttr)
|
|
|
|
} catch (e) {
|
|
|
|
displayName = undefined
|
|
|
|
logger.debug('\'id_token[%s]\' is undefined. Setting \'displayName\' to \'undefined\'.\n%s', config.oauth2.userProfileDisplayNameAttr, e.message)
|
|
|
|
}
|
|
|
|
|
|
|
|
const emails: string[] = []
|
|
|
|
try {
|
|
|
|
const email = extractProfileAttribute(data, config.oauth2.userProfileEmailAttr)
|
|
|
|
if (email !== undefined) {
|
|
|
|
emails.push(email)
|
|
|
|
} else {
|
|
|
|
logger.debug('\'id_token[%s]\' is undefined. Setting \'emails\' to [].', config.oauth2.userProfileEmailAttr)
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
logger.debug('\'id_token[%s]\' is undefined. Setting \'emails\' to [].\n%s', config.oauth2.userProfileEmailAttr, e.message)
|
|
|
|
}
|
2020-04-13 09:45:29 -04:00
|
|
|
|
|
|
|
return {
|
|
|
|
id: username,
|
|
|
|
username: username,
|
|
|
|
displayName: displayName,
|
2020-06-17 09:09:47 -04:00
|
|
|
emails: emails
|
2020-04-13 09:45:29 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class OAuth2CustomStrategy extends OAuth2Strategy {
|
|
|
|
private readonly _userProfileURL: string;
|
|
|
|
|
|
|
|
constructor (options, verify) {
|
|
|
|
options.customHeaders = options.customHeaders || {}
|
|
|
|
super(options, verify)
|
|
|
|
this.name = 'oauth2'
|
|
|
|
this._userProfileURL = options.userProfileURL
|
|
|
|
this._oauth2.useAuthorizationHeaderforGET(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
userProfile (accessToken, done): void {
|
|
|
|
this._oauth2.get(this._userProfileURL, accessToken, function (err, body, _) {
|
|
|
|
let json
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
return done(new InternalOAuthError('Failed to fetch user profile', err))
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
if (body !== undefined) {
|
|
|
|
json = JSON.parse(body.toString())
|
|
|
|
}
|
|
|
|
} catch (ex) {
|
|
|
|
return done(new Error('Failed to parse user profile'))
|
|
|
|
}
|
|
|
|
|
|
|
|
const profile = parseProfile(json)
|
|
|
|
profile.provider = ProviderEnum.oauth2
|
|
|
|
|
|
|
|
done(null, profile)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export { OAuth2CustomStrategy }
|