# OAuth Login for User's Tokens

# Security

Please take good care of your OAuth Client id and secret: Client id can be added to the SPAs and mobile apps as that gets used in the redirection urls etc., but use Client secret only in the server to server communications.

NOTE: Remember to keep the user access, refresh and PAT tokens on the server side to prevent them from leaking.

If the secret gets leaked to a malicious attacker, they can basically pretend to function as your service, which puts the users' data at risk regarding privacy and monetary issues. If detecting such a situation you must inform UKKO.fi immediately. UKKO.fi reserves the right to revoke the OAuth Client id and secret in case of misuse or security issues.

# Prerequisites

NOTE This is work in progress so please ask questions and give feedback to us, so we can improve the documentation and the API through the Slack channel you've been/will be invited to.

Read more about OAuth 2. See: https://oauth.net/2/

# What we need from you

  • Name of your service
  • Short description of your service
  • What features from UKKO API your service needs. Note that changing scopes will require asking the permissions again from the user as the feature scopes are tied to the user's tokens
  • Redirect urls back to your sandbox and production environments after UKKO authentication, i.e.https://sandbox.service.com/redirect/ukko. For production these must be behind HTTPS, and they need to be exactly the same as sent to us as they are whitelisted, no wildcards are supported.
  • Production info may be delivered later on after the sandbox implementation works as intended.

After sending these to our specified Slack we'll generate and send to you OAuth Client ID and Client Secret for your service.

# Registration Flow

As we need to get user to do our current onboarding process with strong identification etc., it's better if you offer the user to make a registration on our site in address: https://app.ukko.fi/register.

This ensures that the user is ready for making invoices and other actions.

# OAuth Flow

We're using the authorization code flow for login + asking the user if she wants to give permission to handle her data through your service. This provides the user transparency what's happening and explicit control over the usage of her account.

# Authentication

To start the authentication with UKKO.fi for getting the permission and tokens, you'll need to redirect the user's browser to UKKO.fi login portal. Redirection should start with a button which says something like "Integrate your UKKO. fi-account" or "Login to UKKO.fi and authorise us".

# Starting url structure:



HOST: Will be given by UKKO
OAUTH_CLIENT_ID: ie. 12345, will be given by UKKO
YOUR_SERVICE_REDIRECT_URL: 'http://example.com/callback',
YOUR_SCOPES: pat:create, 
STATE_GENERATED_BY_YOU: random string generated by your service. You need to verify this later on,

After this you'll get either a:

# Successful login and permission authorization

UKKO.fi redirects user's browser back to your service to YOUR_SERVICE_REDIRECT_URL with following parameters


Verify the STATE_GENERATED_BY_YOU with the one your service generated in the beginning to ensure the login try is really originating from your service.

# Error cases

UKKO.fi redirects user's browser back to your service to YOUR_SERVICE_REDIRECT_URL with following parameters


No more specific errors are returned. You can show the user a generic error.

# Exchanging authorization code to short term OAuth access token

NOTE: This needs to happen server-to-server as the client secret must not be exposed to the user's browser.

After validating the STATE_GENERATED_BY_YOU and getting the AUTH_CODE from the redirect. Send HTTP POST form-data request to:


With the following form parameters:

'grant_type' => 'authorization_code',
'client_id' => [CLIENT_ID],
'client_secret' => [CLIENT_SECRET],
'redirect_uri' => [YOUR_SERVICE_REDIRECT_URL],
'code' => [AUTH_CODE],

Which returns :

    "token_type": "Bearer",
    "expires_in": 600,
    "access_token": "token_string..."
    "refresh_token": "token_string..."

If you only need short-lived token, you can now make new requests with the following headers for the future requests.

Remember to store timestamp now + expires_in, access_token and refresh_token for renewing the expired access_token with the refresh_token.

Accept: 'application/json'
Authorization: 'Bearer [ACCESS_TOKEN]',

If you need to have one long term Personal Access Token, so you can act on the behalf of the user for longer times without constantly renewing the short-lived access and refresh tokens, see the chapter below.

# Creating new long term Personal Access Token for continuous service-to-service integrations

In case your service requires continuous credentials on the behalf of the user, we recommend using long term personal access token. You can request one for the user with the following request:

POST https://[UKKO_HOST]/oauth/personal-access-tokens

HTTP Headers:

    Accept:     'application/json'
    Authorization:  'Bearer [ACCESS_TOKEN]',

JSON  Payload:
    name:   "[YOUR_SERVICE_NAME]",
    scopes: ["invoice:create", "invoice:send", "utility"]


    accessToken: "[LONG_TERM_PAT_STRING]"
    token: {
        id: "id",
        user_id: 123,
        client_id: 123,
        name: [Your Service Name],
        scopes: ["scope 1", "scope 2"],
        revoked: false,
        created_at: "2020-01-21 09:24:37",
        updated_at: "2020-01-21 09:24:37",
        expires_at: "2021-01-20 09:24:37"

Store securely the token, scopes and expiry time for the future requests you need to do on the behalf of the user.

The accessToken must be used as the Bearer token header instead of the short term access token.

The PAT token is valid for one year. When it expires, you must ask the user to redo the whole authorization process, so you can get a new PAT token. This ensures that the user is still aware of the integration and wants to continue.

# Get User data

If you need to verify user data or save the UKKO user id for future use, you can now make a GET request to https://[UKKO_HOST]/v2/me with the following headers:

NOTE: All the authenticated API endpoints require that you pass the headers defined below

    Accept:     'application/json'
    Authorization:  'Bearer [PAT_TOKEN|ACCESS_TOKEN]',

If everything went ok, you'll get a response like this

    "data": {
        "id": 317,
        "created_at": "2023-06-22T13:30:12+03:00",
        "first_name": "Eddie",
        "last_name": "Example",
        "full_name": "Eddie Example",
        "language": "fi",
        "profession": null,
        "marketing_name": null,
        "options": {
            "show_primary_phone_number_on_invoice": null,
            "is_fast_payment_enabled": false,
            "salary_payment_setting": "IMMEDIATE"
        "flags": {
            "can_enable_fast_payment": false,
            "can_change_name": true,
            "has_active_foreclosure": false,
            "has_pending_name_change_lock": false,
            "has_sent_first_invoice": false,
            "has_active_strong_identification": false,
            "strong_identification_valid_until": null,
            "is_intro_done": true,
            "registered_with_2023_new_pipeline": true
        "terms_and_services": {
            "privacy_policy_accepted": true,
            "terms_of_service_accepted": false,
            "industry_restrictions": false,
            "has_confirmed_no_business_id": false

# Differences between the production and sandbox environments

In production: you can create only two active PAT token with same scopes. This security limitation is disabled for development sandboxes so integration developers don't have to create new users every time there's need to test this flow.