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
| Parameter | Description |
|---|---|
client_id | Your app's identifier, which you get from the partner OAuth 2.0 settings page. |
response_type | Always code. Comes from the OAuth 2.0 Authorization Code Grant flow. |
scope | The permissions you're requesting for. For example, send-invoices. You'll find a list of permissions below. |
state | A 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_uri | URL where the user is redirected after success or error. If you don't include a URL, the first white-listed address is used. |
country | ISO 3166 code of the organization's country for which you're requesting access. For example, EE. |
registry_code | Registry 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.
| Parameter | Description |
|---|---|
code | Authorization code that you can exchange for an API key. |
state | The 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.
| Parameter | Description |
|---|---|
error | Error code. You can find a list of values in the OAuth 2.0 standard |
error_description | Error 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.
| Permission | Description |
|---|---|
send-invoices | Sending 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.
| Parameter | Description |
|---|---|
grant_type | Always authorization_code. Comes from the OAuth 2.0 Authorization Code Grant flow. |
code | Authorization code |
redirect_uri | The 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"
}
| Field | Description |
|---|---|
token_type | Always basic. |
access_token | Billberry API key for use in the Authorization header. |
organization_country | ISO 3166 code of the authorized organization's country. |
organization_registry_code | Registry code of the authorized organization. |
scope | Granted 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.
| Field | Description |
|---|---|
error | Error code. You can find a list of values in the OAuth 2.0 standard |
error_description | Error 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`.
| Parameter | Description |
|---|---|
token | API 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.
| Field | Description |
|---|---|
error | Error code. You can find a list of values from the OAuth 2.0 standard and RFC 7009 standard. |
error_description | Error 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:
- The attacker creates an account in your app and initiates the authorization flow.
- The attacker grants access to some organization in Billberry.
- Billberry redirects the attacker back to your app, but instead of following the redirect, they save the address (along with the authorization code).
- 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="…">). - 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.