A modern ASP.NET Core minimal Web API for managing general and special use registrations with JWT-based authentication and SQLite persistence.
This API provides a complete registration management system supporting two registration types:
- General Use Registrations: Basic registrations requiring only contact information
- Special Use Registrations: Enhanced registrations requiring organization details and licensing information
All endpoints (except authentication and health checks) are protected with 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 10.0 SDK or later
- Git
-
Clone the repository
git clone https://github.com/APSIMInitiative/APSIM.RegistrationAPIV2.git cd APSIM.RegistrationAPIV2 -
Configure environment variables
Copy the example configuration:
cp .env.example .env
Then edit
.envwith your configuration:AUTH_USERNAME=your-username AUTH_PASSWORD=your-secure-password JWT_ISSUER=APSIM.RegistrationAPIV2 JWT_AUDIENCE=APSIM.RegistrationAPIV2.Client JWT_SIGNING_KEY=your-secret-key-minimum-32-characters-long JWT_TOKEN_EXPIRY_MINUTES=60
-
Restore dependencies
dotnet restore
-
Build the solution
dotnet build
dotnet run --project APSIM.RegistrationAPIV2The API will start on https://localhost:7276 (HTTPS) and http://localhost:5276 (HTTP).
Press F5 or select Debug > Start Debugging.
Once the API is running, browse to:
- Swagger UI: https://localhost:7276/swagger/index.html
- OpenAPI Spec: https://localhost:7276/swagger/v1/swagger.json
POST /api/auth/token
Content-Type: application/json
{
"username": "your-username",
"password": "your-secure-password"
}
Response (200 OK):
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresAtUtc": "2026-03-25T15:30:00Z"
}Errors:
401 Unauthorized- Invalid credentials
GET /health
Response (200 OK):
{
"status": "ok"
}No authentication required
All registration endpoints require JWT bearer authentication:
Authorization: Bearer <accessToken>
GET /api/registrations
Query Parameters (optional):
registrationType- Filter by type:GeneralUseorSpecialUselicenceStatus- Filter by status (see Enums section)contactEmail- Filter by email address
Example:
GET /api/registrations?registrationType=SpecialUse&licenceStatus=Active
Response (200 OK):
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"registrationType": "GeneralUse",
"contactName": "John Doe",
"contactEmail": "[email protected]",
"applicationDate": "2026-03-20T10:30:00Z",
"licenceStatus": "GeneralUse"
}
]GET /api/registrations/{id}
Response (200 OK):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"registrationType": "GeneralUse",
"contactName": "John Doe",
"contactEmail": "[email protected]",
"applicationDate": "2026-03-20T10:30:00Z",
"licenceStatus": "GeneralUse"
}Errors:
404 Not Found- Registration not found
POST /api/registrations
Content-Type: application/json
{
"registrationType": "GeneralUse",
"contactName": "Jane Smith",
"contactEmail": "[email protected]"
}
General Use Registration (minimal):
{
"registrationType": "GeneralUse",
"contactName": "Jane Smith",
"contactEmail": "[email protected]"
}Special Use Registration (required fields):
{
"registrationType": "SpecialUse",
"contactName": "Jane Smith",
"contactEmail": "[email protected]",
"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": "[email protected]",
"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": "[email protected]"
}
Response (200 OK):
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"registrationType": "GeneralUse",
"contactName": "Jane Smith Updated",
"contactEmail": "[email protected]",
"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:
APSIMRegistrationV2_Dev.db - Production:
APSIMRegistrationV2.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
-
Consider Additional Security
- Implement rate limiting
- Add request logging and monitoring
- Enable CORS with appropriate origin restrictions
"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 APSIMRegistrationV2*.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
[Your License Here]
For questions or support, contact the APSIM team in this repository by creating an issue.