An access token can be obtained using basic authentication with:
POST /api/v1/token
Important
External clients are required to activate a subscription via:
POST /api/v1/clients/{key}/subscriptions
Note
The access token can be validated via:
POST /api/v1/token/status.
Access and refresh tokens can be obtained using:
POST /api/v2/token
-
An OTP can be requested via:
POST /api/v1/otp -
The OTP can be exchanged for access and refresh tokens via:
POST /api/v3/token
Important
To authenticate with OTP, users are required to verify their email via:
POST /api/v1/email/verification/{token}
Note
The access token can be validated via:
POST /api/v1/token/status.
Tip
The access token can be refreshed via:
PUT /api/v2/token
SDK: .NET Core 10
Databases:SQL Server 2022
ORM:Entity Framework Core
Caching:Redis
Message Brokers:RabbitMQ
Additional Libraries:Serilog,Polly,AutoMapper,GoogleAuthenticator
Tests:xUnit,FluentAssertions,Bogus,Respawn
Important
The appsettings.json is validated during API startup.
Important
Database creation and schema management are handled through Entity Framework Core migrations at runtime.
Important
Initialization scripts are automatically executed using DbUp at runtime.
Tip
The application supports updating appsettings.json at runtime without requiring a restart.
Note
The application follows Clean Architecture.
| Functionality | Description | Reference |
|---|---|---|
| Documentation | Implemented using Swagger. |
SwaggerExtensions.cs |
| Supported Headers | X-Request-IDX-Correlation-ID |
HttpHeaderHandlingMiddleware.cs |
| Logger | Global exception logging is handled from ExceptionHandlingMiddleware. Global HTTP request and response logging is handled from HttpMessageLoggingMiddleware. Logged data can be controlled with SkipLogAttribute and SensitiveDataAttribute. |
LoggerSetup.cs |
| Data Mapping | Used AutoMapper for request/response mappings.Used manual mapping for entity mappings. |
Request/Response Mappings Entity Mappings |
| Paginated Report | For retrieval of both customers and users. | PaginatedReportService.cs |
| RabbitMQ | Used for asynchronous communication with Notify API for sending notifications to users. Added resilience using Polly. |
RabbitMqSetup.cs RabbitMqService.cs RabbitMqExtensions RabbitMqEventAttribute.cs |
| Functionality | Description | Reference |
|---|---|---|
| Tokens | Implemented using the Strategy Pattern. | SecurityTokenHandler.cs |
| Custom Authorization | Set by AuthorizeUserAttribute. | UserAuthorizationHandler.cs |
| Rate Limiter | Used with fixed window counter, configurable by appsettings::Security::RateLimiter. |
AddRateLimiter() |
| User Password | Hashed with Pbkdf2. |
Pbkdf2Key.cs |
| Client Contracts | Encrypted using AES when saved and decrypted upon download.A checksum is calculated for each file using MD5. |
CreateAndAesEncryptAsync() ReadAndAesDecryptAllBytesAsync() ComputeMd5Checksum() |
| Functionality | Description | Reference |
|---|---|---|
| Redis | Distributed caching. | RedisService.cs RedisKeyBuilder.cs |
| Functionality | Description | Reference |
|---|---|---|
| Unit Of Work Pattern | Manages all access to the database. | UnitOfWork.cs |
| Repository Pattern | The full list of repositories can be viewed here. | Repository.cs |
| Migrations | Automatically executes pending migrations at startup. This functionality is configurable by appsettings::Database::IsDbMigrationAllowed. |
ApplyDbPendingMigrationsAsync() |
| DbUp | Automatically executes pending scripts at startup. This functionality is configurable by appsettings::Database::IsDbUpAllowed. |
ApplyDbPendingMigrationsAsync() |
- Client credentials are received in the
Authorizationheader using the format:
Basic <base64_encoded_key>:<base64_encoded_secret> - The credentials from the header are decoded.
- A database query is executed to fetch client data using the provided
key. - If the
keyexists, theclient statusis validated. - If the
client statusis acceptable, the system checks for an activesubscription. - If there is an active
subscription, the storedsecretis compared with the provided secret. - If the
secretis valid, thefailed login attempt counteris reset. - An
access token(JWT) is generated, scoped to theclient IDand the applications the client is allowed to access.
Warning
Invalid key or secret results in HTTP status code 401 Unauthorized.
Warning
Invalid client status or missing active subscription results in HTTP status code 403 Forbidden.
Caution
If the failed login attempt counter exceeds the allowed limit, the client status is updated to BLOCKED.
- User
usernameandpasswordare received in the request body. - A database query is executed to fetch user data using the provided
username. - If the
usernameexists, theuser statusis validated. - If the
user statusis acceptable, the providedpasswordis hashed using the same method as the stored one. - The hashed
passwordis compared with the stored password. - If the passwords match, the
failed login attempt counteris reset. - An
access token(JWT) is generated, scoped to theuser ID,username,user roleanduser status. - A
refresh token(JWT) is generated, scoped to theuser ID.
Note
An event is sent when a login attempt is made through a new IP address.
Warning
Invalid username or password results in HTTP status code 401 Unauthorized.
Warning
Invalid user status results in HTTP status code 403 Forbidden.
Caution
If the failed login attempt counter exceeds the allowed limit, the user status is updated to BLOCKED and an alert for login attempt is registered.
- User
usernameandpasswordare received in the request body. - A database query is executed to fetch user data using the provided
username. - If the
usernameexists, theuser statusis validated. - If the
user statusis acceptable, the providedpasswordis hashed using the same method as the stored one. - The hashed
passwordis compared with the stored password. - If the passwords match, the
failed login attempt counteris reset. - A
one-time passwordis generated and saved toRedis. - A message is registered to send the
one-time passwordto the user via the preferred notification provider. - The user receives the
one-time passwordand enters it. - An attempt is made to fetch the
one-time passwordfromRedis. - If the
one-time passwordis valid and not expired, it is deleted fromRedis. - An
access token(JWT) is generated, scoped to theuser ID,username,user roleanduser status. - A
refresh token(JWT) is generated, scoped to theuser ID.
Note
Each one-time password is stored in Redis with an absolute expiration.
Warning
Invalid username or password or one-time password results in HTTP status code 401 Unauthorized.
Warning
Invalid user status results in HTTP status code 403 Forbidden.
Caution
A failed login attempt counter is maintained in Redis per one-time password. If the counter exceeds the allowed limit, the one-time password is deleted.

