Open API V2 Authentication
AuditDashboard Open API v2 — Authentication#
The v2 API (/api/v2/*) authenticates with a JWT bearer token obtained through an interactive OAuth loopback flow: a real user signs in through their SSO provider (Microsoft / Google) and your app receives a bearer and a refresh token. Every call is made as that user and is scoped to their permissions.Once you hold a bearer token, every v2 request carries it the same way:Authorization: Bearer {token}
Loopback OAuth flow#
1. Open the login URL in a browser#
https://{tenant}.auditdashboard.{suffix}/Account/Login/{provider}?returnUrl=http://127.0.0.1:{port}/
{tenant} — portal subdomain (e.g. yoursite)
{provider} — Microsoft or Google
returnUrl — a loopback listener you control
https://yoursite.auditdashboard.com/Account/Login/Microsoft?returnUrl=http://127.0.0.1:8123/
The user completes SSO in the browser.2. Receive the one-time code on your loopback listener#
Because returnUrl is a genuine loopback address, the server does not set a cookie. Instead it mints a short-lived one-time code and redirects the browser to:http://127.0.0.1:{port}/?code={code}
Your local HTTP listener reads code from the query string.The code is single-use and expires 2 minutes after it is issued.
Only real loopback hosts trigger this: 127.0.0.0/8, localhost, ::1, http scheme only. The host is validated by parsing the URL (not a string prefix), and embedded credentials (http://127.0.0.1@evil.com) are rejected — so a code is never handed to an external host. Any non-loopback returnUrl falls through to normal cookie sign-in and no code is minted.
3. Exchange the code for tokens#
POST /api/account/exchangeToken
Content-Type: application/json
{
"code": "96534024690e470eaa70d966d56c1c49"
}
No auth header is required on this call — the code is the credential.
(POST /api/account/outlookToken is a legacy alias that behaves identically; new clients should use exchangeToken.){
"token": "<JWT access token>",
"expires": "2026-08-06T12:34:56Z",
"refreshToken": "<opaque refresh token>",
"refreshExpires": "2026-08-13T12:34:56Z",
"email": "user@example.com",
"userid": 123,
"name": "Jane Doe",
"roles": [ ... ],
"companyName": "...",
"customerPortalName": "..."
}
A missing, expired, already-used, or unknown code returns 401 Unauthorized.4. Call the API#
Refreshing the access token#
Access tokens are short-lived; the refresh token lasts 7 days. When the access token expires:POST /api/account/refresh
Content-Type: application/json
{
"refreshToken": "<opaque refresh token>"
}
Returns the same body shape as exchangeToken with a fresh token and a rotated refreshToken — the old refresh token is revoked on use, so always store the new one. An invalid, revoked, or expired refresh token returns 401.
Summary#
| Step | Method | Route | Auth |
|---|
| Login | GET (browser) | /Account/Login/{provider}?returnUrl=http://127.0.0.1:{port}/ | interactive SSO |
| Exchange code | POST | /api/account/exchangeToken | the one-time code (no header) |
| Refresh | POST | /api/account/refresh | the refresh token (no header) |
| API calls | any | /api/v2/* | Authorization: Bearer {token} |
Modified at 2026-09-09 22:11:40