{"id":28000695,"url":"https://github.com/edcsu/kycverificationapi","last_synced_at":"2025-07-22T19:35:08.751Z","repository":{"id":291728642,"uuid":"978565542","full_name":"edcsu/KYCVerificationAPI","owner":"edcsu","description":"Software Developer Take-Home Case","archived":false,"fork":false,"pushed_at":"2025-05-22T16:11:17.000Z","size":227,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-22T17:37:35.236Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/edcsu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-06T07:21:58.000Z","updated_at":"2025-05-22T16:11:21.000Z","dependencies_parsed_at":"2025-05-08T23:54:17.810Z","dependency_job_id":"587560c6-cb32-4d73-bd99-0f015cbbcb47","html_url":"https://github.com/edcsu/KYCVerificationAPI","commit_stats":null,"previous_names":["edcsu/kycverificationapi"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/edcsu/KYCVerificationAPI","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edcsu%2FKYCVerificationAPI","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edcsu%2FKYCVerificationAPI/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edcsu%2FKYCVerificationAPI/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edcsu%2FKYCVerificationAPI/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/edcsu","download_url":"https://codeload.github.com/edcsu/KYCVerificationAPI/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edcsu%2FKYCVerificationAPI/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266561469,"owners_count":23948632,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-05-08T23:54:15.716Z","updated_at":"2025-07-22T19:35:08.741Z","avatar_url":"https://github.com/edcsu.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🔏 KYC Verification API\n\nA simple yet functional KYC (Know Your Customer) verification REST API that is built using **.NET 9**.\nIt is designed to simulate the process of validating a user's identity against a national ID system. \nThe project highlights my ability to solve real-world problems through [vertical slice architecture](https://blog.ndepend.com/vertical-slice-architecture-in-asp-net-core/).\n\n## 📑 Table of Contents\n\n- [Why This Feature](#-why-this-feature)\n- [Prerequisites](#-prerequisites)\n- [Getting Started](#-getting-started)\n- [API Endpoints](#api-endpoints)\n   - [Verifications](#-verifications)\n   - [Sample Requests](#-sample-requests)\n- [Disclaimer](#-disclaimer)\n- [Project Structure](#-project-structure)\n- [Running Tests](#-running-tests)\n- [Docker Support](#-docker-support)\n- [HTTP Status Codes](#-http-status-codes)\n- [Rate Limiting](#-rate-limiting)\n- [Logging](#-logging)\n- [Background Jobs with Hangfire](#-background-jobs-with-hangfire)\n   - [Dashboard Access](#dashboard-access)\n   - [Security](#security)\n\n## 🚀 Why This Feature\n\nIn many financial and identity-sensitive applications, \nverifying a user’s national ID is a critical step. \nThis API mimics such a scenario, integrating:\n\n- ✅ Clean separation of concerns with vertical slice architecture\n- ✅ Secured REST API using JWT tokens\n- ✅ Verify customer identity using a mock external service\n- 📡 RESTful API fully documented with [Scalar UI](https://guides.scalar.com/scalar/scalar-api-references/net-integration)\n- ✅ Unit tests using xUnit and Moq\n- ✅ Integration tests using xUnit and test containers\n- ✅ Background jobs to handle long-running tasks\n- ✅ Error handling and retry logic\n- ✅ Robust input validation\n- ✅ OpenTelemetry ready\n- ✅ Admin restricted endpoint to generate compliance reports\n- 🐳 Optional Docker setup\n\n\n## 📌 Prerequisites\n\n- [.NET 9 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/9.0)\n- [Postgres Database](https://www.docker.com/blog/how-to-use-the-postgres-docker-official-image/)\n\u003e  To quickly set it up locally:\n\u003e``` bash\n\u003e  docker run --name some-postgres \\ \n\u003e  -e POSTGRES_PASSWORD=mysecretpassword -d postgres\n\u003e```\n- An IDE ([Visual Studio](https://visualstudio.microsoft.com/downloads), [Rider](https://www.jetbrains.com/rider/download), or [VS Code](https://code.visualstudio.com/download))\n\n## 🛠️ Getting Started\n\n1. Clone the repository\n    ```bash\n    git clone https://github.com/edcsu/KYCVerificationAPI.git\n    \n    cd KycVerificationApi\n    \n    dotnet run\n    ```\n\u003e Ensure to check out in the `main` branch\n\u003e `git checkout main`\n\n2. Install dependencies\n    ```bash\n     dotnet restore\n    ```\n \n3. Update your settings in the `appsettings.Development.json` to be in this structure below and replace values to fit your setup.\n    ```json lines\n      {\n        \"Logging\": {\n            \"LogLevel\": {\n                \"Default\": \"Information\",\n                \"Microsoft.AspNetCore\": \"Warning\"\n            }\n       },\n       \"Serilog\": {\n        \"MinimumLevel\": {\n            \"Default\": \"Information\"\n        },\n        \"WriteTo\": [\n            {\n                \"Name\": \"Console\",\n                \"outputTemplate\": \"[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}\"\n            },\n            {\n                \"Name\": \"File\",\n                \"outputTemplate\": \"[{Timestamp:HH:mm:ss} {Level}] [{SourceContext}] ({Application}/{MachineName}) {Message}{NewLine}{Exception}\",\n                \"Args\": {\n                    \"path\": \"Logs/applog.log\",\n                    \"rollingInterval\": \"Hour\",\n                    \"retainedFileCountLimit\": 5000\n                }\n            }\n        ]\n        },\n        \"ConnectionStrings\": {\n         \"DefaultConnection\": \"Server=localhost;Port=5432;Database=kyc_db;Username=postgres;Password=password\"\n        },\n        \"OtelConfing\": {\n            \"Endpoint\": \"http://localhost:4317\",\n            \"Enabled\": false\n        },\n        \"Jwt\" : {\n            \"Key\" : \"SuperSecretToken2025ForYouAreGoingToProsper\",\n            \"Issuer\" : \"https://auth.uverify.com\",\n            \"Audience\" : \"https://kyc.uverify.com\"\n        },\n        \"RateLimit\" : {\n          \"PermitLimit\" : 1,\n          \"Window\" : 5,\n          \"QueueLimit\" : 5\n        }\n   }\n    ``` \n   \n4. Run the application\n ```bash \n   dotnet run\n ```\n\u003e The API will be available at `https://localhost:7174` \n\u003e or `http://localhost:5160`\n\u003e By default, it opens in the API documentation link.\n\n## API Endpoints\n\n\u003e The API [https documentation can be accessed here](https://localhost:7174) while\n\u003e the [http documentation is here](http://localhost:5160)\n\nA summary of the endpoints is shown below\n\n### 🔎 Verifications\n\n| Method | Endpoint                    | Description                                         | Auth Required  |\n|--------|-----------------------------|-----------------------------------------------------|----------------|\n| POST   | `/api/auth/token`           | Generate an access token                            | No             |\n| POST   | `/api/verifications`        | Create new verification                             | Yes            |\n| GET    | `/api/verifications/{id}`   | Get verification by ID                              | Yes            |\n| GET    | `/api/verifications`        | Returns the list of the verifications that you made | Yes            |\n| GET    | `/api/verifications/report` | Returns a compliance report (For admins only)       | Yes            |\n\n### 📝 Sample Requests\n\n\u003e you need an access token first to access the verification endpoints\n\n#### 🔒 Get Access token\n\nThe API uses JWT Bearer authentication.\nInclude the token in the Authorization header.\n\n```http\nAuthorization: Bearer your-token-here\n```\n\nFor this showcase, the token is generated as shown below:\n\n1. Make a request to the token endpoint.\nThere should be at least one custom claim for `client:true` to use\nthe verification endpoints. This mimics how I set up clients to use the solution.\n\n`Token endpoints: https://localhost:7174/api/auth/token or https://localhost:5160/api/auth/token`\n\nThe request body for client use\n```json lines\n{\n  \"userId\": \"2032f3c8-ecc3-4205-94d1-5b05d2ea7c65\",\n  \"email\": \"test@example.com\",\n  \"customClaims\": {\n    \"client\": true\n  }\n}\n```\n\n```curl\n   curl https://localhost:7174/api/auth/token \\\n   --request POST \\\n   --header 'Content-Type: application/json' \\\n   --data '{\n      \"userId\": \"2032f3c8-ecc3-4205-94d1-5b05d2ea7c65\",\n      \"email\": \"test@example.com\",\n      \"customClaims\": {\n        \"client\": true\n      }\n   }'\n```\n\u003e💼 Admins access all endpoints, and \n\u003e you need an access token with admin claim to generate\n\u003e the compliance report\n\u003e```json5\n\u003e // The token request body for admins\n\u003e{\n\u003e  \"userId\": \"964e69e2-2a02-4ea1-934c-c75fa7c87045\",\n\u003e  \"email\": \"admin@uverify.com\",\n\u003e  \"customClaims\": {\n\u003e    \"admin\": true\n\u003e  }\n\u003e}\n\u003e```\n\u003e```curl\n\u003ecurl https://localhost:7174/api/auth/token \\\n\u003e--request POST \\\n\u003e--header 'Content-Type: application/json' \\\n\u003e--data '{\n\u003e   \"userId\": \"2032f3c8-ecc3-4205-94d1-5b05d2ea7c65\",\n\u003e   \"email\": \"admin@uverify.com\",\n\u003e   \"customClaims\": {\n\u003e       \"admin\": true\n\u003e   }\n\u003e}'\n\u003e```\n\n#### Create Verification\n\n\u003ehttp POST /api/verifications Content-Type: application/json\n\u003eAuthorization: Bearer {your-token}\n\n```json lines\n{\n  \"firstName\": \"Kalele\",\n  \"givenName\": \"Justice\",\n  \"dateOfBirth\": \"1974-02-12\",\n  \"nin\": \"12345678901234\",\n  \"cardNumber\": \"123456789\"\n}\n```\n\n#### Get Verification\n\n\u003e http GET /api/verifications/{id} Authorization: Bearer {your-token}\n\n#### Get Verification history\n\u003e http GET /api/verifications Authorization: Bearer {your-token}\n\n##### query parameters\n\n| Parameter                | Type         | Description                                           |\n|--------------------------|--------------|-------------------------------------------------------|\n| TransactionId            | `Guid`       | Unique identifier of the verification                 |\n| Status                   | `string`     | Status of the Verification                            |\n| KycStatus                | `bool`       | KYC Status of the Verification                        |\n| NameAsPerIdMatches       | `bool`       | Match result of the names                             |\n| NinAsPerIdMatches        | `bool`       | Match result of the NIN                               |\n| CardNumberAsPerIdMatches | `bool`       | Match result of the card number                       |\n| DateOfBirthMatches       | `bool`       | Match result of the date of birth                     |\n| DateOfBirth              | `DateOnly`   | Date of birth of the holder on their National ID card |\n| FirstName                | `string`     | First name of the holder on their National ID card    |\n| GivenName                | `string`     | Given name of the holder on their National ID card    |\n| CardNumber               | `string`     | Card number of the holder on their National ID card   |\n| Nin                      | `string`     | NIN of the holder on their National ID card           |\n| Page                     | `int`        | The page to view                                      |\n| PageSize                 | `int`        | The number of items per page                          |\n| From                     | `DateOnly`   | The start date of the date range                      |\n| To                       | `DateOnly`   | The end date of the date range                        |\n\n## ⚠️ Disclaimer\nThis is a demo project.\nThe mock external service does not represent a real ID verification provider.\nIt shows the interaction that can be there. \nA few scenarios are shown when the external API call is:\n- ✅ successful\n- ❌ fails\n- ❗️returns an error\n\n## 📁 Project Structure\n\n```\nKYCVerificationAPI/ \n├── KYCVerificationAPI/ \n│ │ Dependencies\n│ ├── Properties/ \n│ ├── wwwroot/ \n│ ├── Core/ \n│ │ ├── Exceptions/ \n│ │ ├── Extensions/ \n│ │ ├── Filters/ \n│ │ ├── Helpers/ \n│ │ ├── Middleware/ \n│ │ ├── ApiConstants\n│ │ ├── BaseModel\n│ │ ├── JwtConfig\n│ │ ├── OtelConfing\n│ │ ├── RateLimitConfig\n│ │ ├── JwtConfig\n│ │ └── VerificationStatus \n│ ├── Data/ \n│ │ ├── Entities/ \n│ │ ├── Repositories/ \n│ │ ├── AppDbContext \n│ │ └── Context \n│ ├── Features/ \n│ │ ├── Auth/ \n│ │ │  ├── Controllers/ \n│ │ │  ├── Requests/ \n│ │ │  ├── Responses/ \n│ │ │  └── Validators/ \n│ │ ├── Scheduler/ \n│ │ │  └── Services/ \n│ │ ├── Vendors/  \n│ │ │  ├── Requests/ \n│ │ │  ├── Responses/ \n│ │ │  └── Services/ \n│ │ ├── Verifications/\n│ │ │  ├── Controllers/ \n│ │ │  ├── Mappings/ \n│ │ │  ├── Requests/ \n│ │ │  ├── Services/ \n│ │ │  └── Validators/  \n│ ├── Logs/ \n│ └── Migrations/ \n├── KYCVerificationAPI.Tests/ \n│ │ Dependencies\n│ └── Repositories/ \n│ │ └──VerificationRepositoryTests\n├── KYCVerificationAPI.IntegrationTests\n│ ├── Dependencies\n│ ├── DatabaseFixture\n│ ├── IntegrationHelpers\n│ └── VerificationRepositoryTests\n├── Dockerfile \n└── README.md\n```\n\n## ⛓️‍💥 Running Tests\n\nThere are two test projects:\n1. Unit tests\n2. Integration tests\n\n- Execute the test suite using:\n```bash \ndotnet test\n```\n\n- For specific test projects:\n```bash \ndotnet test ./tests/KYCVerificationAPI.IntegrationTests/KYCVerificationAPI.IntegrationTests.csproj\n```\n```bash\ndotnet test ./tests/KYCVerificationAPI.Tests/KYCVerificationAPI.Tests.csproj\n```\n \n## 🐳 Docker Support\n\n### 🪏 Build the Docker image\n\n```bash \ndocker build -t kyc-verification-api .\n```\n\n### 🏃🏿‍♂️‍➡️ Run the container\n\n```bash \ndocker run -p 8080:80 \\\n-e \"ConnectionStrings__DefaultConnection=your-connection-string\"\\\nkyc-verification-api\n```\n\n## 📶 HTTP status codes\n\nThe API uses standard HTTP status codes:\n\n- 200: Success\n- 201: Created\n- 400: Bad Request\n- 401: Unauthorized\n- 403: Forbidden\n- 404: Not Found\n- 500: Internal Server Error\n\n## 📈 Rate Limiting\n\nAPI requests are rate-limited except auth, hangfire dashboard and documentation endpoints by initially.\nThis is configurable in the app settings.\n```json5\n{\n   \"RateLimit\" : {\n      \"PermitLimit\" : 1, // Number of requests\n      \"Window\" : 5, // Per number of seconds\n      \"QueueLimit\" : 5, // Allowed requests in the queue\n      // paths exempted from rate limiting\n      \"AllowedPaths\" : [ \n         \"auth\",\n         \"scalar\",\n         \"hangfire\"\n      ]\n   }\n}\n```\n\n## 📋 Logging\nLogs are written to:\n- Console\n- File system (`/logs` directory)\n- OpenTelemetry (if configured)\n\u003e To spin up an open telemetry injester quickly.\n\u003e ```bash\n\u003e docker run --rm -it -d \\\n\u003e  -p 18888:18888 \\\n\u003e  -p 4317:18889 \\\n\u003e  --name aspire-dashboard \\\n\u003e  mcr.microsoft.com/dotnet/aspire-dashboard:9.1\n\u003e ```\n\u003e then update the appsettings file accordingly\n\u003e ```json5\n\u003e {\n\u003e   \"OtelConfing\": {\n\u003e     \"Endpoint\": \"http://localhost:4317\", // default aspire url\n\u003e     \"Enabled\": false\n\u003e   }\n\u003e }\n\u003e ```\n\u003e [.NET Aspire dashboard overview\n](https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard/overview?tabs=bash)\n\n## 🔄 Background Jobs with Hangfire\n\nThe API uses Hangfire for managing and executing background jobs.\nIn particular the calls made to the mocked external Id system.\nThis helps handle long-running tasks asynchronously, \nimproving the API's responsiveness.\n\n### Dashboard Access\n\nThe Hangfire dashboard is available at `/hangfire`.\nTo access the dashboard, navigate to:\n\u003e [https link](https://localhost:7174/hangfire) or \n\u003e [http link](http://localhost:5160/hangfire)\n\nIt provides a real-time view of:\n- Scheduled jobs\n- Failed jobs\n- Recurring jobs\n- Job history\n- Real-time job metrics\n\n### Security\n\nThe Hangfire dashboard is open for this showcase. \n\u003e In production, it should be both secured and require authentication.\n\u003e Only users with administrative access should view and manage jobs.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedcsu%2Fkycverificationapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fedcsu%2Fkycverificationapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedcsu%2Fkycverificationapi/lists"}