fix(lib): add verify_user_token helper with remote JWKS caching#45
Merged
jjantschulev merged 1 commit intonextfrom Apr 20, 2026
Merged
fix(lib): add verify_user_token helper with remote JWKS caching#45jjantschulev merged 1 commit intonextfrom
jjantschulev merged 1 commit intonextfrom
Conversation
Mirrors the TypeScript SDK's verify-user-token.ts so app-proxy-issued
x-whop-user-token JWTs can be verified in Python apps, with the same
key-rotation-friendly semantics (no SDK release needed when keys roll).
- src/whop_sdk/lib/verify_user_token.py (new):
* verify_user_token() — validates against the canonical Whop JWKS at
https://api.whop.com/.well-known/jwks.json by default, with a
module-level cache (12h TTL, 30s cooldown-guarded refetch on kid
miss) mirroring jose's createRemoteJWKSet.
* try_verify_user_token() — returns None on failure instead of raising.
* Accepts a raw token string OR a headers mapping (case-insensitive).
* Options mirror TS: public_key (PEM or JWK JSON static override),
jwks_url (override the endpoint), header_name, app_id.
* Handles legacy kid-less tokens by trying each key in the current
JWKS snapshot.
- src/whop_sdk/lib/__init__.py (new): marks lib/ as a package.
- pyproject.toml: adds optional user-tokens extra
pip install 'whop-sdk[user-tokens]'
which pulls in pyjwt[crypto]>=2.8,<3 for ES256 / ECDSA support.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
126ed07 to
4c06227
Compare
Merged
jjantschulev
added a commit
that referenced
this pull request
Apr 21, 2026
* codegen metadata * codegen metadata * codegen metadata * codegen metadata * fix: ensure file data are only sent as 1 parameter * codegen metadata * codegen metadata * codegen metadata * codegen metadata * feat(api): api update * codegen metadata * feat(api): api update * codegen metadata * fix(lib): add verify_user_token helper with remote JWKS caching (#45) Mirrors the TypeScript SDK's verify-user-token.ts so app-proxy-issued x-whop-user-token JWTs can be verified in Python apps, with the same key-rotation-friendly semantics (no SDK release needed when keys roll). - src/whop_sdk/lib/verify_user_token.py (new): * verify_user_token() — validates against the canonical Whop JWKS at https://api.whop.com/.well-known/jwks.json by default, with a module-level cache (12h TTL, 30s cooldown-guarded refetch on kid miss) mirroring jose's createRemoteJWKSet. * try_verify_user_token() — returns None on failure instead of raising. * Accepts a raw token string OR a headers mapping (case-insensitive). * Options mirror TS: public_key (PEM or JWK JSON static override), jwks_url (override the endpoint), header_name, app_id. * Handles legacy kid-less tokens by trying each key in the current JWKS snapshot. - src/whop_sdk/lib/__init__.py (new): marks lib/ as a package. - pyproject.toml: adds optional user-tokens extra pip install 'whop-sdk[user-tokens]' which pulls in pyjwt[crypto]>=2.8,<3 for ES256 / ECDSA support. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * codegen metadata * codegen metadata * feat(api): api update * fix(types): add BannerImage to forum_update_params (#46) forums.py resource references forum_update_params.BannerImage for the banner_image parameter on PATCH /forums/{id}, but the type file was missing the class and the banner_image field (a codegen-side artifact of the recent merge conflict resolution where forums.py was taken from the generated branch but forum_update_params.py was taken from the integrated branch). Both fields are in the live public API (verified against api.whop.com/openapi.json). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(api): api update * codegen metadata * codegen metadata * codegen metadata * codegen metadata * feat(api): api update * perf(client): optimize file structure copying in multipart requests * codegen metadata * codegen metadata * codegen metadata * fix(tests): pass required company_id to invoices.list() calls (#47) `invoices.list()` (resource) requires `company_id: str` (no Omit), but the bare `test_method_list` / `test_raw_response_list` / `test_streaming_response_list` variants (sync + async) call `list()` with no args. Pyright fails with "Argument missing for parameter company_id". Fallout from the recent merge conflict resolution on PR #884 where the test file was taken from codegen but the resource file had `company_id` as required from a different codegen run. Added `company_id="biz_xxxxxxxxxxxxxx"` (matches the pattern used by `test_method_list_with_all_params`) to the six affected list-variant calls. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * codegen metadata * feat(api): api update * codegen metadata * feat(api): manual updates * release: 0.0.38 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> Co-authored-by: Jordan Jantschulev <j.jantschulev@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a
verify_user_tokenhelper that mirrors the TypeScript SDK'sverify-user-token.ts, so app-proxy-issuedx-whop-user-tokenJWTs can be verified in Python apps with the same key-rotation-friendly semantics (no SDK release needed when keys roll).src/whop_sdk/lib/verify_user_token.py(new):verify_user_token()— validates against the canonical Whop JWKS athttps://api.whop.com/.well-known/jwks.jsonby default, with a module-level cache (12h TTL, 30s cooldown-guarded refetch on kid miss) mirroring jose'screateRemoteJWKSet.try_verify_user_token()— returnsNoneon failure instead of raising.public_key(PEM or JWK JSON static override),jwks_url(override the endpoint),header_name,app_id.src/whop_sdk/lib/__init__.py(new): markslib/as a package.pyproject.toml: adds optionaluser-tokensextra —pip install 'whop-sdk[user-tokens]'pulls inpyjwt[crypto]>=2.8,<3for ES256/ECDSA support.Coordinated with matching changes in the TypeScript and Ruby SDKs (landing in their respective
nextbranches concurrently) so all three ship at the same version.Test plan
public_keypath verifies (PEM and JWK JSON forms)api.whop.com