API

Preservica APIs and Two Factor Authentication

Andy Dean

May 25th, 2023

As part of Preservica’s continual re-evaluation of platform security the Preservica engineering team have extended two factor authentication in version 6.8 to include not only UI based logins but also API authentication.

As part of Preservica’s continual re-evaluation of platform security the Preservica engineering team have extended two factor authentication in version 6.8 to include not only UI based logins but also API authentication.

This means that if you have enabled or intend to enable two factor authentication and wish to authenticate to Preservica to obtain an access token for the purposes of using our APIs then this blog post may be of interest to you.

Background

Since Preservica version 5 it has been possible to pass your Preservica local username and password to the API endpoint:

“https://<server_name>.preservica.com/accesstoken/login”

and acquire a ‘Preservica-Access-Token’ for use during subsequent API calls, and until the token expires or is refreshed.

In Preservica 6.2 the API endpoint “/accesstoken/login” was updated to require that the username and password credentials be passed in the message body of the API call and not in the header, in line with evolving best practices.

In Preservica 6.8 additional options have been provided to extend the authentication process where two factor authentication has been enabled on the tenant. Please refer to the Preservica API documentation at:

https://demo.preservica.com/api/documentation.html

for more details, as well as additional resources available on:

https://developers.preservica.com

Configuring Two Factor Authentication for a New Account

If you are setting up a new (service) account in Preservica that will be used to authenticate API calls there are a few pre-requisites that must be completed. Assuming that 2FA is already enabled for the tenant, complete the following:

1. Create the new Preservica account via “Administration > Manage Accounts”

2. Retrieve the password for the new account (this will have been emailed in most cases)

3. Log into the UI using the new (service) account. You will be prompted to competed 2FA configuration.

Note that, in addition to inspecting the URL embedded in the QR Code, you may also “reveal” the “secretKey” by clicking on “Reveal Key”. Do just that and save the key somewhere convenient.

4. Using your preferred authenticator app, complete the steps necessary to provide a 2FA token, and enter it into the dialog box as shown in the above screenshot. Once you have provided the token for the first time you may decide to retain the authenticator app’s account just in case you run into problems, although once fully set up you should go ahead and remove the account from your authenticator app.

If you lose the “secretKey” at this point you will have to log into the Preservica UI using your standard (non-service) user account and within “Administration > Two Factor Authentication” click on the “Forget 2FA” button for your newly created (service) account. You will then need to repeat “Configuring Two Factor Authentication for a New Account” step 3.

Python Authentication with Two Factor Authentication Enabled

Prior to Preservica 6.8 an authentication request made to the “/accesstoken/login” would respond with either a new token:

{
    "success": true,
    "token": "76bc815d-c882-4864-a4be-e6e2e48166dd",
    "refresh-token": "f4822f41-8efd-43ea-b910-7a046705e2c3",
    "validFor": 15,
    "user": "deaa.prevst@preservica.com"
}

or a failed authentication along with an error message:

{
    "success": false,
    "message": "invalid.user Unable to authenticate user: deaa.prevst@preservica.com”,
    "user": "deaa.prevst@preservica.com"
}

In 6.8 however, if your environment is enabled for 2FA, an authentication request made to “/accesstoken/login” may realise one of the following enhanced json responses:

“needs.2fa.setup”

In this scenario the user account being used to authenticate has passed initial authentication but has NOT been formally set up for two factor authentication.

{
    "success": false,
    "message": "needs.2fa.setup",
    "user": "deaa.prevst@preservica.com",
    "tenant": "PARTNER",
    "continuationToken": "dc75b4b3-d2d0-4cea-b36e-5dd9f656eeb8",
    "secretKey": "G3SFLEV6D2OIPNNS",
    "authLink": "otpauth://totp/Preservica%3AAndy+Dean+Candidate?secret=G3SFLEV6D2OIPNNS&issuer=Preservica"
}

For convenience, the json response includes both the “secretKey” and also an optauth string, either of which may be used to create an account on an authenticator app (such as Google Authenticator).

You may decide to include additional steps in your script whereby the “secretKey” is acquired from the above json response, saved, and then used subsequently to create tokens as required, including handling initial 2FA setup.

If you receive message “needs.2fa.setup and you have already completed the pre-requisite steps in section “Configuring Two Factor Authentication for a New Account” something has gone wrong and you will need to log into the Preservica UI using your standard (non-service) user account and within “Administration > Two Factor Authentication” click on the “Forget 2FA” button for your newly created (service) account. You will then need to repeat “Configuring Two Factor Authentication for a New Account” step 3.

Assume that 2FA setup has been completed within the UI, as per section “Configuring Two Factor Authentication for a New Account”

“needs.2fa”

In this scenario the user account being used to authenticate has passed initial authentication, and has been “formally” set up for two factor authentication, but that a valid 2FA token needs to be provided:

{
    "success": false,
    "message": "needs.2fa",
    "user": "deaa.prevst@preservica.com",
    "tenant": "xxxxxx",
    "continuationToken": "74a06610-1357-4b06-9e7c-031aba0adb59"
}

Preservica 6.8 provides a new API endpoint in support if 2FA authentication:

“/accesstoken/complete-2fa”

which we may now use to pass a valid 2FA token to Preservica.

This endpoint requires the following to be passed to Preservica:

  • account username
  • tenant
  • continuationToken
  • twoFactorToken

The (service) account username and the tenant are already known, and a “continuationToken” has been provided by Preservica in the previous json response. However a 2 FA token has not yet been generated.

To generate a 2FA token you need to submit the previously saved “secretKey” to a “one time password” python library that supports time-based OTPs.

One such library is “pyotp” which simply requires that the (service) account’s “secretKey” be provided and that the client’s system clock is up to date:

import pyotp

def fGetToken(my_secret_key):
    totp = pyotp.TOTP(my_secret_key)
    myotp = totp.now()
    return myotp

my_secret_key = "Y2GZCGFE4U3OOEGX"

print("Current OTP: " + str(fGetToken(my_secret_key)))

Constructing an API call using the “/complete-2fa” endpoint allows the newly generated 2 FA token to be submitted to Preservica:

API call

import requests

url = “https://demo.preservica.com/api/accesstoken/complete-2fa”

twofa_username  = deaa.prevst@preservica.com
twofa_tenant    = "xxxxxx"
twofa_ctoken    = " 8c992539-6f73-4bdf-bb0f-77146ff04e16"
twofa_token     = "123456"

payload  = "username=" + twofa_username \
                   + "&tenant=" + twofa_tenant \
                   + "&continuationToken=" + twofa_ctoken \
                   + "&twoFactorToken=" + twofa_token

headers = {
  'Content-Type': 'application/x-www-form-urlencoded'
}

response = requests.request("POST", url, headers=headers, data=payload)

print(response.text)

Response

{
    "success": true,
    "token": "dfa2e094-c243-4498-adf2-0150460523f5",
    "refresh-token": "d6eb3f57-edb0-4799-a5c6-29311d486510",
    "validFor": 15,
    "user": "deaa.prevst@preservica.com"
}

The Preservica-Access-Token can now be retrieved from the above response and used for ongoing authentication until the token expires.

“2fa.authentication.failed”

If you receive this error it is probable that the provided 2FA token is not valid. It may have timed out or the “secretKey” used to generate the token is out of date.

{
    "success": false,
    "message": "2fa.authentication.failed",
    "user": "deaa,prevst@preservica.com",
    "tenant": "xxxxxx",
    "failedAttempts": 1,
    "maxAttempts": 3
}

Note that three failed attempts will result in the user account being 2FA “locked”. The account will then need to be unlocked within the UI.

“bad.continuation.token”

In this case the continuationToken is invalid or the wrong token has been submitted for the specified username.

{
    "success": false,
    "message": "bad.continuation.token",
    "user": "deaa.prevst@preservica.com"
}

If necessary make a new API call to “/accesstoken/login” and acquire an updated continuationToken.

Token Refresh

Having completed an initial two-factor authentication the returned Preservica-Access-Token will be valid for fifteen (15) minutes. For scripts that support long running processes it is advantageous to request a fresh Preservica-Access-Token approximately every ten to twelve minutes so that authentication failures (401 errors for example) don’t disrupt your process.

To acquire a new access token, pass the current Preservica-Access-Token (highlighted in yellow) and the associated refresh-token (highlighted in green) to “/accesstoken/refresh”.

Here is the successful authentication response from the previous example

Response

{
    "success": true,
    "token": "dfa2e094-c243-4498-adf2-0150460523f5",
    "refresh-token": "d6eb3f57-edb0-4799-a5c6-29311d486510",
    "validFor": 15,
    "user": "deaa.prevst@preservica.com"
}

API call

import requests

url = “https://demo.preservica.com/api/accesstoken/refresh”

payload = “refreshToken= d6eb3f57-edb0-4799-a5c6-29311d486510”

headers = {
  'Preservica-Access-Token': 'dfa2e094-c243-4498-adf2-0150460523f5',
  'Content-Type': 'application/x-www-form-urlencoded'
}

response = requests.request("POST", url, headers=headers, data=payload)

print(response.text)

If successful, a new Preservica-Access-Token and associated refresh-token are returned.

Response

{
      "success":true,
      "token":"cb222d84-4b2d-4eea-aca5-851481ab5037",
      "refresh-token":"90bd16f9-fa44-4f25-ac87-03f04879649a",
      "validFor":15,
      user":"deaa.prevst@preservica.com
}

These can be used again for the next refresh of course.

More updates from Preservica

API

Developer Blog - API Updates in Preservica 7.1

This post provides you with a summary of the API updates we have made in Preservica 7.1.

Richard Smith

March 26th, 2024

API

Developer Blog - API Summary in Preservica 7.0

This post provides you with a summary of the API updates we have made in Preservica 7.0.

Richard Smith

January 23rd, 2024

API

Developer Blog - API Summary in Preservica 6.11 and 6.12

This post provides you with a summary of the API updates we have made in Preservica 6.11 and 6.12.

Richard Smith

December 11th, 2023

API

Event-Driven Integrations. Using The Webhook API To Create Custom Business Processes

This article is a based on the talk of the same name given to the 2023 Preservica User Group in Oxford.

James Carr

October 23rd, 2023

Preservica on Github

Open API library and latest developments on GitHub

Visit the Preservica GitHub page for our extensive API library, sample code, our latest open developments and more.

Preservica.com

Protecting the world’s digital memory

The world's cultural, economic, social and political memory is at risk. Preservica's mission is to protect it.