Authentication
Most operations on the Vasco API require an authenticated user. Authentication uses OAuth 2.0: you exchange a user's credentials for a short-lived access token at the /oauth/token endpoint, then send that token as a Bearer credential on every request to the authenticated GraphQL endpoint.
- Pick the environment you want to target — see API endpoints.
- Request an OAuth2 client from Vasco — see Getting an OAuth2 client below. You cannot authenticate without one.
- Have a user account. Users can register themselves through the public
CreateUsermutation, or be created by an admin (see the User Management recipe).
Getting an OAuth2 client
The OAuth2 flow requires client credentials — a client_id and, for confidential clients, a client_secret — that identify your integration. These are issued by Vasco: they are not self-service.
To obtain them, contact Vasco and provide:
- The environment(s) you need access to (production, preproduction).
- A short description of your integration.
- The grant types you intend to use (typically
passwordandrefresh_token).
Vasco will provision an OAuth2 client and return your client_id and client_secret.
The client_secret is a confidential credential. Store it in a secret manager, never commit it to source control, and never expose it in a browser or mobile client. If it leaks, ask Vasco to rotate it.
Obtaining a token
Send a POST request to /oauth/token with the password grant. Include your client credentials together with the user's credentials:
curl -X POST https://api.<client>.vasco.fund/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password" \
-d "client_id=<YOUR_CLIENT_ID>" \
-d "client_secret=<YOUR_CLIENT_SECRET>" \
-d "username=user@example.com" \
-d "password=<password>"
A successful response is a standard OAuth2 token payload:
{
"token_type": "Bearer",
"expires_in": 900,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpXXXXXX.YYYYYY",
"refresh_token": "def502004a...ZZZZZZ"
}
| Field | Description |
|---|---|
access_token | The JWT to send on authenticated requests. Valid for expires_in seconds. |
expires_in | Access token lifetime in seconds (15 minutes by default). |
refresh_token | Long-lived token used to obtain a new access token without re-sending credentials. |
token_type | Always Bearer. |
Two-factor authentication
If the user has two-factor authentication (TOTP) enabled, you must include their current one-time code as otp_code:
curl -X POST https://api.<client>.vasco.fund/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password" \
-d "client_id=<YOUR_CLIENT_ID>" \
-d "client_secret=<YOUR_CLIENT_SECRET>" \
-d "username=user@example.com" \
-d "password=<password>" \
-d "otp_code=123456"
When 2FA is required and otp_code is missing or invalid, the endpoint returns 400 with error: invalid_grant.
Using the token
Pass the access token as a Bearer credential on every request to the authenticated GraphQL endpoint:
POST /graphql/ HTTP/1.1
Host: api.<client>.vasco.fund
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpXXXXXX.YYYYYY
Content-Type: application/json
{ "query": "query { GetCurrentUser { id email } }" }
Refreshing a token
Access tokens are short-lived. Rather than re-sending the user's credentials, exchange the refresh_token for a fresh access token using the refresh_token grant:
curl -X POST https://api.<client>.vasco.fund/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "client_id=<YOUR_CLIENT_ID>" \
-d "client_secret=<YOUR_CLIENT_SECRET>" \
-d "refresh_token=<YOUR_REFRESH_TOKEN>"
The response has the same shape as the initial token response, with a new access_token and a new refresh_token. Each refresh rotates the refresh token: the one you sent is invalidated, so always store the refresh_token returned by the latest response and use it for the next refresh.
Refresh tokens are valid for 6 hours and cannot be renewed indefinitely. The absolute lifetime of 12 hours is counted from the start of the refresh chain — the original password grant — not from the most recent refresh. Once that limit is reached, the user must authenticate again with the password grant.
Revoking a token
To log a user out, revoke their refresh token at /oauth/revoke (RFC 7009). Revoking a refresh token also invalidates the access token it is linked to:
curl -X POST https://api.<client>.vasco.fund/oauth/revoke \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=<YOUR_CLIENT_ID>" \
-d "client_secret=<YOUR_CLIENT_SECRET>" \
-d "token=<REFRESH_TOKEN>" \
-d "token_type_hint=refresh_token"
Set token_type_hint=access_token to revoke an access token directly. The endpoint returns 200 even for an unknown or malformed token — revocation is idempotent. The one exception is a missing or empty token parameter, which returns 400 with error: invalid_request.
Error responses
Errors follow the OAuth2 convention — a JSON body with an error code and an optional error_description:
{
"error": "invalid_grant",
"error_description": "Invalid credentials."
}
| HTTP status | error | Meaning |
|---|---|---|
400 | invalid_grant | Wrong username/password, or a missing/invalid otp_code. |
400 | invalid_grant | Too many one-time-code attempts (max 5 per 5 minutes) — returned with the same code as an incorrect otp_code. |
400 | invalid_grant | A refresh_token that is expired, already used (rotated away), or has reached the 12-hour absolute lifetime. |
400 | invalid_request | A missing or empty token parameter on /oauth/revoke. |
401 | invalid_client | Unknown client_id or wrong client_secret. |
429 | invalid_grant | Too many failed attempts — the login endpoint is rate-limited per IP and per (username, IP) pair. Wait ~1 minute and retry. |
When an authenticated GraphQL request is rejected because the access token is missing, malformed, or expired, obtain a new token via the refresh flow (or the password grant if the refresh token has also expired).
Forgotten password
A user who has lost their password can trigger the standard reset flow without being authenticated:
RequestPasswordReset— sends a reset email to the user.ResetPassword— consumes the token from the email and sets a new password.
Operations that do not require a token
Sign-up and password-reset operations are exposed on the public GraphQL endpoint and can be called anonymously. Browse the Public API section of the API Reference for the full list.
Deprecated: password login (/auth/login)
The legacy POST /auth/login endpoint — which exchanges a username and password directly for a JWT — is deprecated and will be removed in a future release. It does not support OAuth2 client credentials, refresh tokens, or token revocation. Migrate all integrations to the OAuth2 flow above.
For reference, the deprecated flow sent credentials to /auth/login:
# Deprecated — do not use for new integrations.
curl -X POST https://api.<client>.vasco.fund/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "user@example.com",
"password": "<password>"
}'
and returned the JWT directly:
{ "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpXXXXXX.YYYYYY" }
Migrating from /auth/login
Legacy (/auth/login) | OAuth2 (/oauth/token) |
|---|---|
POST /auth/login, JSON body | POST /oauth/token, application/x-www-form-urlencoded body |
username + password | grant_type=password + client_id + client_secret + username + password |
| No client credentials | Requires an OAuth2 client — request one from Vasco |
Response: { "token": "..." } | Response: { "access_token": "...", "refresh_token": "...", "expires_in": 900, "token_type": "Bearer" } |
| Re-send credentials when the JWT expires | Use the refresh_token grant to renew |
The way you attach the token to GraphQL requests is unchanged: pass it in the Authorization: Bearer header exactly as before. What changes is the token endpoint's response body — { "token": "..." } becomes { "access_token": "...", "refresh_token": "...", ... } — so read the token from access_token instead of token.
Related
- API endpoints — where to send requests
- User Management recipe — create and manage users from the authenticated API
CreateUser— public self-registration