Skip to content

feat: add OAuth PKCE helper functions and example usage - #584

Open
rajarshidattapy wants to merge 1 commit into
OpenRouterTeam:mainfrom
rajarshidattapy:fix/restore-oauth-pkce-helpers
Open

feat: add OAuth PKCE helper functions and example usage#584
rajarshidattapy wants to merge 1 commit into
OpenRouterTeam:mainfrom
rajarshidattapy:fix/restore-oauth-pkce-helpers

Conversation

@rajarshidattapy

Copy link
Copy Markdown

Fixes#583.

Problem

examples/oauth_pkce_example.py — the repository's only example — has been dead on main
since April:

ImportError: cannot import name 'oauth_create_sha256_code_challenge' from 'openrouter.utils'

05f81a5 added two hand-written helper modules under src/openrouter/utils/ plus their
exports in utils/__init__.py. e6b0242 ("clean up removed docs and update generated SDK
files") deleted both modules and the exports as part of a regeneration, and touched nothing
under examples/. The commit message indicates collateral damage rather than an intentional
removal, so this restores the helpers instead of deleting the example.

Change

src/openrouter/pkce.py | new, 164 lines
tests/test_pkce.py | new, 90 lines
examples/oauth_pkce_example.py | +6 -2
.genignore | +2

Public API is unchanged from what shipped in 05f81a5 — same dataclasses, same function
signatures. Only the import path moves, from openrouter.utils to openrouter.pkce.

Two bugs fixed rather than restored

Both were in the deleted code and are verified against the live site:

  1. The authorization URL 404'd._get_server_url() appended /auth to the API base,
    producing https://openrouter.ai/api/v1/authcurl returns 404. The authorization
    page is on the site origin, https://openrouter.ai/auth, which returns 307. The origin
    is now derived with urlsplit on the configured server URL, so regional hosts
    (eu.openrouter.ai) and custom base URLs keep working. Covered by a test.
  2. ParseResult callback URLs were corrupted.callback_url is typed
    Union[str, ParseResult] but was rendered with str(), which on a ParseResult yields
    ParseResult(scheme='https', netloc='app.example', ...) rather than the URL — so half the
    declared signature silently produced a garbage query parameter. Now uses .geturl().

Why this won't be deleted again

The helpers previously lived in src/openrouter/utils/, a directory Speakeasy owns, and
their exports were wired into the generated utils/__init__.py. A regeneration was always
going to take them, and did.

This PR puts them in src/openrouter/pkce.py, outside the generated tree, and modifies no
generated file. .genignore gains src/openrouter/pkce.py and examples.

Genignoring the generated utils/__init__.py to preserve the old import path was considered
and rejected: that would freeze the file, so future generated utils would never be exported —
trading this bug for a slower one. Nothing depends on the old path, since the symbols have
been absent from the package for roughly four months.

Verification

  • uv run python examples/oauth_pkce_example.py runs, and the URL it prints returns 307
  • pytest tests/test_pkce.py — 6 passed (challenge/verifier round-trip against a hand-computed
    SHA-256, RFC 7636 length and charset validation, site-origin URL, custom server_url, PKCE
    and limit params, ParseResult rendering)
  • mypy src clean across 770 files, pylint src --rcfile pylintrc 10.00/10, pyright src 0 errors

Note

tests/test_pkce.py needs uv run --with pytest to run: pytest is not in the dev
dependency group and pr-validation.yaml has no pytest step, so nothing in CI will execute
it — the same situation as the two existing files in tests/. That gap is out of scope here;
happy to file it separately if useful.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

examples/oauth_pkce_example.py is broken: it imports OAuth PKCE helpers that were deleted in e6b0242

1 participant

@rajarshidattapy