Skip to content

Commit f001e20

Browse files
ELITE-496: pre auth util
1 parent febe7c9 commit f001e20

File tree

20 files changed

+167
-11
lines changed

20 files changed

+167
-11
lines changed

packages/authentication/CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# @baseapp-frontend/authentication
22

3+
## 2.1.2
4+
5+
### Patch Changes
6+
7+
- Add `preAuthenticateJWT` function to be used on the server side.
8+
- Deprecates the `usePreAuth` hook.
9+
- Updated dependencies
10+
- @baseapp-frontend/utils@2.2.2
11+
312
## 2.1.1
413

514
### Patch Changes
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = require('@baseapp-frontend/test/__mocks__/fetchMock.ts')
2+
3+
export {}

packages/authentication/modules/access/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
export { default as preAuthenticateJWT } from './preAuthenticateJWT'
2+
13
export { default as useLogin } from './useLogin'
24
export * from '../../utils/login'
35
export type * from './useLogin/types'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import preAuthenticateJWT from '..'
2+
3+
global.fetch = jest.fn()
4+
5+
const mockFetchResponse = (body = {}, ok = true, status = 200) => {
6+
const fetchMock = global.fetch as jest.Mock
7+
fetchMock.mockResolvedValueOnce({
8+
ok,
9+
status,
10+
json: () => Promise.resolve(body),
11+
headers: {
12+
get: () => 'application/json',
13+
},
14+
})
15+
}
16+
17+
describe('preAuthenticateJWT', () => {
18+
beforeEach(() => {
19+
process.env.NEXT_PUBLIC_API_BASE_URL = 'http://localhost:3000'
20+
jest.clearAllMocks()
21+
})
22+
23+
it('should throw an error if no token is provided', async () => {
24+
await expect(preAuthenticateJWT()).rejects.toThrow('No token provided.')
25+
})
26+
27+
it('should call fetch with the correct URL and headers', async () => {
28+
const token = 'test-jwt-token'
29+
const expectedUrl = 'http://localhost:3000/auth/pre-auth/jwt'
30+
mockFetchResponse({ success: true })
31+
32+
await preAuthenticateJWT(token)
33+
34+
expect(fetch).toHaveBeenCalledWith(expectedUrl, {
35+
method: 'POST',
36+
body: JSON.stringify({ token }),
37+
headers: {
38+
Accept: 'application/json',
39+
'Content-Type': 'application/json',
40+
},
41+
})
42+
})
43+
44+
it('should return a response on successful pre-authentication', async () => {
45+
const responseData = { success: true, details: 'User authenticated.' }
46+
mockFetchResponse(responseData)
47+
48+
const response = await preAuthenticateJWT('valid-jwt-token')
49+
50+
expect(response).toEqual(responseData)
51+
})
52+
53+
it('should handle network or server errors gracefully', async () => {
54+
const errorMessage = 'Network error'
55+
const fetchMock = global.fetch as jest.Mock
56+
fetchMock.mockRejectedValueOnce(new Error(errorMessage))
57+
58+
await expect(preAuthenticateJWT('valid-jwt-token')).rejects.toThrow(errorMessage)
59+
})
60+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { IJWTResponse, baseAppFetch } from '@baseapp-frontend/utils'
2+
3+
const preAuthenticateJWT = async (token?: string) => {
4+
try {
5+
if (!token) {
6+
throw new Error('No token provided.')
7+
}
8+
9+
const response = await baseAppFetch<IJWTResponse>('/auth/pre-auth/jwt', {
10+
method: 'POST',
11+
body: { token },
12+
})
13+
14+
if (response instanceof Response && !response.ok) {
15+
throw new Error('Failed to pre-authenticate.')
16+
}
17+
18+
return response
19+
} catch (error) {
20+
return Promise.reject(error)
21+
}
22+
}
23+
24+
export default preAuthenticateJWT

packages/authentication/modules/access/usePreAuth/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import { isJWTResponse } from '../../../utils/login'
99
import { useSimpleTokenUser } from '../../user'
1010
import { IUsePreAuth } from './types'
1111

12+
/**
13+
* @deprecated
14+
* Prefer using the `preAuthenticate` function on the server side.
15+
*/
1216
const usePreAuth = ({
1317
token,
1418
queryOptions = {},

packages/authentication/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@baseapp-frontend/authentication",
33
"description": "Authentication modules.",
4-
"version": "2.1.1",
4+
"version": "2.1.2",
55
"main": "./dist/index.ts",
66
"module": "./dist/index.mjs",
77
"scripts": {

packages/test/CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# @baseapp-frontend/test
22

3+
## 1.1.3
4+
5+
### Patch Changes
6+
7+
- Mock fetch Response on `Jest` tests.
8+
39
## 1.1.2
410

511
### Patch Changes

packages/test/__mocks__/fetchMock.ts

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
class MockResponse {
2+
ok: boolean
3+
status: number
4+
body: any
5+
headers: Map<string, string>
6+
7+
constructor(
8+
body: any,
9+
init?: { ok?: boolean; status?: number; headers?: Record<string, string> },
10+
) {
11+
this.ok = init?.ok ?? true
12+
this.status = init?.status ?? 200
13+
this.body = body
14+
this.headers = new Map(Object.entries(init?.headers || {}))
15+
}
16+
17+
async json() {
18+
return Promise.resolve(this.body)
19+
}
20+
21+
async text() {
22+
return Promise.resolve(JSON.stringify(this.body))
23+
}
24+
}
25+
26+
global.Response = MockResponse as any

packages/test/jest.config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module.exports = {
1313
],
1414
'^.+\\.[t|j]sx?$': 'babel-jest',
1515
},
16-
setupFilesAfterEnv: ['<rootDir>/__mocks__/consoleMock.ts'],
16+
setupFilesAfterEnv: ['<rootDir>/__mocks__/consoleMock.ts', '<rootDir>/__mocks__/fetchMock.ts'],
1717
moduleNameMapper: {
1818
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
1919
'<rootDir>/__mocks__/fileMock.ts',

packages/test/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@baseapp-frontend/test",
33
"description": "Test utils that extends React Testing Library.",
4-
"version": "1.1.2",
4+
"version": "1.1.3",
55
"main": "./dist/index.ts",
66
"module": "./dist/index.mjs",
77
"scripts": {

packages/utils/CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# @baseapp-frontend/utils
22

3+
## 2.2.2
4+
5+
### Patch Changes
6+
7+
- Add generic response types to `baseAppFetch`.
8+
- Handle `!response.ok` on `getAccessToken`.
9+
- Add pre-auth endpoints to `SERVICES_WITHOUT_TOKEN`.
10+
311
## 2.2.1
412

513
### Patch Changes

packages/utils/__mocks__/fetchMock.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = require('@baseapp-frontend/test/__mocks__/fetchMock.ts')
2+
3+
export {}

packages/utils/constants/axios.ts packages/utils/constants/fetch.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ export const SERVICES_WITHOUT_TOKEN = [
44
/\/register$/,
55
/\/forgot-password$/,
66
/\/forgot-password\/reset$/,
7-
/\/users\/\d+\/pre-auth$/,
7+
/\/auth\/pre-auth\/jwt$/,
8+
/\/auth\/pre-auth\/auth-token$/,
89
]

packages/utils/functions/axios/createAxiosInstance/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import _axios from 'axios'
22
import humps from 'humps'
33
import Cookies from 'js-cookie'
44

5-
import { SERVICES_WITHOUT_TOKEN } from '../../../constants/axios'
65
import { ACCESS_COOKIE_NAME, REFRESH_COOKIE_NAME } from '../../../constants/cookie'
76
import { LOGOUT_EVENT } from '../../../constants/events'
7+
import { SERVICES_WITHOUT_TOKEN } from '../../../constants/fetch'
88
import { TokenTypes } from '../../../constants/token'
99
import { eventEmitter } from '../../events'
1010
import { buildQueryString } from '../../string'

packages/utils/functions/fetch/baseAppFetch/index.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import humps from 'humps'
22
import includes from 'lodash/includes'
33

4-
import { SERVICES_WITHOUT_TOKEN } from '../../../constants/axios'
54
import { ACCESS_COOKIE_NAME, REFRESH_COOKIE_NAME } from '../../../constants/cookie'
65
import { LOGOUT_EVENT } from '../../../constants/events'
6+
import { SERVICES_WITHOUT_TOKEN } from '../../../constants/fetch'
77
import { TokenTypes } from '../../../constants/token'
88
import { eventEmitter } from '../../events'
99
import { buildQueryString } from '../../string'
1010
import { decodeJWT, getToken, isUserTokenValid, refreshAccessToken } from '../../token'
11-
import { BaseAppFetchOptions, RequestOptions } from './types'
11+
import { BaseAppFetch, RequestOptions } from './types'
1212

1313
/**
1414
*
@@ -64,7 +64,7 @@ import { BaseAppFetchOptions, RequestOptions } from './types'
6464
* })
6565
* ```
6666
*/
67-
export const baseAppFetch = async (
67+
export const baseAppFetch: BaseAppFetch = async (
6868
path: `/${string}` | '',
6969
{
7070
accessCookieName = ACCESS_COOKIE_NAME,
@@ -78,7 +78,7 @@ export const baseAppFetch = async (
7878
stringifyBody = true,
7979
setContentType = true,
8080
...options
81-
}: BaseAppFetchOptions,
81+
} = {},
8282
) => {
8383
const url = `${baseUrl}${path}`
8484
const isAuthTokenRequired = !servicesWithoutToken.some((regex) => regex.test(path || ''))

packages/utils/functions/fetch/baseAppFetch/types.ts

+5
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,8 @@ export interface RequestOptions extends Omit<RequestInit, 'body' | 'headers'> {
3737
}
3838

3939
export type BaseAppFetchOptions = RequestOptions & NextFetchOptions & Config
40+
41+
export type BaseAppFetch = <TOutput = any>(
42+
path: `/${string}` | '',
43+
options: BaseAppFetchOptions,
44+
) => Promise<TOutput>

packages/utils/functions/token/getAccessToken/index.ts

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ export const getAccessToken = async (refreshToken: string) => {
1616
'Content-Type': 'application/json',
1717
},
1818
})
19+
20+
if (response instanceof Response && !response.ok) {
21+
throw new Error('Failed to get access token.')
22+
}
23+
1924
const { access: accessToken } = (await response.json()) as IJWTResponse
2025

2126
return accessToken

packages/utils/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export * from './constants/axios'
1+
export * from './constants/fetch'
22
export * from './constants/cookie'
33
export * from './constants/django'
44
export * from './constants/events'

packages/utils/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@baseapp-frontend/utils",
33
"description": "Util functions, constants and types.",
4-
"version": "2.2.1",
4+
"version": "2.2.2",
55
"main": "./dist/index.ts",
66
"module": "./dist/index.mjs",
77
"scripts": {

0 commit comments

Comments
 (0)