Introduction

Welcome to the StoredSafe API

The StoredSafe API is a way for you to automate your interaction with the StoredSafe system. With the API, you can create your own scripts or applications with most of the functionality you can find inside StoredSafe.

The StoredSafe API is RESTlike and HTTP-based. Basically, this means that the communication is made through normal HTTP requests.

Open Source and StoredSafe

StoredSafe develops and maintains some Open source code projects at GitHub, which can be used as reference when coding.

Currently the following projects has been published:

  • tokenhandler logins and aquires a token used for subsequent REST API calls to a StoredSafe instance.
  • x509-vacuumer locates, retrieves and stores X.509 certificates in StoredSafe.
  • csv-importer assists in importing objects via CSV files to StoredSafe.
  • ssh-storedsafe let’s a user login to a remote server using SSH after obtaining the required credentials (password) from StoredSafe.
  • ansible-storedsafe is a lookup module for information stored in StoredSafe.
  • vault-storedsafe-client is an ansible vault password client script helper when using ansible-vault to encrypt playbooks or variables.
  • storedsafe-ruby is a StoredSafe REST-API class library in Ruby
  • hiera-storedsafe is a Hiera backend to retrieve secrets from Password StoredSafe

Authentication

User credentials

Authentication is needed in order to use the StoredSafe API, and for this a StoredSafe account is required. The credentials used for accessing the API are the same used to StoredSafe - in other words your login id and your pass phrase.

API Key

You will also need an application key - API key (The API keys can be generated by a StoredSafe System Administrator from the system console).

You generate your application key inside the StoredSafe control panel. It is supposed to be unique on an application basis, not user basis. This means that if you produce an application and then distribute it to the public, all users of this application should use the same application key (API key).

The application key (API key) is provided as part of the authentication data.

The authentication method for user credentials is a POST request using JSON data with the required credentials. Upon successful authentication a token will be returned, this token needs be used for all subsequent requests.

Note

Hence this is a RESTlike API, not RESTful

Server may induce token rotation at any time.

Token Rotation

The token might be rotated (changed) by the server at any given moment, the StoredSafe token returned in the CALLINFO object should be used for any subsequent calls.

Attribute Description
<method>.CALLINFO.token Rotated StoredSafe-Token to be used for subsequent calls
<method>.DATA.token StoredSafe-Token as supplied at the call

Example:

GET /api/1.0/template/1001
x-http-token: 7TCRHJy6jPb1qlrZ0r7k3ht29a28c8i5qo433tdr67
{
    "CALLINFO": {
        "token": "MarBjhsl8zA4xGNG1nq6qlp7shsj76bb92296mdid6"
    },
    "DATA": {
        "token": "7TCRHJy6jPb1qlrZ0r7k3ht29a28c8i5qo433tdr67"
    },

Server indicates that it wants to replace the old token with a new one, (CALLINFO.token != DATA.token), so for next API call, the new token is used:

GET /api/1.0/template/1
x-http-token: MarBjhsl8zA4xGNG1nq6qlp7shsj76bb92296mdid6
{
    "CALLINFO": {
        "token": "MarBjhsl8zA4xGNG1nq6qlp7shsj76bb92296mdid6"
    },
    "DATA": {
        "token": "MarBjhsl8zA4xGNG1nq6qlp7shsj76bb92296mdid6"
    },

This time, the server does not impose a token rotation. (CALLINFO.token == DATA.token)

Two-factor Authentication (2FA)

StoredSafe requires each user to use 2FA to authenticate at login. Currently two different methods exists, OTP (Yubico) and TOTP.

Yubico OTP

A Yubico OTP is a 44-character, one use, secure, 128-bit encrypted Public ID and Password. The OTP is comprised of two major parts: the first 12 characters remain constant and represent the Public ID of the YubiKey device itself. The remaining 32 characters make up a unique passcode for each OTP generated.

Example output from a YubiKey where the button has been pressed three times

cccjgjgkhcbbirdrfdnlnghhfgrtnnlgedjlftrbdeut
cccjgjgkhcbbgefdkbbditfjrlniggevfhenublfnrev
cccjgjgkhcbbcvchfkfhiiuunbtnvgihdfiktncvlhck

The passcode is generated from a multitude of random sources, including counters for both YubiKey sessions and OTPs generated. When a Yubico OTP is verified, the session and OTP counter values are compared to last values submitted. If the counters are less than the previously used values the OTP is rejected. Copying an OTP will not allow another user to spoof a YubiKey — the counter value will allow the validation server to know which OTPs have already been used.

Example

Example: Your account is foo@example.com, the chosen pass phrase is ThisIsAPrettyLousyPassPhrase, the API key is My-API-Key and the OTP is OhMyCouldThisReallyBeAnOTP.

Request

> POST /api/1.0/auth HTTP/1.1
> Host: api.storedsafe.com
> {
>   "username": "foo@example.com",
>   "keys": "ThisIsAPrettyLousyPassPhraseMy-API-KeyOhMyCouldThisReallyBeAnOTP"
> }

Response

< HTTP/1.1 200 OK
< Content-Length: 571
< Content-Type: application/json
< {
< "DATA": {
<   "username": "foo@example.com",
<   "keys": "ThisIsAPrettyLousyPassPhraseMy-API-KeyOhMyCouldThisReallyBeAnOTP"
<  },
<  "PARAMS": [
<  ],
<  "CALLINFO": {
<     "token": "rotated_storedsafe_token",
<     "fingerprint": "<PGP fingerprint>",
<     "userid": "42",
<     "password": "ThisIsAPrettyLousyPassPhrase",
<     "userstatus": "396",
<     "username": "sven",
<     "fullname": "Sven Test",
<     "timeout": 3600000,
<     "filesupport": 3,
<     "handler": "AuthHandler",
<     "status": "SUCCESS"
<  }
< }

Python Code Example

“This is too much to read. I just want to get started right now! Give me a simple example!”

Here is a short example of how you can use the API with Python when using Yobico OTP.

#!/usr/bin/python
# -*- coding: utf-8 -*-
import httplib
import ssl
import json

loginJson = {
        'username':'foo@example.com',
        'keys':'ThisIsAPrettyLousyPassPhrase' + 'My-API-Key' + 'OhMyCouldThisReallyBeAnOTP'
}
c = httplib.HTTPSConnection('api.storedsafe.com')
c.request("POST", "/api/1.0/auth", json.dumps(loginJson))
response = c.getresponse()
print response.status, response.reason
data = response.read()
jsonObject = json.loads(data)

TOTP

Time-based One-Time Password algorithm (TOTP is an algorithm that computes a one-time password from a shared secret key and the current time. It has been adopted as Internet Engineering Task Force standard RFC 6238, and is a part of Initiative For Open Authentication (OATH).

TOTP is used by numerous application, Google Authenticator and Microsoft Authenticator being two common examples.

Example

Example: Your account is foo@example.com, the chosen pass phrase is ThisIsAPrettyLousyPassPhrase, the API key is My-API-Key and the TOTP is 123456.

Request

> POST /api/1.0/auth HTTP/1.1
> Host: api.storedsafe.com
> {
>   "username": "foo@example.com",
>   "passphrase": "ThisIsAPrettyLousyPassPhrase",
>   "otp": "123456",
>   "apikey": "My-API-Key",
>   "logintype": "totp"
> }

Response

< HTTP/1.1 200 OK
< Content-Length: 571
< Content-Type: application/json
< {
< "DATA": {
<   "username": "foo@example.com",
>   "passphrase":"ThisIsAPrettyLousyPassPhrase",
>   "otp":"123456",
>   "apikey":"My-API-Key",
>   "logintype":"totp"
<  },
<  "PARAMS": [
<  ],
<  "CALLINFO": {
<     "token": "rotated_storedsafe_token",
<     "fingerprint": "<PGP fingerprint>",
<     "userid": "42",
<     "password": "ThisIsAPrettyLousyPassPhrase",
<     "userstatus": "396",
<     "username": "sven",
<     "fullname": "Sven Test",
<     "timeout": 3600000,
<     "filesupport": 3,
<     "handler": "AuthHandler",
<     "status": "SUCCESS"
<  }
< }

Python Code Example

“This is too much to read. I just want to get started right now! Give me a simple example!”

Here is a short example of how you can use the API with Python when using TOTP.

#!/usr/bin/python
# -*- coding: utf-8 -*-
import httplib
import ssl
import json

loginJson = {
        'username':'foo@example.com',
        'passphrase':"ThisIsAPrettyLousyPassPhrase',
        'otp':'123456',
        'apikey':'My-API-Key',
        'logintype':'totp'
}
c = httplib.HTTPSConnection('api.storedsafe.com')
c.request("POST", "/api/1.0/auth", json.dumps(loginJson))
response = c.getresponse()
print response.status, response.reason
data = response.read()
jsonObject = json.loads(data)

Server Address

The base server address is: https://<your.storedsafe.tld>/api/

Please note that HTTPS is required. You will not be able to connect through unencrypted HTTP.

Providing Parameters

GET requests should provide their parameters as a query string, part of the URL or as HTTP headers.

POST, PUT and DELETE requests should provide their parameters in JSON-encoded. This should be part of the body.

The encoding of the query string should be standard URL-encoding, as provided by various programming libraries.

HTTP Status Code Definitions

The HTTP status code returned by a successful API request is defined in the documentation for that method. Usually, this will be 200 (HTTP2) or 200 OK (HTTP/1.1).

If something goes wrong, other codes may be returned. The API uses standard HTTP2 and HTTP/1.1 status codes as defined by RFC 7540 and RFC 2616, respectively.

JSON Responses

All responses are sent JSON-encoded. The specific responses (successful ones) are described in the documentation section for each method.

However, if something goes wrong, our standard JSON error message (together with an appropriate status code) follows this format:

{
    "CALLINFO": {
        "errorcodes": 1,
        "errors": 1,
        "general": [],
        "handler": "Handler",
        "status": "FAIL",
        "token": "rotated_storedsafe_token"
    },
    "DATA": {
        "token": "your_storedsafe_token"
    },
    "ERRORCODES": {
        "2000": "Errormessages exists"
    },
    "ERRORS": [
        "NO SUCH TEMPLATE"
    ],
    "HEADERS": {
        "Accept": "*/*",
        "Host": "safe.domain.cc",
        "User-Agent": "curl/7.64.1",
        "X-Http-Token": "your_storedsafe_token"
    },
    "PARAMS": []
}

The Internet is unreliable

Networks in general are unreliable, and particularly one as large and complex as the Internet. Your application should not assume it will get an answer. There may be timeouts.

Limitations

  • We do not support “Expect: 100-continue” header from RFC 7231, section 5.1.1: Expect.
  • We do not support chunked transfer encoding - “Transfer-Encoding: chunked

REST Requests

In the others sections, you will find descriptions and examples for all methods.