Generate a JWT

If you are using OpenID Connect authentication, see Authentication with OpenID Connect.

📘

Generate access keys

During onboarding to the Pismo platform, you must generate at least one pair of public and private keys for OpenID authentication. Then, you must generate and sign a JSON Web Token (JWT) for each key pair that you generate.

The simplest way to generate access keys is to use Pismo Control Center. For details, see Manage access keys.

JWT (JSON Web Token) is an open standard that defines a method for secure information exchange using a JSON object called a token. The token is a unique string of characters that you request from the Pismo platform as part of the initial authentication process. A token can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with HMAC) or a public/private key pair (using RSA or ECDSA).

The token serves as a proof of identity and defines what you are authorized to see and do on the platform. After login, each subsequent request will pass the JWT, allowing access to the routes, services, and resources that are permitted with that token. Since a JWT is self-contained, it reduces the need to query the database multiple times for verification, thereby improving performance.

{
  "alg": "RS256"
  "iss": "https://my-auth-server.com",
  "sub": "1234567890",
  "aud": "https://myapp.com",
  "iat": 1516239022,
  "exp": 1697051689,
  "tenant_id": "tn-34778262-f4f0-464d-b4c6-a14e2dc6f4be",
  "uid": "123456abcd",
  "pismo": "_pismo-v1:sample-group:rw_"
}

You generate and sign a JWT using the private key you generated. This JWT must contain the following claims:

ClaimTypeMandatoryValueDescription
algFixed string valueYesExample: RS256 (RSA using SHA256) Cryptographic algorithm that is used to sign the token.

Note: This claim is part of the header and not the payload.
issFixed string valueYesExample: https://my-auth-server.comIdentifies the issuer of the JWT. Essentially, it specifies the entity (for example, an app, API, server, or client) created and signed the token.

Value can be a case-sensitive string or a URI.
subFixed string valueYesExample: JDoe123Identifies the subject that the JWT is about.

Value can be a case-sensitive string or a URI.
audFixed string valueYesExample: https://myapp.comIdentifies the audience for whom the JWT is intended.

Value provided by Pismo.
iatTimestamp numberYesExample: 1697044489Current time (in seconds) since the UNIX epoch.
expTimestamp numberYesExample: 1697051689Time (in seconds) since the UNIX epoch, at which the token expires. This can be no longer than 3600 seconds after the iat.
tenant_idFixed string valueYesExample: tn-34778262-f4f0-464d-b4c6-a14e2dc6f4beOrg ID.

Value provided by Pismo.
uidString valueYesExample:
tn-34778262-f4f0-464d-b4c6-a14e2dc6f4be or 123456abcd
Pismo account ID or external ID. If you don't want the token to contain an account ID, you can use the Org ID.
pismo Object typeYesExample:
"pismo": { "group": "_pismo-v1:sample-group:rw_" },
Custom claim that identifies the Pismo permission group that you want your token to have access to. (A permission group defines access to a certain subset of endpoints.)

📘

To generate JWTs, Pismo recommends using one of the libraries provided at https://jwt.io/libraries.


Example code

The following code sample shows how to create a signed JWT in a few languages:

package main

import (
    "fmt"
    "time"

    "github.com/dgrijalva/jwt-go"
)

var mySigningKey = []byte(`
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
`)

func main() {

    key, err := jwt.ParseRSAPrivateKeyFromPEM(mySigningKey)
    fmt.Println(err)

    token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
        "iss":       "[email protected]",
        "aud":       "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
        "exp":       time.Now().Add(time.Hour).Unix(),
        "iat":       time.Now().Unix(),
        "sub":       "[email protected]",
        "uid":       "12345678910",
        "tenant_id": "test-pbrj4",
    })

    // Sign and get the complete encoded token as a string using the secret
    tokenString, err := token.SignedString(key)
    fmt.Println(tokenString, err)

}
package com.star.sud;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.Instant;
import java.util.*;

import io.jsonwebtoken.*;


public class JwtTokenwithRsa {

	//Reference to your private key
	public static final String PRIVATE_KEY_FILE_PATH = "RSA/rsakey.pem";

	public static void main(String[] args) {
		try {
			JwtTokenwithRsa tokenwithRsa = new JwtTokenwithRsa();
			tokenwithRsa.generateJWTWithRsa();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public String generateJwtToken(PrivateKey privateKey) {
		JwtBuilder builder = Jwts.builder().signWith(SignatureAlgorithm.RS256, privateKey);

		long issuedAt = Instant.now().getEpochSecond();
		long expiresAt = Instant.now().plusSeconds(3600).getEpochSecond();
		Map<String,String> pismoClaims = new HashMap<String,String>();
		pismoClaims.put("group","pismo-v1:samplegroup:rw");

		builder.claim("aud", "value provided by Pismo");
		builder.claim("sub", "value provided by Pismo");
		builder.claim("iss", "value provided by Pismo");
		builder.claim("exp", expiresAt);
		builder.claim("iat",issuedAt);
		builder.claim("pismo", pismoClaims);
		builder.claim("tenant_id", "Your Tenant ID");
		builder.claim("uid", "Account Id or Tenant Id");


		String token = builder.compact();
		return token;
	}

	public void generateJWTWithRsa() throws Exception {
		KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA");
		keyGenerator.initialize(1024);

		File f = new File(PRIVATE_KEY_FILE_PATH);
		PrivateKey privateKey = readPrivateKey(f);


		// Generate Token
		String id = UUID.randomUUID().toString();
		String token = generateJwtToken(privateKey);
		System.out.println("TOKEN:");
		System.out.println(token);

	}

	public RSAPrivateKey readPrivateKey(File file) throws Exception {
		String key = new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset());

		String privateKeyPEM = key
				.replace("-----BEGIN PRIVATE KEY-----", "")
				.replaceAll(System.lineSeparator(), "")
				.replace("-----END PRIVATE KEY-----", "");

		byte[] encoded = Base64.getDecoder().decode(privateKeyPEM);

		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
		return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
	}

}
To be released
To be released

Use the JWT to request a Pismo access token

After generating the signed JWT, you can use it to request an access token. This access token request is an HTTPS POST request.

Method: POST

Content-Type: application/json

Endpoint: {environment}/passport/v1/oauth2/token

Request body payload:

Property NameTypeDescription
tokenstringA custom token you use to request an access token.

The following sample code shows a cURL POST request for an access token in the Pismo Sandbox:

curl --request POST \
--url https://sandbox.pismolabs.io/passport/v1/oauth2/token \
--header 'Content-Type: application/json' \
--data '{
"token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJmaXJlYmFzZS1hZG1pbnNkay12ZnA3dkBkZXYtZ2tlLTdhNjUuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJhdWQiOiJodHRwczovL2lkZW50aXR5dG9vbGtpdC5nb29nbGVhcGlzLmNvbS9nb29nbGUuaWRlbnRpdHkuaWRlbnRpdHl0b29sa2l0LnYxLklkZW50aXR5VG9vbGtpdCIsImV4cCI6MTU3NTY3MzEzMiwiaWF0IjoxNTc1NjY5NTMyLCJzdWIiOiJmaXJlYmFzZS1hZG1pbnNkay12ZnA3dkBkZXYtZ2tlLTdhNjUuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJ1aWQiOiJjYXBpdGFvYW1lcmljYUBwaXNtby5pbyIsImNsYWltcyI6eyJ0ZXN0MSI6InRlc3QxIiwidGVzdDIiOiJ0ZXN0MiIsInRlc3QzIjoidGVzdDMifX0.uPCDrPAS0iGGHphG5NSIzHuGlO1Dh9U3FcJ-mpk_cOh04CHnw3Bm56tEFUxJCBpk3CeWHRDK_Q0ybCdotYGPxF7y-HFnFf04t95FgGZgtg1JY2m6UfaDZ3VTD9nWk5wgKJWfKGvwlvBI4ZVTJQKlzj_0QUHxHlzNJGo9sjgSc_roOm_pRZIVkiKm_9e1V5Rgk3tyN0Ov5ACGROyO0NVvWDYrv0w9MyxwZU97kG4O9VdZyc6pqouaYYCQVWWFtwIucOlBc010-ccRovE5ITzNGxiW90fTxbdPdcP_0LOsNH_nPFd8AMk_eYwk5afb_wSE8XFCf19UrjstpWON7su9RA"
}'

Handling the response

If the JWT is properly formed and your service has permission to perform the operation, then the JSON response from the authorization server should include an access token. The following is an example response.

{
  "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6InNrSUJOZyJ9.eyJpc3MiOiJodHRwczovL2lkZW50aXR5dG9vbGtpdC5nb29nbGUuY29tLyIsImF1ZCI6ImRldi1na2UtN2E2NSIsImlhdCI6MTU3NjE4MDg4MSwiZXhwIjoxNTc3MzkwNDgxLCJ1c2VyX2lkIjoiY2FwaXRhb2FtZXJpY2FAcGlzbW8uaW8iLCJzaWduX2luX3Byb3ZpZGVyIjoiY3VzdG9tIiwiZXh0cmFfY2xhaW1zIjp7InRlc3QxIjoidGVzdDEiLCJ0ZXN0MiI6InRlc3QyIiwidGVzdDMiOiJ0ZXN0MyJ9fQ.LAv--CRbsZqDhlRXcpnZlyK403oU2i1__fFR7CZ-bgp5e4lF187r1W-9jpZCVLit-QhneHJiaySxlDTAiwYfUu6bpVPw0BaXT-GimmGi-MDudUX3R44VtdQPvOXUHgx43DDD1NsDOwDn5QtrA-pvrfyT-Ijs6GcH4R66XPYaIm_ASHSEPCupK0ZW6VFjiWx3goGtCKG-TQlaGyq-iQ_ZBRJksJf6xIN8bFC1HqODePm3yDbtum2hbxwuZGtBQ0DcgkH4in4TWKwe8qrk37cl5TOkRx1yyhh9Jkhigmu6NuTvCk6ic-aappIR2P9iBB-g3W33-HVutXqqWfd0DXQb5w",
  "refresh_token": "AE0u-NcsvZAKSuwq8Q4E7ZEE9NBmmio7-0JQASKqw8L2AWaFH9EIggs4wuzvE0nnVoC9",
  "expires_in": "3600"
}

📘

Token expiration

Your application should catch the error generated when this happens. It can then generate another JWT, sign it, and request another access token.


Calling Pismo APIs

After your application obtains an access token, you can use it to make calls to a Pismo API on behalf of the given user account. To do this, include the access token in the authorization: Bearer header of each request, as example below.

curl --request POST \
  --url https://sandbox.pismolabs.io/service/v1/service/example/payment \
  --header 'Content-Type: application/json' \
  --header 'accept: application/json' \
  --header 'authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6InNrSUJOZyJ9.eyJpc3MiOiJodHRwczovL2lkZW50aXR5dG9vbGtpdC5nb29nbGUuY29tLyIsImF1ZCI6ImRldi1na2UtN2E2NSIsImlhdCI6MTU3NjE4MDg4MSwiZXhwIjoxNTc3MzkwNDgxLCJ1c2VyX2lkIjoiY2FwaXRhb2FtZXJpY2FAcGlzbW8uaW8iLCJzaWduX2luX3Byb3ZpZGVyIjoiY3VzdG9tIiwiZXh0cmFfY2xhaW1zIjp7InRlc3QxIjoidGVzdDEiLCJ0ZXN0MiI6InRlc3QyIiwidGVzdDMiOiJ0ZXN0MyJ9fQ.LAv--CRbsZqDhlRXcpnZlyK403oU2i1__fFR7CZ-bgp5e4lF187r1W-9jpZCVLit-QhneHJiaySxlDTAiwYfUu6bpVPw0BaXT-GimmGi-MDudUX3R44VtdQPvOXUHgx43DDD1NsDOwDn5QtrA-pvrfyT-Ijs6GcH4R66XPYaIm_ASHSEPCupK0ZW6VFjiWx3goGtCKG-TQlaGyq-iQ_ZBRJksJf6xIN8bFC1HqODePm3yDbtum2hbxwuZGtBQ0DcgkH4in4TWKwe8qrk37cl5TOkRx1yyhh9Jkhigmu6NuTvCk6ic-aappIR2P9iBB-g3W33-HVutXqqWfd0DXQb5w' \
  --data '{< JSON body the service expects to receive>}'