Cotter
  • 🚀Getting Started
  • Features & Concepts
    • 💬Sign In with Email/Phone Number
    • 🔐Sign In with Device
      • How it works
    • 🧬Sign In with WebAuthn
  • 📌Quickstart Guides
    • All Guides & Tutorials
    • HTML – Sign in with Email/Phone
    • React – Sign in with Email/Phone
    • React – WebAuthn
    • ▲ Next.js
    • Angular
    • Webflow
    • Bubble.io
    • Python SDK for a CLI
    • React Native – Sign in with Device
    • iOS – Sign in with Device
    • Flutter – Sign in with Device
  • 📘SDK Reference
    • Web
      • Sign In with Email/Phone Number
        • Customize the Form
        • Checking the email or phone before sending a verification code
        • Sending Code or Link via WhatsApp
        • Styling
        • Older SDK
          • Customize the Form
      • Sign in with Social Login
        • Getting Access Tokens from Social Login Providers
        • Github Instructions
        • Google Instructions
      • Sign In with WebAuthn
        • Register WebAuthn for a logged-in user
      • Sign In with Device
        • Steps for Pop Up Authentication Prompt
        • Advanced Customization for Login Form
        • Advanced Customization for Pop Up Authentication Prompt
      • Getting Access Token and Logged-In User Info
      • Sending Successful Form Submission
      • FAQ & Troubleshooting
    • React Native
      • Installation
      • Sign In with Device
        • Add Email/Phone Verification
        • Authenticate from a Non-Trusted Device
        • Add a new Trusted Device
        • Remove Trusted Device
      • Sign In with Email/Phone Number
      • Getting Stored OAuth Tokens and User Information
      • FAQ
      • Older SDK Versions
        • Sign in with Email/Phone
        • Sending Code via WhatsApp
        • Sign In with Device
          • Authenticate from a Non-Trusted Device
          • Add a new Trusted Device
          • Customization
    • Flutter
      • Sign In with Device
        • Add Email/Phone Verification
        • Authenticate from a Non-Trusted Device
      • Sign in with Email/Phone Number
      • Getting the Logged-in User
      • Getting OAuth Tokens
      • Signing a User Out
    • iOS
      • Sign In with Email/Phone Number
      • Sign In with Device
        • Authenticate from a Non-Trusted Device
        • Push Notification
        • Check if Trusted Device is Enrolled
        • Add a New Trusted Device
        • Remove Trusted Device
      • Older Versions
        • Biometric/Pin
    • Android
      • Sign In with Device
        • Authenticate from a Non-Trusted Device
        • Check if Trusted Device is Enrolled
        • Add a new Trusted Device
        • Remove Trusted Device
        • Customization
      • Sign In with Email/Phone Number
      • Biometric/Pin
        • Advanced Methods
        • Customization
        • Setting Strings
        • Styling
      • Older SDK Version
        • Sign In with Device
          • Authenticate from a Non-Trusted Device
    • Python (for CLI)
    • API for Other Mobile Apps or CLI
      • Verify Email/Phone Number
        • Handling URL Scheme
    • Backend: Handling Response
  • 🛡️ Protecting Your Account
    • Only Allow Your Website/App to Use Your API Key
    • Rate Limit
    • Enable reCAPTCHA to Protect Against Automated Abuse
  • 🗝️ Getting Access Token
    • Cotter's OAuth 2.0 Tokens Specification
    • Getting the Tokens
      • Get Tokens during Authentication
      • Using the Refresh Token
    • Storing and Removing Tokens
    • Renewing Expired Tokens
    • Verifying JWT Tokens
    • Requesting Custom Fields on your JWT Token
    • Older API
      • Using HTTP Requests
      • Getting the Tokens
        • During Authentication
          • During Email/Phone Verification
        • During enrolling Trusted Devices
  • 🔌API Reference
    • User API
      • User Object
    • OAuth Tokens API
      • Verify JWT Token using API (serverless)
      • Requesting Custom Claims on your Access Token
      • Older API
    • OAuth Tokens from Social Login
    • Event Object
    • Reset PIN API
  • Older API
    • Validating Cotter's Identity Token
    • Validating Cotter's Event Response
Powered by GitBook
On this page
  • When to verify JWT Tokens?
  • Don't have a backend server? Use the API.
  • How to Verify the Access Token
  • Third-Party JWT Libraries to Verify the Tokens
  • Examples
  1. 🗝️ Getting Access Token

Verifying JWT Tokens

PreviousRenewing Expired TokensNextRequesting Custom Fields on your JWT Token

Last updated 4 years ago

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:

  • Golang:

  • Node.js:

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

How to Verify the Access Token

The access token is a JWT Token, and it's signed using Asymmetric Signing Algorithm ES256. This means, unlike symmetric JWT tokens that are signed and verified using the same secret key, this asymmetric JWT Token is signed using a secret key that only Cotter knows, but can be verified using a public key that you can find .

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

For Cotter's JWT Tokens, use:

  • Algorithm: ES256

    • 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.

Examples

Access tokens are usually included in oauth_token.access_token in the responses from the SDK, or in the Authorization Header.

// 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
  console.log(e)
}

// Read access token
let decoded = new cotterToken.CotterAccessToken(access_token);
console.log(decoded);

// 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] !== "cotter.app") {
  throw "Please use cotter business email instead of a personal email";
}
# Using https://github.com/nov/json-jwt

require 'net/http'
require 'json/jwt'


jwks_raw = Net::HTTP.get URI("https://www.cotter.app/api/v0/token/jwks")
jwk_set = JSON::JWK::Set.new(
  JSON.parse(
    jwks_raw
  )
)

access_token_string = 'eyJhbGciOiJFUzI1NiIsImtpZCI6IlNQQUNFX0pXVF9QVUJM...'
decoded_token = JSON::JWT.decode access_token_string, jwk_set

expected_aud = '<YOUR_API_KEY_ID>'
expected_iss = 'https://www.cotter.app'
unless (
  decoded_token[:iss] == expected_iss &&
  decoded_token[:aud] == expected_aud &&
  decoded_token[:sub].present? &&
  Time.at(decoded_token[:iat]).between?(5.minutes.ago, Time.now) &&
  Time.at(decoded_token[:exp]) > Time.now
)
  raise 'Access Token Verification Failed!'
end

print 'Cotter User id = ' + decoded_token[:sub]
# 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__)
CORS(app)

@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;
<?php 
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(
  $algorithmManager
);

// The serializer manager. We only use the JWS Compact Serialization Mode.
$serializerManager = new JWSSerializerManager([
  new CompactSerializer(),
]);

$http = new GuzzleHttp\Client();
$response = $http->request('GET', 'https://www.cotter.app/api/v0/token/jwks', []);

$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
?>
package middleware

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/google/uuid"
	"github.com/labstack/echo/v4"
	"gopkg.in/square/go-jose.v2"
	"gopkg.in/square/go-jose.v2/jwt"
)

// 👇 Enter your API KEY ID here
const API_KEY_ID = "YOUR_API_KEY_ID"
const JWKSURL = "https://www.cotter.app/api/v0/token/jwks"
const JWKSLookupKeyID = "SPACE_JWT_PUBLIC:8028AAA3-EC2D-4BAA-BE7A-7C8359CCB9F9"

func getKey() ([]byte, error) {
	// Fetch the JWT Public Key from the URL
	resp, err := http.Get(JWKSURL)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// Parse the response into our keys struct
	keyset := make(map[string][]map[string]interface{})
	err = json.Unmarshal(body, &keyset)
	if err != nil {
		return nil, err
	}

	// It's a Key Set = there might be multiple keys
	// Find the key with kid = JWKSLookupKeyID
	if len(keyset["keys"]) <= 0 {
		return nil, errors.New("Key set is empty")
	}
	for _, k := range keyset["keys"] {
		if k["kid"] == JWKSLookupKeyID {
			key, err := json.Marshal(k)
			if err != nil {
				return nil, err
			}
			return key, nil
		}
	}
	return nil, errors.New("Cannot find key with kid")
}

// validateClientAccessToken validates access token created above
func validateClientAccessToken(accessToken string) (map[string]interface{}, error) {
	tok, err := jwt.ParseSigned(accessToken)
	if err != nil {
		return nil, errors.New("Fail parsing access token")
	}

	keys, err := getKey()
	if err != nil {
		return nil, err
	}
	key := jose.JSONWebKey{}
	key.UnmarshalJSON(keys)

	token := make(map[string]interface{})
	if err := tok.Claims(key, &token); err != nil {
		return nil, errors.New("Fail parsing access token to claims")
	}

	// Check that the aud is our API KEY ID
	apiKeyID, ok := token["aud"].(string)
	if !ok {
		return nil, errors.New("fail asserting aud from jwt.MapClaims")
	}
	if apiKeyID != API_KEY_ID {
		return nil, errors.New("Invalid aud, not meant for this api key id")
	}

	return token, nil
}

// This middleware assumes that your API endpoint looks like this:
// GET https://something.com/user/1
// Authorization: Bearer <access_token> 👈  We want to verify this

// CotterAuth is used to authenticate cotter access_tokens
func CotterAuth(next echo.HandlerFunc) echo.HandlerFunc {
	return func(ctx echo.Context) error {
		// get the access token from auth header
		reqToken := ctx.Request().Header.Get("Authorization")
		splitToken := strings.Split(reqToken, " ")
		if len(splitToken) != 2 {
			return errors.New("authorization header malformed")
		}
		tokenString := splitToken[1]

		// Validate that the access token and signature is valid
		token, err := validateClientAccessToken(tokenString)
		if err != nil {
			return echo.NewHTTPError(http.StatusBadRequest, err.Error())
		}

		// Read other claims, such as sub = Cotter User ID
		uIDStr, ok := token["sub"].(string)
		if !ok {
			return echo.NewHTTPError(http.StatusBadRequest, "fail asserting sub from jwt.MapClaims")
		}
		cotterUserID, err := uuid.Parse(uIDStr)
		if err != nil {
			return echo.NewHTTPError(http.StatusBadRequest, "invalid user id format in jwt claims")
		}
		fmt.Println("User logging in = ", cotterUserID)

		return next(ctx)
	}
}

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

Public Keys:

https://echo.labstack.com/cookbook/jwt
http://www.passportjs.org/
Verify JWT Token using API (serverless)
here
Check the list of third party libraries here
https://www.cotter.app/api/v0/token/jwks