diff --git a/apps/mana/apps/web/src/lib/api/credits.ts b/apps/mana/apps/web/src/lib/api/credits.ts index 3daf227c5..b1425caf9 100644 --- a/apps/mana/apps/web/src/lib/api/credits.ts +++ b/apps/mana/apps/web/src/lib/api/credits.ts @@ -11,6 +11,14 @@ export interface CreditBalance { balance: number; totalEarned: number; totalSpent: number; + /** + * Daily-free-credit accounting. Optional because the backend only + * returns these fields when the user has a free-tier allowance + * configured (paying users get them too but with `dailyFreeCredits = 0`). + * Settings UIs render the "free today" tile only when both are present. + */ + freeCreditsRemaining?: number; + dailyFreeCredits?: number; } export interface CreditTransaction { diff --git a/apps/mana/apps/web/src/routes/(app)/settings/+page.svelte b/apps/mana/apps/web/src/routes/(app)/settings/+page.svelte index 0c8ac30ab..720c4f05a 100644 --- a/apps/mana/apps/web/src/routes/(app)/settings/+page.svelte +++ b/apps/mana/apps/web/src/routes/(app)/settings/+page.svelte @@ -274,7 +274,7 @@
Deine eindeutige Kennung
- {authStore.user?.sub?.slice(0, 8) || '...'}...
+ {authStore.user?.id?.slice(0, 8) || '...'}...
diff --git a/packages/shared-auth-ui/src/stores/createManaAuthStore.svelte.ts b/packages/shared-auth-ui/src/stores/createManaAuthStore.svelte.ts
index 0ec2436d8..db6606983 100644
--- a/packages/shared-auth-ui/src/stores/createManaAuthStore.svelte.ts
+++ b/packages/shared-auth-ui/src/stores/createManaAuthStore.svelte.ts
@@ -314,6 +314,71 @@ export function createManaAuthStore(config: ManaAuthStoreConfig = {}) {
return { success: false, error: error instanceof Error ? error.message : 'Unknown error' };
}
},
+
+ // Passkey CRUD — thin passthroughs to authService. The settings page
+ // (and any other consumer) needs these to render the PasskeyManager
+ // UI. Each method swallows the SSR/no-service case the same way the
+ // rest of the wrapper does.
+ async listPasskeys() {
+ const authService = getAuthService();
+ if (!authService) return [] as unknown[];
+ return authService.listPasskeys();
+ },
+ async registerPasskey(friendlyName?: string) {
+ const authService = getAuthService();
+ if (!authService) return { success: false, error: 'Auth not available on server' };
+ return authService.registerPasskey(friendlyName);
+ },
+ async deletePasskey(passkeyId: string) {
+ const authService = getAuthService();
+ if (!authService) return { success: false, error: 'Auth not available on server' };
+ return authService.deletePasskey(passkeyId);
+ },
+ async renamePasskey(passkeyId: string, friendlyName: string) {
+ const authService = getAuthService();
+ if (!authService) return { success: false, error: 'Auth not available on server' };
+ return authService.renamePasskey(passkeyId, friendlyName);
+ },
+
+ // Two-factor passthroughs. enableTwoFactor refreshes the local user
+ // snapshot on success because the JWT issued post-enrollment carries
+ // the new flag and downstream UI gates on it.
+ async enableTwoFactor(password: string) {
+ const authService = getAuthService();
+ if (!authService) return { success: false, error: 'Auth not available on server' };
+ const result = await authService.enableTwoFactor(password);
+ if (result.success) user = await authService.getUserFromToken();
+ return result;
+ },
+ async disableTwoFactor(password: string) {
+ const authService = getAuthService();
+ if (!authService) return { success: false, error: 'Auth not available on server' };
+ const result = await authService.disableTwoFactor(password);
+ if (result.success) user = await authService.getUserFromToken();
+ return result;
+ },
+ async generateBackupCodes(password: string) {
+ const authService = getAuthService();
+ if (!authService) return { success: false, error: 'Auth not available on server' };
+ return authService.generateBackupCodes(password);
+ },
+
+ // Sessions + audit log passthroughs.
+ async listSessions() {
+ const authService = getAuthService();
+ if (!authService) return [] as unknown[];
+ return authService.listSessions();
+ },
+ async revokeSession(sessionId: string) {
+ const authService = getAuthService();
+ if (!authService) return { success: false, error: 'Auth not available on server' };
+ return authService.revokeSession(sessionId);
+ },
+ async getSecurityEvents(limit?: number) {
+ const authService = getAuthService();
+ if (!authService) return [] as unknown[];
+ return authService.getSecurityEvents(limit);
+ },
};
}
diff --git a/packages/shared-auth/src/types/index.ts b/packages/shared-auth/src/types/index.ts
index f57bf412b..515ca0c82 100644
--- a/packages/shared-auth/src/types/index.ts
+++ b/packages/shared-auth/src/types/index.ts
@@ -52,6 +52,13 @@ export interface UserData {
email: string;
role: string;
tier: string;
+ /**
+ * Whether 2FA is enrolled. Optional because the field is only present
+ * when the JWT was minted with the `twofa` claim — guest tokens and
+ * legacy sessions omit it. Settings UIs that gate the "enable 2FA"
+ * button on this should default to `false` when undefined.
+ */
+ twoFactorEnabled?: boolean;
}
/**