OAuth: Let Your Users Log In with Haufe
Your users log in with their Haufe account, and your product calls Copilot via API with their own access token.
You like to integrate a Haufe copilot into your own product. Your users have a Haufe account. They log in with that account inside your product. Your backend then calls Copilot via API with the access token of the user. The content package of the user decides which content the copilot can use for the answer.
This page describes how you get your OAuth client, how you run the login flow, and how you send the token. If your application calls the API in its own name, use an API key instead.
How It Works
- Your backend redirects the user to the Haufe login.
- The user logs in with their Haufe account and returns to your redirect URI with an authorization code.
- Your backend exchanges the code for an access token and a refresh token.
- Your backend sends the access token as a bearer token to Copilot via API.
- The API checks the signature and the expiry of the token and the subscription of your client.
- The API reads the content package of the user from the token, then answers the request.
The flow is the standard OAuth 2.0 authorization code flow with PKCE. The browser of the user never sees a token. Keep the client secret and all tokens on your backend.
Get Your Client ID and Client Secret
Send your redirect URI to Haufe
The redirect URI is the endpoint of your backend that receives the authorization code, for example https://app.example.com/auth/callback.
Use an https URI in production. For local tests, http URIs are accepted for localhost and loopback addresses only, for example http://localhost:9999/callback.
We recommend a separate application for local tests. Then the client secret of your production application stays on your backend.
Your Haufe contact person creates the OAuth application with this URI and assigns it to your account.
Read your credentials in the portal
Open the application in the Developer Portal. The settings show the client ID and the client secret. Renew client secret creates a new secret. The old secret stops working immediately.
Manage your redirect URIs in the portal
You can add and remove redirect URIs in the application settings. Save the application after the change. The client ID and the client secret stay the same after a save.
Treat the client secret like a password. Store it on your backend only. Never ship it in a browser or mobile app.
Run the Login Flow
The Haufe login is an OpenID Connect provider. Use an OpenID Connect library for your platform and configure it with these values.
Most libraries read the endpoints from the discovery document at https://iam.auth.haufe.io/.well-known/openid-configuration.
| Setting | Value |
|---|---|
| Issuer | https://iam.auth.haufe.io |
| Authorization endpoint | https://iam.auth.haufe.io/oauth/v2/authorize |
| Token endpoint | https://iam.auth.haufe.io/oauth/v2/token |
| Grant type | authorization_code with PKCE (S256) |
| Client authentication | client_secret_basic |
| Scopes | openid profile email offline_access |
The scope offline_access gives you a refresh token. Without it, the user must log in again when the access token expires.
Redirect the user to the authorization endpoint
https://iam.auth.haufe.io/oauth/v2/authorize
?response_type=code
&client_id=<CLIENT_ID>
&redirect_uri=https://app.example.com/auth/callback
&scope=openid%20profile%20email%20offline_access
&state=<RANDOM_STATE>
&code_challenge=<CODE_CHALLENGE>
&code_challenge_method=S256The user logs in with their Haufe account. The login then redirects the browser to your redirect URI with the query parameters code and state.
Check that state matches the value you sent.
Exchange the code for tokens
curl --request POST \
--url https://iam.auth.haufe.io/oauth/v2/token \
--user '<CLIENT_ID>:<CLIENT_SECRET>' \
--header 'content-type: application/x-www-form-urlencoded' \
--data 'grant_type=authorization_code' \
--data 'code=<CODE>' \
--data 'redirect_uri=https://app.example.com/auth/callback' \
--data 'code_verifier=<CODE_VERIFIER>'{
"access_token": "<ACCESS_TOKEN>",
"token_type": "Bearer",
"expires_in": 43199,
"refresh_token": "<REFRESH_TOKEN>",
"id_token": "<ID_TOKEN>"
}access_tokenis the token you send to Copilot via API. It is a JSON Web Token. Send it as it is and do not build logic on its content.expires_inis the lifetime of the access token in seconds. Refresh the token before it expires.id_tokenis a JSON Web Token with the identity of the user. Use it for your own session, not for the API.
Refresh the access token
curl --request POST \
--url https://iam.auth.haufe.io/oauth/v2/token \
--user '<CLIENT_ID>:<CLIENT_SECRET>' \
--header 'content-type: application/x-www-form-urlencoded' \
--data 'grant_type=refresh_token' \
--data 'refresh_token=<REFRESH_TOKEN>'The response has the same shape as the token response. If the refresh fails, send the user through the login again.
Call the API with the Access Token
Send the access token in the Authorization header. Do not send an api-key header.
curl --request GET \
--url https://api.haufe.ai/agents/v1/assistants \
--header 'Authorization: Bearer <ACCESS_TOKEN>'All endpoints in the Quickstart and the API Reference work the same way.
The examples there show the api-key header. Replace it with the Authorization header.
With OAuth user tokens, the content package of the user comes from the token. The request metadata meta_data.user_data.licence has no effect. Do not send it.
Error Codes
| HTTP Status | Response | Cause |
|---|---|---|
| 401 | {"message": "Unauthorized", "http_status_code": 401} | The request has no Authorization header, the access token is expired or not valid, or your client has no active subscription. |
| 403 | {"error_code": "assistant_not_licensed", "detail": "The access token has no licensed product for this assistant"} | The user has no license for a product of this assistant. GET /v1/assistants lists only the assistants the user can use. |
| 403 | {"error_code": "thread_requires_access_token", "detail": "The thread was created with an access token. Send an access token to run it"} | The run has no access token, but the thread was created with one. Send the Authorization header on every run of this thread. |
| 404 | {"error_code": "thread_not_found", "detail": "Thread not found"} | The run has the access token of a different user than the user that created the thread. Run the thread with the token of that user. |
| 422 | {"error_code": "user_id_conflicts_with_token", "detail": "Do not send a user_id with an access token. The token gives the user"} | The request body has a user_id. Remove user_id. The API gets the user from the token. |
| 422 | {"error_code": "user_tenant_id_conflicts_with_token", "detail": "Do not send a user_tenant_id with an access token. The token gives the tenant"} | The request body has a user_tenant_id. Remove user_tenant_id. The API gets the tenant from the token. |
If you get a 401 with a token that worked before, refresh the token and retry once. If the refresh fails, send the user through the login again. If every token of your client gets a 401, the subscription of your application is missing or not active. Contact your Haufe contact person.
Next Steps
Quickstart
Make your first API call in minutes with a step-by-step guide.
Error Handling
Error codes, rate limit headers, and streaming errors.
Rate Limits
Understand request limits and how to handle throttling.
Developer Portal
Manage your application and renew your client secret.