What it is
tcl_oauth2_library is a self-contained OAuth 2.0 client written in pure Tcl. It implements the Authorization Code flow: it opens the user's browser, catches the redirect on a one-shot local web server, exchanges the code for tokens, persists them to a 0600 JSON file, refreshes expired access tokens, and provides a one-liner for authenticated API calls.
Dependencies: http (bundled with the core Tcl distribution) and tls (the standard Tcl TLS extension). JSON parsing and encoding are handled by a decoder included in the file, and base64 by core Tcl's binary encode base64, so nothing from Tcllib is required. tls is the one unavoidable extension: HTTPS cannot be done in pure Tcl, as there is no pure-Tcl TLS handshake.
It is licensed under the Tcl/Tk license (BSD-style), the same license used by Tcl/Tk and Tcllib, and is structured to drop into Tcllib as the module modules/oauth2/.
Features
- Authorization Code flow — send the browser to the provider, catch the redirect on a local socket, exchange the code, and save the tokens. Non-OAuth redirect parameters (such as Intuit's
realmId) are kept under the token'sextrakey. - Refresh —
oauth2::tokenreturns a valid access token, refreshing it when it is within 60 seconds of expiry.oauth2::requestattaches it as aBearerheader and, on an HTTP 401, refreshes once and retries. - PKCE (RFC 7636) — a single
-pkce S256option. The SHA-256 it needs is implemented in-package (verified against the SHA-256 known-answer vectors and the RFC 7636 appendix-B test vector), keeping the dependency footprint athttp+tls.plainis also supported. The code verifier is not included in the authorize URL. - Provider-agnostic — the same code path drives providers that each deviate from the spec differently. Each deviation is expressed as configuration via
-auth_extra/-token_exchange_extra/-token_refresh_extradicts and a-token_auth body|basicswitch. - Additional grants and helpers — Client Credentials (RFC 6749 §4.4) for machine-to-machine flows, Token Introspection (RFC 7662), and a non-verifying
jwt_decodefor inspecting an access token's claims. - Cross-platform — runs unchanged on Unix, macOS, and Windows. Browser launch,
~expansion, and CA-bundle discovery are handled internally.
Supported providers & examples
The library ships three self-standing examples in examples/. Each is a single file that depends only on the oauth2 library, configures one provider inline, runs the login, and makes one API call. Each runs two ways — plain Tcl, or behind a Tk UI with -gui 1 — and takes its credentials from the command line or the environment, not from the repo.
- Twitter / X (
twitter_login.tcl) — Authorization Code with mandatory PKCE (-pkce S256), thenGET /2/users/me. A confidential client (with a secret) authenticates with HTTP Basic; a public client (no secret) relies on PKCE alone; the example selects the mode automatically. - Basecamp (
basecamp_login.tcl) — 37signals Launchpad usestype=web_serverin place ofresponse_type=code/grant_type=…, expressed through the-*_extraoptions. Shows identity and accounts, then lists projects. - QuickBooks Online / Intuit (
qbo_login.tcl) — close to standard OAuth2 but requires HTTP Basic auth on the token endpoint (-token_auth basic) and, in production, banslocalhostredirect URIs. Register a public bounce page that 302-redirects to a local…/callback; the local callback server listens on that port. Fetches the connected company's profile viaCompanyInfo.
A fourth directory, examples/testing/, has five runnable programs that each demonstrate a way to test an OAuth2 configuration — OIDC discovery, Client Credentials, Introspection, live token validation, and JWT decoding. They run as-is against a public IdentityServer demo (no registration needed), then point at another provider by editing a few lines at the top.
Using it
Load the library through the package system or by sourcing the file directly:
lappend auto_path /path/to/tcl-oauth2
package require oauth2
Create a client, log in once, and make a call. Credentials shown here are placeholders; supply real ones from environment variables:
set client [oauth2::new \
-name myapp \
-auth_url https://provider.example/authorize \
-token_url https://provider.example/token \
-client_id YOUR_CLIENT_ID \
-client_secret YOUR_CLIENT_SECRET \
-redirect_uri http://localhost:9876/callback \
-scope "read write" \
-auth_extra {response_type code} \
-token_exchange_extra {grant_type authorization_code} \
-token_refresh_extra {grant_type refresh_token} \
-token_file ~/.config/myapp/tokens.json]
oauth2::login $client ;# first run: opens browser, saves tokens
set json [oauth2::request $client GET https://api.example/v1/things]
After the first login, tokens live in -token_file and are refreshed automatically — subsequent runs skip straight to oauth2::request.
Core API surface:
oauth2::new— create a client (loads any existing token file immediately).oauth2::login/authorize_url/exchange_code— interactive or manual Authorization Code flow.oauth2::token— a currently-valid access token, refreshing as needed.oauth2::request/get/post— authenticated TLS calls with Bearer auth and refresh-and-retry on 401.oauth2::validate— live-test the current token, optionally auto-refreshing.oauth2::refresh— force a refresh-token grant now.oauth2::client_credentials/introspect/jwt_decode— the extra grants and helpers.oauth2::tokens/set_tokens/save/load/logout/config— token and configuration management.oauth2::json_parse/json_get— decode a JSON body and reach into it by dotted path with array indices, e.g.QueryResponse.Item[0].Id.
Security. Client secrets are read from the environment, never hard-coded. Tokens are written with mode 0600 and excluded from version control. The state parameter is generated per-login and verified on the redirect as a CSRF guard.
Status
- In production use, driving logins against Intuit (QuickBooks Online) and Basecamp.
- PKCE (RFC 7636) is implemented and verified against the standard test vectors.
- Ships with Tcllib packaging artifacts: a doctools reference manual (
oauth2.man, renders viadtplite) and atcltestsuite (oauth2.test, 36 offline tests pass — SHA-256/PKCE vectors, JSON parse/get, JWT, authorize-URL construction, option validation, and0600token persistence). - Relicensed to the Tcl/Tk license (from GPL-3), for submission of the package to Tcllib.
Repository
Source, examples, manual, and tests: https://github.com/johnbuckman/tcl_oauth2_library