Clients
A client represents an application or service identity for OAuth and API access. Clients are how applications authenticate with Hantera without requiring human interaction.
What is a Client?
Clients represent applications or services that need to access Hantera APIs - whether they’re mobile apps, web applications, backend services, or third-party integrations.
Key characteristics:
- OAuth 2.0 configuration (redirect URIs, grant types, scopes)
- Client secret authentication
- No password-based login (cannot log in to portal)
- Can be assigned roles for API access
- System clients are protected from modification
Example client:
{ "id": "01933e8f-7c45-7123-9abc-123456789xyz", "type": "client", "properties": { "name": "Mobile App", "description": "iOS and Android mobile application", "redirectUris": ["myapp://callback", "myapp://logout"], "grantTypes": ["authorization_code", "refresh_token"], "scopes": ["openid", "profile", "email"], "tokenEndpointAuthMethod": "client_secret_post" }, "roles": ["app:mobile:access"], "suspendedAt": null}When to Use Clients
✅ Use clients for:
- Third-party integrations
- Mobile or web applications (OAuth flows)
- Service-to-service communication
- Webhook consumers
- Background jobs requiring API access
- Developer tools and scripts
❌ Don’t use clients for:
- Human users needing portal access (use Principals instead)
- Accounts that need password login (use Principals instead)
Client Types
Interactive Clients
Interactive clients use OAuth flows where a user grants permission to the client.
Characteristics:
- Require redirect URIs for OAuth callbacks
- Support authorization code flow
- User consent required
- Best for mobile apps, SPAs, web applications
Example use cases:
- Mobile application that accesses user’s Hantera data
- Third-party app integration
- Partner portal access
Grant types:
authorization_code- Standard OAuth flowrefresh_token- Long-lived access
Example configuration:
{ "properties": { "name": "Partner Mobile App", "redirectUris": ["myapp://callback"], "grantTypes": ["authorization_code", "refresh_token"], "scopes": ["openid", "profile", "orders:read"] }}Service Accounts
Service accounts are non-interactive clients for server-to-server communication.
Characteristics:
- No redirect URIs needed
- Use client credentials flow
- No user interaction required
- Best for background services, APIs, batch jobs
Example use cases:
- Integration service syncing data
- Scheduled batch processing
- Webhook receiver
- Internal microservice
Grant types:
client_credentials- Server-to-server flow
Example configuration:
{ "properties": { "name": "Integration Service", "description": "Data synchronization service", "grantTypes": ["client_credentials"] }, "roles": ["integration:sync:access"]}System Clients
System clients are built-in clients managed by Hantera.
Characteristics:
- Cannot be modified or deleted
- Reserved for Hantera’s internal use
- Predefined configuration
OAuth Configuration
Grant Types
Clients support different OAuth 2.0 grant types:
authorization_code
Standard OAuth authorization flow for user-facing applications.
Flow:
- User redirects to authorization endpoint
- User grants permission
- Client receives authorization code
- Client exchanges code for access token
Use for:
- Web applications
- Mobile applications
- Single-page applications (SPA)
refresh_token
Allows obtaining new access tokens without user interaction.
Flow:
- Client uses refresh token
- Receives new access token
Use for:
- Long-lived sessions
- Background sync
- Always combine with
authorization_code
client_credentials
Server-to-server authentication without user context.
Flow:
- Client authenticates with client ID and secret
- Receives access token
Use for:
- Service accounts
- Background jobs
- API integrations
- Webhook handlers
Redirect URIs
Redirect URIs specify where users are sent after OAuth authorization.
Requirements:
- Must be HTTPS in production (HTTP allowed for localhost)
- Custom schemes allowed for mobile apps (e.g.,
myapp://callback) - Exact match required (no wildcards)
- Multiple URIs supported
Example:
{ "redirectUris": [ "https://app.example.com/oauth/callback", "https://app.example.com/oauth/logout", "myapp://callback", "http://localhost:3000/callback" ]}Scopes
Scopes define what permissions the client requests.
Standard OpenID Connect scopes:
openid- Basic identity informationprofile- User profile dataemail- User email address
Custom Hantera scopes:
- Resource-based scopes (e.g.,
orders:read,orders:write) - Application-specific scopes
Example:
{ "scopes": [ "openid", "profile", "orders:read", "customers:read" ]}Token Endpoint Authentication
Clients authenticate at the token endpoint using different methods:
client_secret_post
Client sends ID and secret in POST body.
POST /oauth/tokenContent-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=ABC123&client_id=client_id_here&client_secret=secret_hereclient_secret_basic
Client sends ID and secret in Authorization header.
POST /oauth/tokenAuthorization: Basic base64(client_id:client_secret)Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=ABC123Client Secret Management
Creating Secrets
Generate a new client secret:
POST /resources/iam/clients/{id}/secretsIf-Match: "etag-value"{ "description": "Production API access", "expiresAt": "2026-01-01T00:00:00Z"}Response:
{ "id": "01933e8f-7c45-7123-secret-abc123", "secret": "sk_live_abcdef123456...", "description": "Production API access", "createdAt": "2025-01-07T15:30:00Z", "expiresAt": "2026-01-01T00:00:00Z", "lastUsedAt": null}Secret Best Practices
Expiration:
- Always set expiration dates for secrets
- Rotate secrets before expiration
- Use short expiration for high-security environments
Description:
- Document secret purpose
- Include environment (production, staging)
- Note the system using the secret
Security:
- Never commit secrets to version control
- Use environment variables or secret managers
- Rotate compromised secrets immediately
- Monitor
lastUsedAtfor unused secrets
Listing Secrets
View all active secrets for a client:
GET /resources/iam/clients/{id}/secretsResponse:
[ { "id": "01933e8f-7c45-7123-secret-abc123", "description": "Production API access", "createdAt": "2025-01-07T15:30:00Z", "expiresAt": "2026-01-01T00:00:00Z", "lastUsedAt": "2025-01-08T10:00:00Z" }]Revoking Secrets
Delete a client secret:
DELETE /resources/iam/clients/{id}/secrets/{secretId}Returns 204 No Content on success. The secret is immediately invalidated.
When to revoke:
- Secret compromised or exposed
- Secret no longer needed
- Before secret expiration (graceful rotation)
- During security incidents
List API Features
The clients list endpoint supports filtering, search, and cursor-based pagination.
Cursor-Based Pagination
All list operations use cursor-based pagination:
Query parameters:
limit- Items per page (default: 50, max: 100)after- Cursor for next pagebefore- Cursor for previous page
Response format:
{ "clients": [...], "totalCount": 42, "lastCursor": "01933e8f-7c45-7123-9abc-123456789abc", "firstCursor": "01933e8f-7c45-7123-9abc-123456789def"}Example:
GET /resources/iam/clients?limit=50GET /resources/iam/clients?limit=50&after=cursor_hereSearch
Search by client name (case-insensitive, partial matches):
GET /resources/iam/clients?search=mobileFiltering
Filter by type:
GET /resources/iam/clients?type=service_accountInclude suspended clients:
# By default, suspended clients are excludedGET /resources/iam/clients?includeSuspended=trueSorting
Sort results using the orderBy parameter:
# Sort by name ascending (default)GET /resources/iam/clients?orderBy=name asc
# Sort by created dateGET /resources/iam/clients?orderBy=createdAt descClient Management
Create or Update Client
Full replacement of a client:
PUT /resources/iam/clients/{id}If-Match: "etag-value"{ "properties": { "name": "Mobile App", "description": "iOS and Android application", "redirectUris": ["myapp://callback"], "grantTypes": ["authorization_code", "refresh_token"], "scopes": ["openid", "profile", "orders:read"], "tokenEndpointAuthMethod": "client_secret_post" }, "roles": ["app:mobile:access"]}Partial Update
Update specific properties:
PATCH /resources/iam/clients/{id}If-Match: "etag-value"{ "name": "Mobile App v2", "description": "Updated mobile application"}Update Roles
Replace the entire roles list:
PUT /resources/iam/clients/{id}/rolesIf-Match: "etag-value"{ "roles": ["app:mobile:access", "integration:api:read"]}Get Client Details
Retrieve a client with all properties:
GET /resources/iam/clients/{id}Response:
{ "id": "01933e8f-7c45-7123-9abc-123456789xyz", "name": "Mobile App", "description": "iOS and Android application", "redirectUris": ["myapp://callback"], "grantTypes": ["authorization_code", "refresh_token"], "scopes": ["openid", "profile", "orders:read"], "tokenEndpointAuthMethod": "client_secret_post", "roles": ["app:mobile:access"], "suspendedAt": null, "createdAt": "2024-01-01T10:00:00Z", "etag": "W/\"abc123\""}Delete Client
Remove a client permanently:
DELETE /resources/iam/clients/{id}Returns 204 No Content on success.
Integration Patterns
OAuth Authorization Code Flow
-
Create OAuth client
PUT /resources/iam/clients/mobile-app-v1{"properties": {"name": "Mobile App","redirectUris": ["myapp://callback"],"grantTypes": ["authorization_code", "refresh_token"]}} -
Generate client secret
POST /resources/iam/clients/mobile-app-v1/secrets{"description": "Production secret"} -
Redirect user to authorization endpoint
GET /oauth/authorize?response_type=code&client_id=mobile-app-v1&redirect_uri=myapp://callback&scope=openid%20profile%20orders:read -
Exchange authorization code for tokens
POST /oauth/token{"grant_type": "authorization_code","code": "auth_code_here","client_id": "mobile-app-v1","client_secret": "secret_here","redirect_uri": "myapp://callback"} -
Use access token for API calls
GET /resources/orders/123Authorization: Bearer access_token_here
Service Account Pattern
-
Create service account client
PUT /resources/iam/clients/sync-service{"properties": {"name": "Data Sync Service","grantTypes": ["client_credentials"]},"roles": ["integration:sync:full"]} -
Generate long-lived secret
POST /resources/iam/clients/sync-service/secrets{"description": "Production sync service","expiresAt": "2026-12-31T23:59:59Z"} -
Obtain access token
POST /oauth/token{"grant_type": "client_credentials","client_id": "sync-service","client_secret": "secret_here"} -
Make API calls
GET /resources/ordersAuthorization: Bearer access_token_here
Secret Rotation
-
Create new secret (before old expires)
POST /resources/iam/clients/{id}/secrets{"description": "Rotated secret 2025-01","expiresAt": "2026-01-31T00:00:00Z"} -
Deploy new secret to application Update environment variables or secret manager
-
Verify new secret works Test authentication with new secret
-
Revoke old secret
DELETE /resources/iam/clients/{id}/secrets/{old-secret-id}
API Endpoints
Client management endpoints:
GET /resources/iam/clients- List clientsGET /resources/iam/clients/{id}- Get clientPUT /resources/iam/clients/{id}- Create or update (full)PATCH /resources/iam/clients/{id}- Update properties (partial)DELETE /resources/iam/clients/{id}- Delete clientPUT /resources/iam/clients/{id}/roles- Update rolesGET /resources/iam/clients/{id}/secrets- List secretsPOST /resources/iam/clients/{id}/secrets- Create secretDELETE /resources/iam/clients/{id}/secrets/{secretId}- Revoke secret
For complete endpoint documentation, request/response formats, and error codes, see the HTTP API Reference.
Security Considerations
Secret Storage
Never:
- ❌ Commit secrets to version control
- ❌ Log secrets in plain text
- ❌ Include secrets in URLs
- ❌ Send secrets via email
- ❌ Store secrets in client-side code
Always:
- ✅ Use environment variables
- ✅ Use secret management services (AWS Secrets Manager, Azure Key Vault)
- ✅ Rotate secrets regularly
- ✅ Set expiration dates
- ✅ Monitor secret usage
Rate Limiting
Implement rate limiting for client authentication:
- Prevent brute force attacks
- Monitor failed authentication attempts
- Suspend clients with excessive failures
Audit Logging
Track client activity:
- Authentication attempts
- API calls made
- Secret creation/revocation
- Configuration changes
Troubleshooting
Invalid Client Error
Cause: Client ID doesn’t exist or client is suspended
Solution:
- Verify client ID is correct
- Check if client exists:
GET /resources/iam/clients/{id} - Check if client is suspended (look for
suspendedAt)
Invalid Client Secret
Cause: Secret is incorrect, expired, or revoked
Solution:
- Verify secret is correct
- Check secret expiration date
- Generate new secret if needed
- Ensure secret hasn’t been revoked
Invalid Redirect URI
Cause: Redirect URI doesn’t match client configuration
Solution:
- Verify exact URI match (including protocol, port, path)
- Check client configuration:
GET /resources/iam/clients/{id} - Update client if redirect URI changed
Unsupported Grant Type
Cause: Requested grant type not configured for client
Solution:
- Check client’s
grantTypesconfiguration - Update client to include required grant type
Related Resources
- IAM Overview - Introduction to IAM concepts
- Principals - User identity management
- Authentication Guide - OAuth flows and patterns
- Access Control - Permission patterns
- HTTP API Reference - Complete endpoint documentation