VegBank API uses three authorization scopes that determine what operations user can perform:
| Scope | Purpose | Operations |
|---|---|---|
vegbank:user |
Create/update datasets | Create/update datasets from existing vegbank observations |
vegbank:contributor |
Create/update access | Upload new data, submit plot observations, create new records |
vegbank:admin |
Administrative access | All operations, including administrative functions |
When you authenticate, you may request one or more scopes to be included in your OIDC token. These scopes then determine the levels of access you have for the various Vegbank API methods.
VegBank API uses OAuth 2.0 with OpenID Connect (OIDC) for secure authentication and authorization. When you log in, you receive a token that grants you access to specific API operations based on your requested scopes. For more information on these standards, see the OAuth 2.0 Authorization Framework and OpenID Connect Core documentation.
All authentication and authorization error responses use a consistent JSON format:
{
"error": {
"message": "Description of the error",
"details": "Additional context (optional)"
}
}The details field is included when there is additional context about the error (e.g., exception details) and omitted otherwise.
VegBank uses Keycloak for token management. To obtain a token:
Visit the VegBank login endpoint, and it will redirect you to the ORCID authentication page. Login with your ORCID credentials.
On success (200 OK):
{
"message": "Authorization successful",
"token": {
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI..."
}
}Store both tokens. The access_token is used for API requests and has a short expiry. The refresh_token is long-lived and can be used to obtain a new access token without logging in again (see Refreshing Your Token).
Error responses:
| Status | Cause |
|---|---|
401 |
Authentication failed |
403 |
Authentication is not available in the current deployment mode |
500 |
Unexpected server error |
Include your token in the Authorization header of your requests to api.vegbank.org:
Authorization: Bearer <YOUR_TOKEN_HERE>Access tokens are short-lived. When your access token expires, use the POST /refresh endpoint to obtain a new access token and refresh token without requiring the user to log in again.
curl -s -L -X POST 'https://api.vegbank.org/refresh' \
-H "Content-Type: application/json" \
-d "{
\"refresh_token\": \"${REFRESH}\",
\"scope\": \"openid web-origins acr offline_access profile basic email vegbank:contributor vegbank:user\"
}"Request body (JSON):
| Field | Type | Required | Description |
|---|---|---|---|
refresh_token |
string | Yes | The refresh token returned from /login or a previous /refresh call |
scope |
string | No | Space-separated list of scopes for the new access token. If no scopes are provided, the new access token will have the same scopes as the original token. The requested scopes must match or be a subset of the original scopes granted. |
Note: In most cases, you can safely omit the
scopeparameter. The OIDC provider will reissue the token with the same scopes that were originally granted, which is typically the desired behavior.
On success (200 OK):
{
"message": "Authorization successful",
"token": {
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI..."
}
}Replace your stored tokens with the new values returned. Because each refresh token can only be used once, each refresh call issues a new refresh token, and so the old one must be discarded.
Error responses:
| Status | Cause |
|---|---|
400 |
refresh_token field missing from the request body |
401 |
Refresh token is invalid, expired, or revoked; or client authentication failed |
500 |
Unexpected server error |
Upload new plot observations (requires vegbank:contributor scope):
curl -X POST \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI..." \
-F "plot_observations=@plot_data.parquet" \
https://api.vegbank.org/plot-observationscurl -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI..." \
https://api.vegbank.org/plot-observationsTo enable auth support we require the following environment variables. See the helm README for more details.
# Flask secret key
FLASK_SECRET_KEY=your-secret-key
# OIDC/Keycloak configuration
OIDC_CLIENT_SECRETS_FILE=/path/to/client_secrets.json
# Standard OIDC scopes to request during login (default: "openid email profile")
# VegBank role scopes (VB_SCOPE_ADMIN/CONTRIBUTOR/USER) are always appended automatically.
# Set via the auth.oidcDefaultScopes Helm value.
VB_OIDC_DEFAULT_SCOPES=openid email profileThe client_secrets.json file is required for communication with the OIDC provider.
{
"client_id": "vegbank-api-client",
"client_secret": "your-client-secret-from-keycloak",
"server_metadata_url": "https://auth.vegbank.org/auth/realms/vegbank/.well-known/openid-configuration"
}The server_metadata_url tells the system where to find OIDC keys, token endpoints, and other config.
Protects an endpoint to require any valid, unexpired JWT:
from vegbank.auth import require_token
@app.route("/taxon-observations", defaults={'vb_code': None}, methods=['GET', 'POST'])
@require_token
def taxon_observations(claims, vb_code):
"""
Facilitates retrieving and uploading of new taxon observations
"""Protects an endpoint to require a valid token and a specific scope:
from vegbank.auth import require_scope, SCOPE_CONTRIBUTOR, SCOPE_ADMIN
@app.route("/taxon-observations", defaults={'vb_code': None}, methods=['GET', 'POST'])
@require_scope(SCOPE_CONTRIBUTOR)
def taxon_observations(claims, vb_code):
"""
Facilitates retrieving and uploading of new taxon observations
"""Protects specific HTTP methods for an endpoint that require a valid token and a specific scope:
from vegbank.auth import require_scope, SCOPE_CONTRIBUTOR, SCOPE_ADMIN
@app.route("/taxon-observations", defaults={'vb_code': None}, methods=['GET', 'POST'])
@require_scope(SCOPE_CONTRIBUTOR, methods=['POST'])
def taxon_observations(claims, vb_code):
"""
Facilitates retrieving and uploading of new taxon observations
"""The require scope only applies to the list of HTTP methods passed to the decorator. If no methods are passed the scope requirement is applied to all methods.
All authentication and authorization errors return a consistent JSON structure via _auth_error_response():
{
"error": {
"message": "Description of the error",
"details": "Additional context (present when available)"
}
}The _token_error_response() helper maps exceptions to HTTP status codes:
| Exception | Message | Status |
|---|---|---|
DecodeError |
Token decoding failed | 401 |
InvalidClientError |
OIDC client authentication failed | 401 |
InvalidTokenError |
Token validation failed | 401 |
InvalidGrantError |
Invalid or expired refresh token | 401 |
BadSignatureError |
Token signature verification failed | 401 |
OAuthError |
Authorization failed | 401 |
OAuth2Error |
An OAuth2 error occurred | 401 |
KeyError / TypeError |
Invalid token structure | 401 |
MissingParameterError |
Missing required parameter | 400 |
ValueError |
OIDC provider configuration error | 500 |
RequestException |
Failed to fetch OIDC provider keys | 502 |
Protected endpoints (@require_token / @require_scope) return:
| Status | Cause |
|---|---|
401 |
Missing or invalid Authorization header, or any token validation failure from the table above |
403 |
Token is valid but missing the required scope |
403 |
Upload attempted in read_only deployment mode |
For questions or issues with the API authorization:
- Check the VegBank GitHub Issues
- Ask questions in VegBank Discussions
- Email help@vegbank.org