Building Secure JWT Authentication with Bcrypt and SHA-256
When designing a robust authentication system for modern applications, balancing security, performance, and scalability is crucial. One effective approach is combining JSON Web Tokens (JWT) with multi-session management, backed by Redis and a persistent database. In this post, we’ll explore a token management flow that uses both bcrypt and SHA-256 hashing to maximize security and efficiency.
Login Flow – Token Issuance and Session Metadata
When a user logs in, the system generates two tokens: a short-lived access token (typically valid for 15 minutes) and a long-lived refresh token (valid for about 30 days).
To securely manage these tokens, especially the refresh token which is sensitive and long-lived, the system applies two hashing strategies:
- The refresh token is hashed using bcrypt before being stored in the database. Bcrypt is a slow, adaptive hash that makes brute-force attacks computationally expensive. This protects refresh tokens in the event of a database breach.
- Simultaneously, the refresh token is hashed with SHA-256 to create a fast, deterministic key used in Redis. This allows the system to quickly look up session data in memory for rapid token validation.
Additional metadata like the client's User-Agent and IP address are also parsed and stored to enrich session information, helping with device recognition and security monitoring.
Where the Data Goes: Redis and Database
Redis – Fast In-Memory Lookup
Redis acts as a blazing-fast in-memory store to handle token validation and revocation efficiently.
- The key session:<sha256_refresh_token_hash> stores the latest access token for that session, with its TTL (time to live) matching the refresh token’s expiration.
- The access token itself maps back to the same session key, enabling quick revocation and validation from either token.
This bidirectional mapping between the hashed refresh token and the access token facilitates atomic and instant session management.
Database – Persistent Session Store
The database stores session records containing:
- A unique session identifier
- User ID linking the session to a user
- The bcrypt hashed refresh token (to protect the actual token)
- The SHA-256 hash of the refresh token (for lookup when Redis fails)
- Parsed User-Agent and IP location data for session context
- Expiry and timestamps (creation, revocation status)
This durable storage ensures sessions can be audited and recovered even if Redis is flushed or restarted.
Refresh Flow – Redis First, Database Fallback
When the client sends a refresh token to obtain a new access token, the backend first hashes the token using SHA-256 and attempts a Redis lookup with the key session:<sha256_refresh_token_hash>.
- If found and valid (not expired or revoked), the backend issues a new access token and updates Redis accordingly.
- If Redis misses the session (due to eviction or restart), the backend falls back to the database:
- It queries for a non-revoked session matching the SHA-256 hash of the refresh token.
- Then it securely verifies the refresh token by comparing the plain token with the bcrypt hash stored in the database.
- If valid and not expired, it issues a new access token and rehydrates Redis with the session data, restoring fast lookup capability.
This layered approach ensures both blazing-fast access token validation and reliable recovery in case of cache failure.
Logout & Revocation Flow – Clean Token Invalidations
When a user logs out or a session is compromised, the backend cleanly invalidates the session by:
- Using the access token to find the corresponding session:<sha256_refresh_token_hash> in Redis and deleting both the session key and the access token key.
- Marking the session as revoked in the database with a timestamp.
This ensures:
- Immediate session termination, preventing further use of tokens.
- Persistent session revocation that remains effective even if Redis data is lost.
Why This Architecture Works So Well
This combination of bcrypt and SHA-256 hashing, paired with Redis and a persistent database, offers numerous benefits:
- Multi-device support: Each session is uniquely tied to a device/browser via session metadata.
- Fast token validation: Redis provides O(1) time complexity for session lookups, enabling real-time authentication.
- Database fallback: Token refresh remains functional even if Redis is down or flushed.
- Security: Bcrypt protects refresh tokens from brute-force attacks in persistent storage, while SHA-256 enables efficient cache indexing.
- Atomic revocation: Access and refresh tokens can be revoked instantly and atomically.
- Automatic cleanup: Redis TTLs ensure memory isn’t clogged with stale sessions.
- Auditability: Full session logs and revocation status are stored in the database.
Revocation Chain in Action
When a token is revoked (via logout or compromise), the backend deletes the relevant Redis keys and updates the session in the database as revoked.
This prevents any further use of the refresh token and access token—even across system restarts—while keeping a traceable audit trail for security reviews.
Conclusion
By combining bcrypt and SHA-256 hashing with a layered Redis plus database architecture, this JWT multi-session pattern delivers a secure, scalable, and performant authentication foundation. It balances the need for strong security with operational efficiency, making it an ideal pattern for modern distributed systems.
If you’re building or refining your token-based authentication system, consider this approach to enhance both security and user experience.


