322 lines
9.0 KiB
TypeScript
322 lines
9.0 KiB
TypeScript
import * as odd from '@oddjs/odd';
|
|
import type FileSystem from '@oddjs/odd/fs/index';
|
|
import { checkDataRoot, initializeFilesystem, isUsernameValid, isUsernameAvailable } from './account';
|
|
import { getBackupStatus } from './backup';
|
|
import { Session } from './types';
|
|
import { CryptoAuthService } from './cryptoAuthService';
|
|
import { loadSession, saveSession, clearStoredSession, getStoredUsername } from './sessionPersistence';
|
|
|
|
export class AuthService {
|
|
/**
|
|
* Initialize the authentication state
|
|
*/
|
|
static async initialize(): Promise<{
|
|
session: Session;
|
|
fileSystem: FileSystem | null;
|
|
}> {
|
|
console.log('Initializing authentication...');
|
|
|
|
// First try to load stored session
|
|
const storedSession = loadSession();
|
|
let session: Session;
|
|
let fileSystem: FileSystem | null = null;
|
|
|
|
if (storedSession && storedSession.authed && storedSession.username) {
|
|
console.log('Found stored session for:', storedSession.username);
|
|
|
|
// Try to restore ODD session with stored username
|
|
try {
|
|
const program = await odd.program({
|
|
namespace: { creator: 'mycrozine', name: 'app' },
|
|
username: storedSession.username
|
|
});
|
|
|
|
if (program.session) {
|
|
// ODD session restored successfully
|
|
fileSystem = program.session.fs;
|
|
const backupStatus = await getBackupStatus(fileSystem);
|
|
session = {
|
|
username: storedSession.username,
|
|
authed: true,
|
|
loading: false,
|
|
backupCreated: backupStatus.created
|
|
};
|
|
console.log('ODD session restored successfully');
|
|
} else {
|
|
// ODD session not available, but we have crypto auth
|
|
session = {
|
|
username: storedSession.username,
|
|
authed: true,
|
|
loading: false,
|
|
backupCreated: storedSession.backupCreated
|
|
};
|
|
console.log('Using stored session without ODD');
|
|
}
|
|
} catch (oddError) {
|
|
console.warn('ODD session restoration failed, using stored session:', oddError);
|
|
session = {
|
|
username: storedSession.username,
|
|
authed: true,
|
|
loading: false,
|
|
backupCreated: storedSession.backupCreated
|
|
};
|
|
}
|
|
} else {
|
|
// No stored session, try ODD initialization
|
|
try {
|
|
const program = await odd.program({
|
|
namespace: { creator: 'mycrozine', name: 'app' }
|
|
});
|
|
|
|
if (program.session) {
|
|
fileSystem = program.session.fs;
|
|
const backupStatus = await getBackupStatus(fileSystem);
|
|
session = {
|
|
username: program.session.username,
|
|
authed: true,
|
|
loading: false,
|
|
backupCreated: backupStatus.created
|
|
};
|
|
} else {
|
|
session = {
|
|
username: '',
|
|
authed: false,
|
|
loading: false,
|
|
backupCreated: null
|
|
};
|
|
}
|
|
} catch (error) {
|
|
console.error('Authentication initialization error:', error);
|
|
session = {
|
|
username: '',
|
|
authed: false,
|
|
loading: false,
|
|
backupCreated: null,
|
|
error: String(error)
|
|
};
|
|
}
|
|
}
|
|
|
|
return { session, fileSystem };
|
|
}
|
|
|
|
/**
|
|
* Login with a username using cryptographic authentication
|
|
*/
|
|
static async login(username: string): Promise<{
|
|
success: boolean;
|
|
session?: Session;
|
|
fileSystem?: FileSystem;
|
|
error?: string;
|
|
}> {
|
|
try {
|
|
// First try cryptographic authentication
|
|
const cryptoResult = await CryptoAuthService.login(username);
|
|
|
|
if (cryptoResult.success && cryptoResult.session) {
|
|
// If crypto auth succeeds, also try to load ODD session
|
|
try {
|
|
const program = await odd.program({
|
|
namespace: { creator: 'mycrozine', name: 'app' },
|
|
username
|
|
});
|
|
|
|
if (program.session) {
|
|
const fs = program.session.fs;
|
|
const backupStatus = await getBackupStatus(fs);
|
|
|
|
return {
|
|
success: true,
|
|
session: {
|
|
username,
|
|
authed: true,
|
|
loading: false,
|
|
backupCreated: backupStatus.created
|
|
},
|
|
fileSystem: fs
|
|
};
|
|
}
|
|
} catch (oddError) {
|
|
console.warn('ODD session not available, using crypto auth only:', oddError);
|
|
}
|
|
|
|
// Return crypto auth result if ODD is not available
|
|
const session = cryptoResult.session;
|
|
if (session) {
|
|
saveSession(session);
|
|
}
|
|
return {
|
|
success: true,
|
|
session: cryptoResult.session,
|
|
fileSystem: undefined
|
|
};
|
|
}
|
|
|
|
// Fallback to ODD authentication
|
|
const program = await odd.program({
|
|
namespace: { creator: 'mycrozine', name: 'app' },
|
|
username
|
|
});
|
|
|
|
if (program.session) {
|
|
const fs = program.session.fs;
|
|
const backupStatus = await getBackupStatus(fs);
|
|
|
|
const session = {
|
|
username,
|
|
authed: true,
|
|
loading: false,
|
|
backupCreated: backupStatus.created
|
|
};
|
|
saveSession(session);
|
|
|
|
return {
|
|
success: true,
|
|
session,
|
|
fileSystem: fs
|
|
};
|
|
} else {
|
|
return {
|
|
success: false,
|
|
error: cryptoResult.error || 'Failed to authenticate'
|
|
};
|
|
}
|
|
} catch (error) {
|
|
console.error('Login error:', error);
|
|
return {
|
|
success: false,
|
|
error: String(error)
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register a new user with cryptographic authentication
|
|
*/
|
|
static async register(username: string): Promise<{
|
|
success: boolean;
|
|
session?: Session;
|
|
fileSystem?: FileSystem;
|
|
error?: string;
|
|
}> {
|
|
try {
|
|
// Validate username
|
|
const valid = await isUsernameValid(username);
|
|
if (!valid) {
|
|
return {
|
|
success: false,
|
|
error: 'Invalid username format'
|
|
};
|
|
}
|
|
|
|
// First try cryptographic registration
|
|
const cryptoResult = await CryptoAuthService.register(username);
|
|
|
|
if (cryptoResult.success && cryptoResult.session) {
|
|
// If crypto registration succeeds, also try to create ODD session
|
|
try {
|
|
const program = await odd.program({
|
|
namespace: { creator: 'mycrozine', name: 'app' },
|
|
username
|
|
});
|
|
|
|
if (program.session) {
|
|
const fs = program.session.fs;
|
|
|
|
// Initialize filesystem with required directories
|
|
await initializeFilesystem(fs);
|
|
|
|
// Check backup status
|
|
const backupStatus = await getBackupStatus(fs);
|
|
|
|
return {
|
|
success: true,
|
|
session: {
|
|
username,
|
|
authed: true,
|
|
loading: false,
|
|
backupCreated: backupStatus.created
|
|
},
|
|
fileSystem: fs
|
|
};
|
|
}
|
|
} catch (oddError) {
|
|
console.warn('ODD session creation failed, using crypto auth only:', oddError);
|
|
}
|
|
|
|
// Return crypto registration result if ODD is not available
|
|
const session = cryptoResult.session;
|
|
if (session) {
|
|
saveSession(session);
|
|
}
|
|
return {
|
|
success: true,
|
|
session: cryptoResult.session,
|
|
fileSystem: undefined
|
|
};
|
|
}
|
|
|
|
// Fallback to ODD-only registration
|
|
const program = await odd.program({
|
|
namespace: { creator: 'mycrozine', name: 'app' },
|
|
username
|
|
});
|
|
|
|
if (program.session) {
|
|
const fs = program.session.fs;
|
|
|
|
// Initialize filesystem with required directories
|
|
await initializeFilesystem(fs);
|
|
|
|
// Check backup status
|
|
const backupStatus = await getBackupStatus(fs);
|
|
|
|
const session = {
|
|
username,
|
|
authed: true,
|
|
loading: false,
|
|
backupCreated: backupStatus.created
|
|
};
|
|
saveSession(session);
|
|
return {
|
|
success: true,
|
|
session,
|
|
fileSystem: fs
|
|
};
|
|
} else {
|
|
return {
|
|
success: false,
|
|
error: cryptoResult.error || 'Failed to create account'
|
|
};
|
|
}
|
|
} catch (error) {
|
|
console.error('Registration error:', error);
|
|
return {
|
|
success: false,
|
|
error: String(error)
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Logout the current user
|
|
*/
|
|
static async logout(): Promise<boolean> {
|
|
try {
|
|
// Clear stored session
|
|
clearStoredSession();
|
|
|
|
// Try to destroy ODD session
|
|
try {
|
|
await odd.session.destroy();
|
|
} catch (oddError) {
|
|
console.warn('ODD session destroy failed:', oddError);
|
|
}
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error('Logout error:', error);
|
|
return false;
|
|
}
|
|
}
|
|
}
|