All API access to the Digital Post Platform is authenticated using certificate-based machine-to-machine authentication and OAuth 2.0.
Before sending messages or accessing platform APIs, a system integration must be created and configured, and a valid access token must be obtained.
This section explains how to configure authentication and obtain credentials for API access.
Authentication is based on:
- Client certificate authentication
- JSON Web Tokens (JWT)
- OAuth 2.0 token exchange
Each integrating system authenticates using a registered system integration and a client certificate.
The general process is:
- Generate the private key
- Generate the public certificate
- Create a System Integration
- Generate JWTs using the private key
- Exchange JWTs for access tokens
Authentication is based on X.509 client certificates. For each system integration, the integrator must hold a private key that matches the registered public certificate. This private key is used locally by the integrating system to sign JWTs and must never be shared.
Generate a 2048-bit RSA private key in PEM format using OpenSSL:
<code >openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private-key.pem</code>Result
- private-key.pem — your private RSA key in PEM format.
Important notes
- Store private-key.pem securely (preferably in a secret manager or HSM where available).
- Limit file access (e.g., strict permissions) and ensure it is not committed to source control.
- The private key must remain with the integrating system and must never be uploaded or distributed.
Each system integration must have one registered public certificate with a corresponding private key held by the integrator. The public certificate is uploaded in the Administration Portal so the platform can verify the JWT signatures created using your private key.
Generate a self-signed X.509 certificate (public certificate) in PEM format from your private key. Keep the validity at 365 days:
openssl req -new -x509 -key private-key.pem -out publickey.cer -days 365Result
- publickey.cer — your public X.509 certificate in PEM format (contains the public key).
Important notes
- Upload publickey.cer to the Administration Portal when creating/configuring the system integration.
- The certificate is used to verify the JWT signatures (the JWTs are signed with the private key; verification happens using the uploaded public certificate).
- Plan certificate rotation before expiry (365 days) to avoid authentication interruptions.
A system integration represents the technical client that authenticates against the platform.
System integrations are created in the Administration Portal.
To create a system integration:
- Select an organisation
- Navigate to Distribution → System integration
- Click Add System integration
- Enter a name
- Upload a certificate
- Enter contact information
- Select permissions:
- Send message
- Retrieve billing data (if required)
- Set status to Active
- Click Save
Only active system integrations can authenticate.
Each system integration is assigned a unique:
- machineId
This identifier is required for authentication.
Before implementing authentication, the following identifiers must be collected from the Administration Portal.
machineId
Identifies the system integration.
Certificate thumbprint
Used as the issuer value in JWTs.
These identifiers are required when creating JWTs and submitting API requests.
Authentication requires creating a signed JWT in accordance with RFC 7519.
The JWT must be signed using RS256 and the private key registered with the system integration.
| Claim | Full name | Description |
|---|---|---|
| jti | JWT id | Unique id for token e.g. GUID. |
| iss | Issuer | Thumbprint of client certificate used for signing the JWT. |
| sub | Subject | Id of the System integration to request a token for (Machine id). |
| aud | Audience | URL of the Security Token Service requesting the token from, e.g. admin-test.digitalpost.dk. |
| exp | Expiration time | NumericDate (Epoch) specifying the time from which the token is valid. It must not be more than 5 minutes before Issued At. |
| nbf | Not before | NumericDate (Epoch) specifying the time from which the token is valid. It must not be more than 5 minutes before Issued At. |
| iat | Issued at | NumericDate (Epoch) specifying the time the token was issued. |
{
"jti": "56aec5e8-5895-45eb-8a2b-6afb65d9bf4s",
"iss": "38d00f87072131f1444d779ca6c75219b102de50",
"sub": "38",
"aud": "admin.digitalpostbox.xxx",
"iat": 1572382814,
"exp": 1572382899,
"nbf": 1572382814
}The token lifetime must not exceed five minutes.
Pseudocode example:
now = getCurrentTime()
platformDomain = getPlatformDomain()
header = {
alg: "RS256",
typ: "JWT"
}
payload = {
jti: generateUuid(),
iss: getCertificateThumbprint(),
sub: getMachineId(),
aud: platformDomain,
exp: now + 300,
nbf: now,
iat: now
}
encodedHeader = base64Url(header)
encodedPayload = base64Url(payload)
signingInput = encodedHeader + "." + encodedPayload
signature = sign(signingInput, privateKey)
token = signingInput + "." + signatureOnce a JWT has been created, it must be exchanged for an access token. To obtain an access token, the signed JWT must be sent to the platform Login Service token endpoint.
The JWT is submitted as a UTF-8 and Base64 encoded form parameter named token.
The OAuth grant type clientcertificate and client id clientcertificate must be used.
No client secret is required.
If authentication is successful, the Security Token Service issues an access token valid for approximately ten minutes.
This token must be included as a Bearer token in the Authorization header when calling platform APIs.
POST https://<platform-domain>/loginservice/connect/tokenContent Type: application x www form urlencoded
grant_type=clientcertificate
&client_id=clientcertificate
&token=<base64-encoded-jwt>No client secret is required.
POST /loginservice/connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=clientcertificate&client_id=clientcertificate&token=eyJhbGciOi...The token endpoint returns an access token.
This token must be included in all API requests:
Authorization: Bearer <access-token>All protected APIs require a valid bearer token.
Access tokens are valid for approximately ten minutes.
After expiration:
- A new JWT must be generated
- A new token must be requested
Integrations should implement automatic token renewal.
Tokens must not be reused after expiration.
Some APIs may require user based authentication using OAuth 2.0 Authorization Code Grant with PKCE.
This flow is used for interactive user access and is separate from machine authentication.
Most system integrations only require machine authentication.
This section provides a practical example of generating certificates, signing JWTs, and obtaining access tokens using commonly available tools.
It supplements the general authentication flow described above and is intended for development and testing purposes.
openssl genpkey -algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-out private-key.pem
openssl req -new -x509 \
-key private-key.pem \
-out publickey.cer \
-days 1825Upload publickey.cer when creating the system integration.
Store private-key.pem securely.
Tools such as jwt.io can be used for testing and validation during development only; they must not be used to generate JWTs in production, and private key material (e.g., a PEM file) must never be uploaded to third-party online services.
Select: • Algorithm: RS256 • Header: Default
Example payload:
{
"jti": "b639b8d9-d302-4cc5-8b83-aac451854492",
"iss": "BF6FAFD79CDFA2FBC9CCB685E78C5D9DBCF713B9",
"sub": "1733",
"aud": "http://securitytokenservice",
"exp": 1568991626,
"nbf": 1568991086,
"iat": 1568991326
}Paste the public certificate and private key to validate the signature.
Example request:
curl -X POST \
https://<platform-domain>/loginservice/connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=clientcertificate&client_id=clientcertificate&token=<jwt-token>"The response contains the access token.
Common authentication issues include:
- Invalid certificate
- Incorrect thumbprint
- Expired JWT
- Invalid audience value
- Incorrect machineId
- Unsynchronized system clocks
If authentication fails:
- Verify certificate registration
- Verify JWT claims
- Check token endpoint URL
- Confirm system time accuracy
After authentication is configured, continue with: