JSON Web Tokens
JSON Web Tokens are used as part of Signed Requests with squatch.js and Open Endpoints. They are used to validate the data being supplied to Referral SaaSquatch.
๐ What is a JWT?
A JWT is an open standard for securely sharing information as a JSON object. JWTs are small enough to be used in a GET or POST parameter or an HTTP header and because they are digitally signed the information inside can be trusted. This allows us to use a JWT like a checksum to verify that the squatch.js init parameters are correct, but with the convenience of using a library to generate the JWT.
๐ How do I generate a JWT?
JWTs can easily be generated using one of the libraries available. There are even more libraries in even more languages available on GitHub in addition to the ones recommended by JWT.io.
๐ Example: Building the JWT
๐ 1. Collect the Data Object
Whether you are using a JWT with squatch.js or the Open Endpoints you will need to start with the data object that you are trying to sign.
For Open Endpoint and squatch.js calls the data might look like:
{
"id": "abc_123",
"accountId": "abc_123",
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"locale": "en_US",
"referralCode": "JOHNDOE"
}
๐ 2. Assemble the JWT payload
The JWT payload structures the data trying to be signed as follows:
Please Note: Do not include
tenantAlias
in the JWT payload for squatch.js.
For Open Endpoint and squatch.js calls:
{
"user":{
"id": "abc_123",
"accountId": "abc_123",
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"locale": "en_US",
"referralCode": "JOHNDOE"
}
}
๐ 3. Sign the Payload
Use your chosen library to build the JWT with the payload, and sign it with your tenant's API key.
Test mode vs. live mode - Your SaaSquatch program provides both a live and test mode. Each of these tenants provides an independant API key. Make sure to use the correct API key for call you are trying to sign.
using System.Collections.Generic;
using System.Text;
using Jose;
namespace JWTExample
{
class Program
{
public static string buildJWT(string secret, string accountId, string userId, string email, string firstName, string lastName, long expiryDate, string referralCode)
{
var userPayload = new Dictionary<string, object>()
{
{ "id", userId },
{ "accountId", accountId },
{ "firstName", firstName },
{ "lastName", lastName },
{ "email", email },
{ "locale", locale },
{ "referralCode", referralCode }
};
var payload = new Dictionary<string, object>()
{
{ "user", userPayload },
{ "exp", expiryDate } //optional date in seconds since the epoch
};
//the encoding must match the encoding of your secret, UTF8 is just an example
var byteSecret = Encoding.UTF8.GetBytes(secret);
return Jose.JWT.Encode(payload, byteSecret, JwsAlgorithm.HS256);
}
}
}
require 'jwt'
def buildJWT(secret, userId, accountId, email, firstName, lastName, referralCode, locale, expiryDate)
secret = 'Referral SaaSquatch API key'
return payload = JWT.encode({
user: {
id: userId,
accountId: accountId,
firstName: firstName,
lastName: lastName,
email: email,
locale: locale,
referralCode: referralCode
},
exp: expiryDate #optional date in seconds since the epoch
}, secret)
end
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtExample {
public static String buildJwt(String secret, Date expiryDate) {
// Build user object
final Map<String, Object> userMap = new HashMap<>();
userMap.put("id", "REPLACEME");
userMap.put("accountId", "REPLACEME");
userMap.put("firstName", "REPLACEME");
userMap.put("lastName", "REPLACEME");
userMap.put("email", "REPLACEME");
// Extra user fields, etc.
final JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.HS256)
.type(JOSEObjectType.JWT)
.build();
final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.claim("user", userMap)
// Not having an expiry works, but having an expiry is strongly recommended
.expirationTime(expiryDate)
.build();
final SignedJWT jwt = new SignedJWT(header, claimsSet);
try {
jwt.sign(new MACSigner(secret));
} catch (JOSEException e) {
// This will happen if your secret is shorter than 256 bits.
// If you are using your tenant API key, you won't need to worry about it.
throw new RuntimeException(e);
}
return jwt.serialize();
}
}
use \Firebase\JWT\JWT;
function buildJWT($secret, $userId, $accountId, $email, $firstName, $lastName, $locale, $referralCode) {
//build user object
$payload = array(
"user" => array(
"id" => $userId,
"accountId" => $accountId,
"firstName" => $firstName,
"lastName" => $lastName,
"email" => $email,
"locale" => $locale,
"referralCode" => $referralCode,
),
"exp" => $expiryDate //optional date in seconds since the epoch
);
//the encoder defaults to HS256, no need to specify an algorithm
return JWT::encode($payload, $secret);
}
import jwt
def buildJWT(secret, userId, accountId, email, firstName, lastName, locale, referralCode, expiryDate):
return jwt.encode({
'user': {
'id': userId,
'accountId': accountId,
'firstName': firstName,
'lastName': lastName,
'email': email,
'locale': locale,
'referralCode': referralCode
},
'exp': expiryDate #optional date in seconds since the epoch
}, secret, algorithm='HS256')
๐ 4. Include the JWT
๐ squatch.js
Once you have created the JWT, be sure to include it with all of your calls. An example of a squatch.js call including the JWT is included below:
๐ Open Endpoint API Call
Note - Open Endpoint API calls made from a server should be signed with your tenant's API key. Only Open Endpoint calls from a client should be signed with a JWT.
For use with an Open Endpoint API call the JWT should be included in the API call as a header with the key X-SaaSquatch-User-Token
.
cURL uses the -H
flag to pass an extra header. You may specify any number of extra headers.
curl -X POST https://app.referralsaasquatch.com/api/v1/{tenant_alias}/open/account/{accountId}/user/{userId} \
-H "X-SaaSquatch-User-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoidTEyMzQiLCJhY2NvdW50SWQiOiJhNTY3OCIsImVtYWlsIjoiam9lQGV4YW1wbGUuY29tIiwiZmlyc3ROYW1lIjoiSm9lIiwibGFzdE5hbWUiOiJUZXN0ZXJzb24iLCJsb2NhbGUiOiJlbl9VUyIsInJlZmVycmFsQ29kZSI6IkpPRVRFU1RFUlNPTiJ9fQ.TymCGn2W7H_QGwVqX4k5ELB7HS04RfRSpDATAsl67zI" \
-H "Content-Type: application/json" \
-d '{
"id": "u1234",
"accountId": "a5678",
"email": "Joe@example.com",
"firstName": "Joe",
"lastName": "Testerson",
"locale": "en_US",
"referralCode": "JOETESTERSON"
}'
๐ Additional Resources
Read more about Signed Requests for squatch.js and Open Endpoints. This article covers case-specific details about parameters to exclude and how to add a JWT to a call.