Backend Authentication Guide
Square provides authentication to your backend APIs with JWTs (JSON web tokens). These JWTs are generated automatically by Square and attached to your request. The JWT signing algorithm is RS256, see Signing Algorithms.
Getting the secret
After creating an Add-on, you are also able to create an add-on secret via the Developer Dashboard Add-on page, see this on how to navigate there. Each Add-on has at most one add-on secret. The add-on secret contains the asymmetric private and public key pair that is used to sign and verify the JWT, respectively. Square will securely store and use the private key, while you can copy the public key from Developer Dashboard. The public key shared with you is Base64 encoded. For instructions on how to use it, see the section Validating the request below.
Note that we don’t support a seamless secret rotation as we are still in Alpha. You can replace the secret at any time in the Developer Dashboard.
What the request looks like
For all requests to your backend APIs, Square will attach a JWT signed using this secret to a request header in this format:
Authorization: "Bearer jwt"
What the JWT looks like:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3d3d3cuc3F1YXJldXAuY29tL2Rhc2hib2FyZCIsImF1ZCI6IlBMQUNFSE9MREVSX0RPX05PVF9NT0RJRlkiLCJzdWIiOiJNTEU3VEUxV1JKTlpEIiwiaWF0IjoxNzIxOTQ3MTU0LCJleHAiOjE3MjE5NDc0NTR9.eN_DhN29m_xil2Hm-X7-3bvQQn3D0lYknAv2W8IHv9SgdmqePadRPaeAfVhHEJ2PGmmIpplc4M319PRvrkrXQUgCGsI097lV-KdUxlgrhAxzxgkhjXatQjnfEFxgBZmkHq7Hgjic688sk9H1f1sbGgl6OzYpecFBdXKWDq_XzM8E89CCZJhYAaIxcXTAwLPRarcVCs5rflhIshnlrQ_69DtQQx2Ew1E_U0vWklLvNLOAVAG9M-KH9qhAbpsHD5-KwIiytxPg6gYQji3WWUUVOwwB3hPHDZW9m7v9We9saIqsapr8CghxQWCxVNHbTHOfl6mpbJ2SSa5gL4LGR4OUrA
The JWT has the following format when decoded:
// Headers:
{
"alg": "RS256",
"typ": "JWT"
}
// Payload:
{
"iss": "https://wwww.squareup.com/dashboard",
"aud": "PLACEHOLDER_DO_NOT_MODIFY", //The AddonId
"sub": "MLE7TE1WRJNZD", // The MerchantId
"iat": 1721947154,
"exp": 1721947454
}
// Signature:
Signed with the AddonSecret PrivateKey and RS256
The headers provide information on the JWT and how it’s signed.
The payload provides information to the Add-on Developer.
iss
: Issuer. The party (Square) that created and signed the JWT.aud
: Audience. The recipient that is expected to receive the JWT. Square uses the add_on_id for this.sub
: Subject. The subject of the JWT, the merchant. Square uses the merchant_id for this.iat
: Issued at time. The epoch time in seconds that this JWT was created.exp
: Expiration time. The epoch time in seconds that this JWT expires. 300 seconds after issued at.
Note that the JWT will only be attached if your manifest has your backend domain specified in the CSPs. See our example backend for details on how that is set up.
Validating the request
After receiving the JWT in the request header, to ensure that it hasn’t been tampered with and is from your Dashboard Add-on, you must validate the JWT. In our example backend server, you can see how we handle and validate the JWT.
To validate the JWT, you must use the provided public key from the add-on secret. We provide it for you as a Base64 encoded string. In each language, there are common JWT libraries you can use to validate. Some examples below:
Typescript
import { jwtVerify, importSPKI } from 'jose';
// Convert the public key to SPKI PEM format
const jwtPublicKeyPem = const jwtPublicKeyPem = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz07Wt3S8k4+Fn5ngltXJ/z5fy1BzhBxVJOI/ZJX2K//QuZU5sNu4C/yKZHngt+lZJ3FQ5uDqHP1SbiJERD6kMvEeeYoueTx9MRGH/veJnzrvXyV3p7AbSgvmFOZD2wIdLQnZV2ID7FlPpn77NBNUJ2Ar2Q60bHO29vHSpmKIH0bzGYDBOFNS2D0lSA6sphZsnG4gweX7LihBIOhM4Wda8gVxOLNXQ6Uqg9PesLehwY4U0ltiZh+t1U5/yg1HRKrzN1pIh0u+Wyt8TS2lL9gh60mJcRiq9lzPNc0zrEYcoCqAC0FuLPIrdxLY1igp9BtY5cOEC2zxl2i8xbMeLClfZQIDAQAB
-----END PUBLIC KEY-----`
async function validateAndGetClaimsFromJwt(jwtVal: string) {
return await jwtVerify(jwtVal, await importSPKI(jwtPublicKeyPem, 'RS256'));
}
Java
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
final String base64PublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz07Wt3S8k4+Fn5ngltXJ/z5fy1BzhBxVJOI/ZJX2K//QuZU5sNu4C/yKZHngt+lZJ3FQ5uDqHP1SbiJERD6kMvEeeYoueTx9MRGH/veJnzrvXyV3p7AbSgvmFOZD2wIdLQnZV2ID7FlPpn77NBNUJ2Ar2Q60bHO29vHSpmKIH0bzGYDBOFNS2D0lSA6sphZsnG4gweX7LihBIOhM4Wda8gVxOLNXQ6Uqg9PesLehwY4U0ltiZh+t1U5/yg1HRKrzN1pIh0u+Wyt8TS2lL9gh60mJcRiq9lzPNc0zrEYcoCqAC0FuLPIrdxLY1igp9BtY5cOEC2zxl2i8xbMeLClfZQIDAQAB";
public Claims validateAndGetClaimsFromJwt(String jwt) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] keyBytes = Base64.getDecoder().decode(base64PublicKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
PublicKey publicKey = keyFactory.generatePublic(keySpec);
return Jwts.parser()
.setSigningKey(publicKey)
.build()
.parseClaimsJws(jwt)
.getBody();
}
Python
import jwt # Installed from PyJWT
# Convert the public key to SPKI PEM format
public_key = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz07Wt3S8k4+Fn5ngltXJ/z5fy1BzhBxVJOI/ZJX2K//QuZU5sNu4C/yKZHngt+lZJ3FQ5uDqHP1SbiJERD6kMvEeeYoueTx9MRGH/veJnzrvXyV3p7AbSgvmFOZD2wIdLQnZV2ID7FlPpn77NBNUJ2Ar2Q60bHO29vHSpmKIH0bzGYDBOFNS2D0lSA6sphZsnG4gweX7LihBIOhM4Wda8gVxOLNXQ6Uqg9PesLehwY4U0ltiZh+t1U5/yg1HRKrzN1pIh0u+Wyt8TS2lL9gh60mJcRiq9lzPNc0zrEYcoCqAC0FuLPIrdxLY1igp9BtY5cOEC2zxl2i8xbMeLClfZQIDAQAB
-----END PUBLIC KEY-----"""
def validate_and_get_claims_from_jwt(jwtVal, public_key):
# Note that expected audience is required. For local testing it is "PLACEHOLDER_DO_NOT_MODIFY", and for private installation it is your add-on Id
return jwt.decode(jwtVal, public_key, algorithms=["RS256"], audience="PLACEHOLDER_DO_NOT_MODIFY")
For more information on what a JWT is, see Introduction to JSON Web Tokens.
Development with the JWT
When you are developing in local-testing mode, we will attach a JWT signed with a constant private key, with everything else being the same as in production. The public key to validate this JWT is also constant:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz07Wt3S8k4+Fn5ngltXJ/z5fy1BzhBxVJOI/ZJX2K//QuZU5sNu4C/yKZHngt+lZJ3FQ5uDqHP1SbiJERD6kMvEeeYoueTx9MRGH/veJnzrvXyV3p7AbSgvmFOZD2wIdLQnZV2ID7FlPpn77NBNUJ2Ar2Q60bHO29vHSpmKIH0bzGYDBOFNS2D0lSA6sphZsnG4gweX7LihBIOhM4Wda8gVxOLNXQ6Uqg9PesLehwY4U0ltiZh+t1U5/yg1HRKrzN1pIh0u+Wyt8TS2lL9gh60mJcRiq9lzPNc0zrEYcoCqAC0FuLPIrdxLY1igp9BtY5cOEC2zxl2i8xbMeLClfZQIDAQAB
For Addons installed via private-install or installed in production, it will use the actual add-on secret you created to generate the JWT. In this case, you must use the provided public key to validate it.