dcb-service-core-api/src/modules/auth/auth.service.ts
Mamadou Khoussa [028918 DSI/DAC/DIF/DS] 300a5205df first commit
2025-10-21 23:10:16 +00:00

246 lines
6.4 KiB
TypeScript

import {
Injectable,
UnauthorizedException,
BadRequestException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { PrismaService } from '../../shared/services/prisma.service';
import { OperatorsService } from '../operators/operators.service';
import * as bcrypt from 'bcrypt';
import { AuthInitDto, AuthValidateDto, LoginDto } from './dto/auth.dto';
@Injectable()
export class AuthService {
constructor(
private readonly prisma: PrismaService,
private readonly jwtService: JwtService,
private readonly operatorsService: OperatorsService,
) {}
async initializeUserAuth(partnerId: string, dto: AuthInitDto) {
// Vérifier le partenaire
const partner = await this.prisma.partner.findUnique({
where: { id: partnerId },
});
if (!partner || partner.status !== 'ACTIVE') {
throw new UnauthorizedException('Invalid partner');
}
// Déterminer l'opérateur basé sur le numéro
const operator = this.detectOperator(dto.msisdn, dto.country);
// Obtenir l'adaptateur approprié
const adapter = this.operatorsService.getAdapter(operator, dto.country);
// Initialiser l'authentification avec l'opérateur
const authResponse = await adapter.initializeAuth({
msisdn: dto.msisdn,
country: dto.country,
metadata: dto.metadata,
});
// Créer une session temporaire
const session = await this.prisma.authSession.create({
data: {
sessionId: authResponse.sessionId,
partnerId: partnerId,
msisdn: dto.msisdn,
operator: operator,
country: dto.country,
authMethod: dto.authMethod,
challengeId: authResponse.challengeId,
status: 'PENDING',
expiresAt: authResponse.expiresAt,
},
});
return {
sessionId: session.sessionId,
authMethod: dto.authMethod,
status: 'PENDING',
redirectUrl: authResponse.redirectUrl,
challengeId: authResponse.challengeId,
expiresAt: authResponse.expiresAt,
};
}
async validateUserAuth(dto: AuthValidateDto) {
// Récupérer la session
const session = await this.prisma.authSession.findUnique({
where: { sessionId: dto.sessionId },
});
if (!session) {
throw new BadRequestException('Invalid session');
}
if (session.status !== 'PENDING') {
throw new BadRequestException('Session already processed');
}
if (new Date() > session.expiresAt) {
throw new BadRequestException('Session expired');
}
// Obtenir l'adaptateur
const adapter = this.operatorsService.getAdapter(
session.operator,
session.country,
);
// Valider avec l'opérateur
const validationResponse = await adapter.validateAuth({
challengeId: session.challengeId,
otpCode: dto.otpCode,
msisdn: session.msisdn,
country: session.country,
});
if (!validationResponse.success) {
await this.prisma.authSession.update({
where: { id: session.id },
data: { status: 'FAILED' },
});
throw new UnauthorizedException('Authentication failed');
}
// Créer ou mettre à jour l'utilisateur
const user = await this.prisma.user.upsert({
where: { msisdn: session.msisdn },
update: {
userToken: validationResponse.userToken,
userAlias: validationResponse.userAlias,
updatedAt: new Date(),
},
create: {
msisdn: session.msisdn,
userToken: validationResponse.userToken,
userAlias: validationResponse.userAlias,
operatorId: await this.getOperatorId(session.operator, session.country),
partnerId: session.partnerId,
country: session.country,
},
});
// Mettre à jour la session
await this.prisma.authSession.update({
where: { id: session.id },
data: {
status: 'SUCCESS',
userId: user.id,
},
});
// Créer un JWT pour le partenaire
const payload = {
userId: user.id,
partnerId: session.partnerId,
msisdn: user.msisdn,
operator: session.operator,
};
return {
success: true,
accessToken: this.jwtService.sign(payload),
userToken: validationResponse.userToken,
userAlias: validationResponse.userAlias,
msisdn: session.msisdn,
operator: session.operator,
country: session.country,
expiresAt: validationResponse.expiresAt,
};
}
async loginPartner(dto: LoginDto) {
const partner = await this.prisma.partner.findUnique({
where: { email: dto.email },
});
if (!partner) {
throw new UnauthorizedException('Invalid credentials');
}
const isPasswordValid = await bcrypt.compare(
dto.password,
partner.passwordHash,
);
if (!isPasswordValid) {
throw new UnauthorizedException('Invalid credentials');
}
const payload = {
partnerId: partner.id,
email: partner.email,
type: 'partner',
};
return {
accessToken: this.jwtService.sign(payload),
partner: {
id: partner.id,
name: partner.name,
email: partner.email,
status: partner.status,
},
};
}
private detectOperator(msisdn: string, country: string): string {
// Logique pour détecter l'opérateur basé sur le préfixe
const prefixMap = {
CI: {
'07': 'ORANGE',
'08': 'ORANGE',
'09': 'ORANGE',
'04': 'MTN',
'05': 'MTN',
'06': 'MTN',
'01': 'MOOV',
},
SN: {
'77': 'ORANGE',
'78': 'ORANGE',
'76': 'FREE',
'70': 'EXPRESSO',
},
// Ajouter d'autres pays
};
const countryPrefixes = prefixMap[country];
if (!countryPrefixes) {
throw new BadRequestException(`Country ${country} not supported`);
}
const prefix = msisdn.substring(0, 2);
const operator = countryPrefixes[prefix];
if (!operator) {
throw new BadRequestException(`Cannot detect operator for ${msisdn}`);
}
return operator;
}
private async getOperatorId(
operatorCode: string,
country: string,
): Promise<string> {
const operator = await this.prisma.operator.findFirst({
where: {
code: operatorCode as any,
country: country,
},
});
if (!operator) {
throw new BadRequestException(
`Operator ${operatorCode} not found in ${country}`,
);
}
return operator.id;
}
}