A modern ASP.NET Core minimal Web API for managing APSIM registrations with JWT-based authentication and SQLite persistence.
This API provides a complete registration management system supporting:
- General Use Registrations: Basic registrations with user contact information
- Special Use Registrations: Enhanced registrations with organization details and licensing
- Download Tracking: Audit trail for APSIM downloads and access events
- JWT Authentication: Secure token-based API access
All endpoints (except /health and /api/auth/token) require JWT bearer token authentication.
- .NET 10.0 - Latest .NET runtime
- ASP.NET Core Minimal APIs - Lightweight API endpoints without controllers
- Entity Framework Core 10.0.5 - ORM with SQLite provider
- JWT Bearer Authentication - Token-based security
- Swagger/OpenAPI - Interactive API documentation
- SQLite - File-based relational database
- .NET SDK 10.0 (matching solution)
- Git
From the repository root:
# Restore all dependencies
dotnet restore
# Build the solution
dotnet builddotnet run --project RegistrationWebAPI/RegistrationWebAPI.csprojThe API will start on https://localhost:7276 (HTTPS) and http://localhost:5276 (HTTP).
- Open VS Code in the repository
- Select Terminal > Run Task...
- Choose Build WebApi
docker build -f RegistrationWebAPI/Dockerfile -t apsim-registration-webapi .
docker run -p 7276:7276 apsim-registration-webapiRegistrationWebAPI/
├── Data/ (Entity Framework Core)
│ ├── RegistrationDbContext.cs - EF Core DbContext
│ ├── UserEntity.cs - User database entity
│ ├── OrganisationEntity.cs - Organisation database entity
│ └── DownloadAuditEntity.cs - Download audit trail entity
│
├── Models/ (Request/Response DTOs)
│ ├── AuthTokenRequest.cs - Login request model
│ ├── AuthTokenResponse.cs - JWT token response model
│ ├── RegistrationErrorResponse.cs - Error response wrapper
│ ├── DownloadEventRequest.cs - Download tracking request
│ ├── DownloadAuditResponse.cs - Download audit response
│ ├── MemberOrganisationResponse.cs - Organisation response
│ ├── BulkDeleteRegistrationsRequest.cs - Bulk delete request
│ └── RegistrationType.cs - Registration type enum
│
├── Migrations/ (EF Core Database Migrations)
│ ├── 20260526014802_InitialMigration.cs
│ └── 20260527223413_AddDownloadAudits.cs
│
├── Utilities/ (Helper Classes)
│ └── MailUtility.cs - Email utility functions
│
├── Program.cs (API Startup & Configuration)
├── appsettings.json (Configuration)
├── appsettings.Development.json (Development overrides)
├── Dockerfile (Container configuration)
└── RegistrationWebAPI.csproj (Project file)
└── update-ef-migration.bat (Database migration script)
When modifying any database entity classes in the Data/ folder (UserEntity, OrganisationEntity, DownloadAuditEntity, etc.), you must generate a new Entity Framework Core migration.
Execute the batch file from the RegistrationWebAPI directory:
cd RegistrationWebAPI
update-ef-migration.batThis script will:
- Create a new migration file in the
Migrations/folder - Update the database context snapshot
Important: Commit both the new migration file and any changes to the snapshot file to version control.
If the batch file is unavailable, run:
dotnet ef migrations add <MigrationName>Once the API is running, browse to:
- Swagger UI: https://localhost:7276/swagger/index.html
- OpenAPI Spec: https://localhost:7276/swagger/v1/swagger.json
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /api/auth/token |
Generate JWT authentication token | ❌ |
| GET | /health |
API health check | ❌ |
| GET/POST | /api/registrations |
List or create registrations | ✅ |
| GET | /api/registrations/{id} |
Get registration by ID | ✅ |
| GET/POST | /api/users |
User management | ✅ |
| GET/POST | /api/organisations |
Organisation management | ✅ |
| POST | /api/downloads/events |
Track download events | ✅ |
| GET | /api/downloads/audits |
Get download audit trail | ✅ |
Generate JWT Token
POST /api/auth/token
Content-Type: application/json
{
"username": "admin",
"password": "secure-password"
}Response:
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresAtUtc": "2026-06-23T16:30:00Z"
}Get API Status (No authentication required)
GET /healthResponse:
{
"status": "ok"
}All requests must include:
Authorization: Bearer <accessToken>List Registrations
GET /api/registrations?registrationType=GeneralUse&licenceStatus=ActiveResponse:
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"registrationType": "GeneralUse",
"contactName": "John Doe",
"contactEmail": "john@example.com",
"applicationDate": "2026-03-20T10:30:00Z",
"licenceStatus": "Active"
}
]Get Registration by ID
GET /api/registrations/{id}}
**Errors**:
- `404 Not Found` - Registration not found
---
#### 5. Create Registration
```text
POST /api/registrations
Content-Type: application/json
{
"registrationType": "GeneralUse",
"contactName": "Jane Smith",
"contactEmail": "jane@example.com"
}
General Use Registration (minimal):
{
"registrationType": "GeneralUse",
"contactName": "Jane Smith",
"contactEmail": "jane@example.com"
}Special Use Registration (required fields):
{
"registrationType": "SpecialUse",
"contactName": "Jane Smith",
"contactEmail": "jane@example.com",
"organisationName": "ACME Corp",
"organisationAddress": "123 Main St",
"licencePathway": "TypeOne",
"annualTurnover": "BelowTwoMillion"
}Response (201 Created):
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"registrationType": "GeneralUse",
"contactName": "Jane Smith",
"contactEmail": "jane@example.com",
"applicationDate": "2026-03-25T12:00:00Z",
"licenceStatus": "GeneralUse"
}Location Header:
Location: /api/registrations/550e8400-e29b-41d4-a716-446655440001
Errors:
400 Bad Request- Validation failed
PUT /api/registrations/{id}
Content-Type: application/json
{
"registrationType": "GeneralUse",
"contactName": "Jane Smith Updated",
"contactEmail": "jane-new@example.com"
}
Response (200 OK):
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"registrationType": "GeneralUse",
"contactName": "Jane Smith Updated",
"contactEmail": "jane-new@example.com",
"applicationDate": "2026-03-25T12:00:00Z",
"licenceStatus": "GeneralUse"
}Errors:
404 Not Found- Registration not found400 Bad Request- Validation failed
DELETE /api/registrations/{id}
Response (204 No Content):
(empty body)
Errors:
404 Not Found- Registration not found
contactName(required) - Name of the contact personcontactEmail(required) - Email address of the contact- Status auto-set to
GeneralUse
All of the above, plus:
organisationName(required) - Organization nameorganisationAddress(required) - Organization addressorganisationWebsite(optional) - Organization websitecontactPhone(optional) - Contact phone numberlicencePathway(required) -TypeOneorTypeTwoannualTurnover(required) - See enum values below- Status auto-set to
SpecialAwaitingReview
TypeOne- Modifications shared backTypeTwo- Modifications private
BelowTwoMillion- Less than $2M AUDTwoToFortyMillion- $2M - $40M AUDAboveFortyMillion- Over $40M AUD
None- No licenceGeneralUse- General use licenceSpecialAwaitingReview- Special use pending reviewSpecialProvisional- Special use provisionalSpecialInvoiced- Special use invoicedSpecialActive- Special use activeSpecialDeclined- Special use declinedCancelled- Registration cancelledExpired- Registration expired
| Variable | Purpose | Example |
|---|---|---|
AUTH_USERNAME |
API authentication username | your-username |
AUTH_PASSWORD |
API authentication password | your-secure-password |
JWT_ISSUER |
JWT issuer claim | APSIM.RegistrationAPIV2 |
JWT_AUDIENCE |
JWT audience claim | APSIM.RegistrationAPIV2.Client |
JWT_SIGNING_KEY |
Secret key for signing JWT tokens (min 32 chars) | your-secret-key-... |
JWT_TOKEN_EXPIRY_MINUTES |
Token expiration time in minutes | 60 |
The API uses SQLite for persistence. The database file is created automatically on first run:
- Development:
APSIMRegistrationSystemWebAPI.db - Production:
APSIMRegistrationSystemWebAPI.db
Database schema is managed with Entity Framework Core migrations. All migrations are applied automatically on startup.
APSIM.RegistrationAPIV2/
├── Data/
│ ├── RegistrationDbContext.cs # EF Core DbContext
│ └── RegistrationEntity.cs # Database entity model
├── Models/
│ ├── RegistrationUpsertRequest.cs # Request DTO
│ ├── RegistrationResponse.cs # Response DTO
│ ├── RegistrationType.cs # Local enum
│ ├── AuthTokenRequest.cs # Auth request DTO
│ └── AuthTokenResponse.cs # Auth response DTO
├── Services/
│ ├── RegistrationMapping.cs # Entity-to-DTO mappers
│ └── RegistrationValidation.cs # Business logic validation
├── Migrations/
│ └── [EF Core migrations]
├── Program.cs # API startup configuration
└── appsettings*.json # Configuration files
APSIM.Registration.Contracts/
├── Enums/
│ ├── LicenceStatus.cs
│ ├── LicencePathway.cs
│ └── AnnualTurnover.cs
├── Interfaces/
│ └── IRegistration.cs
└── Models/
├── GeneralUseRegistration.cs
└── SpecialUseRegistration.cs
# Clean build
dotnet clean
dotnet build
# Release build
dotnet build -c Release
# Restore NuGet packages
dotnet restoredotnet test(Tests can be added to the Tests/ folder)
- Tokens expire after the configured
JWT_TOKEN_EXPIRY_MINUTES(default: 60 minutes) - Tokens are signed with
HS256using a symmetric key - Token validation includes issuer, audience, and signing key verification
-
Rotate Credentials
- Replace
AUTH_PASSWORDwith a strong password - Replace
JWT_SIGNING_KEYwith a random 32+ character string
- Replace
-
Use Secrets Manager
- Store credentials in Azure Key Vault, AWS Secrets Manager, or similar
- Never commit
.envfiles to version control
-
HTTPS Only
- Ensure HTTPS is enforced in production
- Update
AllowedHostsinappsettings.jsonto specific domains
"dotenv.net not found": Run dotnet restore to restore all NuGet packages.
"SQLite database locked": Stop any running instances and try again. Kill the process if necessary:
# PowerShell
Stop-Process -Name dotnet -Force"Auth:Password is not configured": Ensure the .env file exists and contains AUTH_PASSWORD.
"Jwt:SigningKey is not configured": Check that .env includes JWT_SIGNING_KEY with at least 32 characters.
"Database migration failed": Delete APSIMRegistrationSystemWebAPI.db files to reset the database, then rebuild.
- Create a feature branch:
git checkout -b feature/my-feature - Commit changes:
git commit -am 'Add my feature' - Push to branch:
git push origin feature/my-feature - Open a Pull Request
For questions or support, contact the APSIM team in this repository by creating an issue.