Files
ERP_Auth_Service/API_DOCUMENTATION.md
Dhananjaya99 2481f056ab add role api
2026-07-18 23:43:19 +05:30

11 KiB

AuthHex API Documentation

Base route: api All endpoints are [AllowAnonymous] at the HTTP layer, but individual functions may require a Bearer JWT (read via HttpContext claims) — noted per function below.

Common envelope

Request (ApiRequest)

{
  "functionName": "string (required, ignored for dedicated routes below)",
  "payload": { "...": "function-specific object" },
  "reference": "string (required, can be empty)"
}

Response (ApiResponse)

{
  "statusCode": 200,
  "success": true,
  "message": "success",
  "data": { "...": "function-specific object" }
}

Errors: 404 unknown function, 400 (KeyNotFoundException/InvalidOperationException), 500 (ArgumentException).


Routes

Route Method Dispatches to FunctionName
/api/status GET — (health check)
/api/user POST UserManager from body
/api/recovery POST RecoveryManager from body
/api/registerUser POST UserManager forced registerUser
/api/loginUser POST UserManager forced loginUser
/api/forgotPassword POST RecoveryManager forced forgotPassword
/api/alt POST AltOptionManager from body
/api/role POST RoleManager from body

GET /api/status

Response:

{ "status": "API is running", "timestamp": "2026-07-03T00:00:00Z" }

/api/user — UserManager functions

registerUser

Payload:

{
  "userId": "guid (required)",
  "fullname": "string?",
  "userName": "string?",
  "nic": "string?",
  "email": "string?",
  "mobileNumber": "string?",
  "deviceName": "string? (default 'Unknown Device')",
  "roleId": "guid (required)",
  "userTypeId": "guid (required)",
  "chkUser": "bool? (if true, checks for existing conflicting user)",
  "password": "string? (auto-generated if empty)",
  "sendCredentialsEmail": "bool? (default true; emails `email` the username + password when set)"
}

The generated/supplied password is now persisted (hashed) against the user record (previously a bug left it unset). When email is present and sendCredentialsEmail is not false, an email with the username and password is sent best-effort after the user is committed (failures do not roll back registration).

Data:

{
  "accessToken": "jwt",
  "refreshToken": "string",
  "expiresIn": 3600,
  "user": { "id": "guid", "fullName": "", "userName": "", "email": "", "mobileNumber": "", "emailVerified": false, "mobileNumberVerified": false, "isMfaEnabled": false, "roleId": "guid", "userTypeId": "guid" }
}

loginUser

Payload:

{
  "identifier": "string (required)",
  "password": "string?",
  "userTypeId": "guid?",
  "deviceName": "string? (default 'Unknown Device')"
}

Data: same shape as registerUser data.

VerifyOtpForLogin

Payload:

{ "referenceNumber": "string (required)", "otpCode": "string (required)", "deviceName": "string?" }

Data:

{
  "success": true,
  "message": "OTP verified successfully.",
  "data": {
    "referenceNumber": "", "userId": "guid", "verified": true,
    "accessToken": "jwt", "refreshToken": "", "expiresIn": 3600,
    "user": { "...": "as above" }
  }
}

refreshToken

Payload:

{ "refreshToken": "string (required)", "deviceName": "string?" }

Data: same shape as registerUser data (rotates session).

getUserDetails

Payload:

{ "userId": "guid (required)" }

Data:

{
  "id": "guid", "fullName": "", "userName": "", "email": "", "nic": "",
  "mobileNumber": "", "emailVerified": false, "mobileNumberVerified": false,
  "isMfaEnabled": false, "isActive": true, "isLocked": false, "createdAt": "",
  "role": { "roleId": "", "code": "", "name": "" },
  "userType": { "userTypeId": "", "code": "", "description": "" }
}

listUserTypes

No payload. Data: array of

{ "userTypeId": "guid", "code": "", "description": "" }

getUserSessions

Requires auth (userId from claims). No payload needed. Data: array of

{ "sessionId": "", "deviceName": "", "browser": "", "os": "", "ipAddress": "", "createdAt": "", "expiresAt": "", "revokedAt": "", "isActive": true }

ChangeUserStatus

Requires auth. Payload:

{ "isActive": "bool (required)" }

Data: { "id", "fullName", "userName", "email", "mobileNumber", "isActive" }

LockUserAccount

Requires auth. Payload:

{ "isLocked": "bool (required)" }

Data: { "id", "fullName", "userName", "email", "mobileNumber", "isLocked", "deletedAt" } (Also invalidates all user sessions.)

ChangeUserPassword

Requires auth. Payload:

{ "currentPassword": "string (required)", "newPassword": "string (required)" }

Data: { "message": "Password changed successfully" }

LogoutUser

Payload:

{ "userId": "guid (required)" }

Data: { "message": "User logged out successfully" }

UpdateUser

Requires auth. Payload (all optional, at least one required):

{
  "fullName": "string?", "userName": "string?", "nic": "string?", "address": "string?",
  "optional1": "string?", "optional2": "string?",
  "email": "string|null", "mobileNumber": "string|null",
  "currentPassword": "string? (required if newPassword set and existing password set)",
  "newPassword": "string?"
}

Data:

{
  "message": "User updated successfully",
  "user": { "id", "fullName", "userName", "nic", "address", "optional1", "optional2", "email", "emailVerified", "mobileNumber", "mobileNumberVerified" }
}

2FA — initiateTwoFASetup

Requires auth. No payload. Data: third-party-service-defined setup result (QR/secret info).

2FA — completeTwoFASetup

Requires auth. Payload:

{ "secretKey": "string (required)", "verificationCode": "string (required)" }

Data:

{
  "message": "2FA setup completed successfully",
  "backupCodes": ["..."],
  "user": { "id", "fullName", "userName", "email", "isMfaEnabled" }
}

2FA — verifyTwoFA

Requires auth. Payload:

{ "verificationCode": "string (required)" }

Data: raw third-party verification result object.

2FA — disableTwoFA

Requires auth. Payload:

{ "verificationCode": "string (required)" }

Data:

{ "message": "2FA has been disabled successfully", "user": { "id", "fullName", "userName", "email", "isMfaEnabled" } }

2FA — getTwoFAStatus

Requires auth. No payload. Data:

{ "isMfaEnabled": false, "isVerified": false, "lastUsedAt": null, "verifiedAt": null, "hasBackupCodes": false }

/api/recovery — RecoveryManager functions

forgotPassword

Payload:

{ "identifier": "string (required)", "useResetLink": "bool? (default false)", "numberOfDigits": "int? (default 6)" }

Data (useResetLink = true):

{ "success": true, "message": "Password reset link sent to your email.", "data": { "referenceNumber": "", "expiresAt": "", "recoveryType": "ResetLink" } }

Data (useResetLink = false, OTP flow):

{ "success": true, "message": "OTP sent successfully.", "data": { "referenceNumber": "", "expiresAt": "", "recoveryType": "OTP" } }

verifyOTP

Payload:

{ "referenceNumber": "string (required)", "otpCode": "string (required)" }

Data:

{ "success": true, "message": "OTP verified successfully. You can now reset your password.", "data": { "referenceNumber": "", "userId": "guid", "verified": true } }

resetPasswordWithToken

Payload:

{ "resetToken": "string (required)", "newPassword": "string (required, min 8 chars)", "confirmPassword": "string (required, must match)" }

Data:

{ "success": true, "message": "Password reset successful. Please login with your new password.", "data": { "userId": "guid", "email": "", "resetAt": "" } }

resetPassword

Payload:

{ "referenceNumber": "string (required)", "newPassword": "string (required, min 8 chars)", "confirmPassword": "string (required, must match)" }

(Requires recovery status = "Verified" via prior verifyOTP call.) Data: same shape as resetPasswordWithToken.


/api/alt — AltOptionManager functions

IsAvailable

Payload:

{ "Identifier": "string?", "Recovery": "string? (any non-null value flags recovery-mode)" }

Data (available): { "isAvailable": true, "message": "Identifier is available" } Data (taken, no Recovery): { "isAvailable": false, "message": "Identifier already in use" } Data (taken, Recovery set): { "existingUsers": [...] }

sendOtp

Payload:

{ "identifier": "string (required, email or mobile)", "numberOfDigits": "int? (default 4)", "newUser": "bool? (default false)" }

Data (newUser = true):

{ "success": true, "message": "OTP sent successfully for new user.", "data": { "referenceNumber": "", "expiresAt": "", "recoveryType": "OTP FOR NEW USER" } }

Data (existing user):

{ "success": true, "message": "OTP sent successfully.", "data": { "referenceNumber": "", "expiresAt": "", "recoveryType": "OTP FOR LOGIN" } }

VerifyOTP (alt)

Payload:

{
  "userId": "guid?", "referenceNumber": "string (required)", "otpCode": "string (required)",
  "newUser": "bool? (default false)", "identifier": "string? (email/mobile/other — marks it verified on user)",
  "deviceName": "string? (default 'Unknown Device')"
}

Data:

{
  "success": true,
  "message": "OTP verified successfully.",
  "data": {
    "referenceNumber": "", "userId": "guid", "verified": true,
    "accessToken": "jwt", "refreshToken": "", "expiresIn": 3600,
    "user": { "id", "fullName", "userName", "email", "mobileNumber", "emailVerified", "mobileNumberVerified", "isMfaEnabled", "roleId", "userTypeId" }
  }
}

/api/role — RoleManager functions

Null-tolerant fields (fixed 2026-07-18): isSystemRole is read via data["isSystemRole"].ValueKind != JsonValueKind.Null before calling GetBoolean() in both createRole and updateRole — an explicit JSON null (as opposed to the key being absent) previously threw an unhandled InvalidOperationException, surfaced to ERPCore callers as a generic 500/AUTH_UPSTREAM_ERROR.

createRole

Payload:

{ "code": "string (required)", "name": "string? (defaults to code)", "isSystemRole": "bool? (default false)" }

Data:

{ "roleId": "guid", "code": "", "name": "", "isSystemRole": false, "createdAt": "" }

getRole

Payload:

{ "roleId": "guid (required)" }

Data: same shape as createRole.

listRoles

No payload. Data: array of createRole-shaped objects.

updateRole

Payload (all fields optional except roleId):

{ "roleId": "guid (required)", "code": "string?", "name": "string?", "isSystemRole": "bool?" }

Data: same shape as createRole.

deleteRole

Payload:

{ "roleId": "guid (required)" }

Blocked with a 400 ("ROLE_IN_USE: role is assigned to one or more users") if any User.RoleId references it (FK is Restrict). Data: { "message": "Role deleted successfully" }


Notes

  • All timestamps are UTC.
  • deviceName, Browser, OS, IPAddress are captured per session from request headers for session tracking (getUserSessions).
  • Password login validation (PasswordHasher.Verify) is performed in loginUser — invalid/missing password hashes are rejected.
  • JWT access tokens issued with expiresIn: 3600 (1 hour); refresh tokens/sessions expire after 30 days.