Skip to content

Commit

Permalink
feat: remove use of jwt-decode in provider (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
ivangabriele committed Feb 15, 2022
1 parent 45768a0 commit d735c5a
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 126 deletions.
46 changes: 22 additions & 24 deletions src/AuthProvider/index.tsx
Expand Up @@ -11,11 +11,10 @@ import handleError from '../helpers/handleError.js'
import isBrowser from '../helpers/isBrowser.js'
import matchOneOfPatterns from '../helpers/matchOneOfPatterns.js'
import useIsMounted from '../hooks/useIsMounted.js'
import jwtClient from '../libs/jwtClient.js'
import Context from './Context.js'

import type ApiResponse from '../libs/ApiResponse'
import type { AccessTokenPayload, User } from '../types'
import type { AccessTokenPayload, RefreshTokenPayload, User } from '../types'
import type {
AuthContext,
AuthLogInError,
Expand Down Expand Up @@ -54,7 +53,7 @@ const AuthProvider: FunctionComponent<AuthProviderProps> = ({ children, Loader,
throw new Error('logIn() must be called within a browser environment.')
}

const { data: tokenPair } = await ky
const { data: tokenPairWithPayload } = await ky
.post('/api/auth/login', {
json: {
email,
Expand All @@ -64,22 +63,19 @@ const AuthProvider: FunctionComponent<AuthProviderProps> = ({ children, Loader,
.json<
ApiResponse<{
accessToken: string
accessTokenPayload: AccessTokenPayload
refreshToken: string
refreshTokenPayload: RefreshTokenPayload
}>
>()

const accessTokenPayload = jwtClient.parse<AccessTokenPayload>(tokenPair.accessToken)
if (accessTokenPayload === undefined) {
throw new Error('`accessToken` is unparseable. This should never happen.')
}

window.localStorage.setItem('NEXAUTH_REFRESH_TOKEN', tokenPair.refreshToken)
$accessTokenExpirationTimestamp.current = accessTokenPayload.exp
window.localStorage.setItem('NEXAUTH_REFRESH_TOKEN', tokenPairWithPayload.refreshToken)
$accessTokenExpirationTimestamp.current = tokenPairWithPayload.accessTokenPayload.exp

if (isMounted()) {
setUser(accessTokenPayload.data)
setUser(tokenPairWithPayload.accessTokenPayload.data)
setState({
accessToken: tokenPair.accessToken,
accessToken: tokenPairWithPayload.accessToken,
isAuthenticated: true,
isLoading: false,
})
Expand Down Expand Up @@ -170,32 +166,34 @@ const AuthProvider: FunctionComponent<AuthProviderProps> = ({ children, Loader,
return null
}

const { data: tokenPair } = await ky
const { data: tokenPairWithPayload } = await ky
.post('/api/auth/refresh', {
json: {
refreshToken,
},
})
.json<ApiResponse>()

const accessTokenPayload = jwtClient.parse<AccessTokenPayload>(tokenPair.accessToken)
if (accessTokenPayload === undefined) {
throw new Error('`accessToken` is unparseable. This should never happen.')
}
.json<
ApiResponse<{
accessToken: string
accessTokenPayload: AccessTokenPayload
refreshToken: string
refreshTokenPayload: RefreshTokenPayload
}>
>()

window.localStorage.setItem('NEXAUTH_REFRESH_TOKEN', tokenPair.refreshToken)
$accessTokenExpirationTimestamp.current = accessTokenPayload.exp
window.localStorage.setItem('NEXAUTH_REFRESH_TOKEN', tokenPairWithPayload.refreshToken)
$accessTokenExpirationTimestamp.current = tokenPairWithPayload.accessTokenPayload.exp

if (isMounted()) {
setUser(accessTokenPayload.data)
setUser(tokenPairWithPayload.accessTokenPayload.data)
setState({
accessToken: tokenPair.accessToken,
accessToken: tokenPairWithPayload.accessToken,
isAuthenticated: true,
isLoading: false,
})
}

return tokenPair.accessToken
return tokenPairWithPayload.accessToken
} catch (err) {
if (err instanceof HTTPError) {
window.localStorage.removeItem('NEXAUTH_REFRESH_TOKEN')
Expand Down
12 changes: 11 additions & 1 deletion src/actions/logIn.ts
Expand Up @@ -10,7 +10,14 @@ import ApiError from '../libs/ApiError.js'
import ApiResponse from '../libs/ApiResponse.js'
import jwt from '../libs/jwt.js'

import type { Adapter, NexauthConfig, NexauthOptions, RefreshTokenPayload, UserWithPassword } from '../types'
import type {
AccessTokenPayload,
Adapter,
NexauthConfig,
NexauthOptions,
RefreshTokenPayload,
UserWithPassword,
} from '../types'
import type { NextApiRequest, NextApiResponse } from 'next'

const { CI } = process.env
Expand Down Expand Up @@ -81,6 +88,7 @@ export default async function logIn<U extends UserWithPassword = UserWithPasswor

const accessTokenPayloadUser = pick(accessTokenPublicUserProps)(user)
const accessTokenValue = await jwt.sign(userId, ACCESS_TOKEN_EXPIRATION_IN_SECONDS, accessTokenPayloadUser)
const accessTokenPayload = await jwt.verify<AccessTokenPayload>(accessTokenValue)

const refreshTokenValue = await jwt.sign(userId, REFRESH_TOKEN_EXPIRATION_IN_SECONDS)
const refreshTokenPayload = await jwt.verify<RefreshTokenPayload>(refreshTokenValue)
Expand All @@ -102,7 +110,9 @@ export default async function logIn<U extends UserWithPassword = UserWithPasswor
res.status(200).json(
new ApiResponse({
accessToken: accessTokenValue,
accessTokenPayload,
refreshToken: refreshTokenValue,
refreshTokenPayload,
}),
)
} catch (err) {
Expand Down
20 changes: 15 additions & 5 deletions src/actions/refresh.ts
Expand Up @@ -9,7 +9,14 @@ import ApiError from '../libs/ApiError.js'
import ApiResponse from '../libs/ApiResponse.js'
import jwt from '../libs/jwt.js'

import type { Adapter, NexauthConfig, NexauthOptions, RefreshTokenPayload, UserWithPassword } from '../types'
import type {
AccessTokenPayload,
Adapter,
NexauthConfig,
NexauthOptions,
RefreshTokenPayload,
UserWithPassword,
} from '../types'
import type { NextApiRequest, NextApiResponse } from 'next'

const { CI } = process.env
Expand Down Expand Up @@ -88,19 +95,20 @@ export default async function refresh<U extends UserWithPassword = UserWithPassw

const newAccessTokenPayloadUser = pick(accessTokenPublicUserProps)(user)
const newAccessTokenValue = await jwt.sign(userId, ACCESS_TOKEN_EXPIRATION_IN_SECONDS, newAccessTokenPayloadUser)
const newAccessTokenPayload = await jwt.verify<AccessTokenPayload>(newAccessTokenValue)
const newRefreshTokenValue = await jwt.sign(userId, REFRESH_TOKEN_EXPIRATION_IN_SECONDS)
const newRefreshTokenData = await jwt.verify<RefreshTokenPayload>(newRefreshTokenValue)
if (newRefreshTokenData === undefined) {
const newRefreshTokenPayload = await jwt.verify<RefreshTokenPayload>(newRefreshTokenValue)
if (newRefreshTokenPayload === undefined) {
throw new Error('`newRefreshTokenValue` is unverifiable. This should never happen.')
}

const expiredAt = new Date(newRefreshTokenData.exp * 1000)
const expiredAt = new Date(newRefreshTokenPayload.exp * 1000)
const familyId = cuid()

await adapter.refreshToken.create({
expiredAt,
familyId,
id: newRefreshTokenData.jti,
id: newRefreshTokenPayload.jti,
ip,
userId,
value: newRefreshTokenValue,
Expand All @@ -109,7 +117,9 @@ export default async function refresh<U extends UserWithPassword = UserWithPassw
res.status(200).json(
new ApiResponse({
accessToken: newAccessTokenValue,
accessTokenPayload: newAccessTokenPayload,
refreshToken: newRefreshTokenValue,
refreshTokenPayload: newRefreshTokenPayload,
}),
)
} catch (err) {
Expand Down
3 changes: 1 addition & 2 deletions src/index.ts
Expand Up @@ -4,7 +4,6 @@ import { NexauthError } from './constants.js'
import useAuth from './hooks/useAuth.js'
import getUser from './libs/getUser.js'
import jwt from './libs/jwt.js'
import jwtClient from './libs/jwtClient.js'
import Nexauth from './Nexauth.js'

export { AuthProvider, getUser, jwt, jwtClient, Nexauth, NexauthError, PrismaAdapter, useAuth }
export { AuthProvider, getUser, jwt, Nexauth, NexauthError, PrismaAdapter, useAuth }
56 changes: 0 additions & 56 deletions src/libs/__tests__/jwtClient.test.ts

This file was deleted.

38 changes: 0 additions & 38 deletions src/libs/jwtClient.ts

This file was deleted.

0 comments on commit d735c5a

Please sign in to comment.