Security

    JWT handling in mobile apps: doing it securely

    A 2026 view of a JWT stored in the Keychain with a short expiry, its base64 payload shown as readable, and the server validating the signature on every request

    JSON Web Tokens are everywhere in mobile authentication, and they invite a few specific mistakes. The two that come up most: storing the token in insecure storage where it can be read off the device, and assuming the token's payload is private when it is just base64, readable by anyone. Add long-lived tokens and trusting the token on the client, and you have the common JWT failure pattern. None of it is hard to avoid once you know it. Here is what a JWT is, the mistakes mobile apps make, and how to handle them securely.

    Short answer

    A JSON Web Token (JWT) is a signed token, header, payload, and signature, used to authenticate requests, and mobile apps mishandle it in predictable ways. Per OWASP MASVS, the key points are: store JWTs in the Keychain or Keystore, not in UserDefaults, SharedPreferences, or other plain storage; treat the payload as readable, since it is base64-encoded, not encrypted, so never put secrets in it; keep access tokens short-lived with refresh; and never let the client be the authority, because the server must validate the token's signature on every request. Used this way, a JWT is a fine session mechanism; misused, it leaks or is trusted when it should not be.

    What you should know

    • Store JWTs securely: in the Keychain or Keystore, not plain storage.
    • The payload is not secret: it is base64-encoded, readable by anyone.
    • Keep tokens short-lived: short access-token expiry with refresh.
    • The server validates: it must verify the signature on every request.
    • The client is not the authority: do not make security decisions on an unverified token.

    What is a JWT, and what is the trap?

    A JWT is a compact, signed token whose contents are encoded, not encrypted. It has three parts, a header, a payload of claims, and a signature, joined and base64url-encoded, and the signature lets a server verify the token was issued by it and not tampered with. The trap is the encoding: because the payload is base64, not encrypted, anyone who has the token can decode and read its claims, so a JWT does not hide what is inside it. The signature provides integrity and authenticity, proof the token is genuine and unaltered, but not confidentiality. So the two things to internalize are that the payload is readable by anyone holding the token, and that the security comes from the server verifying the signature, not from the client trusting the token's contents.

    What are the common JWT mistakes in mobile apps?

    A handful of recurring errors. The table lists them.

    MistakeWhy it is a problem
    Storing the token in plain storageUserDefaults or SharedPreferences can be read off the device
    Putting sensitive data in the payloadThe payload is base64, readable by anyone with the token
    Long-lived access tokensA stolen token stays valid for a long time
    Trusting the token on the clientA client decision can be bypassed; the server must verify
    Not validating the signature server-sideAn unverified token can be forged or altered

    The most common is insecure storage: a JWT in UserDefaults, SharedPreferences, or web storage can be extracted on a compromised or backed-up device, so it belongs in the Keychain or Keystore. Close behind is putting sensitive data in the payload, which is exposed because the payload is merely encoded, and trusting the token on the client to gate access, when only the server validating the signature actually enforces anything.

    How do you handle JWTs securely?

    Store them securely, keep them short, and let the server be the authority. Store access and refresh tokens in the Keychain or Keystore so they are not readable from plain storage, and transmit them only over HTTPS. Keep access tokens short-lived and use a refresh token to obtain new ones, so a stolen access token has a small window, and store and rotate the refresh token carefully. Put no sensitive data in the payload, since it is readable, only the claims needed for authorization. On the server, validate the signature on every request, check expiry and the expected claims, and never accept a token without verifying it, which is also where you guard against signature-algorithm tricks. And do not have the client make security decisions based on the token's contents alone; the client can read claims for display, but enforcement happens server-side. The token is a credential to protect and verify, not a source of truth the client can trust.

    What to watch out for

    The first trap is storing the JWT in UserDefaults, SharedPreferences, or AsyncStorage, where it can be extracted; use the Keychain or Keystore. The second is putting sensitive data in the payload, which is base64-encoded and readable by anyone holding the token. The third is the client trusting the token to grant access, when only server-side signature validation enforces it. A pre-submission scan such as PTKD.com (https://ptkd.com) reads the compiled APK, AAB, or IPA against OWASP MASVS and surfaces insecure token storage and how the app handles sensitive data, so you can confirm JWTs are stored securely before you ship. Validating signatures and keeping tokens short-lived is work on the token issuer and your server.

    What to take away

    • A JWT is a signed token whose payload is base64-encoded, not encrypted, so anyone with the token can read its claims, while the signature provides integrity, not confidentiality.
    • Store JWTs in the Keychain or Keystore, not plain storage, transmit them over HTTPS, and keep access tokens short-lived with refresh.
    • Put no sensitive data in the payload, and never let the client be the authority; the server must validate the signature on every request.
    • Use a pre-submission scan such as PTKD.com to confirm tokens are stored securely rather than in plain storage.
    • #jwt
    • #tokens
    • #authentication
    • #secure-storage
    • #owasp-masvs
    • #app-security
    • #mobile

    Frequently asked questions

    Is a JWT payload encrypted?
    No. A JWT's payload is base64url-encoded, not encrypted, so anyone who holds the token can decode and read its claims. The signature provides integrity and authenticity, proof the token is genuine and unaltered, but not confidentiality. So never put sensitive data in the payload, and remember that a JWT does not hide its contents from whoever possesses it. The security of a JWT comes from the server verifying the signature, not from the payload being secret.
    Where should I store a JWT in a mobile app?
    In the Keychain on iOS or the Keystore-backed secure storage on Android, not in UserDefaults, SharedPreferences, AsyncStorage, or web storage, which can be read off a compromised or backed-up device. Transmit tokens only over HTTPS. Storing a JWT in plain storage is the most common mobile JWT mistake, because it leaves the credential extractable. Treat both the access and refresh tokens as secrets that belong in secure storage.
    What are common JWT mistakes in mobile apps?
    Storing the token in plain storage where it can be extracted; putting sensitive data in the payload, which is readable because it is only base64-encoded; using long-lived access tokens that stay valid if stolen; trusting the token on the client to gate access; and not validating the signature server-side. The fixes are secure storage, no secrets in the payload, short-lived tokens with refresh, server-side validation on every request, and treating the client as untrusted.
    Can the client trust a JWT to make access decisions?
    No. The client can read the token's claims for display, but it must not be the authority for access control, because a client-side check can be bypassed on a device the attacker controls. Only the server validating the token's signature and claims on each request actually enforces anything. So decode the JWT on the client if you need to show the user's name or role, but enforce permissions server-side, where the signature is verified and the token cannot be forged.
    How do I confirm my app stores tokens securely?
    Scan the build. A pre-submission scan such as PTKD.com reads the compiled APK, AAB, or IPA against OWASP MASVS and surfaces insecure token storage and how the app handles sensitive data, so you can confirm JWTs are kept in the Keychain or Keystore rather than plain storage before you ship. If it finds a token in UserDefaults or SharedPreferences, the fix is to move it to secure storage, and to keep access tokens short-lived with refresh.

    Keep reading

    Scan your app in minutes

    Upload an APK, AAB, or IPA. PTKD returns an OWASP-aligned report with copy-paste fixes.

    Try PTKD free