Verifying JWT Tokens

When to verify JWT Tokens?

In every API call to your backend server, you should include the access_token in the header of your requests. You need to verify the access_token on each endpoint that you deem necessary. Usually, you would use a middleware so it automatically handles the verification for each of your routes.

Some good JWT middleware libraries that you can use:

Don't have a backend server? Use the API.

How to Verify the Access Token

  1. Make sure that the token is not expired

  2. Make sure that the aud matches your API_KEY_ID

  3. Check the authentication_method and scopes to match your API requirements (scopes are defaulted to access for now, and cannot be changed).

  4. Check that the JWT is well-formed.

  5. Check the signature.

Checking other attributes of the user

If you want to check the user's email or phone number before allowing access, for example, you want to allow only emails with a specific domain to log in, you should do the check here.

Third-Party JWT Libraries to Verify the Tokens

You can use third party libraries to verify JWT tokens. Check the list of third party libraries here. Make sure you check for the algorithm that the JWT token uses.

For Cotter's JWT Tokens, use:

  • Algorithm: ES256

  • Public Keys:

    • take the key with kid = SPACE_JWT_PUBLIC:8028AAA3-EC2D-4BAA-BE7A-7C8359CCB9F9

    • Make sure you take the keys from this endpoint, and cache when necessary, but don't hard-code it. The key may change.


Access tokens are usually included in oauth_token.access_token in the responses from the SDK.

Python (Flask)
// Install Dependency
yarn add cotter-node
// Validate token
var cotterNode = require("cotter-node");
var cotterToken = require("cotter-token-js");
// Validate access token
const access_token = oauth_token.access_token;
try {
var valid = await cotterNode.CotterValidateJWT(access_token);
} catch (e) {
// Not Valid
// Read access token
let decoded = new cotterToken.CotterAccessToken(access_token);
// Check that `aud` is your API KEY
const audience = decoded.getAudience();
if (audience !== YOUR_API_KEY_ID) {
throw "Audience doesn't match"
// (Optional) Checking other attributes of the user
// Example, only allow my company domain to login
const email = decoded.getIdentifier();
if (email.split("@")[1] !== "") {
throw "Please use cotter business email instead of a personal email";
# Using
require 'net/http'
require 'json/jwt'
jwks_raw = Net::HTTP.get URI("")
jwk_set =
access_token_string = 'eyJhbGciOiJFUzI1NiIsImtpZCI6IlNQQUNFX0pXVF9QVUJM...'
decoded_token = JSON::JWT.decode access_token_string, jwk_set
expected_aud = '<YOUR_API_KEY_ID>'
expected_iss = ''
unless (
decoded_token[:iss] == expected_iss &&
decoded_token[:aud] == expected_aud &&
decoded_token[:sub].present? &&[:iat]).between?(5.minutes.ago, &&[:exp]) >
raise 'Access Token Verification Failed!'
print 'Cotter User id = ' + decoded_token[:sub]
Python (Flask)
# Install Dependencies
pip install cotter
pip install -U flask-cors
# Add a flask Endpoint
from flask import Flask
from flask import request
from flask_cors import CORS
from cotter import validate
app = Flask(__name__)
@app.route('/login', methods=['POST'])
def login(name=None):
req = request.get_json();
# Getting access token and validate it
token = req["oauth_token"]["access_token"]
access_token_decoded = validate.validate_access_token(token, API_KEY_ID)
# User Authenticated!
# a) Either use Cotter's Access Token for your entire API authorization
# OR
# b) You can Generate your JWT Tokens or other session management here
return resp;
require __DIR__ . '/vendor/autoload.php';
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Core\JWK;
use Jose\Component\Signature\Algorithm\ES256;
use Jose\Component\Signature\Serializer\JWSSerializerManager;
use Jose\Component\Signature\Serializer\CompactSerializer;
use Jose\Component\Signature\JWSVerifier;
// The algorithm manager with the HS256 algorithm.
$algorithmManager = new AlgorithmManager([
new ES256(),
// We instantiate our JWS Verifier.
$jwsVerifier = new JWSVerifier(
// The serializer manager. We only use the JWS Compact Serialization Mode.
$serializerManager = new JWSSerializerManager([
new CompactSerializer(),
$http = new GuzzleHttp\Client();
$response = $http->request('GET', '', []);
$keys = json_decode((string) $response->getBody(), true);
$jwk = new JWK($keys["keys"][0]);
$token = "eyJhbGciOiJFUzI1NiIsImtpZCI6IlNQQU...";
// We try to load the token.
$jws = $serializerManager->unserialize($token);
// We verify the signature. This method does NOT check the header.
// The arguments are:
// - The JWS object,
// - The key,
// - The index of the signature to check. See
$isVerified = $jwsVerifier->verifyWithKey($jws, $jwk, 0);
// continue your logic here