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:
Claim | Type | Mandatory | Value | Description |
---|---|---|---|---|
alg | Fixed string value | Yes | Example: 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. |
iss | Fixed string value | Yes | Example: https://my-auth-server.com | Identifies 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. |
sub | Fixed string value | Yes | Example: JDoe123 | Identifies the subject that the JWT is about. Value can be a case-sensitive string or a URI. |
aud | Fixed string value | Yes | Example: https://myapp.com | Identifies the audience for whom the JWT is intended. Value provided by Pismo. |
iat | Timestamp number | Yes | Example: 1697044489 | Current time (in seconds) since the UNIX epoch. |
exp | Timestamp number | Yes | Example: 1697051689 | Time (in seconds) since the UNIX epoch, at which the token expires. This can be no longer than 3600 seconds after the iat . |
tenant_id | Fixed string value | Yes | Example: tn-34778262-f4f0-464d-b4c6-a14e2dc6f4be | Org ID. Value provided by Pismo. |
uid | String value | Yes | Example: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 type | Yes | Example:"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 Name | Type | Description |
---|---|---|
token | string | A 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
- OpenID Connect tokens expire after 1 hour.
- s2s tokens (basic authentication) expire after 10 minutes.
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>}'
Updated 4 months ago