TOTP

Authen exposes APIs to create, manage and verify TOTP codes as part of a 2FA authentication flow. The flow for setting up and using TOTP is:

  1. Generate and display a QR code-encoded secret to the user,
  2. Associate the secret with the user once they have provided a verification code, and
  3. On every login (or other protected action) collect the user's code and verify it using the stored secret

Common Parameters

While each endpoint has its own input parameters, three common parameters worth exploring in detail: user_id, type and key.

user_id

The user_id parameter is a string, from 1 to 100 characters, which is used to identify the user. In most cases, this would by the id of your user. From Authen's point of view, it is an opaque value.

type

The type parameter is a string, from 1 to 100 characters, which, along with the user_id acts as a unique identifier for a record. In most cases a user will have a single TOTP secret and thus type can be omitted (it's always optional). But some systems use different TOTP secrets for distinct actions (e.g. login vs approving a transfer), in which case type should be specified.

The record's primary key is actually project_id, user_id and type. But the project_id is only meaningful when running in multi-tenancy

key

The key parameter is more complicated. Technically, it is a 32-byte key that Authen uses for encrypting and decrypting a TOTP secret. It's essentially a password for accessing the TOTP secret. When calling the APIs, it must be hex-encoded (thus, it must be exactly 64 characters). But what value should be used?

The least secure option is to use the same 32-byte value for every TOTP record. If this key is leaked and the authen database compromised, an attacker would be able to generate a valid TOTP code for all users. A more secure alternative is to derive the key from the user's password. This is reasonable since, from a data flow point of view, the password is available whenever the TOTP secret must be accessed. The downside to this approach is that the key must be updated whenever the user changes their password (see the change_key api.)

Here are examples of generating a 32-byte hex-encoded key from a string (assumed to be the user's password):

import (
"crypto/rand"
"encoding/hex"
"golang.org/x/crypto/scrypt"
)

func main() {
// this would be the user's password
password := []byte("Ghanima Atr3id3s")

// the salt will need to be stored along the user data
salt := make([]byte, 8)
rand.Read(salt)

key, err := scrypt.Key(password, salt, 32768, 8, 1, 32)
if err != nil {
...
}
// send encodedKey to authen as the `key` parameter
encodedKey := hex.EncodeToString(key)
}
const crypto = require('crypto');

// this would be the user's password
const password = 'Ghanima Atr3id3s';

// the salt will need to be stored along the user data
const salt = crypto.randomBytes(16);

crypto.scrypt(password, salt, 32, (err, key) => {
// send encodedKey to authen as the `key` parameter
const encodedKey = key.toString('hex');)
})

Note that these examples generate a salt, which needs to be store (in plain text) with the user record.

Generate Secret

The first step in configuring, or changing, a TOTP secret is to generate a secret and displaying that secret to the user as a QR code:

POST /v1/totps
nametypereqdesc
user_idstring [1..100]

see note.

typestring [1..100]

see note.

keystring [64]

see note.

accountstring [1..100]

Parameter of the generated QR code to help the user identify which site the code belongs to (the account + issuer tend to appear in most TOTP client apps as a label). Authen does not store this value.

nametypedesc
qrstring

Base64 encoded PNG of otpauth url (what the user scans). Most apps will simply return this to the client and let the client display the QR code.

secretstring

The TOTP secret (this is encoded in the QR code ). Most apps will ignore this value.

curl -X POST "http://127.0.0.1:5200/v1/totp" \
	-H "Content-Type: application/json" \
	-d '{
	"user_id": "df178fd7-4f0d-4109-bd4b-ae36c11b677b",
	"key": "861d97eb1edad3e8add7c88cfa9577cbe9261cc3cda7329fb632178199414b5f",
	"account": "leto@test.goblgobl.com"
}'
{
	"qr":"iVBORw0KGgoAAAANSUhEUgAAAQAAAAEAAQMAAABmvDolAAAABlBMVEX///8AAABVwtN+AAACqUlEQVR42uyYQY6zMAyFH8oiyxwhN4GLoULFxcJNcoQss0C8X890mHb+AxCkejMa9FWq6bP9bHzjGzcMT5IJXUGYMYKZieSqp+VGQAZ8Qo9H2CLzBk+/i4drCSD9SpapjBjtEXe/lolLewCfZYzkFjGgq12LQOnRccOUXQbIGwKAgKA0F0vTp/8UdTWgIjuBvEWfau//1ubFgEVnqgYQXcZQVYufcTEgPTCFFEjmEY4V3PUJzg0BrKjAUDorvZdoB5LbnYCMgXvYgbBwyYikfonw/H0PLQCe+isEysOzDnXgs6AdAJHJ79C7XTKwQT2tdnyepdcEYKW3lqlM2VH/+t2nwOJuBHiaYMg9bBjjhjpwrcB0/liNAPXodAtJRGnap9aAemTx5KKWIP2s9b30GgBebkC9l8ojq6Nx5f6bx/WAXrVXb5iKy2M8m9geZtwIyDKQKj0Nusijx+14vBxwE4C+ts2LzuRiqu6YcNrFFgBP6xAsMAdB+YfV2vDUFuBT2PEIM1zeTA/eRjFuBOQKUwi0TtI6XFeHgnCuDw0AZPIsMgwTNZhlL000bAfQ16695kUZbRGrqJ3s4jmaGwDkxMybP4vjki2PhD7MeLNqVwNqCLVT1wobJjL7VIE+sIw3AmR5bZ2cA/NkG5E3k/Pb5RoAMqAswmnFB9twXvtlK4CKD0cjdtT6YJ72U1GXAxLx63iIKD1Aenhbgm4B+GTbwgNTBlyWYGxaz+0APzcQPYkbKP30cuZ/DzVXAq8eJ8nMUV0OQz32so+T+MXAcZcb7E7rspPnsSzea/N6wC6cHWecx8Pd683zdoBdm6esRG1eQJ4HrQEaakt2lBur3fsBtA3gcIjFTl509KkOWh/K2BBwHJO1nGsx3+LP5P2ozdaBb3yjqfgXAAD//2lg3x8tmF2oAAAAAElFTkSuQmCC",
	"secret":"5LBTYHXEBKHR4INTKRBZBSYQ4M"
}
codedesc
102005

The project has reached the maximum configured TOTP entries.

The general error section details Authen's error responses as well as detailing all errors, including global errors, such as validation and internal server errors.

Verify Code

Verify the user's TOTP code. This has two modes, based on the pending parameter. When true, the recently generated secret (via the POST /v1/totps endpoint), is being confirmed. When false (the default), an already confirmed code is being verified.

Put differently, setting up a new code (or changing an existing one), is a two step process where first the secret is generated and then verified with pending: true. Subsequent verification, say on user login, is done by calling verify with pending: false (or omitted).

POST /v1/totps/verify
nametypereqdesc
user_idstring [1..100]

see note.

typestring [1..100]

see note.

keystring [64]

see note.

codestring [6]

The TOTP code.

pendingbool

When true, confirm a recently generated secret. When false, or omitted, verify an already-confirmed secret.

Empty. A 200 status code indicates success.

curl -X POST "http://127.0.0.1:5200/v1/totp/verify" \
  -H "Content-Type: application/json" \
  -d '{
"user_id": "df178fd7-4f0d-4109-bd4b-ae36c11b677b",
"key": "861d97eb1edad3e8add7c88cfa9577cbe9261cc3cda7329fb632178199414b5f",
"code": "611207"
}'
codedesc
102006

The TOTP entry could not be found. This means the user_id or optionally user_id + type did not correspond to an existing TOTP (or project_id + user_id + type when multi-tenancy is enabled). Note that TOTP that are setup but not confirmed before the configured TTL are periodically deleted.

102007

The key parameter to decrypt/encrypt the TOTP secret was incorrect.

102008

The code parameter to confirm or verify a TOTP was incorrect.

The general error section details Authen's error responses as well as detailing all errors, including global errors, such as validation and internal server errors.

Delete Secret

Deletes the secret.

POST /v1/totps/delete
nametypereqdesc
user_idstring [1..100]

see note.

typestring [1..100]

see note.

all_typesboolWhen true, the type parameter is ignored and all of the user's entries are deleted.

nametypedesc
deletedint

The number of deleted entries.

curl -X POST "http://127.0.0.1:5200/v1/totp/delete" \
  -H "Content-Type: application/json" \
  -d '{
"user_id": "df178fd7-4f0d-4109-bd4b-ae36c11b677b"
}'
{"deleted": 1}

The general error section details Authen's error responses as well as detailing all errors, including global errors, such as validation and internal server errors.

Change Key

Change the encryption key of an entry.

While using a per-user key derived from the user's password is the most secure options, it has the obvious downside of needing to be changed whenever the user changes their password.

POST /v1/totps/change_key
nametypereqdesc
user_idstring [1..100]

see note.

typestring [1..100]

see note.

keystring [64]

see note.

new_keystring [64]

see note.

Empty. A 200 status code indicates success.

curl -X POST "http://127.0.0.1:5200/v1/totp/change_key" \
  -H "Content-Type: application/json" \
  -d '{
"user_id": "df178fd7-4f0d-4109-bd4b-ae36c11b677b",
"key": "861d97eb1edad3e8add7c88cfa9577cbe9261cc3cda7329fb632178199414b5f",
"new_key": "4486e68dd57059570c79b146e6ae00c136ec60e65b0ee730f89795e5f73aef5e"
}'
codedesc
102006

The TOTP entry could not be found. This means the user_id or optionally user_id + type did not correspond to an existing TOTP (or project_id + user_id + type when multi-tenancy is enabled). Note that TOTP that are setup but not confirmed before the configured TTL are periodically deleted.

102007

The key parameter to decrypt/encrypt the TOTP secret was incorrect.

The general error section details Authen's error responses as well as detailing all errors, including global errors, such as validation and internal server errors.