Skip to main content

Authentication

This document describes how partners and customers authenticate with the EnergyZero Platform and obtain access tokens for our APIs.

EnergyZero uses OAuth 2.0 as its authentication standard. Depending on your use case, you will use one of three flows:

  • Client Credentials Grant — authenticate as a partner application to access partner-scoped resources.
  • Impersonation Flow — exchange your partner token for a user-scoped token to act on behalf of a specific customer or user.
  • Authorization Code Flow — authenticate as an end user (customer) through a login UI, with optional 2FA.

Partner & User Model

When using the Client Credentials grant, your application authenticates at the partner level. Within a partner, multiple users can exist with different roles — a user does not have to be a customer. For example, a support colleague and a customer with a contract can both be users under the same partner.

+---------------------+
| Partner |
+---------------------+
|
+-----+-----+---------------------+
| | |
+------+ +----------+ +----------+
|User A| |Customer B| |Support C |
+------+ +----------+ +----------+
(staff) (customer) (staff, more perms)

Some APIs are scoped to the user/customer perspective and cannot be called with a partner-level token. For those, you must either impersonate a user (machine-to-machine) or use the Authorization Code Flow (user-initiated login).

Environments

EnvironmentBase URL
Prerelease (Testing)https://auth.api.staging.energyzero.nl
Productionhttps://auth.api.energyzero.nl

Client Credentials Grant

The Client Credentials grant lets your application obtain a partner-level access token without any user interaction. Use this token to access resources that belong to your partner scope.

EnergyZero provisions your client_id and client_secret via a 1Password share 🔐. Keep these credentials secure — treat them like a password.

Flow

Request

Credentials are passed as form fields (application/x-www-form-urlencoded):

curl --location 'https://auth.api.staging.energyzero.nl/oauth2/token' \
--form 'grant_type="client_credentials"' \
--form 'scope="openid offline"' \
--form 'client_id="<CLIENT_ID>"' \
--form 'client_secret="<CLIENT_SECRET>"'

Response

On success, you receive an access token scoped to your partner. This token is valid for 5 minutes.

{
"access_token": "<PARTNER_ACCESS_TOKEN>",
"expires_in": 299,
"scope": "openid offline",
"token_type": "bearer"
}

Impersonation Flow

Some EnergyZero APIs operate from the perspective of a specific user or customer and cannot be accessed with a partner-level token. The impersonation flow lets you exchange your partner token for a user-scoped token, allowing you to act on behalf of any user that belongs to your partner.

Flow

Step 1 — Obtain a Partner Token

First, authenticate as your partner application using the Client Credentials grant as described above. The partner token returned here is what you pass to the impersonation endpoint.

curl --location 'https://auth.api.staging.energyzero.nl/oauth2/token' \
--form 'grant_type="client_credentials"' \
--form 'scope="openid offline"' \
--form 'client_id="<CLIENT_ID>"' \
--form 'client_secret="<CLIENT_SECRET>"'

Step 2 — Impersonate a User

Using the partner token from Step 1, call the impersonation endpoint with the user_id of the user you want to act on behalf of.

FieldValue
EndpointGET /v1/auth/impersonate
HeaderAuthorization: Bearer <partner_token>
Query parameteruser_id — the ID of the user to impersonate
curl --location 'https://auth.api.staging.energyzero.nl/v1/auth/impersonate?user_id=<USER_ID>' \
--header 'Authorization: Bearer <PARTNER_ACCESS_TOKEN>'

On success, a user-scoped token is returned. This token is valid for 1 hour.

{
"access_token": "<USER_ACCESS_TOKEN>",
"expires_in": 3599,
"scope": "openid offline",
"token_type": "bearer"
}

Step 3 — Call User-Scoped APIs

Use the token from Step 2 to call any EnergyZero API that requires a user context.

curl --location 'https://customer.api.staging.energyzero.nl' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <USER_ACCESS_TOKEN>' \
--data '{
"query": "query me { me { firstName surname } }",
"variables": {}
}'

Authorization Code Flow

The Authorization Code Flow is used when a customer logs in directly through your application's UI. Unlike the machine-to-machine flows above, this flow involves a real user entering their credentials (and optionally a 2FA code) in a browser. At the end of the flow, your application receives an access token and refresh token scoped to that specific user.

Use this flow when you are building a frontend application where customers log in themselves, rather than having a backend system act on their behalf.

Flow

Step 1 — Initiate the Login Flow

Your frontend starts the flow by redirecting the user to the authorization endpoint. This kicks off the login session with the auth server and returns a login_challenge that ties the session together.

ParameterDescription
client_idYour OAuth2 client identifier
audienceThe API(s) your token should grant access to
code_challengeA PKCE value to secure the authorization code exchange
response_typeMust be code
scopeRequested scopes, e.g. openid offline
GET https://auth.api.staging.energyzero.nl/oauth2/auth
?client_id=<CLIENT_ID>
&response_type=code
&scope=openid%20offline
&audience=energyzero
&code_challenge=<PKCE_CHALLENGE>
&code_challenge_method=S256

The auth server redirects the user through the Identity Provider, which ultimately lands the user on your configured login page (/signin) with a login_challenge in the query string.

Step 2 — Submit Credentials

Your login page collects the user's username and password and posts them to the login endpoint, along with the login_challenge received in Step 1.

curl --location 'https://auth.api.staging.energyzero.nl/v1/auth/login' \
--header 'Content-Type: application/json' \
--data '{
"login": "<USERNAME>",
"password": "<PASSWORD>",
"loginChallenge": "<LOGIN_CHALLENGE>"
}'

Step 3 — Complete 2FA (if enabled)

If the client or user has 2FA configured, the Identity Provider redirects to a 2FA screen instead of proceeding directly. There are two sub-cases:

First-time 2FA registration:
The user is redirected to your configured registration URL with a temporary JWT and the login_challenge as query parameters. Your frontend fetches the barcode data from the TOTP register endpoint (passing the temp JWT as a bearer token), renders it for the user to scan with their authenticator app, then submits their first code to validate.

# Fetch barcode data
GET /v1/auth/totp/register
Authorization: Bearer <TEMP_JWT>

# Validate the first TOTP code
GET /v1/auth/totp/validate?code=<2FA_CODE>&login_challenge=<LOGIN_CHALLENGE>
Authorization: Bearer <TEMP_JWT>

Returning user with 2FA already set up:
The user is redirected to your configured validation URL. Your frontend prompts for the code and submits it directly.

GET /v1/auth/totp/validate?code=<2FA_CODE>&login_challenge=<LOGIN_CHALLENGE>
Authorization: Bearer <TEMP_JWT>

Step 4 — Exchange the Authorization Code

After successful authentication, the auth server redirects the user to your /callback URL with a short-lived code in the query string. Your backend exchanges this code for tokens.

curl --location 'https://auth.api.staging.energyzero.nl/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data 'grant_type=authorization_code' \
--data 'code=<AUTHORIZATION_CODE>' \
--data 'client_id=<CLIENT_ID>' \
--data 'code_verifier=<PKCE_VERIFIER>'

On success, you receive an access token and a refresh token scoped to the authenticated user.

{
"access_token": "<USER_ACCESS_TOKEN>",
"refresh_token": "<REFRESH_TOKEN>",
"expires_in": 3599,
"scope": "openid offline",
"token_type": "bearer"
}

Step 5 — Call User-Scoped APIs

Use the access token to call any EnergyZero API on behalf of the logged-in user.

curl --location 'https://customer.api.staging.energyzero.nl' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <USER_ACCESS_TOKEN>' \
--data '{
"query": "query me { me { firstName surname } }",
"variables": {}
}'

When the access token expires, use the refresh_token to obtain a new one without requiring the user to log in again.


Token Lifetime Summary

TokenValid forObtained via
Partner access token5 minutesClient Credentials grant
User access token1 hourImpersonation or Auth Code
Refresh tokenVariesAuthorization Code Flow

Note: Partner tokens are short-lived by design. If you are running a long-lived process that repeatedly calls the impersonation endpoint, make sure to refresh your partner token before it expires.