File length: 8321 # Authentication - Unified Login - Verify tokens Source: https://supertokens.com/docs/authentication/unified-login/verify-tokens ## Overview You can verify an **OAuth2 Access Token** in two ways: - By using a standard **JWT**, JSON Web Token, verification library - By calling the special introspection endpoint that gets exposed through the core service One thing to note is that, besides the standard **OAuth2** token claims, the **Unified Login** implementation includes an additional one called `stt`. This stands for `SuperTokens Token Type`. It ensures that the validation occurs for the correct token type: - `0` represents a **SuperTokens Session Access Token** - `1` represents an **OAuth2 Access Token** - `2` represents an **OAuth2 ID Token**. :::caution no-title The following guide covers only **OAuth2 Tokens** verification. For information on how to verify **SuperTokens Session Tokens** please refer to the [following section](/docs/additional-verification/session-verification/protect-api-routes). ::: --- ## Using a JWT verification library This is the standard validation method and you should use it for most of the operations that need protection. It indicates 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 the token undergoes a check for the required scope for an action to occur. :::info If your scenario involves a common backend with multiple frontend clients you can drop the `client_id` check. ::: For NodeJS you can use [`jose`](https://github.com/panva/jose) to verify the token. ```tsx const JWKS = jose.createRemoteJWKSet(new URL('jwt/jwks.json')) // Follow this example if you are using the Authorization Code Flow async function validateToken(jwt: string) { const requiredScope = ""; const clientId = ''; 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`](https://github.com/lestrrat-go/jwx) library to verify the token. ```go return true } } return false; } ``` You can use the [PyJWT](https://github.com/jpadilla/pyjwt) library to verify the token. ```python from typing import Optional, List use Firebase\JWT\JWT; use Firebase\JWT\Key; function validateToken($jwt) { $apiDomain = ""; $apiBasePath = ""; $jwksUrl = $apiDomain . $apiBasePath . '/jwt/jwks.json'; $requiredScope = ""; $clientId = ""; $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](https://github.com/auth0/java-jwt) library to verify the token. Here is an example of how you can do that: ```java public class JWTVerifier { private static final String JWKS_URL = "jwt/jwks.json"; private static final String CLIENT_ID = ""; private static Map 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 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 scopes = jwt.getClaim("scp").asList(); return scopes.contains(requiredScope); } catch (Exception e) { return false; } } } ``` You can use the [IdentityModel](https://github.com/IdentityModel/IdentityModel) library to verify the token. ```csharp 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 ValidateToken(string jwtStr) { string apiDomain = ""; string apiBasePath = ""; string clientId = ""; string requiredScope = ""; HttpClient client = new HttpClient(); var response = await client.GetStringAsync($"//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 verification If you are using email and password based authentication, and you want to validate if the user has verified their email, you must check if the `email_verified` claim is true. --- ## Using the token introspection API When a user logs out, their token gets removed from the **SuperTokens Core** database. That change does not reflect in token validation process that uses a JWT verification library. The token remains valid until its expiration time. To ensure that the token remains valid, you can directly call the **SuperTokens Core** service. It is advisable to perform this process for high security operations to avoid the risk of a malicious agent using a token. Here is an example of how you can use this validation method: ```tsx async function validateToken(token: string) { const { status } = await OAuth2Provider.validateOAuth2AccessToken( token, { clientId: "", scopes: [""], }, true ); return status === "OK"; } ``` :::caution The Go SDK does not support creating OAuth2 providers. ::: ```python from supertokens_python.recipe.oauth2provider.interfaces import OAuth2TokenValidationRequirements