---
title: "Verifying tokens — JWT for Laravel | Roundly"
description: "Verify RS256 user tokens offline with only the public key, read claims through typed accessors and handle failures."
url: https://roundly-consulting.com/open-source/docs/jwt-for-laravel/verifying-tokens
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[JWT for Laravel](https://roundly-consulting.com/open-source/docs/jwt-for-laravel.md)

# Verifying tokens

Any app holding the public key verifies user tokens offline — no database lookup and no call back to the issuer:

```php
$claims = Jwt::verify($jwt);   // or app(UserTokenVerifier::class)->verify($jwt)
$claims->string('sub');
$claims->list('permissions');

Jwt::verify($jwt, 'app-clients');      // pin another audience for this call
Jwt::guard('clients')->verify($jwt);   // …or the audience of a configured guard
```

verify() checks the signature, exp, nbf and iat within the configured leeway, and pins iss and aud. Scope, denylist and token-version checks belong to the guard, not verify(). An explicit empty audience throws JwtMisconfigured.

## Typed claim accessors

Claims is an immutable bag produced only after the signature and time checks pass:

```php
$claims->has('org');            // bool
$claims->get('org');            // mixed, null when absent
$claims->require('org');        // mixed, throws ClaimMismatch when absent
$claims->string('sub');         // string
$claims->int('exp');            // int
$claims->list('permissions');   // list<string>
$claims->all();                 // array<string, mixed>
```

| Method | Returns | Behaviour |
| --- | --- | --- |
| `has($name)` | bool | Whether the claim is present. |
| `get($name)` | mixed | The value, or null when absent. |
| `require($name)` | mixed | Throws ClaimMismatch when absent. |
| `string($name)` | string | Throws ClaimMismatch when absent or not a string. |
| `int($name)` | int | Accepts whole floats such as 1.0; throws ClaimMismatch on fractional or non-numeric values and on a whole float outside the 64-bit integer range (INF, 1e19), which a cast would wrap. |
| `list($name)` | list<string> | Throws unless the claim is a list of strings. |
| `sessionId()` | ?string | The sid claim, or null when absent. |
| `authMethods()` | list<string> | The amr claim, or \[\] when absent. |
| `authTime()` | ?int | The auth\_time claim, or null when absent. |
| `all()` | array | The whole claim map. |

## Handling failures

Invalid tokens throw a JwtException subclass. Minting a claim set that can’t be JSON-encoded throws UnencodableClaims — also a JwtException — so one catch covers both minting and verifying:

```php
use RoundlyConsulting\Jwt\Facades\Jwt;
use RoundlyConsulting\Jwt\Jose\Exceptions\JwtException;
use RoundlyConsulting\Jwt\Jose\Exceptions\TokenExpired;

try {
    $claims = Jwt::verify($jwt);
} catch (TokenExpired $e) {
    // Prompt a re-login / refresh flow.
} catch (JwtException $e) {
    // Any other token failure: 401.
}
```

A failed Jwt::verify() dispatches TokenVerificationFailed before rethrowing. See Exceptions for the full hierarchy.

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/jwt-for-laravel.md)
