mirror of
https://github.com/supabase/supabase.git
synced 2026-10-08 19:05:06 +03:00
Exposes the access token expiry (`JWT_EXP`) under `Auth -> Sessions` settings as opposed to the Legacy JWT settings previously used. <img width="1632" height="1199" alt="Screenshot 2026-07-23 at 10 06 33" src="https://github.com/user-attachments/assets/85356e57-da95-404c-852a-21cf9cab2b74" /> <img width="1198" height="1119" alt="Screenshot 2026-07-23 at 10 06 19" src="https://github.com/user-attachments/assets/bfa64b3b-1902-45eb-83ed-ca8bc12673af" /> <img width="1237" height="513" alt="Screenshot 2026-07-23 at 10 03 44" src="https://github.com/user-attachments/assets/85779e9b-30f2-48c5-9faa-4c650d450227" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an **Access Tokens** section to configure JWT expiration with dedicated Save/Cancel controls and success/error toasts. * Enforced a maximum JWT expiration value (must be **less than 604800 seconds**). * **Bug Fixes** * Updated the Sessions auth page text to better clarify configuration for access tokens, refresh tokens, and user sessions. * **Documentation** * Updated JWT expiration guidance to point to **Auth settings → Access Tokens** (replacing legacy JWT secret references). * **Chores** * Expanded automated tests covering Access Tokens saving and validation. * **Refactor** * Removed JWT expiration editing from the legacy JWT Secrets area, consolidating it under Access Tokens. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
107 lines
3.5 KiB
TypeScript
107 lines
3.5 KiB
TypeScript
import { fireEvent, screen, waitFor, within } from '@testing-library/react'
|
|
import { mockAnimationsApi } from 'jsdom-testing-mocks'
|
|
import { HttpResponse } from 'msw'
|
|
import { describe, expect, test, vi } from 'vitest'
|
|
|
|
import { SessionsAuthSettingsForm } from './SessionsAuthSettingsForm'
|
|
import { customRender } from '@/tests/lib/custom-render'
|
|
import { addAPIMock } from '@/tests/lib/msw'
|
|
|
|
// FormMessage animates via framer-motion, which relies on the Web Animations API.
|
|
mockAnimationsApi()
|
|
|
|
vi.mock('@/lib/constants', async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import('@/lib/constants')>()
|
|
return { ...actual, IS_PLATFORM: true }
|
|
})
|
|
|
|
vi.mock('@/hooks/misc/useCheckEntitlements', () => ({
|
|
useCheckEntitlements: () => ({ hasAccess: true, isLoading: false }),
|
|
}))
|
|
|
|
vi.mock('@/hooks/misc/useCheckPermissions', () => ({
|
|
useAsyncCheckPermissions: () => ({ can: true, isLoading: false, isSuccess: true }),
|
|
}))
|
|
|
|
function mockAuthConfig(overrides: Record<string, unknown> = {}) {
|
|
addAPIMock({
|
|
method: 'get',
|
|
path: '/platform/auth/:ref/config',
|
|
response: () =>
|
|
HttpResponse.json<any>({
|
|
JWT_EXP: 3600,
|
|
REFRESH_TOKEN_ROTATION_ENABLED: true,
|
|
SECURITY_REFRESH_TOKEN_REUSE_INTERVAL: 10,
|
|
SESSIONS_TIMEBOX: 0,
|
|
SESSIONS_INACTIVITY_TIMEOUT: 0,
|
|
SESSIONS_SINGLE_PER_USER: false,
|
|
...overrides,
|
|
}),
|
|
})
|
|
}
|
|
|
|
function mockUpdateAuthConfig(onPatch?: (body: unknown) => void) {
|
|
addAPIMock({
|
|
method: 'patch',
|
|
path: '/platform/auth/:ref/config',
|
|
response: async ({ request }) => {
|
|
const body = await request.json()
|
|
onPatch?.(body)
|
|
return HttpResponse.json<any>({ ...(body as object) })
|
|
},
|
|
})
|
|
}
|
|
|
|
describe('SessionsAuthSettingsForm — Access Tokens', () => {
|
|
test('renders the access token expiry from the auth config', async () => {
|
|
mockAuthConfig({ JWT_EXP: 3600 })
|
|
|
|
customRender(<SessionsAuthSettingsForm />)
|
|
|
|
expect(await screen.findByText('Access Tokens')).toBeInTheDocument()
|
|
expect(await screen.findByDisplayValue('3600')).toBeInTheDocument()
|
|
expect(screen.getByText('Access token expiry time')).toBeInTheDocument()
|
|
})
|
|
|
|
test('saving a new expiry issues a PATCH with the JWT_EXP payload', async () => {
|
|
mockAuthConfig({ JWT_EXP: 3600 })
|
|
let patchBody: unknown
|
|
mockUpdateAuthConfig((body) => {
|
|
patchBody = body
|
|
})
|
|
|
|
customRender(<SessionsAuthSettingsForm />)
|
|
|
|
const input = await screen.findByDisplayValue('3600')
|
|
fireEvent.change(input, { target: { value: '7200' } })
|
|
|
|
const form = input.closest('form') as HTMLFormElement
|
|
const saveButton = within(form).getByRole('button', { name: 'Save changes' })
|
|
await waitFor(() => expect(saveButton).toBeEnabled())
|
|
fireEvent.click(saveButton)
|
|
|
|
await waitFor(() => expect(patchBody).toEqual({ JWT_EXP: 7200 }))
|
|
})
|
|
|
|
test('blocks submit when the expiry exceeds the maximum', async () => {
|
|
mockAuthConfig({ JWT_EXP: 3600 })
|
|
let patchCalled = false
|
|
mockUpdateAuthConfig(() => {
|
|
patchCalled = true
|
|
})
|
|
|
|
customRender(<SessionsAuthSettingsForm />)
|
|
|
|
const input = await screen.findByDisplayValue('3600')
|
|
fireEvent.change(input, { target: { value: '999999' } })
|
|
|
|
const form = input.closest('form') as HTMLFormElement
|
|
const saveButton = within(form).getByRole('button', { name: 'Save changes' })
|
|
await waitFor(() => expect(saveButton).toBeEnabled())
|
|
fireEvent.click(saveButton)
|
|
|
|
expect(await screen.findByText('Must be less than 604800')).toBeInTheDocument()
|
|
expect(patchCalled).toBe(false)
|
|
})
|
|
})
|