Title options:
- How to test Descope auth without real keys
- How to test Descope passwordless auth without a project
Descope is easy to start with, but the part I would test first is not the OTP screen or the magic-link email.
It is what your app does with the session JWT after Descope returns it.
FetchSandbox has a Descope sandbox for the flows most apps wire up first:
- email OTP signup
- email magic-link sign-in
- session refresh
- agent/access-key exchange
No real Descope project. No real keys. No inbox setup.
The useful part is that the sandbox models the failure cases too: wrong OTP, expired session, replayed magic link, and the big one: accepting a forged session JWT because the app decoded it instead of verifying it.
Here is the concrete flow I would test.
- Send a magic link:
POST /v1/auth/magiclink/signin/email
- Verify the one-time token:
POST /v1/auth/magiclink/verify
The sandbox returns the shape your app should care about:
{
"sessionJwt": "...",
"refreshJwt": "...",
"user": {
"userId": "U2..."
}
}
- Call your protected route with the
sessionJwt.
This is where the bug usually lives.
Bad handlers do something like:
const claims = jwt.decode(sessionJwt);
if (claims.role === "admin") allow();
That accepts whatever claims are inside the token. A forged alg: none token can become role=admin if the handler never checks the signature.
The fixed version verifies the session JWT against Descope's JWKS, or uses Descope's SDK validation path. It should reject alg: none, reject bad signatures, cache JWKS, and refresh on kid mismatch.
The sandbox route for this bug is meant to prove that difference:
- forged unsigned token ->
401 - valid signed session token ->
200
That is the whole point. Do not just test "magic link works." Test that the session you trust is actually verified.
You can also test refresh behavior:
POST /v1/auth/refresh
GET /v1/auth/me
If refresh returns 401, send the user back through sign-in. Do not keep rendering authenticated UI from a decoded-but-unverified token.
Docs are here if you want the exact Descope sandbox flows: https://fetchsandbox.com/docs/descope
Top comments (0)