mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-21 06:11:45 +00:00
feat(dashboard): mobile layout, glass UI, context bottom nav + OIDC PKCE (#1079)
* feat(dashboard): mobile layout, glass tiles, plain-text countdown, place photos - Rework the mobile dashboard: cover hero, separate boarding-pass card, trimmed atlas (trips + days only), stacked widgets - New floating bottom tab bar with a centred context-aware + button (new trip / place / journey / entry depending on the page) - Move profile + notifications into a small top strip on the dashboard - Desktop: glassmorphic tiles (light + dark), neutral dark palette, plain-text countdown module, real place photos in the boarding pass * i18n(dashboard): translate new dashboard keys across all locales Fill the dashboard-rework keys (hero, atlas, fx, tz, upcoming, copy dialog, aria labels, countdown) that were left as English placeholders, plus the new startsIn/aria keys, for all 19 languages. * feat(oidc): send PKCE (S256) in the OIDC login flow The OIDC client now generates a code_verifier per login, sends the S256 code_challenge on the authorize request and the code_verifier on the token exchange. Works whether the provider has PKCE optional or required (fixes login against providers that require PKCE, e.g. Pocket ID).
This commit is contained in:
@@ -83,17 +83,20 @@ afterAll(() => {
|
||||
// ── createState / consumeState ────────────────────────────────────────────────
|
||||
|
||||
describe('createState / consumeState', () => {
|
||||
it('OIDC-SVC-001: createState returns a hex token', () => {
|
||||
const state = createState('https://example.com/callback');
|
||||
it('OIDC-SVC-001: createState returns a hex token + PKCE S256 challenge', () => {
|
||||
const { state, codeChallenge } = createState('https://example.com/callback');
|
||||
expect(state).toMatch(/^[0-9a-f]{64}$/);
|
||||
expect(codeChallenge).toMatch(/^[A-Za-z0-9_-]{43}$/); // base64url SHA-256, no padding
|
||||
});
|
||||
|
||||
it('OIDC-SVC-002: consumeState returns stored data and deletes state', () => {
|
||||
const state = createState('https://example.com/callback', 'invite-abc');
|
||||
it('OIDC-SVC-002: consumeState returns stored data (incl. verifier) and deletes state', () => {
|
||||
const { state } = createState('https://example.com/callback', 'invite-abc');
|
||||
const data = consumeState(state);
|
||||
expect(data).not.toBeNull();
|
||||
expect(data!.redirectUri).toBe('https://example.com/callback');
|
||||
expect(data!.inviteToken).toBe('invite-abc');
|
||||
expect(typeof data!.codeVerifier).toBe('string');
|
||||
expect(data!.codeVerifier.length).toBeGreaterThan(20);
|
||||
// State is consumed — second call returns null
|
||||
expect(consumeState(state)).toBeNull();
|
||||
});
|
||||
@@ -103,8 +106,8 @@ describe('createState / consumeState', () => {
|
||||
});
|
||||
|
||||
it('OIDC-SVC-004: two different states do not conflict', () => {
|
||||
const s1 = createState('http://a.example.com');
|
||||
const s2 = createState('http://b.example.com');
|
||||
const { state: s1 } = createState('http://a.example.com');
|
||||
const { state: s2 } = createState('http://b.example.com');
|
||||
expect(s1).not.toBe(s2);
|
||||
expect(consumeState(s1)!.redirectUri).toBe('http://a.example.com');
|
||||
expect(consumeState(s2)!.redirectUri).toBe('http://b.example.com');
|
||||
|
||||
Reference in New Issue
Block a user