Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/juescoryisus/QualityDocD/llms.txt

Use this file to discover all available pages before exploring further.

QualityDocD ships two independent authentication systems that serve different integration audiences. The Node.js REST API (default port 5000) issues tenant-scoped JWTs identified by companySlug, making it the right choice for multi-tenant application clients. The .NET MVC API exposes a simpler /api/auth surface used by the PHP portal and external tooling integrations. Both systems sign tokens with HMAC-SHA256 and accept them through the standard Authorization: Bearer <token> HTTP header.

Overview

PropertyNode.js API.NET API
Login endpointPOST /auth/loginPOST /api/auth/login
Signing secretSESSION_SECRET env varApiAuth:JwtSecret config key
AlgorithmHS256HS256
Token lifetime24 hours8 hours (expires_in: 28800)
Tenant scopingYes — via companySlugNo
Once you obtain a token, attach it to every protected request:
Authorization: Bearer <your-jwt-token>
The Node.js API’s SESSION_SECRET and the .NET API’s ApiAuth:JwtSecret are separate secrets. Tokens issued by one system are not valid on the other.

Node.js API

POST /auth/login

Authenticates a user by email address within a specific company tenant. The companySlug field resolves the tenant — it must exactly match the slug column in the companies table. The user is then looked up by email within that company, and the supplied password is verified against the stored bcrypt hash.
email
string
required
The user’s email address. Must belong to the company identified by companySlug.
password
string
required
The user’s plaintext password. Compared against a bcrypt hash server-side.
companySlug
string
required
The URL-safe identifier of the company tenant (e.g. "my-company"). Must match an existing record in the companies table.
Example request
curl -X POST http://localhost:5000/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"admin@company.com","password":"Admin123!","companySlug":"my-company"}'
200 — Success
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": 7,
    "companyId": 3,
    "name": "Admin User",
    "email": "admin@company.com",
    "role": "COMPANY_ADMIN"
  }
}
token
string
Signed JWT. Valid for 24 hours. Include this value in the Authorization: Bearer header on subsequent requests.
user
object
A snapshot of the authenticated user’s profile at login time.
The JWT payload embedded in token contains:
ClaimTypeDescription
userIdnumberThe user’s primary key
companyIdnumberThe user’s company
companySlugstringThe company’s slug (e.g. "my-company")
rolestringThe user’s role string
Error responses
StatusCondition
400 Bad RequestOne or more required fields (email, password, companySlug) are missing or not strings
401 UnauthorizedThe companySlug does not match any company, the email is not found within that company, or the password is incorrect
All three failure modes — unknown company, unknown email, wrong password — return the same "Invalid credentials" message. This is intentional to prevent user enumeration.

POST /auth/validate

Verifies a JWT that was previously issued by the Node.js login endpoint and returns its decoded payload. This endpoint does not require an Authorization header — the token is passed in the request body. It is primarily useful for server-side session checks (e.g. from a background worker or a second microservice).
token
string
required
A JWT string previously returned by POST /auth/login.
Example request
curl -X POST http://localhost:5000/auth/validate \
  -H "Content-Type: application/json" \
  -d '{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}'
200 — Valid token
{
  "valid": true,
  "userId": 7,
  "companyId": 3,
  "companySlug": "my-company",
  "role": "COMPANY_ADMIN"
}
200 — Invalid or expired token
{
  "valid": false,
  "userId": null,
  "companyId": null,
  "companySlug": null,
  "role": null
}
valid
boolean
true if the token is a well-formed JWT signed with the correct secret and has not expired; false otherwise.
userId
number | null
The user’s primary key extracted from the token, or null when valid is false.
companyId
number | null
The user’s company ID, or null when valid is false.
companySlug
string | null
The company’s slug string, or null when valid is false.
role
string | null
The user’s role string, or null when valid is false.
This endpoint always returns HTTP 200. An invalid or expired token produces { "valid": false, ... } rather than a 401. Check the valid field in your application logic.
Error responses
StatusCondition
400 Bad RequestThe token field is missing from the request body

.NET API

POST /api/auth/login

Authenticates a user against the .NET application’s SQL Server database. The username field is matched against the Username column in the Users table (case-sensitive depending on database collation). This endpoint is used by the PHP portal and any external integration that does not require multi-tenant scoping.
username
string
required
The user’s username. Matched against the Username column in the Users table. The user must also have IsActive = true.
password
string
required
The user’s plaintext password. Verified against a BCrypt hash stored in PasswordHash.
Example request
curl -X POST http://localhost:5001/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"jdoe","password":"Secret123!"}'
200 — Success
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_in": 28800,
  "user": {
    "id": 12,
    "username": "jdoe",
    "email": "jdoe@company.com",
    "role": "Operator",
    "department": "Quality"
  }
}
token
string
Signed JWT issued by the .NET ApiTokenService. Valid for 8 hours (configurable via ApiAuth:ExpirationHours).
expires_in
number
Token lifetime in seconds. Default is 28800 (8 hours).
user
object
The authenticated user’s profile.
Error responses
StatusCondition
400 Bad Requestusername or password is missing or blank
401 UnauthorizedNo matching active user found, or BCrypt verification fails

POST /api/auth/validate

Verifies a JWT issued by the .NET login endpoint. The token is validated against the ApiAuth:JwtSecret secret, the QualityDocD issuer, and the QualityDocD-Clients audience, with a 5-minute clock skew allowance.
token
string
required
A JWT string previously returned by POST /api/auth/login.
Example request
curl -X POST http://localhost:5001/api/auth/validate \
  -H "Content-Type: application/json" \
  -d '{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}'
200 — Valid token
{
  "valid": true,
  "id": 12,
  "username": "jdoe",
  "email": "jdoe@company.com",
  "role": "Operator",
  "department": "Quality"
}
200 — Invalid or expired token
{
  "valid": false
}
valid
boolean
true when the token passes all validation checks (signature, issuer, audience, and expiry); false otherwise.
id
number
The user’s primary key. Present only when valid is true.
username
string
The user’s login username. Present only when valid is true.
email
string
The user’s email address. Present only when valid is true.
role
string
The user’s role string. Present only when valid is true.
department
string
The user’s department. Present only when valid is true.
Error responses
StatusCondition
400 Bad RequestThe token field is missing or blank

GET /api/auth/me

Returns the profile of the currently authenticated user. The token is read from the Authorization: Bearer header (the same header used by all other protected endpoints). This is a convenient way for a client to confirm its identity and fetch up-to-date profile data without re-parsing the JWT locally. Example request
curl http://localhost:5001/api/auth/me \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
200 — Success
{
  "id": 12,
  "username": "jdoe",
  "email": "jdoe@company.com",
  "role": "Operator",
  "department": "Quality"
}
id
number
The authenticated user’s primary key.
username
string
The authenticated user’s login username.
email
string
The authenticated user’s email address.
role
string
The authenticated user’s role string.
department
string
The authenticated user’s department.
Error responses
StatusCondition
401 UnauthorizedThe Authorization header is missing, malformed, or contains an invalid/expired token

Build docs developers (and LLMs) love