OAuth 2.0 Authorization Code flow with PKCE for Longbridge OpenAPI.
Desktop / interactive (browser available)
{:ok, config} = Longbridge.OAuth.authorize("your-client-id")
{:ok, ctx} = Longbridge.QuoteContext.start_link(config)The first call opens a browser, exchanges the code for a token, and persists the token to disk. Subsequent calls reuse the cached token transparently.
Server / headless (no browser)
Run the authorization flow on a machine with a browser:
Longbridge.OAuth.authorize("your-client-id")This writes the token to
~/.longbridge/openapi/tokens/<client_id>.Copy that file to the server (same path).
On the server, load the persisted token:
{:ok, config} = Longbridge.OAuth.load_token("your-client-id")Tokens auto-refresh via the
refresh_tokengrant when expired. No browser, no interaction needed after the initial copy.
CLI compatibility
If you already ran longbridge auth login, the token is stored at
the same path. load_token/1 picks it up transparently.
Token storage
Tokens are persisted as JSON under:
~/.longbridge/openapi/tokens/<client_id>The file stores access_token, refresh_token, expires_at,
token_type, and (when provided) the http_url used at auth time.
Registering a client
If you don't have a client_id yet:
{:ok, client_id} = Longbridge.OAuth.register_client("My App")
Summary
Functions
Runs the full OAuth 2.0 Authorization Code flow with PKCE.
Generates the authorization URL for the OAuth flow.
Exchanges an authorization code for an access token.
Exports the persisted token as a map.
Generates a PKCE code verifier (43-128 character URL-safe string).
Loads the persisted OAuth token and returns a Longbridge.Config.
Computes the PKCE S256 code challenge from a verifier.
Refreshes an expired access token using the persisted refresh token.
Registers a new OAuth client and returns the client_id.
Functions
@spec authorize( String.t(), keyword() ) :: {:ok, Longbridge.Config.t()} | {:error, term()}
Runs the full OAuth 2.0 Authorization Code flow with PKCE.
Starts a local callback server, opens the user's browser, waits for
the callback, exchanges the code for a token, and persists the
token to disk. Returns a Longbridge.Config ready for use.
Options
:callback_port— TCP port for the local callback server (default 60355). Must match a registered redirect URI.:scope— OAuth scope (default"3").:china— Use the.longbridge.cnendpoint (defaultfalse).:http_url— Override the OAuth base URL.:open_url_fn— Override the "open browser" function (default:&open_browser/1). Tests can pass a no-op.:timeout— How long to wait for the user to approve (default 5 minutes).:storage— CustomLongbridge.OAuth.TokenStorageimplementation. Defaults toLongbridge.OAuth.FileTokenStorage(writes to~/.longbridge/openapi/tokens/<client_id>).
Generates the authorization URL for the OAuth flow.
The user must open this URL in a browser and approve the request.
After approval the browser redirects to redirect_uri with a code
parameter.
code_challenge is the PKCE S256 challenge, produced by
pkce_challenge(verifier).
@spec exchange_code(String.t(), String.t(), String.t(), String.t(), keyword()) :: {:ok, map()} | {:error, term()}
Exchanges an authorization code for an access token.
Returns {:ok, token_map} with keys :access_token, :refresh_token,
:expires_at, :token_type, and any other fields returned by the
server (scope, user_id, sub_accounts).
Exports the persisted token as a map.
Useful when copying a token from a developer workstation to a production server, or when loading it into a secret manager.
Returns {:ok, token_map} or {:error, reason}.
@spec generate_code_verifier() :: String.t()
Generates a PKCE code verifier (43-128 character URL-safe string).
The output is 86 characters of base64url-encoded randomness, which is within the RFC 7636 range (43-128 chars).
@spec load_token( String.t(), keyword() ) :: {:ok, Longbridge.Config.t()} | {:error, :not_found | {:refresh_failed, term()} | {:refresh_token_revoked, String.t(), String.t() | nil}}
Loads the persisted OAuth token and returns a Longbridge.Config.
Designed for server-side programs that cannot open a browser.
The token file must have been written by a previous authorize/2
call (e.g. on a developer's workstation).
If the access token is expired (or within :refresh_skew seconds
of expiry), attempts a refresh via the stored refresh_token
before returning.
Options
:storage— ALongbridge.OAuth.TokenStorageimplementation (defaultLongbridge.OAuth.FileTokenStorage).:refresh_skew— Seconds before expiry to proactively refresh. Default0(only refresh after expiry). Set to e.g.300to refresh 5 minutes early.:http_url— Override the OAuth base URL.
Error cases
{:error, :not_found}— No token persisted for this client_id.{:error, {:refresh_failed, reason}}— Refresh attempted but failed for a non-OAuth reason (e.g. network error).{:error, {:refresh_token_revoked, error, description}}— The refresh_token was rejected by the OAuth server (invalid_grantor similar). User must re-authorize.
Computes the PKCE S256 code challenge from a verifier.
Challenge = base64url(sha256(verifier)) without padding, per RFC 7636.
Refreshes an expired access token using the persisted refresh token.
If client_id is given, reads the refresh token from disk first.
Returns {:ok, token_map} or {:error, reason}.
Registers a new OAuth client and returns the client_id.
The client is registered with the default PKCE settings and a
localhost redirect URI. Use this to obtain a client_id before
calling authorize/2 for the first time.
Options
:client_name— Display name (default"My Longbridge OpenAPI").:redirect_uris— List of redirect URIs (default["http://127.0.0.1:60355/callback"]).:http_url— Override the OAuth base URL.