Skip to content

feat(oauth): add OAuth revoke/clear and use PKCE in the authorization flow - #616

Merged
Colbert-Ran merged 1 commit into
mainfrom
feat/oauth-revoke-clear
Nov 3, 2026
Merged

Colbert-Ran merged 1 commit into
mainfrom
feat/oauth-revoke-clear

Conversation

@Colbert-Ran

@Colbert-Ran Colbert-Ran commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • OAuth.revoke() / OAuth.clear() in every SDK layer (Rust, C, C++, Java, Node.js, Python).
    • revoke() revokes the current token on the server (RFC 7009): the refresh token when present, otherwise the access token. It only contacts the server.
    • clear() drops the token locally: it deletes the persisted token first and clears the in-memory copy only when that succeeds. It does not contact the server, so call revoke() first to fully disconnect.
    • New TokenStorage::delete (default implementation returns an error, so existing custom backends still compile but clear() fails loudly instead of reporting success), OAuthError::RevokeFailed and OAuthError::TokenFileDelete.
    • revoke() on a from_token client (no client ID) fails with a clear RevokeFailed message.
    • C: a NULL handle passed to lb_oauth_revoke / lb_oauth_clear is reported through the callback instead of crashing (this also covers a moved-from C++ OAuth).
    • Python: the blocking calls release the GIL and reuse the shared Tokio runtime.
  • PKCE (RFC 7636, S256) in the browser authorization flow. The server requires PKCE for public clients, which previously failed with OAuth authorization failed: server_error.

Breaking changes

  • Rust: OAuthError is now #[non_exhaustive] (and gains two variants). Exhaustive matches outside the crate need a wildcard arm.

Test plan

  • cargo test -p longbridge-oauth (30 unit tests + 2 doctests), including new tests for the PKCE authorize URL, revoke_error_message, from_token revoke, and clear() when the storage delete fails or is unsupported
  • cargo clippy on longbridge-oauth and longbridge-c (no new warnings); longbridge-python, longbridge-java and longbridge-nodejs compile
  • C++ (CMake + Ninja, Debug) builds and exports OAuth::revoke / OAuth::clear; all Java sources compile with javac --release 9; Node.js npm run build:debug succeeds and the generated OAuth / OAuthBuilder declarations match the committed index.d.ts
  • cargo +nightly fmt --all -- --check reports no diff

🤖 Generated with Claude Code

https://claude.ai/code/session_017BJcMMB4GdF4MgepE5mW2K

… flow

- Add `OAuth.revoke()` (RFC 7009) and `OAuth.clear()` to every SDK layer
  (Rust, C, C++, Java, Node.js, Python), plus `TokenStorage::delete` and
  `OAuthError::{RevokeFailed, TokenFileDelete}`.
- `clear()` deletes the persisted token first and clears memory only on
  success; `revoke()` rejects `from_token` clients with a clear error.
- `TokenStorage::delete` defaults to an error so `clear()` never reports
  success for a backend it cannot wipe.
- C: NULL handles passed to `lb_oauth_revoke` / `lb_oauth_clear` are reported
  through the callback instead of crashing.
- Python: blocking `revoke()` / `clear()` release the GIL and reuse the shared
  Tokio runtime.
- Use PKCE (S256) in the browser authorization flow; the server requires it
  for public clients.
- Mark `OAuthError` as `#[non_exhaustive]`.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017BJcMMB4GdF4MgepE5mW2K
@Colbert-Ran
Colbert-Ran merged commit fdfff60 into main Nov 3, 2026
56 checks passed
@Colbert-Ran
Colbert-Ran deleted the feat/oauth-revoke-clear branch November 3, 2026 09:09
hogan-yuan added a commit to longbridge/openapi-go that referenced this pull request Nov 4, 2026
… flow (#140)

## Summary

Ports longbridge/openapi#616 to the Go SDK.

- **`OAuth.Revoke(ctx)`** revokes the current token on the server (RFC
7009, `POST /oauth2/revoke`): the refresh token when present, otherwise
the access token. It only contacts the server. The request matches what
the Rust `oauth2` crate sends (`token`, `token_type_hint`, `client_id`
in the form body; `Accept: application/json`). Failures carry the
server's `error: error_description`. It uses the `*http.Client` stored
in `ctx` under `oauth2.HTTPClient` when present (as token refreshes do),
otherwise a client with a 30s timeout.
- **`OAuth.Clear()`** drops the token locally: deletes
`~/.longbridge/openapi/tokens/<client_id>` first (a missing file is not
an error) and clears the in-memory token only when that succeeds.
- **Concurrency:** `AccessToken` and `Build` release the lock during
network I/O. A generation counter now makes a refresh or authorization
that was in flight when `Clear` ran get discarded instead of re-saving
the token (`Build` returns an error; `AccessToken` returns the
replacement token if one was stored, otherwise an error).
- **PKCE (RFC 7636, `S256`)** in the browser authorization flow: the
authorization URL carries `code_challenge`, and the code exchange sends
the matching `code_verifier`. The server requires PKCE for `public`
clients.

Not ported: `from_token` / `TokenStorage` handling, which do not exist
in the Go SDK.

## Test plan

- [x] `go test ./oauth/ -race -count=1`: new tests cover the PKCE
authorize URL, `code_verifier` in the exchange, `Revoke` (refresh-token
preference, access-token fallback, server error, no token), `Clear`
(memory + file, missing file, delete failure keeps the token), and a
refresh in flight in `AccessToken` / `Build` being discarded after
`Clear`
- [x] `go vet ./oauth/`, `go build ./...`

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to 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.

1 participant