feat(oauth): add StatelessOAuthStateStore using JWT#1916
Conversation
Implements a stateless OAuth state store that embeds expiry into a signed JWT (HS256/HMAC-SHA256) so no persistent storage is required. Resolves slackapi#1823.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1916 +/- ##
==========================================
+ Coverage 84.15% 84.16% +0.01%
==========================================
Files 117 118 +1
Lines 13366 13423 +57
==========================================
+ Hits 11248 11298 +50
- Misses 2118 2125 +7 ☔ View full report in Codecov by Harness. |
|
Thanks for this contribution! The implementation looks good and we've confirmed it works end-to-end with Bolt's OAuth flow. One thing to consider might be to use compact JSON serialization. Per RFC 7515, JWT serialization should be compact (no spaces). This also produces shorter tokens: # Current
json.dumps({"alg": "HS256", "typ": "JWT"}).encode()
# Fix
json.dumps({"alg": "HS256", "typ": "JWT"}, separators=(",",
":")).encode()Same for the payload |
…tatelessOAuthStateStore
Great point, thanks for the RFC 7515 reference! Added separators=(",", ":") to both json.dumps calls in issue() (header and payload) to produce proper compact JWT serialization. |
Implements a stateless OAuth state store that embeds expiry into a signed JWT (HS256/HMAC-SHA256) so no persistent storage is required.
Resolves #1823.
Summary
Adds
StatelessOAuthStateStoretoslack_sdk.oauth.state_store- a stateless OAuth state store that requires no persistent storage (no files, no database, no S3).Instead of saving the state somewhere, it encodes the expiry timestamp into a signed JWT (HS256) and returns that as the state string. On
consume(), it verifies the HMAC signature and checks the expiry, no I/O needed. Implemented entirely with Python stdlib (hmac,hashlib,base64,json), zero new dependencies.Trade-off: since there's no storage,
consume()is repeatable - the same valid token returnsTruewithin its expiry window, unlikeFileOAuthStateStorewhich deletes on first consume. This is an accepted limitation of stateless design, consistent with the Node SDK'sClearStateStore.Testing
./scripts/run_tests.sh tests/slack_sdk/oauth/state_store/test_stateless.pyStatelessOAuthStateStoreis importable:from slack_sdk.oauth.state_store import StatelessOAuthStateStoreTrue. Consume with a wrong secret or tampered token, should returnFalse.Category
/docs(Documents)/tutorial(PythOnBoardingBot tutorial)tests/integration_tests(Automated tests for this library)Requirements
python3 -m venv .venv && source .venv/bin/activate && ./scripts/run_validation.shafter making the changes.