Errors
This SDK throws exactly one error type, and only from one method. Everything else fails closed by value, not by exception.
TokenVerificationError
Thrown (rejected) by IamClient.verifyToken when a token cannot be trusted.
import { TokenVerificationError } from '@padosoft/laravel-iam-react-native';
class TokenVerificationError extends Error {
readonly name = 'TokenVerificationError';
readonly reason: string; // short machine-ish cause, also embedded in message
// message: `token verification failed: ${reason}`
// constructed with { cause } where available (Error cause chaining)
}
| Member | Type | Description |
|---|---|---|
name |
'TokenVerificationError' |
Stable discriminator for instanceof / name checks. |
message |
string |
"token verification failed: <reason>". |
reason |
string |
The short cause (see below). |
cause |
unknown |
The underlying error (jose / fetch / JWKS), when present. |
When it’s raised
| Cause | Typical reason |
|---|---|
| No audience configured or passed | audience is required: set verify.audience … |
| Empty / non-string token | empty token |
Bad signature, wrong iss/aud, expired/not-yet-valid |
the underlying jose message |
| JWKS unreachable / non-2xx / malformed document | jwks: <detail> |
| Web Crypto unavailable on the runtime | the underlying crypto failure (see Hermes & Web Crypto) |
After a no-matching-key error, verifyToken first refetches the JWKS once (key rotation) and retries; it only rejects if the retry also fails.
Handling it — fail-closed
A rejection is the deny signal for authentication. The catch must lead to an unauthenticated/denied path — never to trusting the token.
import { TokenVerificationError } from '@padosoft/laravel-iam-react-native';
try {
const claims = await client.verifyToken(jwt);
return authenticate(claims);
} catch (e) {
if (e instanceof TokenVerificationError) {
console.warn('token rejected:', e.reason);
return signOut(); // fail-closed
}
throw e; // genuinely unexpected — rethrow
}
catch { use(decodeJwt(jwt)) } trusts a token you just failed to verify — a complete fail-open. The only correct catch leads to “unauthenticated”. See Fail-closed discipline.
Why the other methods don’t throw
check, can, and listResources never throw — every failure folds into a fail-closed value:
| Method | Failure value |
|---|---|
check |
a deny(reason) Decision (allowed: false) |
can |
false |
listResources |
[] |
ADR — why tokens reject but decisions don’t
Problem. Consistency would suggest all methods behave the same. Why does only verifyToken throw?
Decision. A decision has a safe value to return on failure (a deny), so returning it removes any exception a caller could mishandle into a fail-open. A token has no safe value — there are no trustworthy claims to hand back — so rejection is the unambiguous deny signal, and there’s nothing permissive to leak.
Consequences. You wrap verifyToken in try/catch; you never need to wrap check/can/listResources. Both shapes are fail-closed — by value where a safe value exists, by rejection where it doesn’t. See Fail-closed by design.
Construction errors (not TokenVerificationError)
The IamClient constructor throws plain Errors for misconfiguration — these are programmer errors surfaced loudly at startup, not runtime auth failures:
baseUrlmissing →IamClient: \baseUrl` is required`baseUrlnot absolute →IamClient: \baseUrl` must be an absolute URL …`- no
fetchavailable →IamClient: no \fetch` available …`
Catch these in tests/config validation; in production they should never fire past first run.
Next steps
- IamClient API —
verifyTokenand the construction guards. - Verifying tokens (JWKS) — the full verification flow.
- Hermes & Web Crypto — a common cause of rejections.