Implementing JWTs in an app, one can use Flask-JWT-Extended, the popular Flask extension that simplifies JWT handling, covering token creation, verification, revocation, and refresh token management.
Here are the steps:
Step 1 set up python environment
Step 2 Create .env for storing secret key, use specialized package to generate this key, for example, Python’s secrets module (Recommended for new keys) or Using Python’s os.urandom.
JWT_SECRET_KEY="super-secret-jwt-key-change-this-in-production-seriously-long-random-string"
What happened is
Authentication (Using Username/Password)
- What happens: This is the traditional way to verify a user’s identity.
- The user provides their username and password (or other credentials like a biometric scan, etc.) to the server.
- The server takes these credentials and verifies them against its user database. This involves hashing the provided password and comparing it to the stored hash, ensuring they match.
- Purpose: To confirm “Who are you?” and “Are you really who you say you are?” This is the one time the server needs to know your secret password.
- Outcome (if successful): The server now trusts that you are a legitimate user.
Authorization & Credential Issuance (Using JWT)
Outcome: You now have a credential (the JWT) that you’ll use for future interactions.
What happens:After the server has successfully authenticated you using your username and password, it then issues you a JWT.
The server creates a special, signed token (the JWT) that contains information about you (like your user ID, roles, and when the token expires).
It sends this JWT back to your client (your web browser, mobile app, etc.).
Your client then stores this JWT (e.g., in localStorage in a web app, or secure storage in a mobile app).
Purpose: To give you a temporary, secure “access pass” so that for all your subsequent requests, the server can quickly verify “Are you allowed to do this?” without you having to re-enter your username and password every single time.
Tokens issued can be access or refresh token for different convenience.
Usually use the HS256 algorithm (by default) to sign the token, what does it mean?
What “Signing” Means for a JWT
A JWT actually has three parts, separated by dots: Header.Payload.Signature.
- Header: Contains metadata about the token itself, like the type of token (
JWT) and the signing algorithm being used (HS256,RS256, etc.). - Payload: Contains the actual claims (the information you want to convey, like
user_id,roles,exp,iat). - Signature: This is the cryptographic “seal” that verifies the token’s authenticity and integrity.
“Signing” is the process of generating this third part (the Signature). It’s a cryptographic operation that involves:
- Taking the Header (encoded).
- Taking the Payload (encoded).
- Using a secret key (or a private key in asymmetric cryptography).
- Applying a specific cryptographic algorithm (like HS256).
The result is a unique string (the Signature) that is highly sensitive to any changes in the Header, Payload, or the secret key itself.
How HS256 Specifically Signs a JWT
HS256 stands for HMAC-SHA256.
- HMAC: Stands for Hash-based Message Authentication Code. It’s a specific type of message authentication code (MAC) that involves a cryptographic hash function (like SHA-256) and a secret cryptographic key.
- SHA256: Stands for Secure Hash Algorithm 256. It’s a one-way cryptographic hash function that produces a 256-bit (32-byte) hash value.
Here’s the step-by-step process of how HS256 signs a JWT:
- Encode the Header:
- The JSON header
{"alg": "HS256", "typ": "JWT"}is converted into a Base64Url-encoded string. - Example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
- The JSON header
- Encode the Payload:
- The JSON payload
{"user_id": 123, "roles": ["user"], "exp": ..., "iat": ...}is converted into a Base64Url-encoded string. - Example:
eyJ1c2VyX2lkIjoxMjMsInJvbGVzIjpbInVzZXIiXSwiZXhwIjoxNjczMDc1NzIzLCJpYXQiOjE2NzMwNzIxMjN9
- The JSON payload
- Concatenate for Hashing:
- These two encoded strings are concatenated together with a dot in between them.
- This creates the “message” that will be signed:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsInJvbGVzIjpbInVzZXIiXSwiZXhwIjoxNjczMDc1NzIzLCJpYXQiOjE2NzMwNzIxMjN9
- Apply HMAC-SHA256 with the Secret Key:
- This concatenated string (the message) is then fed into the
HMAC-SHA256algorithm. - Crucially, your
JWT_SECRET_KEYis also fed into this algorithm as the secret key for the HMAC function. - The HMAC-SHA256 algorithm combines the message and the secret key to produce a unique hash value.
HMAC_SHA256(secret_key, Header_Encoded + "." + Payload_Encoded)
- This concatenated string (the message) is then fed into the
- Encode the Signature:
- The resulting hash value (the raw bytes of the signature) is then converted into a Base64Url-encoded string.
- This final Base64Url-encoded string is the Signature.
- Example:
S7UqL8Zk9Xw2Y_b0c_C_2m_Y_1m_F_9m_A_1m_B_2m_C_9m_D_3m_E_0m_F_4m_G_5m_H_6m_I_7m_J_8m_K_9m_L_0m_M_1m_N_2m_O_3m_P_4m_Q_5m_R_6m_S_7m_T_8m_U_9m_V_0m_W_1m_X_2m_Y_3m_Z_4m_a_5m_b_6m_c_7m_d_8m_e_9m_f_0m_g_1m_h_2m_i_3m_j_4m_k_5m_l_6m_m_7m_n_8m_o_9m_p_0m_q_1m_r_2m_s_3m_t_4m_u_5m_v_6m_w_7m_x_8m_y_9m_z(this is a placeholder, actual signatures look like random strings)
The Complete JWT
Finally, these three Base64Url-encoded parts are joined together with dots to form the complete JWT:
Base64Url(Header) . Base64Url(Payload) . Base64Url(Signature)
Why is this important? (Integrity and Authenticity)
- Integrity: If anyone (even slightly) modifies the Header or the Payload of a JWT, the server can detect it. When the server receives a JWT, it performs the exact same signing process on the received Header and Payload using its own
JWT_SECRET_KEY. If the newly computed signature does not match the Signature part of the received JWT, the token has been tampered with, and the server rejects it. - Authenticity: Only someone who knows the
JWT_SECRET_KEYcan produce a valid signature. Since only your server knows this secret (in the case of HS256), your server can be sure that a token with a valid signature genuinely originated from it.
So, HS256 (or any other signing algorithm) doesn’t “create” the entire token. It’s the cryptographic engine that generates the Signature part, which is essential for making the Header and Payload trustworthy and tamper-proof. The creation of the token (assembling the header, payload, and then signing it) is done by the JWT library (like PyJWT or Flask-JWT-Extended‘s create_access_token function) using your secret key.
Additionally, if you want to make the app easier for users to get access, OAuth Would Be Simpler, so user s don’t need to remember nor enter user name password over and over again, as long as they have Google or facebook account, they can verify user’s identity, issue you an id_token(JWT too) to confirm user’s identity.