Skip to content

Commit d65c5c5

Browse files
authored
Merge pull request #30 from Azm-Tech/feat/add-otp-verification
Feat/add otp verification
2 parents c93374c + 629dd62 commit d65c5c5

41 files changed

Lines changed: 4665 additions & 7 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

backend/src/CCE.Api.Common/Localization/Resources.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,31 @@ CONTENT_UPDATE_FAILED:
412412
ar: "عذراً، حدثت مشكلة أثناء تحديث المحتوى"
413413
en: "Sorry, a problem occurred while updating the content"
414414

415+
OTP_NOT_FOUND:
416+
ar: "طلب التحقق غير موجود."
417+
en: "Verification request not found."
418+
OTP_EXPIRED:
419+
ar: "انتهت صلاحية رمز التحقق. يرجى طلب رمز جديد."
420+
en: "The verification code has expired. Please request a new one."
421+
OTP_INVALID_CODE:
422+
ar: "رمز التحقق غير صحيح."
423+
en: "The verification code is incorrect."
424+
OTP_MAX_ATTEMPTS:
425+
ar: "تجاوزت الحد الأقصى لمحاولات التحقق. يرجى طلب رمز جديد."
426+
en: "Maximum verification attempts reached. Please request a new code."
427+
OTP_COOLDOWN_ACTIVE:
428+
ar: "يرجى الانتظار 60 ثانية قبل طلب رمز جديد."
429+
en: "Please wait 60 seconds before requesting a new code."
430+
OTP_INVALIDATED:
431+
ar: "تم إلغاء صلاحية رمز التحقق هذا."
432+
en: "This verification code has been invalidated."
433+
OTP_SENT:
434+
ar: "تم إرسال رمز التحقق بنجاح."
435+
en: "Verification code sent successfully."
436+
OTP_VERIFIED:
437+
ar: "تم التحقق بنجاح."
438+
en: "Verification successful."
439+
415440
NOTIFICATION_SETTINGS_UPDATED:
416441
ar: "تم تحديث إعدادات الإشعارات بنجاح"
417442
en: "Notification settings updated successfully"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using CCE.Domain.Verification;
2+
3+
namespace CCE.Api.External.Endpoints.Verification;
4+
5+
public sealed record RequestVerificationRequest(
6+
string? Token,
7+
string? ProviderName,
8+
string Contact,
9+
OtpVerificationType TypeId);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using CCE.Api.Common.Extensions;
2+
using CCE.Application.Verification.Commands.RequestVerification;
3+
using CCE.Application.Verification.Commands.VerifyOtp;
4+
using MediatR;
5+
6+
namespace CCE.Api.External.Endpoints.Verification;
7+
8+
public static class VerificationEndpoints
9+
{
10+
public static IEndpointRouteBuilder MapVerificationEndpoints(this IEndpointRouteBuilder app)
11+
{
12+
var verification = app.MapGroup("/verification").WithTags("Verification");
13+
14+
verification.MapPost("/request", async (
15+
RequestVerificationRequest req,
16+
ISender sender,
17+
CancellationToken ct) =>
18+
{
19+
var cmd = new RequestVerificationCommand(
20+
req.Token, req.ProviderName, req.Contact, req.TypeId);
21+
var result = await sender.Send(cmd, ct).ConfigureAwait(false);
22+
return result.ToHttpResult();
23+
})
24+
.AllowAnonymous()
25+
.WithName("RequestVerification");
26+
27+
verification.MapPost("/verify", async (
28+
VerifyOtpRequest req,
29+
ISender sender,
30+
CancellationToken ct) =>
31+
{
32+
var cmd = new VerifyOtpCommand(req.VerificationId, req.Code);
33+
var result = await sender.Send(cmd, ct).ConfigureAwait(false);
34+
return result.ToHttpResult();
35+
})
36+
.AllowAnonymous()
37+
.WithName("VerifyOtp");
38+
39+
return app;
40+
}
41+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
namespace CCE.Api.External.Endpoints.Verification;
2+
3+
public sealed record VerifyOtpRequest(Guid VerificationId, string Code);

backend/src/CCE.Api.External/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using CCE.Api.Common.OpenApi;
99
using CCE.Api.Common.RateLimiting;
1010
using CCE.Api.External.Endpoints;
11+
using CCE.Api.External.Endpoints.Verification;
1112
using CCE.Api.External.Hubs;
1213
using CCE.Application;
1314
using CCE.Infrastructure.Notifications;
@@ -116,6 +117,7 @@
116117
app.MapAboutSettingsPublicEndpoints();
117118
app.MapPoliciesSettingsPublicEndpoints();
118119
app.MapMediaPublicEndpoints();
120+
app.MapVerificationEndpoints();
119121

120122
app.MapGet("/health", async (IMediator mediator) =>
121123
{

backend/src/CCE.Api.External/appsettings.Development.json

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,7 @@
8989
"Seq": {
9090
"ServerUrl": "http://localhost:5341"
9191
},
92-
"Messaging": {
93-
"Transport": "InMemory",
94-
"UseAsyncDispatcher": true
95-
// For RabbitMQ production:
96-
// "Transport": "RabbitMQ",
97-
// "RabbitMqHost": "amqp://guest:guest@localhost",
98-
// "RabbitMqVirtualHost": "/cce-dev"
92+
"Otp": {
93+
"HmacSecret": "3ahs3DvW/rdx+InzjOCpqSUDSFuvyF59sPjziVdeIhE="
9994
}
10095
}

backend/src/CCE.Api.External/appsettings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,8 @@
7373
"ApiKey": "",
7474
"OtlpEndpoint": "http://localhost:5341/ingest/otlp",
7575
"EnableTracing": true
76+
},
77+
"Otp": {
78+
"HmacSecret": "replace-with-32-byte-base64-hmac-secret"
7679
}
7780
}

backend/src/CCE.Api.Internal/appsettings.Development.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,8 @@
7979
},
8080
"Seq": {
8181
"ServerUrl": "http://localhost:5341"
82+
},
83+
"Otp": {
84+
"HmacSecret": "3ahs3DvW/rdx+InzjOCpqSUDSFuvyF59sPjziVdeIhE="
8285
}
8386
}

backend/src/CCE.Api.Internal/appsettings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,8 @@
7373
"ApiKey": "",
7474
"OtlpEndpoint": "http://localhost:5341/ingest/otlp",
7575
"EnableTracing": true
76+
},
77+
"Otp": {
78+
"HmacSecret": "replace-with-32-byte-base64-hmac-secret"
7679
}
7780
}

backend/src/CCE.Application/Common/Interfaces/ICceDbContext.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using CCE.Domain.Notifications;
1010
using CCE.Domain.PlatformSettings;
1111
using CCE.Domain.Surveys;
12+
using CCE.Domain.Verification;
1213
using Microsoft.AspNetCore.Identity;
1314
using DomainCountry = CCE.Domain.Country;
1415

@@ -70,11 +71,16 @@ public interface ICceDbContext
7071
IQueryable<KnowledgePartner> KnowledgePartners { get; }
7172
IQueryable<PolicySection> PolicySections { get; }
7273

74+
// ─── Verification ───
75+
IQueryable<OtpVerification> OtpVerifications { get; }
76+
IQueryable<UserVerification> UserVerifications { get; }
77+
7378
// ─── Media ───
7479
IQueryable<MediaFile> MediaFiles { get; }
7580

7681
// Write operations
7782
void Add<T>(T entity) where T : class;
83+
void Attach<T>(T entity) where T : class;
7884
void Delete<T>(T entity) where T : class;
7985
void DeleteRange<T>(System.Collections.Generic.IEnumerable<T> entities) where T : class;
8086

0 commit comments

Comments
 (0)