Google OAuth consent helpers for user (refresh-token) credentials,
built on hot.dev/oauth2.
Flow
- Redirect the user to
authorize-url(...)(offline access, consent prompt and incremental scopes are on by default, so Google returns a refresh token). - In your callback,
exchange-code(...)turns?code=into tokens. - Store
refresh_tokenper user (it is long-lived), then pass it per call as{refresh-token, client-id, client-secret}opts, or set thegoogle.oauth.*context variables for a single-user app.
::oauth ::google::oauth
verifier ::oauth/pkce-verifier()
url ::oauth/authorize-url(client-id, "https://app.example.com/oauth/google",
["drive.file"], state, {code-verifier: verifier})
// callback handler:
tokens ::oauth/exchange-code(client-id, client-secret, request.query.code,
"https://app.example.com/oauth/google", {code-verifier: verifier})
tokens.refresh_token
Functions
authorize-url
fn (client-id: Str, redirect-uri: Str, scopes: Str | Vec, state: Str): Str
fn (client-id: Str, redirect-uri: Str, scopes: Str | Vec, state: Str, opts: Map): Str
Build the Google consent URL. scopes is a Vec or space/comma
separated Str (short names such as "drive.file" are expanded);
state is your CSRF token. See build-authorize-params for opts.
url ::google::oauth/authorize-url("123.apps.googleusercontent.com",
"https://app.example.com/oauth/google", "drive.readonly", "csrf-123")
build-authorize-params
fn (opts: Map): Map
The Google-specific authorization parameters for authorize-url
(pure). Options (all optional):
access-type-"offline"(default; returns a refresh token) or"online"prompt-"consent"(default; guarantees a refresh token on re-consent),"select_account","none", or null to omitinclude-granted-scopes-true(default) for incremental authorizationlogin-hint- pre-fill the account emailhd- restrict the account chooser to a Workspace domaincode-verifier- a PKCE verifier; its S256 challenge is sentcode-challenge- a precomputed S256 challenge (instead ofcode-verifier)extra- any other parameters, merged last
exchange-code
fn (client-id: Str, client-secret: Str?, code: Str, redirect-uri: Str): Map
fn (client-id: Str, client-secret: Str?, code: Str, redirect-uri: Str, opts: Map): Map
Exchange an authorization code for tokens. Returns Google's token
response (access_token, expires_in, refresh_token, scope,
token_type, id_token when openid was requested), or a
::google::auth/TokenError such as
"google: token exchange failed: invalid_grant (Bad Request)".
opts.code-verifier completes a PKCE flow. The client secret and the
returned tokens are masked in call records.
granted-scopes
fn (tokens: Map): Vec
The scopes a token response says were granted (scope split on spaces). Check it after consent: users can untick scopes.
missing-scopes
fn (tokens: Map, required: Str | Vec): Vec
The required scopes (Vec or Str, short names ok) that a token response did not grant. Empty when everything was granted.
refresh-access-token
fn (client-id: Str, client-secret: Str?, refresh-token: Str): Map
Exchange a refresh token for a fresh access token (no cache). Returns the token response or a TokenError. A revoked or expired refresh token yields error-code: "invalid_grant"; ask the user to consent again.
revoke-token
fn (token: Str): Bool
Revoke an access or refresh token (revoking a refresh token also revokes its access tokens). Returns true, or a TokenError. The token is masked in call records.