Billberry OAuth 2.0

Using Billberry's OAuth 2.0, you can have your client conveniently share access to their e-invoices. This way your client doesn't have to generate nor email you an API key themselves and can do it securely with just a few clicks. They don't even need an existing Billberry account. OAuth and the API is free to use for all apps.

Billberry's OAuth 2.0 follows the OAuth 2.0 Authorization Framework (RFC 6749). Using OAuth requires a Billberry partner account, which you can request by contacting us. The rest you can set up yourself.

Setup an OAuth Client

The OAuth 2.0 standard calls every app a "client" that requests access to some resource (e.g., e-invoices) on behalf of an end user. In practice, it's a web app, but could also be a desktop or mobile app that doesn't have its own server component. The flow for apps without server components is not yet supported in Billberry, but can easily be implemented if desired.

Every client app in OAuth 2.0 needs an identifier and password, with which to authenticate to Billberry's authorization server. The OAuth standard calls these the client id and client secret.

You can generate the OAuth 2.0 client id and client secret on Billberry's partner page, whose link you received via email when contacting us. In practice, the OAuth 2.0 settings page is at the following address, with {partnerId} standing in for your partner id:

Partner OAuth 2.0 settings page
https://app.billberry.ee/partners/{partnerId}/oauth2
Test environment partner OAuth 2.0 settings page
https://dev.billberry.ee/partners/{partnerId}/oauth2

In addition to the OAuth 2.0 id and password, you can also configure a set of white-listed redirect addresses (redirection endpoint). After the user has been authenticated on Billberry's page and they've authorized access for your application, they'll be redirected back only to predefined addresses (you can choose one when starting the authentication flow). This is a security measure to prevent requesting access on behalf of one application, but redirecting back to another. At least one allowed address must be set for OAuth2 to work.

Authorization Flow

Billberry supports the OAuth 2.0 standard "Authorization Code Grant" flow. In summary that means you send the client to Billberry, where Billberry validates their representation rights and asks them to share access to their e-invoices. After granting that, the person is redirected back to one of the previously white-listed addresses, along with an authorization code (authorization code). The authorization code must be exchanged for a Billberry API session key (access token), which can later be used to make Billberry client API requests (such as sending e-invoices).

Authorization Endpoint

The authorization endpoint is where you send the user's browser to start the authorization flow.

Authorization endpoint
https://app.billberry.ee/authorizations/oauth2/new
Test environment authorization endpoint
https://dev.billberry.ee/authorizations/oauth2/new

For Billberry to know which app to associate the authorization with, where to redirect back, and which organization's access to request, you need to pass a few query parameters to the authorization endpoint.

Authorization endpoint parameters

ParameterDescription
client_idYour app's identifier, which you get from the partner OAuth 2.0 settings page.
response_typeAlways code. Comes from the OAuth 2.0 Authorization Code Grant flow.
scopeThe permissions you're requesting for.
For example, send-invoices. You'll find a list of permissions below.
stateA value you can use to maintain authorization flow context. When redirecting the user back, Billberry retains the state value.
Usually used to prevent cross-site request forgery attacks by generating a random value and checking it after the redirect.
redirect_uriURL where the user is redirected after success or error.
If you don't include a URL, the first white-listed address is used.
countryISO 3166 code of the organization's country for which you're requesting access.
For example, EE.
registry_codeRegistry code of the organization you're requesting access to.

Don't include your client secret when sending the user to the authorization endpoint. You only need the password when exchanging the returned authorization code for Billberry's API key.

When the organization's representative has authorized your app (or an error occurred), they're redirected back to either the previously set redirect address or to the redirect_uri parameter you included.

Successful Authorization Response

The (authorization response) is sent by redirecting the user back to your application (for example, to the redirect_uri address) along with the following query parameters.

ParameterDescription
codeAuthorization code that you can exchange for an API key.
stateThe same state parameter value that was sent to the authorization endpoint.
By checking that the value is identical to the one you sent initially, you can protect against CSRF attacks. Read more about the OAuth 2.0 CSRF security vulnerability below.

You can obtain the authorization code on the server side from the query parameter when the user is redirected back. There's no reason to do it in the front-end with JavaScript, as the authorization code can only be exchanged for an API key by the server. Only it knows the client secret.

Authorization Error Response

The (authorization error response) is sent by redirecting the user back to your application (for example, to the redirect_uri adderss) along with the following query parameters.

ParameterDescription
errorError code. You can find a list of values in the OAuth 2.0 standard
error_descriptionError description. Currently may be missing or in English.

Most error cases redirect back to your app. The only exception is when there's an internal error in Billberry that prevents this, or when Billberry doesn't know where to redirect the user. This happens when, for example, no OAuth settings are found for the client_id or the redirection address (URI) given in redirect_uri is not allowed.

Different Permissions (scope)

The OAuth 2.0 standard allows you to use a scope parameter to specify the permissions you require the authorization code and API key to provide. Currently, Billberry only supports requesting e-invoice sending through OAuth, but permitting access to received e-invoices is planned as well. If you need this, let us know.

PermissionDescription
send-invoicesSending e-invoices. Cannot view received e-invoices.

Multiple permissions can be requested by using spaces in the scope parameter.

Requesting an API Key

When the authorization flow succeeds and you've got an authorization code, you can exchange it for an API key through the token endpoint.

The two-step process exists in OAuth to protect the API key from the end user. Since the end user doesn't know your app's OAuth 2.0 client secret, they can't exchange the authorization code for an API key themselves.

API token endpoint
https://api.billberry.ee/sessions/oauth2
Test environment API token endpoint
https://dev-api.billberry.ee/sessions/oauth2

According to the OAuth 2.0 standard, you've got to send a HTTP POST and application/x-www-form-urlencoded content (a regular form) to the token endpoint.

ParameterDescription
grant_typeAlways authorization_code. Comes from the OAuth 2.0 Authorization Code Grant flow.
codeAuthorization code
redirect_uriThe same value you sent to the authorization endpoint. No need to include it if you relied on the default address (i.e. didn't include the redirect_uri parameter).

Exchanging the authorization code for an API key requires an authenticated request. Use Basic authentication for this, where the username is the OAuth application's client_id configured in Billberry and the password client_secret. For example, if client_id is erpsy and client_secret is 2ab96390c7dbe3439de74d0c9b0b1767, the request looks like this:

POST /sessions/oauth2 HTTP/1.1
Host: api.billberry.ee
Authorization: Basic ZXJwc3k6MmFiOTYzOTBjN2RiZTM0MzlkZTc0ZDBjOWIwYjE3Njc=
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Accept: application/json

grant_type=authorization_code&code=0ec1ee0ba914ffb9f9f5cfe19b28b7b9

Why the authorization code exchange request expects a classical form (application/x-www-form-urlencoded) when the responses are standardized to be in JSON, is best asked from the OAuth 2.0 standard authors…

Successful API Key Response

According to the OAuth 2.0 standard, the API key response (the access token response) is in JSON.

HTTP/1.1 201 Session Created
Content-Type: application/json

{
    "token_type": "basic",
    "access_token": "NDI6MmFiOTYzOTBjN2RiZTM0MzlkZTc0ZDBjOWIwYjE3Njc=",
    "organization_country": "EE",
    "organization_registry_code": "10000018",
    "scope": "send-invoices"
}
FieldDescription
token_typeAlways basic.
access_tokenBillberry API key for use in the Authorization header.
organization_countryISO 3166 code of the authorized organization's country.
organization_registry_codeRegistry code of the authorized organization.
scopeGranted permissions.

The token type basic means that the access_token is suitable for direct Basic authentication and for copying to the Authorization header with the "Basic" prefix (Authorization: Basic NDI6MmFiO…). The access_token is actually a Billberry API key id and password encoded in base64 — the same you get when you generate an API key manually in Billberry's app.

You'll find more info in the Billberry Client API guide, but for example, this is how you can send e-invoices using the access_token in the Authorization header:

POST /invoices HTTP/1.1
Host: api.billberry.ee
Authorization: Basic NDI6MmFiOTYzOTBjN2RiZTM0MzlkZTc0ZDBjOWIwYjE3Njc=
Content-Type: application/xml
Accept: application/vnd.billberry.invoice+json; v=1

<E_Invoice></E_Invoice>

API Key Error Response

In case of errors, the OAuth 2.0 standard specifies a HTTP error response in JSON with the following fields.

FieldDescription
errorError code. You can find a list of values in the OAuth 2.0 standard
error_descriptionError description. May be missing from the response.

For example, when trying to exchange an authorization code that was already used up, Billberry will respond:

HTTP/1.1 400 Authorization Already Used
Content-Type: application/json

{"error": "invalid_grant"}

Deleting API Keys

If you want to delete an obtained API key (access token), you can do so following the OAuth 2.0 Token Revocation (RFC 7009) standard.

API key deletion endpoint (token revocation endpoint)
https://api.billberry.ee/sessions/oauth2?_method=DELETE
Test environment API key deletion endpoint (token revocation endpoint)
https://dev-api.billberry.ee/sessions/oauth2?_method=DELETE

To delete a key, send an HTTP POST request with application/x-www-form-urlencoded content (a regular form) to the token revocation endpoint`.

ParameterDescription
tokenAPI key, i.e the previously obtained access_token.

As when exchanging an authorization code, deleting also requires an authenticated request. Use Basic authentication for this, where the username is the OAuth application's client_id configured in Billberry and the password client_secret. For example, if client_id is erpsy and client_secret is 2ab96390c7dbe3439de74d0c9b0b1767, and the key to be deleted is the same as in previous examples, the request looks like this:

POST /sessions/oauth2?_method=DELETE HTTP/1.1
Host: api.billberry.ee
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZXJwc3k6MmFiOTYzOTBjN2RiZTM0MzlkZTc0ZDBjOWIwYjE3Njc=

token=NDI6MmFiOTYzOTBjN2RiZTM0MzlkZTc0ZDBjOWIwYjE3Njc%3D

If you're constructing the request content or form from text in your program, don't forget to URL-encode the token parameter value. For example, = becomes %3D in the form.

If your app supports it, you can also omit the _method=DELETE parameter and use the HTTP DELETE method directly:

DELETE /sessions/oauth2 HTTP/1.1
Host: api.billberry.ee
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZXJwc3k6MmFiOTYzOTBjN2RiZTM0MzlkZTc0ZDBjOWIwYjE3Njc=

token=NDI6MmFiOTYzOTBjN2RiZTM0MzlkZTc0ZDBjOWIwYjE3Njc%3D

Successful Key Deletion Response

The response to deletion, following RFC 7009, is always the HTTP status code 204 and empty content. Despite this, you can find information about whether the token was deleted, was already deleted, or never existed in the first place from the HTTP status message.

HTTP/1.1 204 Session Deleted

Key Deletion Error Response

In case of an error, the RFC 7009 specifies an HTTP error response in JSON with the following fields.

FieldDescription
errorError code. You can find a list of values from the OAuth 2.0 standard and RFC 7009 standard.
error_descriptionError description. May be missing from the response.

For example, for an invalid token format, Billberry responds:

HTTP/1.1 400 Token Invalid
Content-Type: application/json

{"error": "invalid_request"}

Security

The OAuth 2.0 standard highlights various security issues that are worth browsing through.

Cross-Site Request Forgery

The cross-site request forgery security vulnerability in the OAuth 2.0 authorization flow arises when the application doesn't compare the user's state parameter with after the redirected back with what was sent at the beginning. It's equally dangerous if an attacker knows the user's state parameter.

The security vulnerability can be exploited as follows:

  1. The attacker creates an account in your app and initiates the authorization flow.
  2. The attacker grants access to some organization in Billberry.
  3. Billberry redirects the attacker back to your app, but instead of following the redirect, they save the address (along with the authorization code).
  4. The attacker directs a victim signed-in to your app to the address with the authorization code.
    It's easy to get people on the web to visit URLs without their knowledge. A visit can be triggered, for example, by a hidden image (<img src="…">).
  5. From your app's perspective, it was the victim who initiated the authorization and whose account should now be linked to the attacker's organization.

Whether the above results in a security vulnerability or just a bug depends on the application. For example, if all people associated with one organization can access each other's data, a data leak may occur. If you support signing in with Google accounts, and the attacker could associate their Google account with a victim's account in your app, you've given the attacker access to a victim's account.

One solution to prevent this security vulnerability is to set a random "CSRF token" cookie (e.g. named oauth_csrf_token) before redirecting the user to the authorization page. This random value can be passed to Billberry in the state parameter. After authorization, Billberry redirects the user back with the same state parameter. Your app should then check whether the oauth_csrf_token cookie exists and that its content matches the state parameter value. If not, it may be a CSRF attack.

It's vital to check for the existence of the oauth_csrf_token cookie before comparing values. Otherwise, an attacker could simply omit the state parameter. If the user has never started the OAuth flow themselves, they won't have a CSRF token cookie either.