Skip to content

Commit

Permalink
refactor: provide raw idToken through account object (#1211)
Browse files Browse the repository at this point in the history
* refactor: provide raw idToken through account object

* docs: clear up accessToken naming

* refactor: provide raw token response to account

* chore: fix grammar in comments
  • Loading branch information
balazsorban44 committed Feb 1, 2021
1 parent 645b53e commit 93f051c
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 54 deletions.
64 changes: 26 additions & 38 deletions src/server/lib/oauth/callback.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,20 @@ export default async function oAuthCallback (req) {
}

try {
const { accessToken, refreshToken, results } = await client.getOAuthAccessToken(code, provider, pkce.code_verifier)
const tokens = { accessToken, refreshToken, idToken: results.id_token }
const tokens = await client.getOAuthAccessToken(code, provider, pkce.code_verifier)
let profileData
if (provider.idToken) {
// If we don't have an ID Token most likely the user hit a cancel
// button when signing in (or the provider is misconfigured).
//
// Unfortunately, we can't tell which, so we can't treat it as an
// error, so instead we just returning nothing, which will cause the
// user to be redirected back to the sign in page.
if (!results?.id_token) {
throw new OAuthCallbackError()
if (!tokens?.id_token) {
throw new OAuthCallbackError('Missing JWT ID Token')
}

// Support services that use OpenID ID Tokens to encode profile data
profileData = decodeIdToken(results.id_token)
profileData = jwtDecode(tokens.id_token, { json: true })
} else {
profileData = await client.get(provider, accessToken, results)
profileData = await client.get(provider, tokens.accessToken, tokens)
}

return _getProfile({ profileData, provider, tokens, user })
return getProfile({ profileData, provider, tokens, user })
} catch (error) {
logger.error('OAUTH_GET_ACCESS_TOKEN_ERROR', error, provider.id, code)
throw error
Expand All @@ -68,20 +61,14 @@ export default async function oAuthCallback (req) {
const {
oauth_token: oauthToken, oauth_verifier: oauthVerifier
} = req.query
const { accessToken, refreshToken, results } = await client.getOAuthAccessToken(oauthToken, null, oauthVerifier)
const tokens = await client.getOAuthAccessToken(oauthToken, null, oauthVerifier)
const profileData = await client.get(
provider.profileUrl,
accessToken,
refreshToken
tokens.accessToken,
tokens.refreshToken
)

const tokens = {
accessToken, refreshToken, idToken: results.id_token
}

return _getProfile({
profileData, tokens, provider
})
return getProfile({ profileData, tokens, provider })
} catch (error) {
logger.error('OAUTH_V1_GET_ACCESS_TOKEN_ERROR', error)
throw error
Expand All @@ -91,10 +78,22 @@ export default async function oAuthCallback (req) {
/**
* //6/30/2020 @geraldnolan added userData parameter to attach additional data to the profileData object
* Returns profile, raw profile and auth provider details
* @param {{
* profileData: object | string
* tokens: {
* accessToken: string
* idToken?: string
* refreshToken?: string
* access_token: string
* expires_in?: string | Date | null
* refresh_token?: string
* id_token?: string
* }
* provider: object
* user?: object
* }} profileParams
*/
async function _getProfile ({
profileData, tokens: { accessToken, refreshToken, idToken }, provider, user
}) {
async function getProfile ({ profileData, tokens, provider, user }) {
try {
// Convert profileData into an object if it's a string
if (typeof profileData === 'string' || profileData instanceof String) {
Expand All @@ -106,8 +105,6 @@ async function _getProfile ({
profileData.user = user
}

profileData.idToken = idToken

logger.debug('PROFILE_DATA', profileData)

const profile = await provider.profile(profileData)
Expand All @@ -121,9 +118,7 @@ async function _getProfile ({
provider: provider.id,
type: provider.type,
id: profile.id,
refreshToken,
accessToken,
accessTokenExpires: null
...tokens
},
OAuthProfile: profileData
}
Expand All @@ -143,10 +138,3 @@ async function _getProfile ({
}
}
}

function decodeIdToken (idToken) {
if (!idToken) {
throw new OAuthCallbackError('Missing JWT ID Token')
}
return jwtDecode(idToken, { json: true })
}
26 changes: 16 additions & 10 deletions src/server/lib/oauth/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,23 +151,29 @@ async function getOAuth2AccessToken (code, provider, codeVerifier) {
return reject(error)
}

let results
let raw
try {
// As of http://tools.ietf.org/html/draft-ietf-oauth-v2-07
// responses should be in JSON
results = JSON.parse(data)
} catch (e) {
raw = JSON.parse(data)
} catch {
// However both Facebook + Github currently use rev05 of the spec and neither
// seem to specify a content-type correctly in their response headers. :(
// Clients of these services suffer a minor performance cost.
results = querystring.parse(data)
raw = querystring.parse(data)
}
let accessToken = results.access_token
if (provider.id === 'slack') {
accessToken = results.authed_user.access_token
}
const refreshToken = results.refresh_token
resolve({ accessToken, refreshToken, results })

const accessToken = provider.id === 'slack'
? raw.authed_user.access_token
: raw.access_token

resolve({
accessToken,
accessTokenExpires: raw.expires_in ?? null,
refreshToken: raw.refresh_token,
idToken: raw.id_token,
...raw
})
}
)
})
Expand Down
4 changes: 2 additions & 2 deletions src/server/routes/callback.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ export default async function callback (req, res) {
logger.debug('OAUTH_CALLBACK_RESPONSE', { profile, account, OAuthProfile })

// If we don't have a profile object then either something went wrong
// or the user cancelled signin in. We don't know which, so we just
// direct the user to the signup page for now. We could do something
// or the user cancelled signing in. We don't know which, so we just
// direct the user to the signin page for now. We could do something
// else in future.
//
// Note: In oAuthCallback an error is logged with debug info, so it
Expand Down
8 changes: 4 additions & 4 deletions www/docs/configuration/callbacks.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ callbacks: {
* @return {object} Session that will be returned to the client
*/
async session(session, token) {
if(token?.access_token) {
if(token?.accessToken) {
// Add property to session, like an access_token from a provider
session.access_token = token.access_token
session.accessToken = token.accessToken
}
return session
}
Expand Down Expand Up @@ -183,8 +183,8 @@ callbacks: {
*/
async jwt(token, user, account, profile, isNewUser) {
// Add access_token to the token right after signin
if (account?.access_token) {
token.access_token = account.access_token
if (account?.accessToken) {
token.accessToken = account.accessToken
}
return token
}
Expand Down

0 comments on commit 93f051c

Please sign in to comment.