Authentication with OpenID Connect for Servers

OpenID Connect for Servers is the preferred way to authenticate with the Pismo platform. It provides greater security guarantees in service communications than basic authentication with client credentials. JSON Web Tokens (JWTs) are a critical part of connecting with OpenID. The section Creating a JWT explains how you generate and sign a JWT.

Simplified URIs

The Pismo platform uses a simplified URI for OpenID endpoints. The new URIs no longer include a service prefix before the version number. For example, with basic authentication, you use the following endpoint to get account data using client credentials:

{environment}/accounts/v1/accounts

With OpendID, you use the following, simplified endpoint:

{environment}/v1/account

This makes it easier to use and understand the APIs.

️ If you send the service prefix, the endpoint will not work.

When using the OpenID method of authentication, you must remove the service prefix from the URI.

Server authentication

Follow these steps to authenticate a request using OpenID.

  1. Use the OpenSSL toolkit to generate a private key and a public key, using the following commands:
Generate a private key:
  $ openssl genpkey -out rsakey.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048
Generate a public key based on the private key yougenerated:
  $ openssl rsa -in rsakey.pem -out rsa.pub -pubout -outform PEM
  1. Store your private key securely, and send the public key to your customer representative, along with your tenant ID, which is the ID of your Org.
  2. Your customer representative will send you an API key (apikey) which you need to use for all API requests.
  3. Request an access token using the following endpoint:
    {environment}/v1/oauth2/token
  4. Retrieve the access token returned in the Pismo response.
  5. Use the access token to make API calls.

The following diagram illustrates this process.

Notes:

  • In step 5, the response includes an access token that you can use with an API endpoint. If the response does not include an access token, your JWT might not be properly formed.
  • When the access token expires, your application can generate another JWT, sign it, and request another access token.

Creating a JWT

You generate and sign a JWT using the private key you generated in step 1. This JWT should contain the following claims:

key

value

description

alg

RS256

iss

Value provided by Pismo.

sub

Value provided by Pismo.

aud

Value provided by Pismo

iat

The current time in seconds since the UNIX epoch.

exp

The time in seconds since the UNIX epoch at which the token expires. It can be a maximum of 3600 seconds later than the iat.

tenant_id

ex: test-32r4

The identifier of the tenant of the user.

uid

ex: 12345645
or
ex: test-32r4

The Account ID of the Pismo customer. If you don't want the token to contain an account id, you can use the same value you used for
tenant_id.

pismo.group

ex: pismo-v1:samplegroup:rw

The identifier of the group that you want your token to have access to. A group has access to a certain subset of endpoints, you can ask your Pismo customer representative for more information on the groups that can be used.

claims (optional)

Optional custom claims.

📘

Most of the endpoints in the Pismo API require auid.

This could present a problem if you are trying to create your first account. However, since the tenant ID can be used in place of the account ID in auid, you can use that to make the request.

To generate JWTs, we recommend using one of the libraries provided at jwt.io.

Another option is to use Google/Firebase helper libraries, where you use the private key in Google's Service Account format and provide it to the libraries. Details about this implementation can be found in the Google Cloud Platform (GCP) documentation.

The following code sample shows how to use JWT-GO to create a signed JWT:

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().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

Using the JWT to request an 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}/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-integration.pismolabs.io/v1/oauth2/token \
--header 'Content-Type: application/json' \
--header 'apikey: 123456789abcapikey' \ 
--data '{
"token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJmaXJlYmFzZS1hZG1pbnNkay12ZnA3dkBkZXYtZ2tlLTdhNjUuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJhdWQiOiJodHRwczovL2lkZW50aXR5dG9vbGtpdC5nb29nbGVhcGlzLmNvbS9nb29nbGUuaWRlbnRpdHkuaWRlbnRpdHl0b29sa2l0LnYxLklkZW50aXR5VG9vbGtpdCIsImV4cCI6MTU3NTY3MzEzMiwiaWF0IjoxNTc1NjY5NTMyLCJzdWIiOiJmaXJlYmFzZS1hZG1pbnNkay12ZnA3dkBkZXYtZ2tlLTdhNjUuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJ1aWQiOiJjYXBpdGFvYW1lcmljYUBwaXNtby5pbyIsImNsYWltcyI6eyJ0ZXN0MSI6InRlc3QxIiwidGVzdDIiOiJ0ZXN0MiIsInRlc3QzIjoidGVzdDMifX0.uPCDrPAS0iGGHphG5NSIzHuGlO1Dh9U3FcJ-mpk_cOh04CHnw3Bm56tEFUxJCBpk3CeWHRDK_Q0ybCdotYGPxF7y-HFnFf04t95FgGZgtg1JY2m6UfaDZ3VTD9nWk5wgKJWfKGvwlvBI4ZVTJQKlzj_0QUHxHlzNJGo9sjgSc_roOm_pRZIVkiKm_9e1V5Rgk3tyN0Ov5ACGROyO0NVvWDYrv0w9MyxwZU97kG4O9VdZyc6pqouaYYCQVWWFtwIucOlBc010-ccRovE5ITzNGxiW90fTxbdPdcP_0LOsNH_nPFd8AMk_eYwk5afb_wSE8XFCf19UrjstpWON7su9RA"
}'

Handling the response

If the JWT and the apikey are 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"
}

📘

The access token expires after one hour.

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

Summarizing the authentication process

The following diagram illustrates the entire authentication process. The steps are numbered to show the process flow.

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 illustrated below.

curl --request POST \
  --url https://sandbox-integration-pci.pismolabs.io/v1/service/example/payment \
  --header 'Content-Type: application/json' \
  --header 'accept: application/json' \
  --header 'apikey: 123456789abcapikey' \
  --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>}'

Did this page help you?