mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-02 20:30:10 +00:00
a50fbc721e
Introduce a shared useApiTokens hook to centralize API token CRUD and UI state (fetch, create, delete, copy, formatting). Refactor ApiTab and ApiTokensSection to consume the hook and remove duplicated logic. Add getInstanceUrl utility for client origin used in curl examples. Include an id alias in TokenPayload and add id into generated JWTs across auth routes and providers; update tests accordingly. Improve auth middleware typing and add debug logging around lastUsedAt updates. Add admin logging when creating a token with a role that differs from the target user's role.
73 lines
1.7 KiB
TypeScript
73 lines
1.7 KiB
TypeScript
/**
|
|
* Component: JWT Utilities Tests
|
|
* Documentation: documentation/backend/services/auth.md
|
|
*/
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
import jwt from 'jsonwebtoken';
|
|
import {
|
|
decodeToken,
|
|
generateAccessToken,
|
|
generateRefreshToken,
|
|
verifyAccessToken,
|
|
verifyRefreshToken,
|
|
} from '@/lib/utils/jwt';
|
|
|
|
describe('JWT utilities', () => {
|
|
it('generates and verifies access tokens', () => {
|
|
const token = generateAccessToken({
|
|
sub: 'user-1',
|
|
id: 'user-1',
|
|
plexId: 'plex-1',
|
|
username: 'user',
|
|
role: 'admin',
|
|
});
|
|
|
|
const payload = verifyAccessToken(token);
|
|
|
|
expect(payload?.sub).toBe('user-1');
|
|
expect(payload?.role).toBe('admin');
|
|
});
|
|
|
|
it('returns null for invalid access tokens', () => {
|
|
const payload = verifyAccessToken('bad-token');
|
|
|
|
expect(payload).toBeNull();
|
|
});
|
|
|
|
it('generates and verifies refresh tokens', () => {
|
|
const token = generateRefreshToken('user-2');
|
|
const payload = verifyRefreshToken(token);
|
|
|
|
expect(payload?.sub).toBe('user-2');
|
|
expect(payload?.type).toBe('refresh');
|
|
});
|
|
|
|
it('returns null when refresh token type does not match', () => {
|
|
const invalid = jwt.sign(
|
|
{ sub: 'user-3', type: 'access' },
|
|
'change-this-to-another-random-secret-key',
|
|
{ expiresIn: '7d' }
|
|
);
|
|
|
|
const payload = verifyRefreshToken(invalid);
|
|
|
|
expect(payload).toBeNull();
|
|
});
|
|
|
|
it('decodes tokens without verification', () => {
|
|
const token = generateAccessToken({
|
|
sub: 'user-4',
|
|
id: 'user-4',
|
|
plexId: 'plex-4',
|
|
username: 'user',
|
|
role: 'user',
|
|
});
|
|
|
|
const decoded = decodeToken(token) as { sub?: string } | null;
|
|
|
|
expect(decoded?.sub).toBe('user-4');
|
|
expect(decodeToken('not-a-jwt')).toBeNull();
|
|
});
|
|
});
|