feat: migrate JWT from RSA encryption to HMAC signing with persistent…#83
Conversation
omatheusmesmo
left a comment
There was a problem hiding this comment.
Analysis Summary
This PR implements persistent JWT signing keys using HMAC-SHA256, addressing the issue where user sessions were invalidated after a server restart (#18). However, the transition from RSA (Asymmetric/JWE) to HMAC (Symmetric/JWS) represents a security regression for the project, but that's on me.
Note: Responsibility for Algorithm Choice
I must emphasize that the shift to HMAC was a direct result of my poorly written issue description in #18. By providing the option of "RSA or HMAC" without specifying that we should maintain the previous encryption standard (JWE), I led the implementation toward a simpler but less secure path. I take full responsibility for this lack of technical precision, which resulted in the removal of the robust RSA-based encryption we already had. I'm sorry.
Identified Issues
1. Loss of Confidentiality (JWE vs. JWS)
The previous implementation used JWE (JSON Web Encryption) with RSA. This ensured that the token's content (claims) was unreadable to third parties.
- Current State: With JWS (HMAC), the payload is only signed. Anyone can decode the Base64 and read sensitive data (e.g., usernames, IDs).
- Misnomer: The
decryptTokenmethod inJwtService.javais now a misnomer, as it only verifies the signature and does not perform decryption.
2. Symmetric vs. Asymmetric Security
- HMAC requires the same secret key to be present everywhere the token is validated. If this key is leaked, an attacker can forge valid tokens.
- RSA allows other services in the future to validate the token using only the Public Key, without ever having access to the Private Key (the token generator).
3. Thread-Safety and Lazy Initialization
The initSignerAndVerifier() implementation is not thread-safe. In a high-concurrency environment during startup, multiple requests might try to initialize the signer and verifier simultaneously in the Spring Singleton.
Proposed Improvement
The original RSA implementation was excellent; it only lacked decoupling key generation from memory. No major structural changes are needed—just pointing to external key files.
Suggested Path:
- Revert to RSA/JWE: Maintain the high security standard of encryption.
- External Key Loading: Use
.pemfiles (Public and Private Key) stored insrc/main/resources/certs/(or a path defined via environment variables). - Configuration via Properties: Use
@ConfigurationPropertiesto inject keys, eliminating the need for manual initialization andReflectionTestUtilsin tests. - Security: Add the certificate folder to
.gitignore.
Recommended References
- Implementation Guide (Spring Security 6 + RSA .pem): Implementing Spring Security 6 with OAuth and JWT
- Official Spring Security Documentation (JWT): OAuth2 Resource Server JWT
Conclusion: I suggest we maintain the RSA standard we already had, simply adjusting how keys are loaded by following persistence best practices.
|
🤖 Hi @felipemelozx, I've received your request, and I'm working on it now! You can track my progress in the logs for more details. |
|
🤖 I'm sorry @felipemelozx, but I was unable to process your request. Please see the logs for more details. |
omatheusmesmo
left a comment
There was a problem hiding this comment.
Excellent work, @felipemelozx — well done! 🎉
Pull Request Description
This PR replaces in-memory RSA key generation with persistent HMAC-SHA256 signing, ensuring JWT tokens remain valid across application restarts.
Related Task
What was done?
Tests
How to Test
export JWT_SECRET_KEY="your-secret-key-here"