Token Generator
A bearer token is a credential where possession alone grants access — no signature to verify, no identity to prove. That simplicity is the point and the risk: whoever holds it is authorised.
Sizing
For a token that must resist guessing, 128 bits of entropy is the working floor and 256 bits is comfortable. Hex encoding gives 4 bits per character, base64url gives 6:
| Bytes | Hex length | Base64url length | Entropy |
|---|---|---|---|
| 16 | 32 | 22 | 128 bits |
| 24 | 48 | 32 | 192 bits |
| 32 | 64 | 43 | 256 bits |
| 48 | 96 | 64 | 384 bits |
+ or /.Opaque Tokens Versus JWTs
| Opaque random token | JWT | |
|---|---|---|
| Contains data | No — it is a lookup key | Yes, readable by anyone |
| Verification | Database or cache lookup | Signature check, offline |
| Revocation | Immediate: delete the row | Hard; needs a denylist |
| Size | ~32–64 characters | Several hundred bytes |
| Leaks information | Nothing | Every claim inside it |
Short Expiry Plus Refresh
The standard arrangement pairs a short-lived access token with a longer-lived refresh token. A stolen access token is useful for minutes; the refresh token is stored more carefully, used rarely, and can be revoked centrally.
Rotating the refresh token on each use adds theft detection: if an old refresh token is presented, someone has a copy, and the whole family can be invalidated.
Handling
- Always over TLS. A bearer token in plaintext is a handed-over credential.
- Never in a URL — logs, history and
Refererheaders all retain them. - Hash before storing, exactly as with an API key.
- Compare in constant time, so response timing does not leak a prefix match.
- Bind to a context where you can — client, audience, scope — so a leaked token is less
The Avalanche Effect
A one-character change produces a completely different digest — not a similar one. That property is what makes a hash useful as a fingerprint:
| Input | MD5 | CRC32 |
|---|---|---|
hello | 5d41402abc4b2a76b9719d911017c592 | 3610a686 |
hello. | d94c10e437d18531e122ed0b45badd2a | 0a39d4f1 |
Hello | 8b1a9953c4611296a827abf8c47804d7 | f7d18982 |
hello and Hello differ by one bit of one byte, and share no part of their output.
RIPEMD-160 of hello is 108f07b8382412612c048d07d13f814118445acd, and of Hello is
d44426aca8ae0a69cdbc4021c64fa5ad68ca32fe — same story.Digest Length and Collision Resistance
| Algorithm | Output | Birthday bound | Status |
|---|---|---|---|
| CRC32 | 32 bits | ~77,000 values | Checksum only |
| MD5 | 128 bits | 2⁶⁴ in theory | Broken — collisions in seconds |
| SHA-1 | 160 bits | 2⁸⁰ in theory | Broken — SHAttered, 2017 |
| RIPEMD-160 | 160 bits | 2⁸⁰ | No practical attack |
| SHA-256 | 256 bits | 2¹²⁸ | Current standard |
| SHA-512 | 512 bits | 2²⁵⁶ | Standard, faster on 64-bit |
Never Hash a Password With These
A general-purpose hash is designed to be fast, which is exactly wrong for passwords: speed helps the attacker. Use a deliberately slow KDF — bcrypt, scrypt or Argon2id — with a per-password salt. A GPU tries billions of SHA-256 guesses a second and a few thousand bcrypt guesses a second, and that gap is the entire defence.