Paid Feature
This is a paid feature.
You can click on the Enable Paid Features button on our dashboard, and follow the steps from there on. Once enabled, this feature is free on the provided development environment.
Verify Tokens
There are two ways with which you can verify an OAuth2 Access Token:
- By using a standard JWT, JSON Web Token, verification library
- By calling the special introspection endpoint that we expose through the core service
We will explain when you should use each method in the sections below.
One thing to note is that, besides the standard OAuth2 token claims, our implementation includes an additional one called stt
.
This stands for SuperTokens Token Type
.
It is used to make sure that the validation is performed for the correct token type:
0
represents a SuperTokens Session Access Token1
represents an OAuth2 Access Token2
represents an OAuth2 ID Token.
info
The following guide covers only OAuth2 Tokens verification. For information on how to verify Supertokens Session Tokens please refer to the following section.
#
Validation Method#
Using a generic JWT verification libraryThis is the standard validation method and should be used for most of the operations that need to be protected. It will tell you that the token is valid from a cryptographic standpoint and that it has not expired.
The code samples show you a basic validation scenario where we check if the token has the required scope in order for an action to be performed.
info
If your scenario involves a common backend with multiple frontend clients you can drop the client_id
check.
- Single app setup
- Multi app setup
- NodeJS
- GoLang
- Python
- Java
- PHP
- C#
For NodeJS you can use jose to verify the token.
import jose from "jose";
const JWKS = jose.createRemoteJWKSet(new URL('<YOUR_API_DOMAIN>/authjwt/jwks.json'))
// Follow this example if you are using the Authorization Code Flow
async function validateToken(jwt: string) {
const requiredScope = "<YOUR_REQUIRED_SCOPE>";
const clientId = '<CLIENT_ID>';
try {
const { payload } = await jose.jwtVerify(jwt, JWKS, {
requiredClaims: ['stt', 'scp', 'client_id'],
});
if(payload.stt !== 1) return false;
if(payload.client_id !== clientId) return false;
const scopes = payload.scp as string[];
return scopes.includes(requiredScope);
} catch (err) {
return false;
}
}
You can use the jwx library to verify the token.
import (
"context"
"fmt"
"github.com/lestrrat-go/jwx/jwt"
"github.com/lestrrat-go/jwx/jwk"
)
func ValidateToken(token string) bool {
apiDomain := "<YOUR_API_DOMAIN>"
apiBasePath := "/auth"
clientId := "<CLIENT_ID>"
requiredScope := "<REQUIRED_SCOPE>"
jwksURL := fmt.Sprintf("%s%sjwt/jwks.json", apiDomain, apiBasePath)
jwks, err := jwk.Fetch(context.Background(), jwksURL)
if err != nil {
return false
}
parsedToken, err := jwt.Parse(
[]byte(token),
jwt.WithKeySet(jwks),
jwt.WithClaimValue("stt", 1),
jwt.WithClaimValue("client_id", clientId),
)
if err != nil {
return false
}
scp, ok := parsedToken.Get("scp")
if !ok {
return false
}
scopes, ok := scp.([]interface{})
if !ok {
return false
}
for _, scope := range scopes {
if scope, ok := scope.(string); ok && scope == requiredScope {
return true
}
}
return false;
}
You can use the PyJWT library to verify the token.
from typing import Optional, List
import requests
import jwt
from jwt import PyJWKClient
def validate_token(token: str) -> bool:
api_domain = "<YOUR_API_DOMAIN>"
api_base_path = "/auth"
client_id = "<CLIENT_ID>"
required_scope = "<YOUR_REQUIRED_SCOPE>"
jwks_url = f"{api_domain}{api_base_path}jwt/jwks.json"
jwks_client = PyJWKClient(jwks_url)
try:
signing_key = jwks_client.get_signing_key_from_jwt(token)
decoded = jwt.decode(
token,
signing_key.key,
algorithms=['RS256'],
audience=audience,
options={"require": ["stt", "client_id", "scp"]}
)
stt: Optional[int] = decoded.get('stt')
if stt != 1:
return False
token_client_id: Optional[str] = decoded.get('client_id', None)
if client_id != token_client_id:
return False
scopes: List[str] = decoded.get('scp', [])
if not isinstance(scopes, list) or required_scope not in scopes:
return False
return True
except Exception as e:
return False
You can use the Firebase JWT library to verify the token.
require 'vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
function validateToken($jwt) {
$apiDomain = "<YOUR_API_DOMAIN>";
$apiBasePath = "/auth";
$jwksUrl = $apiDomain . $apiBasePath . '/jwt/jwks.json';
$requiredScope = "<YOUR_REQUIRED_SCOPE>";
$clientId = "<CLIENT_ID>";
$jwks = json_decode(file_get_contents($jwksUrl), true);
try {
$decoded = JWT::decode($jwt, JWK::parseKeySet($jwks), 'RS256'));
if ($decoded->sst !== 1) {
return false;
}
if ($decoded->client_id !== $clientId) {
return false;
}
return in_array($requiredScope, $decoded->scp);
} catch (Exception $e) {
return false;
}
}
You can use the Auth0 JWT library to verify the token. Here is an example of how you can do that:
import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.auth0.jwt.JWTVerifier.Base;
import com.auth0.jwt.algorithms.Algorithm;
import java.net.URL;
import java.util.Map;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
public class JWTVerifier {
private static final String JWKS_URL = "<YOUR_API_DOMAIN>/authjwt/jwks.json";
private static final String CLIENT_ID = "<CLIENT_ID>";
private static Map<String, Object> fetchJWKS() throws Exception {
URL url = new URL(JWKS_URL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
InputStream responseStream = connection.getInputStream();
Scanner scanner = new Scanner(responseStream, StandardCharsets.UTF_8.name());
String responseBody = scanner.useDelimiter("\\A").next();
scanner.close();
return JWT.decode(responseBody).getHeader();
}
public static boolean validateToken(String token) {
try {
Map<String, Object> jwks = fetchJWKS();
Algorithm algorithm = Algorithm.RSA256(jwks.get("x5c"), null);
JWTVerifier verifier = JWT.require(algorithm)
.build();
DecodedJWT jwt = verifier.verify(token);
if(jwt.getClaim("sst").asInt() != 1) {
return false;
}
if(jwt.getClaim("client_id").asString() != CLIENT_ID) {
return false;
}
List<String> scopes = jwt.getClaim("scp").asList();
return scopes.contains(requiredScope);
} catch (Exception e) {
return false;
}
}
}
You can use the IdentityModel library to verify the token.
using System;
using System.Linq;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
class AuthorizationCodeTokenValidator
{
static async Task<bool> ValidateToken(string jwtStr)
{
string apiDomain = "<YOUR_API_DOMAIN>";
string apiBasePath = "/auth";
string clientId = "<CLIENT_ID>";
string requiredScope = "<REQUIRED_SCOPE>";
HttpClient client = new HttpClient();
var response = await client.GetStringAsync($"{apiDomain}{apiBasePath}jwt/jwks.json");
var jwks = new JsonWebKeySet(response);
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = new TokenValidationParameters
{
IssuerSigningKeys = jwks.Keys
};
try
{
SecurityToken validatedToken;
var principal = tokenHandler.ValidateToken(jwtStr, validationParameters, out validatedToken);
var claims = principal.Claims.ToDictionary(c => c.Type, c => c.Value);
if (!claims.ContainsKey("stt") || claims["stt"] != "1")
{
return false;
}
if (!claims.ContainsKey("client_id") || claims["client_id"] != clientId)
{
return false;
}
var scopes = claims["scp"].Split(" ");
if (!scopes.Contains(requiredScope))
{
return false;
}
return true;
}
catch (Exception)
{
return false;
}
}
}
#
Email VerificationIf you are using email and password based authentication, and you want to validate if the user has verified their email, you will have to check if the email_verified
claim is true.
#
Calling the Token Introspection APIWhen a user logs out, their token will be removed from the SuperTokens Core database. That change will not reflect in token validation process that uses a JWT verification library. The token will stil be valid until its expiration time.
In order to completely make sure that the token has not been removed you can directly call the SuperTokens Core service. We recommend that you perform this process for high security operations, in order to avoid the risk of a token being used by a malicious agent.
Here is an example of how you can use this validation method:
#
By Directly Calling the Introspection Endpoint# The response status should be 200
# and the body should have an "active" property set to true
curl -X POST \
-H "Content-Type: application/json" \
-d '{"token": "<YOUR_TOKEN>"}' \
"<YOUR_API_DOMAIN>/auth/oauth/introspect"
#
By Using the SDK MethodIf you are using a NodeJS backend you can directly call the SDK.
import OAuth2Provider from "supertokens-node/recipe/oauth2provider";
async function validateToken(token: string) {
const { status } = await OAuth2Provider.validateOAuth2AccessToken(
token,
{
clientId: "<CLIENT_ID>",
scopes: ["<REQUIRED_SCOPE>"],
},
true
);
return status === "OK";
}
- NodeJS
- GoLang
- Python
- Java
- PHP
- C#
For NodeJS you can use jose to verify the token.
import jose from "jose";
const JWKS = jose.createRemoteJWKSet(new URL('<YOUR_API_DOMAIN>/authjwt/jwks.json'))
// Follow this example if you are using the Authorization Code Flow
async function validateToken(jwt: string) {
const requiredScope = "<YOUR_REQUIRED_SCOPE>";
const clientId = '<CLIENT_ID>';
try {
const { payload } = await jose.jwtVerify(jwt, JWKS, {
requiredClaims: ['stt', 'scp', 'client_id'],
});
if(payload.stt !== 1) return false;
if(payload.client_id !== clientId) return false;
const scopes = payload.scp as string[];
return scopes.includes(requiredScope);
} catch (err) {
return false;
}
}
You can use the jwx library to verify the token.
import (
"context"
"fmt"
"github.com/lestrrat-go/jwx/jwt"
"github.com/lestrrat-go/jwx/jwk"
)
func ValidateToken(token string) bool {
apiDomain := "<YOUR_API_DOMAIN>"
apiBasePath := "/auth"
clientId := "<CLIENT_ID>"
requiredScope := "<REQUIRED_SCOPE>"
jwksURL := fmt.Sprintf("%s%sjwt/jwks.json", apiDomain, apiBasePath)
jwks, err := jwk.Fetch(context.Background(), jwksURL)
if err != nil {
return false
}
parsedToken, err := jwt.Parse(
[]byte(token),
jwt.WithKeySet(jwks),
jwt.WithClaimValue("stt", 1),
jwt.WithClaimValue("client_id", clientId),
)
if err != nil {
return false
}
scp, ok := parsedToken.Get("scp")
if !ok {
return false
}
scopes, ok := scp.([]interface{})
if !ok {
return false
}
for _, scope := range scopes {
if scope, ok := scope.(string); ok && scope == requiredScope {
return true
}
}
return false;
}
You can use the PyJWT library to verify the token.
from typing import Optional, List
import requests
import jwt
from jwt import PyJWKClient
def validate_token(token: str) -> bool:
api_domain = "<YOUR_API_DOMAIN>"
api_base_path = "/auth"
client_id = "<CLIENT_ID>"
required_scope = "<YOUR_REQUIRED_SCOPE>"
jwks_url = f"{api_domain}{api_base_path}jwt/jwks.json"
jwks_client = PyJWKClient(jwks_url)
try:
signing_key = jwks_client.get_signing_key_from_jwt(token)
decoded = jwt.decode(
token,
signing_key.key,
algorithms=['RS256'],
audience=audience,
options={"require": ["stt", "client_id", "scp"]}
)
stt: Optional[int] = decoded.get('stt')
if stt != 1:
return False
token_client_id: Optional[str] = decoded.get('client_id', None)
if client_id != token_client_id:
return False
scopes: List[str] = decoded.get('scp', [])
if not isinstance(scopes, list) or required_scope not in scopes:
return False
return True
except Exception as e:
return False
You can use the Firebase JWT library to verify the token.
require 'vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
function validateToken($jwt) {
$apiDomain = "<YOUR_API_DOMAIN>";
$apiBasePath = "/auth";
$jwksUrl = $apiDomain . $apiBasePath . '/jwt/jwks.json';
$requiredScope = "<YOUR_REQUIRED_SCOPE>";
$clientId = "<CLIENT_ID>";
$jwks = json_decode(file_get_contents($jwksUrl), true);
try {
$decoded = JWT::decode($jwt, JWK::parseKeySet($jwks), 'RS256'));
if ($decoded->sst !== 1) {
return false;
}
if ($decoded->client_id !== $clientId) {
return false;
}
return in_array($requiredScope, $decoded->scp);
} catch (Exception $e) {
return false;
}
}
You can use the Auth0 JWT library to verify the token. Here is an example of how you can do that:
import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.auth0.jwt.JWTVerifier.Base;
import com.auth0.jwt.algorithms.Algorithm;
import java.net.URL;
import java.util.Map;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
public class JWTVerifier {
private static final String JWKS_URL = "<YOUR_API_DOMAIN>/authjwt/jwks.json";
private static final String CLIENT_ID = "<CLIENT_ID>";
private static Map<String, Object> fetchJWKS() throws Exception {
URL url = new URL(JWKS_URL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
InputStream responseStream = connection.getInputStream();
Scanner scanner = new Scanner(responseStream, StandardCharsets.UTF_8.name());
String responseBody = scanner.useDelimiter("\\A").next();
scanner.close();
return JWT.decode(responseBody).getHeader();
}
public static boolean validateToken(String token) {
try {
Map<String, Object> jwks = fetchJWKS();
Algorithm algorithm = Algorithm.RSA256(jwks.get("x5c"), null);
JWTVerifier verifier = JWT.require(algorithm)
.build();
DecodedJWT jwt = verifier.verify(token);
if(jwt.getClaim("sst").asInt() != 1) {
return false;
}
if(jwt.getClaim("client_id").asString() != CLIENT_ID) {
return false;
}
List<String> scopes = jwt.getClaim("scp").asList();
return scopes.contains(requiredScope);
} catch (Exception e) {
return false;
}
}
}
You can use the IdentityModel library to verify the token.
using System;
using System.Linq;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
class AuthorizationCodeTokenValidator
{
static async Task<bool> ValidateToken(string jwtStr)
{
string apiDomain = "<YOUR_API_DOMAIN>";
string apiBasePath = "/auth";
string clientId = "<CLIENT_ID>";
string requiredScope = "<REQUIRED_SCOPE>";
HttpClient client = new HttpClient();
var response = await client.GetStringAsync($"{apiDomain}{apiBasePath}jwt/jwks.json");
var jwks = new JsonWebKeySet(response);
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = new TokenValidationParameters
{
IssuerSigningKeys = jwks.Keys
};
try
{
SecurityToken validatedToken;
var principal = tokenHandler.ValidateToken(jwtStr, validationParameters, out validatedToken);
var claims = principal.Claims.ToDictionary(c => c.Type, c => c.Value);
if (!claims.ContainsKey("stt") || claims["stt"] != "1")
{
return false;
}
if (!claims.ContainsKey("client_id") || claims["client_id"] != clientId)
{
return false;
}
var scopes = claims["scp"].Split(" ");
if (!scopes.Contains(requiredScope))
{
return false;
}
return true;
}
catch (Exception)
{
return false;
}
}
}
#
Email VerificationIf you are using email and password based authentication, and you want to validate if the user has verified their email, you will have to check if the email_verified
claim is true.
#
Calling the Token Introspection APIWhen a user logs out, their token will be removed from the SuperTokens Core database. That change will not reflect in token validation process that uses a JWT verification library. The token will stil be valid until its expiration time.
In order to completely make sure that the token has not been removed you can directly call the SuperTokens Core service. We recommend that you perform this process for high security operations, in order to avoid the risk of a token being used by a malicious agent.
Here is an example of how you can use this validation method:
#
By Directly Calling the Introspection Endpoint# The response status should be 200
# and the body should have an "active" property set to true
curl -X POST \
-H "Content-Type: application/json" \
-d '{"token": "<YOUR_TOKEN>"}' \
"<YOUR_API_DOMAIN>/auth/oauth/introspect"
#
By Using the SDK MethodIf you are using a NodeJS backend you can directly call the SDK.
import OAuth2Provider from "supertokens-node/recipe/oauth2provider";
async function validateToken(token: string) {
const { status } = await OAuth2Provider.validateOAuth2AccessToken(
token,
{
clientId: "<CLIENT_ID>",
scopes: ["<REQUIRED_SCOPE>"],
},
true
);
return status === "OK";
}