Config & presets (7 functions)
xoauth2UseGithub / UseGoogle / UseWechat / UseMicrosoft / UseCustom presets
One-line setup: GitHub (includes api.github.com/user), Google (OIDC: issuer + JWKS + nonce), WeChat (OAuth only, PKCE off), Microsoft (endpoints and issuer generated per tenant — a third owned URL held by the client and freed by ClientUnit), Custom (your own xoauth2config). Presets carry provider knowledge; transport injection is the separate next step (Config.Http = ...).
xoauth2ConfigInit / xoauth2ClientUnit lifecycle
Zeroes the config (UsePkce on by default, AuthStyle auto); ClientUnit ends a session: zeroes the sensitive state/verifier/nonce, frees preset-owned endpoint URLs and clears the config — re-preset before reuse. A Microsoft tenant longer than 256 characters is rejected (endpoints left NULL).
Login flow (3 functions)
xoauth2BeginLogin authorize entry
Generates a random state (CSRF) and PKCE verifier/challenge (S256, honoring the preset's UsePkce), a nonce when UseNonce is set, and builds the authorize URL (scope/redirect fully form-encoded). Returns the URL (xrtFree) — redirect the user's browser there.
xoauth2CompleteLogin code exchange
Constant-time state compare (burned on match, replay-proof) → builds the token request (AuthStyle=BODY puts fully encoded credentials in the body; =BASIC sends the Authorization header) → burns the verifier → POST via the transport callback → graded response parsing. Returns a token on success (free with xoauth2TokenFree). After a state mismatch you must BeginLogin again.
xoauth2client oauth;
xoauth2UseGithub(&oauth, sClientId, sSecret, "https://app.example/cb");
oauth.Config.Http = xoauth2HttpXrt; /* inject transport */
oauth.Config.HttpContext = pHttp; /* handle from xoauth2HttpXrtCreate */
char* url = xoauth2BeginLogin(&oauth); /* 302 to the authorize page */
/* ...user authorizes; callback arrives with code + state... */
xoauth2token* tok = xoauth2CompleteLogin(&oauth, sCode, sState);
if ( tok ) {
/* tok->AccessToken / RefreshToken / ExpiresAt */
xoauth2TokenFree(tok); /* sensitive fields already zeroed */
}
xoauth2TokenFree free
Frees the token — access/refresh/id token plaintext is zeroed before release.
Token management (2 functions)
xoauth2Refresh refresh
Builds the grant_type=refresh_token request, runs it through the transport and parses the graded response (AuthStyle as in CompleteLogin). The old token is the caller's to free. Four error levels: NETWORK (connect/timeout/TLS) → TOKEN_ENDPOINT (HTTP error status) → TOKEN_DENIED (provider error field) → TOKEN_RESPONSE (2xx body parse failure).
xoauth2TokenExpiring expiry check
Checks against ExpiresAt and the current clock whether the token expires within leewaySeconds. When the provider returns no expires_in (e.g. GitHub) it counts as not expiring — unknown ≠ about to expire, and such tokens usually carry no refresh_token either.
OIDC helpers (3 functions, data-coupling layer)
xoauth2HttpGet generic GET
Runs a GET through the client's transport callback for any URL, returning the response body text (xrtFree). Typical use: fetching the JWKS JSON. Error grading as in token exchange.
xoauth2NonceConsume nonce consume
Constant-time compare against the nonce generated by BeginLogin; on match it is burned (one-shot, replay-proof) and true is returned; a mismatch sets XOAUTH2_ERROR_NONCE_MISMATCH.
xoauth2GetUserInfo userinfo
GETs UserInfoUrl with Authorization: Bearer (dynamically allocated — long tokens are never truncated) and returns the claims object (non-object JSON rejected). Also the right path for non-OIDC providers (e.g. WeChat /sns/userinfo).
xoauth2token* tok = xoauth2CompleteLogin(&oauth, code, state);
int st;
char* jwksJson = xoauth2HttpGet(&oauth, oauth.Config.JwksUrl, NULL, &st);
xjwtjwks* keys = xjwtJwksParse(jwksJson); /* string in */
xjwtcheck ck; xjwtCheckInit(&ck);
ck.Issuer = oauth.Config.Issuer; /* preset knowledge, just a string */
ck.Audience = oauth.Config.ClientId;
xvalue* claims = xjwtVerifyJwks(tok->IdToken, keys, &ck);
char nonce[128];
xjwtClaimString(claims, "nonce", nonce, sizeof(nonce));
xoauth2NonceConsume(&oauth, nonce); /* constant-time + burn */
Convenience transport (5 functions)
xoauth2HttpXrtInit / xoauth2HttpXrtUnit stack variant
Creates or borrows a net engine + system/custom CA verifier + timeout (default 15 s). For C hosts declaring on the stack.
xoauth2HttpXrtCreate / xoauth2HttpXrtDestroy heap variant
Heap-allocated handle — for when the opaque struct cannot be declared on the stack (the natural path for the script layer). Parameters as in Init.
xoauth2HttpXrt transport callback
POST/GET dual method (POST with form headers, GET without entity headers; Authorization passed through in both), http/https schemes, IPv6 literals, future-based TLS/TCP IO, xrt http1 assembly and parsing, 1 MB response cap. Attach on Config.Http.
Utilities & errors (5 functions)
xoauth2PkceGenerate / xoauth2StateGenerate / xoauth2UrlEncode offline tools
PKCE code_verifier (random 43 chars) + challenge (BASE64URL(SHA256(verifier)), reproducibly verifiable); random state (URL-safe); URL percent-encoding — all usable offline for tests and custom scenarios.
xoauth2LastError error read
Returns the xoauth2-domain error code (XOAUTH2_ERROR_*, eight values), 0 for other domains. Read immediately after a failure. The four levels map straight to HTTP semantics: STATE_MISMATCH→400, TOKEN_DENIED→401, ENDPOINT/RESPONSE→502, NETWORK→504.