Skip to content

Commit

Permalink
fix: set initial expires_at
Browse files Browse the repository at this point in the history
refactor: pull refresh token logic to separate function
  • Loading branch information
vnugent committed Nov 3, 2024
1 parent 7338067 commit 48e5ec2
Showing 1 changed file with 53 additions and 21 deletions.
74 changes: 53 additions & 21 deletions src/pages/api/auth/[...nextauth].ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export const authOptions: NextAuthOptions = {
clientId,
clientSecret,
issuer,
authorization: { params: { audience: `${issuer}/api/v2/`, scope: 'offline_access access_token_authz openid email profile read:current_user create:current_user_metadata update:current_user_metadata read:stats update:area_attrs' } },
authorization: { params: { audience: 'https://api.openbeta.io', scope: 'offline_access access_token_authz openid email profile read:current_user create:current_user_metadata update:current_user_metadata read:stats update:area_attrs' } },

client: {
token_endpoint_auth_method: clientSecret.length === 0 ? 'none' : 'client_secret_basic'
}
Expand All @@ -44,6 +45,9 @@ export const authOptions: NextAuthOptions = {
callbacks: {
// See https://next-auth.js.org/configuration/callbacks#jwt-callback
async jwt ({ token, account, profile, user }) {
/**
* `account` object is only populated once when the user first logged in.
*/
if (account?.access_token != null) {
token.accessToken = account.access_token
}
Expand All @@ -52,13 +56,18 @@ export const authOptions: NextAuthOptions = {
token.refreshToken = account.refresh_token
}

/**
* `account.expires_at` is set in Auth0 custom API
* Applications -> API -> (OB Climb API) -> Access Token Settings -> Implicit/Hybrid Access Token Lifetime
*/
if (account?.expires_at != null) {
token.expires_at = account.expires_at
token.expiresAt = account.expires_at
}

if (profile?.sub != null) {
token.id = profile.sub
}

// @ts-expect-error
if (profile?.[CustomClaimUserMetadata] != null) {
// null guard needed because profile object is only available once
Expand All @@ -71,30 +80,20 @@ export const authOptions: NextAuthOptions = {
})
}

if (token?.refreshToken != null && token?.expires_at != null && ((token.expires_at as number) < (Date.now() / 1000))) {
const response = await axios.request({
method: 'POST',
url: `${issuer}/oauth/token`,
headers: { 'content-type': 'application/x-www-form-urlencoded' },
data: new URLSearchParams({
grant_type: 'refresh_token',
client_id: clientId,
client_secret: clientSecret,
refresh_token: token.refreshToken as string
})
})

if (response.data.access_token == null) {
throw new Error('No access token in refresh_token flow')
}
if (token?.refreshToken == null || token?.expiresAt == null) {
throw new Error('Invalid auth data')
}

token.accessToken = response.data.access_token
token.refreshToken = response.data.refresh_token
token.expires_at = Math.floor((Date.now() / 1000) + (response.data.expires_in as number))
if ((token.expiresAt as number) < (Date.now() / 1000)) {
const { accessToken, refreshToken, expiresAt } = await refreshAccessTokenSilently(token.refreshToken as string)
token.accessToken = accessToken
token.refreshToken = refreshToken
token.expiresAt = expiresAt
}

return token
},

async session ({ session, user, token }) {
if (token.userMetadata == null ||
token?.userMetadata?.uuid == null || token?.userMetadata?.nick == null) {
Expand All @@ -111,3 +110,36 @@ export const authOptions: NextAuthOptions = {
}

export default NextAuth(authOptions)

const refreshAccessTokenSilently = async (refreshToken: string): Promise<any> => {
const response = await axios.request<{
access_token: string
refresh_token: string
expires_in: number
}>({
method: 'POST',
url: `${issuer}/oauth/token`,
headers: { 'content-type': 'application/json' },
data: JSON.stringify({
grant_type: 'refresh_token',
client_id: clientId,
client_secret: clientSecret,
refresh_token: refreshToken
})
})

/* eslint-disable @typescript-eslint/naming-convention */
const {
access_token, refresh_token, expires_in
} = response.data

if (access_token == null || refresh_token == null || expires_in == null) {
throw new Error('Missing data in refresh token flow')
}

return {
accessToken: access_token,
refreshToken: refresh_token,
expiresAt: Math.floor((Date.now() / 1000) + expires_in)
}
}

0 comments on commit 48e5ec2

Please sign in to comment.