fix(auth): use getValidToken() instead of getAccessToken() for API calls

getAccessToken() reads the stored JWT without checking expiry, causing
401s when the token ages out — especially for services not covered by
the fetch interceptor (credits, events, api, etc.). getValidToken()
checks validity first and refreshes automatically when needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-12 21:26:10 +02:00
parent 4616798d34
commit 16fef572bf
18 changed files with 18 additions and 18 deletions

View file

@ -104,7 +104,7 @@ export async function fetchWithRetry<T>(
for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
try {
// Get fresh token for each attempt
const token = await authStore.getAccessToken();
const token = await authStore.getValidToken();
const response = await fetch(url, {
...options,

View file

@ -56,7 +56,7 @@ export interface CreditPurchase {
// Helper function for authenticated requests
async function fetchWithAuth<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const token = await authStore.getAccessToken();
const token = await authStore.getValidToken();
const response = await fetch(`${getManaCreditsUrl()}${endpoint}`, {
...options,

View file

@ -9,5 +9,5 @@ import { getManaAuthUrl } from './config';
export const feedbackService = createFeedbackService({
apiUrl: getManaAuthUrl(),
appId: 'mana',
getAuthToken: async () => authStore.getAccessToken(),
getAuthToken: async () => authStore.getValidToken(),
});

View file

@ -85,7 +85,7 @@ async function fetchPublic<T>(endpoint: string): Promise<T> {
// Helper function for authenticated requests
async function fetchWithAuth<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const token = await authStore.getAccessToken();
const token = await authStore.getValidToken();
const response = await fetch(`${getManaAuthUrl()}${endpoint}`, {
...options,

View file

@ -41,7 +41,7 @@ export interface AvatarUploadUrlResponse {
// Helper function for authenticated requests
async function fetchWithAuth<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const token = await authStore.getAccessToken();
const token = await authStore.getValidToken();
const response = await fetch(`${getManaAuthUrl()}${endpoint}`, {
...options,

View file

@ -87,7 +87,7 @@ export class ResearchApiError extends Error {
// ─── Internal helpers ───────────────────────────────────────
async function authHeaders(extra: HeadersInit = {}): Promise<HeadersInit> {
const token = await authStore.getAccessToken();
const token = await authStore.getValidToken();
return {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),

View file

@ -74,7 +74,7 @@ export const myDataService = {
*/
async downloadMyData(): Promise<void> {
const baseUrl = getAuthApiUrl();
const token = await authStore.getAccessToken();
const token = await authStore.getValidToken();
// Use fetch with blob response for file download
const response = await fetch(`${baseUrl}/me/data/export`, {

View file

@ -57,7 +57,7 @@ export interface CurrentSubscription {
// Helper function for authenticated requests
async function fetchWithAuth<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const token = await authStore.getAccessToken();
const token = await authStore.getValidToken();
const response = await fetch(`${getManaAuthUrl()}${endpoint}`, {
...options,

View file

@ -26,7 +26,7 @@ export interface SyncActivateResponse {
// Helper
async function fetchWithAuth<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const token = await authStore.getAccessToken();
const token = await authStore.getValidToken();
const response = await fetch(`${getManaCreditsUrl()}${endpoint}`, {
...options,

View file

@ -26,7 +26,7 @@ export function getVaultClient(): VaultClient {
if (!_instance) {
_instance = createVaultClient({
authUrl: getManaAuthUrl(),
getToken: () => authStore.getAccessToken(),
getToken: () => authStore.getValidToken(),
});
}
return _instance;

View file

@ -58,7 +58,7 @@ async function resolveSystemPrompt(templateId: string | undefined): Promise<stri
* Returns an empty object when no token is available (guest mode).
*/
async function authHeader(): Promise<Record<string, string>> {
const token = await authStore.getAccessToken();
const token = await authStore.getValidToken();
return token ? { Authorization: `Bearer ${token}` } : {};
}

View file

@ -32,7 +32,7 @@ export interface PublicRsvpRecord {
}
async function fetchWithAuth<T>(path: string, init: RequestInit = {}): Promise<T> {
const token = await authStore.getAccessToken();
const token = await authStore.getValidToken();
const res = await fetch(`${getManaEventsUrl()}${path}`, {
...init,
headers: {

View file

@ -121,7 +121,7 @@
if (uploading) return;
uploading = true;
const token = await authStore.getAccessToken();
const token = await authStore.getValidToken();
for (let i = 0; i < uploadFiles.length; i++) {
if (uploadFiles[i]!.status !== 'pending') continue;

View file

@ -49,7 +49,7 @@ export interface UploadMealPhotoResult {
}
async function authHeader(): Promise<Record<string, string>> {
const token = await authStore.getAccessToken();
const token = await authStore.getValidToken();
return token ? { Authorization: `Bearer ${token}` } : {};
}

View file

@ -43,7 +43,7 @@ export interface UploadPhotoResult {
}
async function authHeader(): Promise<Record<string, string>> {
const token = await authStore.getAccessToken();
const token = await authStore.getValidToken();
return token ? { Authorization: `Bearer ${token}` } : {};
}

View file

@ -33,7 +33,7 @@
onMount(async () => {
try {
const token = await authStore.getAccessToken();
const token = await authStore.getValidToken();
if (!token) {
loadingDecks = false;
return;

View file

@ -48,7 +48,7 @@ const API_BASE = '/api/v1/who';
* Same pattern as base-client.ts uses for every other API call.
*/
async function postJson<T>(path: string, body: unknown): Promise<T> {
const token = await authStore.getAccessToken();
const token = await authStore.getValidToken();
if (!token) {
guestPrompt.requireAccount(
'who',

View file

@ -24,5 +24,5 @@ function getAuthUrl(): string {
export const userSettings = createUserSettingsStore({
appId: 'mana',
authUrl: getAuthUrl,
getAccessToken: () => authStore.getAccessToken(),
getAccessToken: () => authStore.getValidToken(),
});