parseJwt.spec.ts
2,099 bytes
| 1 | import { describe, it, expect } from 'vitest' |
|---|---|
| 2 | import { parseJwt, getUserIdFromJwt } from '@/utils/parseJwt' |
| 3 | |
| 4 | // Build a fake JWT: header.payload.signature where payload is base64url(JSON) |
| 5 | function makeJwt(payload: Record<string, unknown>): string { |
| 6 | const header = btoa(JSON.stringify({ alg: 'HS256', typ: 'JWT' })) |
| 7 | const body = btoa(JSON.stringify(payload)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '') |
| 8 | return `${header}.${body}.signature` |
| 9 | } |
| 10 | |
| 11 | describe('parseJwt', () => { |
| 12 | it('decodes the payload of a valid JWT', () => { |
| 13 | const token = makeJwt({ sub: 'user-1', email: 'alice@taltech.ee' }) |
| 14 | expect(parseJwt(token)).toEqual({ sub: 'user-1', email: 'alice@taltech.ee' }) |
| 15 | }) |
| 16 | |
| 17 | it('returns null for a garbage token', () => { |
| 18 | expect(parseJwt('not-a-jwt')).toBeNull() |
| 19 | }) |
| 20 | |
| 21 | it('returns null for a token without payload segment', () => { |
| 22 | expect(parseJwt('header')).toBeNull() |
| 23 | }) |
| 24 | |
| 25 | it('handles base64url-safe characters (- and _)', () => { |
| 26 | // {"x":"?>?"} in JSON → base64 contains '+/' which parseJwt must normalize |
| 27 | const token = makeJwt({ x: '?>?' }) |
| 28 | expect(parseJwt(token)).toEqual({ x: '?>?' }) |
| 29 | }) |
| 30 | }) |
| 31 | |
| 32 | describe('getUserIdFromJwt', () => { |
| 33 | const NAME_ID = |
| 34 | 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier' |
| 35 | |
| 36 | it('returns the ASP.NET Core nameidentifier claim when present', () => { |
| 37 | const token = makeJwt({ [NAME_ID]: 'aspnet-id' }) |
| 38 | expect(getUserIdFromJwt(token)).toBe('aspnet-id') |
| 39 | }) |
| 40 | |
| 41 | it('falls back to `sub` when nameidentifier is missing', () => { |
| 42 | const token = makeJwt({ sub: 'sub-id' }) |
| 43 | expect(getUserIdFromJwt(token)).toBe('sub-id') |
| 44 | }) |
| 45 | |
| 46 | it('prefers nameidentifier over sub', () => { |
| 47 | const token = makeJwt({ [NAME_ID]: 'aspnet-id', sub: 'sub-id' }) |
| 48 | expect(getUserIdFromJwt(token)).toBe('aspnet-id') |
| 49 | }) |
| 50 | |
| 51 | it('returns null when both claims are missing', () => { |
| 52 | const token = makeJwt({ email: 'alice@taltech.ee' }) |
| 53 | expect(getUserIdFromJwt(token)).toBeNull() |
| 54 | }) |
| 55 | |
| 56 | it('returns null for an invalid token', () => { |
| 57 | expect(getUserIdFromJwt('broken')).toBeNull() |
| 58 | }) |
| 59 | }) |
| 60 | |