Skip to main content

Authentication

Before you can start building with the Kavitro API, you need to provide valid authentication through an access token. There are two ways to get one, and which you should use comes down to whether your application acts as itself or as a person.

Access token created in the appPassword grant
Use whenA service talks to Kavitro on its own behalfYour app acts as a specific, logged-in user
Acts asItself, with its own permissionsThe user who logged in, with theirs
ExpiresNever, until revokedAccess token after 3 days
Needs a passwordNoYes, the user's own
Set up byCreating a token in SettingsCalling the login mutation

Most integrations, and every unattended one, want the first. Reach for the password grant only when acting as a particular user is genuinely the point.

Authenticating with access tokens created within the application

This method (which can also be called as API keys) is a standard approach when communicating with APIs. Especially when the communication is made purely on a machine-to-machine (M2M) basis, without a user interface and without a need for the user to interact with the system to do any authentication. Meaning you authorize a service rather than a specific user.

These tokens don't expire, until you manually revoke them.

Getting the access token

This action can be done within the application under Settings > Integrations > API Integration.

We strongly suggest you name your token so that its use case can be easily and understandably distinguished from others.

Revoking the access token

Revoking an access token is always a good idea to do when the integration has become obsolete. Or must be done when your application has become compromised.

The action can be done within the application under Settings > Integrations > API Integration.

Authenticating with a password grant

Password grant should be used when your (highly trusted!) application has to act as a specific user. For instance when you're creating an app where a Kavitro user has to log in with their username and password. After a successful login your application is given a short-lived access token (also known as PAT - personal access token) with what you can make your requests against Kavitro API. All requests are then authorized against an actual user with predefined scopes and permissions. Also all entries and actions are created as that specific user.

Important!

This approach should only really be used if you have a highly trusted application. If your application becomes compromised the only option to resolve the security issue is for your users to change their passwords.

Getting the access token

To get the access token you first have to login via the API:

mutation Login($input: LoginInput) {
login(input: $input) {
accessToken
refreshToken
expiresIn
tokenType
user {
id
email
}
}
}
Query variables
{
"input": {
"username": "[email protected]",
"password": "the-user-s-password"
}
}

Which will give you the following response:

{
"data": {
"login": {
"accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1Ni...",
"refreshToken": "def502004531ba2a1745464642...",
"expiresIn": 259200,
"tokenType": "Bearer",
"user": {
"id": "1",
"email": "[email protected]"
}
}
}
}

tokenType is the scheme to put in front of the token, and user identifies who you are now acting as - useful when your application holds tokens for several people. Both are optional to request; ask for what you need.

With the given accessToken you can now start making requests towards Kavitro API by including the token in the HTTP headers:

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1Ni...

If the login fails, the response carries no token. Like most Kavitro errors it comes back as a normal HTTP 200 with an errors array, so check the body rather than the status code:

{
"errors": [
{
"message": "These credentials do not match our records",
"path": ["login"]
}
]
}

Every failure returns that same message, deliberately. A wrong password, an unknown username and a disabled account are indistinguishable from the outside, so there is nothing to branch on: treat any error from login as "these credentials did not work".

Refreshing the access token

As we saw in the previous section there were 2 additional fields requested with the access token: refreshToken and expiresIn. This means that the access token is usable for only a fixed duration, which is extremely useful for security concerns.

The given accessToken can only be used for the given number of seconds after making the login request (3 days, 259200 seconds). After the given number of seconds have passed you'll need to make a request to refresh the access token:

mutation {
refreshToken(input: { refreshToken: "def502004531ba2a1745464642..." }) {
accessToken
refreshToken
expiresIn
}
}

This will give you a new access token, which is valid for another 3 days.

Important!

You also have to take into account that the refreshToken can and will change after a set amount of time. So overwriting both of the values within your application after each such request have to be made.

Storing and reusing tokens

An access token is meant to be stored and reused for its whole lifetime. The most common integration mistake we see is calling login again for every request, or on a short fixed timer - re-authenticating every few minutes while holding a token that stays valid for three days.

That wastes effort in three separate ways: every login spends request rate limit budget that could be serving real traffic, it forces your application to keep the user's password permanently available, and it throws away a perfectly good token.

Doing machine-to-machine work?

Then you probably should not be calling login at all. An access token created within the application does not expire, never needs refreshing, and requires no stored password. Reach for the password grant only when your application genuinely has to act as a specific, interactively logged-in user.

How long tokens last

TokenLifetime
Access token created within the applicationDoes not expire, until revoked
Access token from login3 days (expiresIn: 259200)
Refresh token from login14 days

The refresh token's 14 days is the one that catches people out. If your integration goes quiet for longer than that, a paused job or a stretch of downtime, the refresh token has expired too and you have to log in from scratch.

What to do instead

  1. Store the access token together with the moment it expires. Work that out once, at login, from expiresIn.
  2. Reuse the token until it is close to expiring. Refresh when it has a few minutes left, rather than on a fixed interval that ignores the token you are holding.
  3. Refresh with refreshToken, not with credentials. Re-sending a username and password is the fallback for when the refresh token itself has expired, not the normal path.
  4. Overwrite both stored values after every refresh. The refresh token rotates, so persist the new pair together.
  5. Only fall back to login when a refresh fails.

Recognising an expired token

An expired or invalid token does not come back as an HTTP 401. Like most Kavitro errors it arrives as a normal HTTP 200 with an errors array:

{
"errors": [
{
"message": "Unauthenticated.",
"extensions": {
"guards": ["api"]
}
}
]
}

Match on that error rather than on a status code. When you see it, refresh once and retry the original request; if the retry fails the same way, log in again. See error handling for how this fits with the rest of the API's errors.

Permissions

We strongly believe that no access token should be allowed to do everything within the system - at least not by default.

That's why we also urge you to follow the principle of least privilege in according to which a user or access token should have only the minimum access privileges to perform a specific task or job and nothing more.

For this we've implemented permissions which can be applied to both users and access tokens created within the application. This allows account administrators to specify exactly what privileges each of them has. Whether they can only access a handful of modules and actions or indeed access the whole system, should it be necessary.

This approach is crucial where you have multiple systems integrated with Kavitro which shouldn't have access to the whole system: ticketing system, website forms etc.

Rather we suggest you create multiple access tokens for different jobs.

Where a token's permissions come from

The two authentication methods differ here, and it matters when you are deciding which to use:

  • A token created in the application carries its own permissions. You grant them when you create the token, and they are independent of whoever set it up. Narrow them to the job at hand.
  • A token from login inherits the permissions of the user who logged in. You cannot narrow them at the token; they are whatever that person can already do. If the account is an administrator, so is your integration.

That asymmetry is the practical reason to prefer a token created in the application for anything unattended: it is the only one of the two you can actually constrain.

Liability

It is your responsibility to keep the tokens in a safe manner.