sync fix - fallback MSAL
This commit is contained in:
parent
a88b2ea93e
commit
a62ada936d
|
|
@ -47,6 +47,8 @@ UI in Settings → "Browser-Authentifizierung" section.
|
||||||
|
|
||||||
Use case: avoids MSAL silent refresh failures (Azure AD returning HTTP 400 on refresh token).
|
Use case: avoids MSAL silent refresh failures (Azure AD returning HTTP 400 on refresh token).
|
||||||
|
|
||||||
|
**401 auto-invalidation:** `authFetch` in `apiClient.ts` detects 401 responses when a browser key was used and calls `setBrowserApiKey(null)` — clears `localStorage` and the store. `isAuthenticated` becomes `false`, restoring the MSAL login UI. Prevents the stuck state where a revoked/expired key blocks both sync and manual login.
|
||||||
|
|
||||||
## Deploy
|
## Deploy
|
||||||
|
|
||||||
`deploy.ps1` uses normal `docker build` with layer caching. Only changed layers are pushed to ACR.
|
`deploy.ps1` uses normal `docker build` with layer caching. Only changed layers are pushed to ACR.
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
1.1.59
|
1.1.62
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import { getAccessToken } from './authStore.js';
|
import { getAccessToken, handleUnauthorized } from './authStore.js';
|
||||||
|
|
||||||
export async function authFetch(url: string, options: RequestInit = {}): Promise<Response> {
|
export async function authFetch(url: string, options: RequestInit = {}): Promise<Response> {
|
||||||
const token = await getAccessToken();
|
const token = await getAccessToken();
|
||||||
const headers = new Headers(options.headers);
|
const headers = new Headers(options.headers);
|
||||||
headers.set('Authorization', `Bearer ${token}`);
|
headers.set('Authorization', `Bearer ${token}`);
|
||||||
return fetch(url, { ...options, headers });
|
const response = await fetch(url, { ...options, headers });
|
||||||
|
if (response.status === 401) handleUnauthorized();
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,14 @@ export function setBrowserApiKey(key: string | null): void {
|
||||||
browserApiKey.set(key);
|
browserApiKey.set(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call after a 401 response to clear a stale browser key and restore MSAL login UI.
|
||||||
|
export function handleUnauthorized(): void {
|
||||||
|
if (get(browserApiKey) !== null) {
|
||||||
|
console.warn('[auth] 401 received — clearing browser API key to allow MSAL fallback');
|
||||||
|
setBrowserApiKey(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const DEV_ACCOUNT = {
|
const DEV_ACCOUNT = {
|
||||||
homeAccountId: 'dev-user',
|
homeAccountId: 'dev-user',
|
||||||
environment: 'localhost',
|
environment: 'localhost',
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import { getAccessToken } from '$lib/auth/authStore';
|
import { getAccessToken, handleUnauthorized } from '$lib/auth/authStore';
|
||||||
|
|
||||||
export type AiLockStatus = { locked: false } | { locked: true; lockedAt: string; expiresAt: string };
|
export type AiLockStatus = { locked: false } | { locked: true; lockedAt: string; expiresAt: string };
|
||||||
|
|
||||||
|
|
@ -9,7 +9,7 @@ const API_BASE = import.meta.env.VITE_API_URL ?? '';
|
||||||
|
|
||||||
async function apiFetch(path: string, init: RequestInit = {}): Promise<Response> {
|
async function apiFetch(path: string, init: RequestInit = {}): Promise<Response> {
|
||||||
const token = await getAccessToken();
|
const token = await getAccessToken();
|
||||||
return fetch(`${API_BASE}${path}`, {
|
const res = await fetch(`${API_BASE}${path}`, {
|
||||||
...init,
|
...init,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|
@ -17,6 +17,8 @@ async function apiFetch(path: string, init: RequestInit = {}): Promise<Response>
|
||||||
...(init.headers ?? {}),
|
...(init.headers ?? {}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
if (res.status === 401) handleUnauthorized();
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function refreshLockStatus(): Promise<void> {
|
export async function refreshLockStatus(): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import { db } from '$lib/db/schema';
|
import { db } from '$lib/db/schema';
|
||||||
import { getAccessToken } from '$lib/auth/authStore';
|
import { getAccessToken, handleUnauthorized } from '$lib/auth/authStore';
|
||||||
import { aiLockStatus } from '$lib/stores/aiLock';
|
import { aiLockStatus } from '$lib/stores/aiLock';
|
||||||
import type { SyncPushRequest, SyncPullResponse } from '@ka-note/shared';
|
import type { SyncPushRequest, SyncPullResponse } from '@ka-note/shared';
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ async function apiFetch(path: string, init: RequestInit): Promise<Response> {
|
||||||
const payload = JSON.parse(atob(token.split('.')[1]));
|
const payload = JSON.parse(atob(token.split('.')[1]));
|
||||||
console.log('[sync] token aud:', payload.aud, '| scp:', payload.scp, '| exp:', new Date(payload.exp * 1000).toISOString());
|
console.log('[sync] token aud:', payload.aud, '| scp:', payload.scp, '| exp:', new Date(payload.exp * 1000).toISOString());
|
||||||
} catch { /* ignore */ }
|
} catch { /* ignore */ }
|
||||||
return fetch(`${API_BASE}${path}`, {
|
const res = await fetch(`${API_BASE}${path}`, {
|
||||||
...init,
|
...init,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|
@ -62,6 +62,8 @@ async function apiFetch(path: string, init: RequestInit): Promise<Response> {
|
||||||
...(init.headers ?? {}),
|
...(init.headers ?? {}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
if (res.status === 401) handleUnauthorized();
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function pullAndMerge(since: Date | null): Promise<string> {
|
async function pullAndMerge(since: Date | null): Promise<string> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue