Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ELITE-496: pre auth util #93

Merged
merged 1 commit into from
Apr 16, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/authentication/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# @baseapp-frontend/authentication

## 2.1.2

### Patch Changes

- Add `preAuthenticateJWT` function to be used on the server side.
- Deprecates the `usePreAuth` hook.
- Updated dependencies
- @baseapp-frontend/utils@2.2.2

## 2.1.1

### Patch Changes
3 changes: 3 additions & 0 deletions packages/authentication/__mocks__/fetchMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = require('@baseapp-frontend/test/__mocks__/fetchMock.ts')

export {}
2 changes: 2 additions & 0 deletions packages/authentication/modules/access/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { default as preAuthenticateJWT } from './preAuthenticateJWT'

export { default as useLogin } from './useLogin'
export * from '../../utils/login'
export type * from './useLogin/types'
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import preAuthenticateJWT from '..'

global.fetch = jest.fn()

const mockFetchResponse = (body = {}, ok = true, status = 200) => {
const fetchMock = global.fetch as jest.Mock
fetchMock.mockResolvedValueOnce({
ok,
status,
json: () => Promise.resolve(body),
headers: {
get: () => 'application/json',
},
})
}

describe('preAuthenticateJWT', () => {
beforeEach(() => {
process.env.NEXT_PUBLIC_API_BASE_URL = 'http://localhost:3000'
jest.clearAllMocks()
})

it('should throw an error if no token is provided', async () => {
await expect(preAuthenticateJWT()).rejects.toThrow('No token provided.')
})

it('should call fetch with the correct URL and headers', async () => {
const token = 'test-jwt-token'
const expectedUrl = 'http://localhost:3000/auth/pre-auth/jwt'
mockFetchResponse({ success: true })

await preAuthenticateJWT(token)

expect(fetch).toHaveBeenCalledWith(expectedUrl, {
method: 'POST',
body: JSON.stringify({ token }),
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
})
})

it('should return a response on successful pre-authentication', async () => {
const responseData = { success: true, details: 'User authenticated.' }
mockFetchResponse(responseData)

const response = await preAuthenticateJWT('valid-jwt-token')

expect(response).toEqual(responseData)
})

it('should handle network or server errors gracefully', async () => {
const errorMessage = 'Network error'
const fetchMock = global.fetch as jest.Mock
fetchMock.mockRejectedValueOnce(new Error(errorMessage))

await expect(preAuthenticateJWT('valid-jwt-token')).rejects.toThrow(errorMessage)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { IJWTResponse, baseAppFetch } from '@baseapp-frontend/utils'

const preAuthenticateJWT = async (token?: string) => {
try {
if (!token) {
throw new Error('No token provided.')
}

const response = await baseAppFetch<IJWTResponse>('/auth/pre-auth/jwt', {
method: 'POST',
body: { token },
})

if (response instanceof Response && !response.ok) {
throw new Error('Failed to pre-authenticate.')
}

return response
} catch (error) {
return Promise.reject(error)
}
}

export default preAuthenticateJWT
4 changes: 4 additions & 0 deletions packages/authentication/modules/access/usePreAuth/index.ts
Original file line number Diff line number Diff line change
@@ -9,6 +9,10 @@ import { isJWTResponse } from '../../../utils/login'
import { useSimpleTokenUser } from '../../user'
import { IUsePreAuth } from './types'

/**
* @deprecated
* Prefer using the `preAuthenticate` function on the server side.
*/
const usePreAuth = ({
token,
queryOptions = {},
2 changes: 1 addition & 1 deletion packages/authentication/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@baseapp-frontend/authentication",
"description": "Authentication modules.",
"version": "2.1.1",
"version": "2.1.2",
"main": "./dist/index.ts",
"module": "./dist/index.mjs",
"scripts": {
6 changes: 6 additions & 0 deletions packages/test/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @baseapp-frontend/test

## 1.1.3

### Patch Changes

- Mock fetch Response on `Jest` tests.

## 1.1.2

### Patch Changes
26 changes: 26 additions & 0 deletions packages/test/__mocks__/fetchMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class MockResponse {
ok: boolean
status: number
body: any
headers: Map<string, string>

constructor(
body: any,
init?: { ok?: boolean; status?: number; headers?: Record<string, string> },
) {
this.ok = init?.ok ?? true
this.status = init?.status ?? 200
this.body = body
this.headers = new Map(Object.entries(init?.headers || {}))
}

async json() {
return Promise.resolve(this.body)
}

async text() {
return Promise.resolve(JSON.stringify(this.body))
}
}

global.Response = MockResponse as any
2 changes: 1 addition & 1 deletion packages/test/jest.config.ts
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ module.exports = {
],
'^.+\\.[t|j]sx?$': 'babel-jest',
},
setupFilesAfterEnv: ['<rootDir>/__mocks__/consoleMock.ts'],
setupFilesAfterEnv: ['<rootDir>/__mocks__/consoleMock.ts', '<rootDir>/__mocks__/fetchMock.ts'],
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/__mocks__/fileMock.ts',
2 changes: 1 addition & 1 deletion packages/test/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@baseapp-frontend/test",
"description": "Test utils that extends React Testing Library.",
"version": "1.1.2",
"version": "1.1.3",
"main": "./dist/index.ts",
"module": "./dist/index.mjs",
"scripts": {
8 changes: 8 additions & 0 deletions packages/utils/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# @baseapp-frontend/utils

## 2.2.2

### Patch Changes

- Add generic response types to `baseAppFetch`.
- Handle `!response.ok` on `getAccessToken`.
- Add pre-auth endpoints to `SERVICES_WITHOUT_TOKEN`.

## 2.2.1

### Patch Changes
3 changes: 3 additions & 0 deletions packages/utils/__mocks__/fetchMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = require('@baseapp-frontend/test/__mocks__/fetchMock.ts')

export {}
Original file line number Diff line number Diff line change
@@ -4,5 +4,6 @@ export const SERVICES_WITHOUT_TOKEN = [
/\/register$/,
/\/forgot-password$/,
/\/forgot-password\/reset$/,
/\/users\/\d+\/pre-auth$/,
/\/auth\/pre-auth\/jwt$/,
/\/auth\/pre-auth\/auth-token$/,
]
Original file line number Diff line number Diff line change
@@ -2,9 +2,9 @@ import _axios from 'axios'
import humps from 'humps'
import Cookies from 'js-cookie'

import { SERVICES_WITHOUT_TOKEN } from '../../../constants/axios'
import { ACCESS_COOKIE_NAME, REFRESH_COOKIE_NAME } from '../../../constants/cookie'
import { LOGOUT_EVENT } from '../../../constants/events'
import { SERVICES_WITHOUT_TOKEN } from '../../../constants/fetch'
import { TokenTypes } from '../../../constants/token'
import { eventEmitter } from '../../events'
import { buildQueryString } from '../../string'
8 changes: 4 additions & 4 deletions packages/utils/functions/fetch/baseAppFetch/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import humps from 'humps'
import includes from 'lodash/includes'

import { SERVICES_WITHOUT_TOKEN } from '../../../constants/axios'
import { ACCESS_COOKIE_NAME, REFRESH_COOKIE_NAME } from '../../../constants/cookie'
import { LOGOUT_EVENT } from '../../../constants/events'
import { SERVICES_WITHOUT_TOKEN } from '../../../constants/fetch'
import { TokenTypes } from '../../../constants/token'
import { eventEmitter } from '../../events'
import { buildQueryString } from '../../string'
import { decodeJWT, getToken, isUserTokenValid, refreshAccessToken } from '../../token'
import { BaseAppFetchOptions, RequestOptions } from './types'
import { BaseAppFetch, RequestOptions } from './types'

/**
*
@@ -64,7 +64,7 @@ import { BaseAppFetchOptions, RequestOptions } from './types'
* })
* ```
*/
export const baseAppFetch = async (
export const baseAppFetch: BaseAppFetch = async (
path: `/${string}` | '',
{
accessCookieName = ACCESS_COOKIE_NAME,
@@ -78,7 +78,7 @@ export const baseAppFetch = async (
stringifyBody = true,
setContentType = true,
...options
}: BaseAppFetchOptions,
} = {},
) => {
const url = `${baseUrl}${path}`
const isAuthTokenRequired = !servicesWithoutToken.some((regex) => regex.test(path || ''))
5 changes: 5 additions & 0 deletions packages/utils/functions/fetch/baseAppFetch/types.ts
Original file line number Diff line number Diff line change
@@ -37,3 +37,8 @@ export interface RequestOptions extends Omit<RequestInit, 'body' | 'headers'> {
}

export type BaseAppFetchOptions = RequestOptions & NextFetchOptions & Config

export type BaseAppFetch = <TOutput = any>(
path: `/${string}` | '',
options: BaseAppFetchOptions,
) => Promise<TOutput>
5 changes: 5 additions & 0 deletions packages/utils/functions/token/getAccessToken/index.ts
Original file line number Diff line number Diff line change
@@ -16,6 +16,11 @@ export const getAccessToken = async (refreshToken: string) => {
'Content-Type': 'application/json',
},
})

if (response instanceof Response && !response.ok) {
throw new Error('Failed to get access token.')
}

const { access: accessToken } = (await response.json()) as IJWTResponse

return accessToken
2 changes: 1 addition & 1 deletion packages/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from './constants/axios'
export * from './constants/fetch'
export * from './constants/cookie'
export * from './constants/django'
export * from './constants/events'
2 changes: 1 addition & 1 deletion packages/utils/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@baseapp-frontend/utils",
"description": "Util functions, constants and types.",
"version": "2.2.1",
"version": "2.2.2",
"main": "./dist/index.ts",
"module": "./dist/index.mjs",
"scripts": {

Unchanged files with check annotations Beta

) {
router.push(redirectTo)
}
}, [user, redirectIfFound, redirectTo, isLoading])

Check warning on line 46 in packages/core/src/auth/login.tsx

GitHub Actions / Lint

React Hook useEffect has a missing dependency: 'router'. Either include it or remove the dependency array
return { user, isLoading, isSuccess, isIdle, status, setUser, refetchUser }
}
}
export function useEnvironment() {
const env = useMemo(() => initEnvironment(), [relayEnvironment])

Check warning on line 178 in packages/graphql/config/environment.ts

GitHub Actions / Lint

React Hook useMemo has an unnecessary dependency: 'relayEnvironment'. Either exclude it or remove the dependency array. Outer scope values like 'relayEnvironment' aren't valid dependencies because mutating them doesn't re-render the component
return env
}
): PreloadedQuery<TQuery> {
useMemo(() => {
writePreloadedQueryToCache(preloadQuery, environment)
}, [preloadQuery])

Check warning on line 37 in packages/graphql/config/useSerializablePreloadedQuery.ts

GitHub Actions / Lint

React Hook useMemo has a missing dependency: 'environment'. Either include it or remove the dependency array
return {
environment,
try {
await mutation.mutateAsync(values)
} catch (error) {
console.log(error, 'error')

Check warning on line 44 in packages/authentication/modules/access/useSignUp/index.ts

GitHub Actions / Lint

Unexpected console statement
}
}
useEffect(() => {
eventEmitter.on(event, callback)
return unsubscribe
}, [])

Check warning on line 15 in packages/utils/hooks/useEventSubscription/index.ts

GitHub Actions / Lint

React Hook useEffect has missing dependencies: 'callback', 'event', and 'unsubscribe'. Either include them or remove the dependency array
return unsubscribe
}