This commit is contained in:
2025-12-31 19:55:43 +01:00
parent 8260bf7baf
commit 167e82a52b
66 changed files with 5124 additions and 228 deletions

View File

@@ -13,23 +13,47 @@ import {
type SignupApplicationError,
type SignupInput,
} from '@core/identity/application/use-cases/SignupUseCase';
import {
ForgotPasswordUseCase,
type ForgotPasswordApplicationError,
type ForgotPasswordInput,
} from '@core/identity/application/use-cases/ForgotPasswordUseCase';
import {
ResetPasswordUseCase,
type ResetPasswordApplicationError,
type ResetPasswordInput,
} from '@core/identity/application/use-cases/ResetPasswordUseCase';
import {
DemoLoginUseCase,
type DemoLoginApplicationError,
type DemoLoginInput,
} from '../../development/use-cases/DemoLoginUseCase';
import type { IdentitySessionPort } from '@core/identity/application/ports/IdentitySessionPort';
import {
AUTH_SESSION_OUTPUT_PORT_TOKEN,
COMMAND_RESULT_OUTPUT_PORT_TOKEN,
FORGOT_PASSWORD_OUTPUT_PORT_TOKEN,
RESET_PASSWORD_OUTPUT_PORT_TOKEN,
DEMO_LOGIN_OUTPUT_PORT_TOKEN,
IDENTITY_SESSION_PORT_TOKEN,
LOGGER_TOKEN,
LOGIN_USE_CASE_TOKEN,
LOGOUT_USE_CASE_TOKEN,
SIGNUP_USE_CASE_TOKEN,
FORGOT_PASSWORD_USE_CASE_TOKEN,
RESET_PASSWORD_USE_CASE_TOKEN,
DEMO_LOGIN_USE_CASE_TOKEN,
} from './AuthProviders';
import type { AuthSessionDTO } from './dtos/AuthDto';
import type { AuthSessionDTO, AuthenticatedUserDTO } from './dtos/AuthDto';
import { LoginParamsDTO, SignupParamsDTO } from './dtos/AuthDto';
import { AuthSessionPresenter } from './presenters/AuthSessionPresenter';
import type { CommandResultDTO } from './presenters/CommandResultPresenter';
import { CommandResultPresenter } from './presenters/CommandResultPresenter';
import { ForgotPasswordPresenter } from './presenters/ForgotPasswordPresenter';
import { ResetPasswordPresenter } from './presenters/ResetPasswordPresenter';
import { DemoLoginPresenter } from './presenters/DemoLoginPresenter';
function mapApplicationErrorToMessage(error: { details?: { message?: string } } | undefined, fallback: string): string {
return error?.details?.message ?? fallback;
@@ -43,11 +67,20 @@ export class AuthService {
@Inject(LOGIN_USE_CASE_TOKEN) private readonly loginUseCase: LoginUseCase,
@Inject(SIGNUP_USE_CASE_TOKEN) private readonly signupUseCase: SignupUseCase,
@Inject(LOGOUT_USE_CASE_TOKEN) private readonly logoutUseCase: LogoutUseCase,
@Inject(FORGOT_PASSWORD_USE_CASE_TOKEN) private readonly forgotPasswordUseCase: ForgotPasswordUseCase,
@Inject(RESET_PASSWORD_USE_CASE_TOKEN) private readonly resetPasswordUseCase: ResetPasswordUseCase,
@Inject(DEMO_LOGIN_USE_CASE_TOKEN) private readonly demoLoginUseCase: DemoLoginUseCase,
// TODO presenters must not be injected
@Inject(AUTH_SESSION_OUTPUT_PORT_TOKEN)
private readonly authSessionPresenter: AuthSessionPresenter,
@Inject(COMMAND_RESULT_OUTPUT_PORT_TOKEN)
private readonly commandResultPresenter: CommandResultPresenter,
@Inject(FORGOT_PASSWORD_OUTPUT_PORT_TOKEN)
private readonly forgotPasswordPresenter: ForgotPasswordPresenter,
@Inject(RESET_PASSWORD_OUTPUT_PORT_TOKEN)
private readonly resetPasswordPresenter: ResetPasswordPresenter,
@Inject(DEMO_LOGIN_OUTPUT_PORT_TOKEN)
private readonly demoLoginPresenter: DemoLoginPresenter,
) {}
async getCurrentSession(): Promise<AuthSessionDTO | null> {
@@ -189,4 +222,94 @@ export class AuthService {
},
};
}
async forgotPassword(params: { email: string }): Promise<{ message: string; magicLink?: string }> {
this.logger.debug(`[AuthService] Attempting forgot password for email: ${params.email}`);
this.forgotPasswordPresenter.reset();
const input: ForgotPasswordInput = {
email: params.email,
};
const executeResult = await this.forgotPasswordUseCase.execute(input);
if (executeResult.isErr()) {
const error = executeResult.unwrapErr() as ForgotPasswordApplicationError;
throw new Error(mapApplicationErrorToMessage(error, 'Forgot password failed'));
}
const response = this.forgotPasswordPresenter.responseModel;
const result: { message: string; magicLink?: string } = {
message: response.message,
};
if (response.magicLink) {
result.magicLink = response.magicLink;
}
return result;
}
async resetPassword(params: { token: string; newPassword: string }): Promise<{ message: string }> {
this.logger.debug('[AuthService] Attempting reset password');
this.resetPasswordPresenter.reset();
const input: ResetPasswordInput = {
token: params.token,
newPassword: params.newPassword,
};
const result = await this.resetPasswordUseCase.execute(input);
if (result.isErr()) {
const error = result.unwrapErr() as ResetPasswordApplicationError;
throw new Error(mapApplicationErrorToMessage(error, 'Reset password failed'));
}
return this.resetPasswordPresenter.responseModel;
}
async demoLogin(params: { role: 'driver' | 'sponsor' | 'league-owner' | 'league-steward' | 'league-admin' | 'system-owner' | 'super-admin' }): Promise<AuthSessionDTO> {
this.logger.debug(`[AuthService] Attempting demo login for role: ${params.role}`);
this.demoLoginPresenter.reset();
const input: DemoLoginInput = {
role: params.role,
};
const result = await this.demoLoginUseCase.execute(input);
if (result.isErr()) {
const error = result.unwrapErr() as DemoLoginApplicationError;
throw new Error(mapApplicationErrorToMessage(error, 'Demo login failed'));
}
const user = this.demoLoginPresenter.responseModel.user;
const primaryDriverId = user.getPrimaryDriverId();
// Use primaryDriverId for session if available, otherwise fall back to userId
const sessionId = primaryDriverId ?? user.getId().value;
const session = await this.identitySessionPort.createSession({
id: sessionId,
displayName: user.getDisplayName(),
email: user.getEmail() ?? '',
});
const userDTO: AuthenticatedUserDTO = {
userId: user.getId().value,
email: user.getEmail() ?? '',
displayName: user.getDisplayName(),
};
if (primaryDriverId !== undefined) {
userDTO.primaryDriverId = primaryDriverId;
}
return {
token: session.token,
user: userDTO,
};
}
}