mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-22 18:26:42 +02:00
refactor(auth): centralize auth stores — 21 apps use createManaAuthStore factory
Created createManaAuthStore in @manacore/shared-auth-stores that replaces ~350 lines of duplicated auth.svelte.ts per app with a ~10 line factory call. The factory handles: SSO, passkeys, 2FA, magic links, token management, password reset, sign up/in/out — everything the old stores did. Each app only provides devBackendPort and optional onAuthenticated callback. Before: 21 apps × ~350 lines = 6,800 lines of duplicated auth code After: 21 apps × ~10 lines = 182 lines total (97% reduction) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e35bbcd09b
commit
e7bcb230be
45 changed files with 430 additions and 6802 deletions
|
|
@ -33,6 +33,7 @@
|
|||
"@manacore/local-store": "workspace:*",
|
||||
"@manacore/shared-app-onboarding": "workspace:*",
|
||||
"@manacore/shared-auth": "workspace:*",
|
||||
"@manacore/shared-auth-stores": "workspace:*",
|
||||
"@manacore/shared-auth-ui": "workspace:*",
|
||||
"@manacore/shared-branding": "workspace:*",
|
||||
"@manacore/shared-config": "workspace:*",
|
||||
|
|
|
|||
|
|
@ -1,205 +1,7 @@
|
|||
import { browser } from '$app/environment';
|
||||
import type { ManaUser } from '$lib/types/auth';
|
||||
import { authService, tokenManager } from '$lib/auth';
|
||||
import type { UserData } from '$lib/auth';
|
||||
|
||||
// Svelte 5 runes-based auth store
|
||||
let user = $state<ManaUser | null>(null);
|
||||
let loading = $state(true);
|
||||
|
||||
/**
|
||||
* Convert UserData from shared-auth to ManaUser
|
||||
* Auth Store — uses centralized Mana auth factory.
|
||||
*/
|
||||
function toManaUser(userData: UserData | null): ManaUser | null {
|
||||
if (!userData) return null;
|
||||
return {
|
||||
id: userData.id,
|
||||
email: userData.email,
|
||||
role: userData.role,
|
||||
};
|
||||
}
|
||||
|
||||
export const authStore = {
|
||||
get user() {
|
||||
return user;
|
||||
},
|
||||
get loading() {
|
||||
return loading;
|
||||
},
|
||||
get isAuthenticated() {
|
||||
return !!user;
|
||||
},
|
||||
import { createManaAuthStore } from '@manacore/shared-auth-stores';
|
||||
|
||||
/**
|
||||
* Initialize auth state from stored tokens
|
||||
* Also tries SSO if no local tokens exist (cross-domain authentication)
|
||||
*/
|
||||
async initialize() {
|
||||
loading = true;
|
||||
try {
|
||||
// First, check if we have valid local tokens
|
||||
let isAuth = await authService.isAuthenticated();
|
||||
|
||||
// If not authenticated locally, try SSO (shared session cookie)
|
||||
if (!isAuth) {
|
||||
console.log('No local tokens, trying SSO...');
|
||||
const ssoResult = await authService.trySSO();
|
||||
if (ssoResult.success) {
|
||||
console.log('SSO successful, user authenticated via shared session');
|
||||
isAuth = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isAuth) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = toManaUser(userData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize auth:', error);
|
||||
user = null;
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set user
|
||||
*/
|
||||
setUser(newUser: ManaUser | null) {
|
||||
user = newUser;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sign out
|
||||
*/
|
||||
async signOut() {
|
||||
try {
|
||||
await authService.signOut();
|
||||
user = null;
|
||||
} catch (error) {
|
||||
console.error('Sign out failed:', error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check authentication status
|
||||
*/
|
||||
async checkAuth() {
|
||||
const isAuth = await authService.isAuthenticated();
|
||||
if (!isAuth) {
|
||||
user = null;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = toManaUser(userData);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = toManaUser(userData);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async sendMagicLink(email: string) {
|
||||
return authService.sendMagicLink(email);
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
return authService.isPasskeyAvailable();
|
||||
},
|
||||
|
||||
async signInWithPasskey() {
|
||||
const result = await authService.signInWithPasskey();
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = toManaUser(userData);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
/**
|
||||
* Sign in with email and password
|
||||
*/
|
||||
async signIn(email: string, password: string) {
|
||||
const result = await authService.signIn(email, password);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = toManaUser(userData);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sign up with email and password
|
||||
*/
|
||||
async signUp(email: string, password: string) {
|
||||
// Pass the current app URL for post-verification redirect
|
||||
const sourceAppUrl = browser ? window.location.origin : undefined;
|
||||
const result = await authService.signUp(email, password, sourceAppUrl);
|
||||
if (result.success && !result.needsVerification) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = toManaUser(userData);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Send password reset email
|
||||
*/
|
||||
async forgotPassword(email: string) {
|
||||
const redirectTo = browser ? window.location.origin : undefined;
|
||||
return authService.forgotPassword(email, redirectTo);
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset password with token (from reset email link)
|
||||
*/
|
||||
async resetPasswordWithToken(token: string, newPassword: string) {
|
||||
try {
|
||||
const result = await authService.resetPassword(token, newPassword);
|
||||
if (!result.success) {
|
||||
return { success: false, error: result.error || 'Failed to reset password' };
|
||||
}
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
return { success: false, error: errorMessage };
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Resend verification email
|
||||
*/
|
||||
async resendVerificationEmail(email: string) {
|
||||
const sourceAppUrl = browser ? window.location.origin : undefined;
|
||||
return authService.resendVerificationEmail(email, sourceAppUrl);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get access token for API calls (raw token, no refresh)
|
||||
* @deprecated Use getValidToken() instead for automatic refresh
|
||||
*/
|
||||
async getAccessToken(): Promise<string | null> {
|
||||
return await authService.getAppToken();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a valid access token for API calls
|
||||
* Automatically refreshes if the token is expired or about to expire
|
||||
*/
|
||||
async getValidToken(): Promise<string | null> {
|
||||
return await tokenManager.getValidToken();
|
||||
},
|
||||
};
|
||||
export const authStore = createManaAuthStore();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue