Manage Token

Manage your Personal Access Token (PAT) to connect and perform actions on Affinidi services on the user’s behalf.

Use a Token to generate a Personal Access Token (PAT) or a machine user to connect and perform operations to the Affinidi services on your behalf. Using PAT, you can automate specific tasks within your application and access multiple projects if granted by the user.

How does PAT authentication works

Personal Access Token (PAT) is like a machine user that acts on your behalf to the Affinidi services. You can use the PAT to authenticate to the Affinidi services and automate specific tasks within your application. A Personal Access Token (PAT) lives outside of Projects, meaning PAT can access multiple projects once granted by the user.

PATs use asymmetric keys where you are responsible of creating and maintaining the key pair. Please read about more about how to use Personal Access Tokens (PAT) and create the keys here.

Use IAM (Policy) service of Affinidi to grant access and restrict the actions that PAT can perform on your projects.

  flowchart TB
    subgraph Affinidi CLI
      user_token["1.1. Developer login to Affinidi"]
      create_project["1.2. Create a project"]
      project_access["1.3. Set an active project"]
      create_token["2.2. Create a token"]
      add_token_project["2.3. Add token to a project"]
      set_policies["2.4. Set policies for token"]

      user_token --> create_project
      create_project --> project_access
      user_token --> create_token
      create_token --> add_token_project
      project_access --> add_token_project
      add_token_project --> set_policies
    end

    subgraph Application
      direction TB
      sign_jwt["3.1. Sign JWT"]
      delegate_token["3.2. Create a delegate token"]
      call_project_api["3.3. Call Affinidi services"]

      sign_jwt --> delegate_token
      delegate_token --> call_project_api
    end

    create_keys["2.1. Create a key pair"]
    create_keys -. "publicKey PEM" .-> create_token
    create_keys -. "privateKey" .-> sign_jwt
    set_policies -. "granted access to" .-> call_project_api

Command References

    affinidi token

Use these commands for Personal Access Token (PAT) management

affinidi token create-token

Creates a Personal Access Token (PAT)

USAGE


affinidi  token create-token [--json] [--no-color] [--no-input] [-n [value]] [-k [value]] [-f [value]] [-a	 RS256|RS512|ES256|ES512] [-w] [-p [value]]

FLAGS

-a, --algorithm=[option] [default: RS256] The specific cryptographic algorithm used with the key [options: RS256|RS512|ES256|ES512]

-f, --public-key-file=[value] Location of the public key PEM file

-k, --key-id=[value] Identifier of the key (kid)

-n, --name=[value] Name of the Personal Access Token, at least 8 chars long

-p, --passphrase=[value] Passphrase for generation of private public key pair

-w, --with-permissions Create ready-to-use PAT with auto-generated private public key pair and set its access policies

GLOBAL FLAGS

--json Format output as json.

--no-color Disables color in the output. If you have trouble distinguishing colors, consider using this flag.

--no-input Disables all the interactive prompts

EXAMPLES


affinidi  token create-token -n MyNewToken -w -p top-secret

affinidi token create-token --name MyNewToken --with-permissions --passphrase top-secret

affinidi token create-token -n MyNewToken -k MyKeyID -f publicKey.pem

affinidi token create-token --name "My new token" --key-id MyKeyID --public-key-file publicKey.pem --algorithm RS256

affinidi token delete-token

Deletes a Personal Access Token (PAT)

USAGE


affinidi  token delete-token [--json] [--no-color] [--no-input] [-i [value]]

FLAGS

-i, --token-id=[value] ID of the Personal Access Token

GLOBAL FLAGS

--json Format output as json.

--no-color Disables color in the output. If you have trouble distinguishing colors, consider using this flag.

--no-input Disables all the interactive prompts

EXAMPLES


affinidi  token delete-token -i [uuid]

affinidi token delete-token --token-id [uuid]

affinidi token get-token

Gets the details of a Personal Access Token (PAT)

USAGE


affinidi  token get-token [--json] [--no-color] [--no-input] [-i [value]]

FLAGS

-i, --token-id=[value] ID of the Personal Access Token

GLOBAL FLAGS

--json Format output as json.

--no-color Disables color in the output. If you have trouble distinguishing colors, consider using this flag.

--no-input Disables all the interactive prompts

EXAMPLES


affinidi  token get-token -i [uuid]

affinidi token get-token --token-id [uuid]

affinidi token list-tokens

Lists your Personal Access Tokens (PATs)

USAGE


affinidi  token list-tokens [--json] [--no-color] [--no-input]

GLOBAL FLAGS

--json Format output as json.

--no-color Disables color in the output. If you have trouble distinguishing colors, consider using this flag.

--no-input Disables all the interactive prompts

EXAMPLES


affinidi  token list-tokens

affinidi token update-token

Updates a Personal Access Token (PAT)

USAGE


affinidi  token update-token [--json] [--no-color] [--no-input] [-i [value]] [-n [value]] [-k [value]] [-f [value]]	 [--algorithm RS256|RS512|ES256|ES512]

FLAGS

-f, --public-key-file=[value] Location of the public key PEM file

-i, --token-id=[value] ID of the Personal Access Token

-k, --key-id=[value] Identifier of the key (kid)

-n, --name=[value] Name of the Personal Access Token, at least 8 chars long

--algorithm=[option] [default: RS256] The specific cryptographic algorithm used with the key [options: RS256|RS512|ES256|ES512]

GLOBAL FLAGS

--json Format output as json.

--no-color Disables color in the output. If you have trouble distinguishing colors, consider using this flag.

--no-input Disables all the interactive prompts

EXAMPLES


affinidi  token update-token -i [uuid] -n MyNewToken -k MyKeyID -f publicKey.pem

affinidi token update-token --token-id [uuid] --name "My new token" --key-id "My key ID" --public-key-file publicKey.pem --algorithm RS256

Setting up your PAT keys

To create and use your Personal Access Token you will require a key-pair. You can either create it yourself or rely on a cloud provider to securely manage your keys, such as AWS KMS, GCP’s Cloud KMS or Azure Key Vault

Create a Key-Pair

To create a key-pair, open your command prompt (Windows) / command line (Linux/Mac OS) and run the command below using either of the following tools:

Using ssh-keygen
ssh-keygen -b 4096 -t rsa -f key-pair

The -t rsa indicates that the key algorithm to use is rsa when creating your key-pair. You can used any of the following key algorithms depending on your requirement: [dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa].

The -b 4096 indicates the key-size of your key-pair (optional).

The -f key-pair indicates the filename of the keys to be created (You may change the key-pair filename).

Using openssl
openssl genpkey -algorithm RSA -out private-key.pem -aes-128-cbc -pass pass:hello

The -algorithm RSA indicates the key algorithm to be RSA.

The -out private-key.pem indicates the output filename of the private key.

The -aes-128-cbc indicates the key will be encrypted with 128 bit AES.

The -pass pass:hello sets hello as the passphrase.

Create a Public Key in PEM Format

After creating your key-pair, you must create the public key in Privacy Enhanced Mail (PEM) format. To do this, run the command below using either of the following tools:

Using ssh-keygen
ssh-keygen -f key-pair.pub -e -m pem > public-key.pem

The -f key-pair.pub is the filename of your public key generated in the previous command which is used to generate the the PEM key file.

The -m pem indicates the key format output to PEM.

Using openssl
openssl rsa -in private-key.pem -pubout -out public-key.pem

The -in private-key.pem indicates the source private-key file where the public key is derived.

The -out public-key.pem indicates the output filename of the public key that is created.

The public-key.pem is the public key file in PEM format, which you use as the input to create the token using Affinidi CLI.

Personal Access Token Usage

You can use Personal Access Token (PAT) to automate a specific task on your project. In this scenario, we will use PAT to automate the creation of Login Configuration and automatically rotate client credentials configured on your application for better security.

Before you begin
  1. Set up Affinidi Vault account. Follow the guide below if you haven’t set it up yet.
Set up Affinidi Vault
  1. Set up an Affinidi Vault account using the web app or install the mobile app .

The same installation steps for mobile app.

  1. Click on Start if you are creating a new account, or click on Restore from Backup if you have an existing backup of your Affinidi Vault.

Use this guide to learn how to migrate your existing Affinidi Vault account.

Affinidi Vault Setup
  1. Secure your Vault by providing a passphrase. Use this passphrase to unlock your Vault.
Affinidi Vault Passphrase
  1. Provide your Email Address to verify with OTP.
Affinidi Vault Email Verification

After successfully providing the OTP, you are redirected to your Affinidi Vault dashboard.

  1. Install the Affinidi CLI. Follow the guide below if you haven’t installed yet.
Set up Affinidi CLI
  1. Download and install NodeJS on your machine if you haven’t set it up yet.
  1. Install Affinidi CLI using Node Package Manager (npm).
npm install -g @affinidi/cli
  1. Verify that the installation is successful.
affinidi --version
  1. Login to the Affinidi CLI.
affinidi start
  1. Optionally, if you have multiple projects and you want to create the Personal Access Token (PAT) on a specific project, execute the following command:

Find the Project ID for the Project you want to switch:

affinidi project list-projects

Switch to the Project where you want to create the Personal Access Token (PAT):

affinidi project select-project \
--project-id="<PROJECT_ID>"

Create a Personal Access Token

  1. Create a Key-pair by following these steps.

  2. Create a Personal Access Token (PAT) using CLI:

affinidi token create-token --name="App Rotate Client" --key-id="AppRotateClient" --public-key-file="public-key.pem"
  1. After creating the Personal Access Token (PAT), we must add the PAT to the current active project and create a policy. To do this, execute the following command:
affinidi iam add-principal -i <TOKEN_ID> -t token
  1. After adding the PAT to the current active project, get the created policy and save it into a file. To do this, execute the following command:
affinidi iam get-policies -i <TOKEN_ID> -t token > policy.json
  1. Open the policy.json file and update the action and resource with full access by setting *.

Sample updated policy:

{
  "version": "2022-12-15",
  "statement": [
    {
      "principal": [
        "ari:iam::d085c5a5-5765-4d8f-b00e-398f0916a161:token/4b14f758-d725-47bc-865a-6e176581edba"
      ],
      "action": [
        "*"
      ],
      "resource": [
        "*"
      ],
      "effect": "Allow"
    }
  ]
}

Learn more on how to define policy here.

  1. After updating the policy file, we will update the policy assigned to the Personal Access Token (PAT) we have created. To do this, execute the following command:
affinidi iam update-policies -i <TOKEN_ID> -t token -f policy.json

Automate tasks and delegate token

After creating and granting access to your Personal Access Token (PAT), it’s time to set up your application to delegate tokens and call Affinidi services programmatically.

  1. To create a delegate token, we will sign the JSON Web Token (JWT) with the private key we generated previously.

function signJWT(privateKey, algorithm, passphrase, keyId, tokenId) {
  const issueTimeSeconds = Math.floor(new Date().getTime() / 1000)
  const payload = {
    iss: tokenId,
    sub: tokenId,
    aud: 'https://apse1.auth.developer.affinidi.io/auth/oauth2/token',
    jti: new Date().toString() + Math.random(),
    exp: issueTimeSeconds + 5 * 60,
    iat: issueTimeSeconds,
  }
  const secret = { key: privateKey, passphrase }
  const options = { algorithm: algorithm, keyid: keyId }
  return jwt.sign(payload, secret, options)
}
  1. After signing the JWT, we will exchange it with the User Scoped Token. The User Scoped Token is used to call the Project APIs and exchange them with the Project Scoped Token to access the resources within the Project.

async function getUserToken(tokenId, signedJWT) {
  const formData = qs.stringify({
    grant_type: 'client_credentials',
    scope: 'openid',
    client_assertion_type:
      'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
    client_assertion: signedJWT,
    client_id: tokenId,
  })
  const response = await fetch(
    'https://apse1.auth.developer.affinidi.io/auth/oauth2/token',
    {
      method: 'POST',
      body: formData,
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
    },
  )
  if (response.status !== 200) throw new Error('Could not get user token')
  return response.json()
}
  1. After generating the User Scoped Token, we will use it to generate the Project Scoped Token to access resources within the projects, such as Login Configuration.

async function getProjectToken(projectId, userScopeToken) {
  const response = await fetch('https://apse1.api.affinidi.io/iam/v1/sts/create-project-scoped-token',
    {
      method: 'POST',
      body: JSON.stringify({ projectId }),
      headers: {
        Authorization: `Bearer ${userScopeToken}`,
        'Content-Type': 'application/json',
      },
    },
  )
  if (response.status !== 200) throw new Error('Could not get project token')
  return response.json()
}

Below is the complete snippet of the code to generate access tokens to access Affinidi services.


import jwt from 'jsonwebtoken'
import qs from 'qs'

// Parameters required to sign JWT
const PRIVATE_KEY = process.env.PRIVATE_KEY
const ALGORITHM = process.env.ALGORITHM
const PASSPHRASE = process.env.PASSPHRASE
const KEY_ID = process.env.KEY_ID

// Parameter required to sign JWT and get User Scoped Token
const TOKEN_ID = process.env.TOKEN_ID

// Parameter required to get Project Scoped Token
const PROJECT_ID = process.env.PROJECT_ID

function signJWT(privateKey, algorithm, passphrase, keyId, tokenId) {
  const issueTimeSeconds = Math.floor(new Date().getTime() / 1000)
  const payload = {
    iss: tokenId,
    sub: tokenId,
    aud: 'https://apse1.auth.developer.affinidi.io/auth/oauth2/token',
    jti: new Date().toString() + Math.random(),
    exp: issueTimeSeconds + 5 * 60,
    iat: issueTimeSeconds,
  }
  const secret = { key: privateKey, passphrase }
  const options = { algorithm: algorithm, keyid: keyId }
  return jwt.sign(payload, secret, options)
}

async function getUserToken(tokenId, signedJWT) {
  const formData = qs.stringify({
    grant_type: 'client_credentials',
    scope: 'openid',
    client_assertion_type:
      'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
    client_assertion: signedJWT,
    client_id: tokenId,
  })
  const response = await fetch(
    'https://apse1.auth.developer.affinidi.io/auth/oauth2/token',
    {
      method: 'POST',
      body: formData,
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
    },
  )
  if (response.status !== 200) throw new Error('Could not get user token')
  return response.json()
}

async function getProjectToken(projectId, userScopeToken) {
  const response = await fetch('https://apse1.api.affinidi.io/iam/v1/sts/create-project-scoped-token',
    {
      method: 'POST',
      body: JSON.stringify({ projectId }),
      headers: {
        Authorization: `Bearer ${userScopeToken}`,
        'Content-Type': 'application/json',
      },
    },
  )
  if (response.status !== 200) throw new Error('Could not get project token')
  return response.json()
}

const signedJWT = signJWT(PRIVATE_KEY, ALGORITHM, PASSPHRASE, KEY_ID, TOKEN_ID)
const userScopeToken = (await getUserToken(TOKEN_ID, signedJWT)).access_token
const projectScopeToken = (await getProjectToken(PROJECT_ID, userScopeToken)).accessToken
  1. After generating the Project Scoped Token, we call the Login Configuration API to generate a Login Configuration.

const response = await fetch('https://apse1.api.affinidi.io/vpa/v1/login/configurations',
    {
      method: 'POST',
      body: JSON.stringify({
          "name": "Login Config Name",
          "redirectUris": [
            "http://localhost:3000/callback"
          ]
        }),
      headers: {
        Authorization: `Bearer ${projectScopeToken}`,
        'Content-Type': 'application/json',
      },
    },
  )

if (response.status !== 200) throw new Error('Could not get project token')

const configuration = await response.json()

With Personal Access Token (PAT), you can automate the creation of Login Configuration and automatically rotate the client credentials used by your application regularly to provide better security to your application users.