Client app: access rights, governance & audit logs
How to integrate Veilio inside your own application so that:
- end-users of your product never need a Veilio account,
- your backend owns roles and access decisions,
- Veilio audit logs still show who revealed which data.
Architecture (who talks to whom)
End-user (logged into YOUR app)
│
▼
Your backend (session + roles)
│ API key (server-side only)
▼
Veilio API → AccessLog (+ optional SIEM)Rules:
- The Veilio API key never leaves your backend (no browser, no mobile bundle).
- Your app authenticates the end-user (session / JWT / SSO).
- Veilio authenticates your integration (the API key tied to your Veilio org).
- On detokenize, pass the end-user identity with
usernameso logs are meaningful.
What Veilio logs by default
Each successful DETOKENIZE creates an access log with:
| Field | Meaning |
|---|---|
| API key / org user | The Veilio account that owns the key (your company) |
| Token id / type | Which token was revealed (not the plaintext) |
reason | Optional business context you send |
username → actorUsername | End-user of your app (optional but recommended) |
| IP / user-agent | Usually your backend server, not the browser |
| Timestamp / success | When it happened |
Without username, the log only says “your API key revealed token X”.
With username, it says “end-user Alice triggered a reveal via your app”.
View logs in the Veilio dashboard (History / admin logs) or export them to your SIEM (SIEM integration).
Use case 1 — Attribute every reveal to the end-user
Situation
Your product has its own users. They open a page that needs plaintext (send email, open a record, export). Your backend calls Veilio with your org API key. You need the Veilio journal to show which of your users caused the reveal — without creating Veilio logins for them.
Pattern
- Resolve the authenticated session in your app (
session.username,session.userId, …). - Call
detokenizefrom the backend and passusername(stable human-readable id: login, email local-part, or opaque staff id). - Optionally set
reasonfor context (open_record,send_notification, …).
JavaScript / TypeScript
import { VeilioClient } from "@veilio/sdk";
const veilio = new VeilioClient({
apiKey: process.env.VEILIO_API_KEY!,
baseUrl: process.env.VEILIO_BASE_URL || "https://app.veilio.fr/api",
});
/**
* Called from YOUR authenticated route handler.
* `session` comes from YOUR auth — never from Veilio.
*/
export async function revealField(session: {
username: string;
}, token: string) {
const { data, accessedAt } = await veilio.detokenize({
token,
username: session.username, // → AccessLog.actorUsername
reason: "open_record",
});
return { data, accessedAt };
}HTTP
POST /api/detokenize
X-API-Key: vk_live_...
Content-Type: application/json
{
"token": "tok_...",
"username": "alice.dupont",
"reason": "open_record"
}Python
result = client.detokenize(
token="tok_...",
username=session["username"],
reason="open_record",
)Result in Veilio logs
- Org / API key: your integration
- Username:
alice.dupont(your end-user) - Reason:
open_record - Token type / id: which field was revealed
Use case 2 — Role-based reveal (your app decides)
Situation
Several roles can open the same screen (role1, role2, role3), but only some roles may see plaintext. Veilio does not know your RBAC. Authorization stays in your app; Veilio records the reveal when you call the API.
Pattern
- Gate detokenize with your role check before calling the SDK.
- If the role is not allowed: return tokens / masked values only — no detokenize call.
- When allowed: detokenize and still pass
username(use case 1).
Example
type AppRole = "role1" | "role2" | "role3";
/** Only role1 may reveal plaintext in this flow. */
const CAN_DETOKENIZE: ReadonlySet<AppRole> = new Set(["role1"]);
export async function loadSensitiveField(
session: { username: string; role: AppRole },
token: string
): Promise<{ value: string; revealed: boolean }> {
if (!CAN_DETOKENIZE.has(session.role)) {
// role2 / role3: see the token or a masked placeholder — no Veilio reveal
return { value: token, revealed: false };
}
const { data } = await veilio.detokenize({
token,
username: session.username,
reason: `reveal_by_${session.role}`,
});
return { value: data, revealed: true };
}Same idea with an explicit variable before the SDK call:
const allowDetokenize = session.role === "role1";
if (allowDetokenize) {
const { data } = await veilio.detokenize({
token,
username: session.username,
reason: "authorized_reveal",
});
// use plaintext `data`
} else {
// keep `token` / masked UI — do not call Veilio
}Governance checklist
| Topic | Owner | Notes |
|---|---|---|
| Login / session | Your app | End-users never sign in to Veilio |
Roles (role1 / role2 / role3) | Your app | Enforce before detokenize |
| API key storage | Your backend / secrets manager | One key per environment |
| Who was revealed for | Veilio audit (username + reason) | Pass on every reveal |
| SIEM / SOC | Veilio webhook → your stack | Same events, including actor.username |
Recommended:
- Always send
usernameon detokenize (and bulk / format when applicable). - Always send a short, stable
reason. - Never detokenize “just in case” on page load for every role.
- Never log plaintext returned by Veilio into your application logs.