bcrypt stores the password as a slow salted hash so attackers can’t recover it from a stolen database. JWT (signed with HS256) is a stateless, signed token that the server hands back to the client after a correct bcrypt compare; subsequent requests just include the JWT, so the server doesn’t need a session store. The token carries sub, scopes, role, exp, signed with a secret only the server knows.
Login: bcrypt.checkpw(submitted, user.password_hash); if True, mint JWT with sub=user.id and scopes.
Verify: get_current_user decodes header, validates signature and exp, raises 401 if invalid.
Refresh: rotate exp or issue a new token; never trust client-supplied claims.
Login: email+password -> bcrypt compare -> sign JWT -> return token
API call: Bearer JWT -> verify sig + exp -> load user -> route
bcrypt keeps the credential safe at rest; JWT keeps the session cheap and stateless — together they cover auth without a database session table.