UNPKG

splitwise

Version:

A TypeScript SDK for the Splitwise API.

67 lines 2.76 kB
"use strict"; /** * OAuth 2.0 Authorization Code grant with PKCE for Splitwise. * * Two-step flow: * 1. `createAuthorizationUrl` builds the consent URL and generates the * `state` and `code_verifier` the caller must persist. * 2. After the user approves and Splitwise redirects back with `?code=...`, * `exchangeAuthorizationCode` swaps that code for an access token. * * PKCE (RFC 7636) is mandatory here. The verifier never leaves the caller * until step 2, which protects against authorization code interception. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.createAuthorizationUrl = createAuthorizationUrl; exports.exchangeAuthorizationCode = exchangeAuthorizationCode; const internal_js_1 = require("./internal.js"); /** * URL-safe base64 per RFC 4648 §5: replace +/= with -_ and strip padding. */ function base64urlEncode(bytes) { const binary = String.fromCharCode(...bytes); return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); } function randomBase64Url(byteLength) { const bytes = new Uint8Array(byteLength); crypto.getRandomValues(bytes); return base64urlEncode(bytes); } async function sha256Base64Url(input) { const data = new TextEncoder().encode(input); const digest = await crypto.subtle.digest('SHA-256', data); return base64urlEncode(new Uint8Array(digest)); } async function createAuthorizationUrl(params, options = {}) { const authorizeUrl = options.authorizeUrl ?? internal_js_1.DEFAULT_AUTHORIZE_URL; // 32 bytes -> 43-char base64url string, comfortably inside RFC 7636's 43-128. const state = params.state ?? randomBase64Url(32); const codeVerifier = randomBase64Url(32); const codeChallenge = await sha256Base64Url(codeVerifier); const url = new URL(authorizeUrl); url.searchParams.set('response_type', 'code'); url.searchParams.set('client_id', params.clientId); url.searchParams.set('redirect_uri', params.redirectUri); url.searchParams.set('state', state); url.searchParams.set('code_challenge', codeChallenge); url.searchParams.set('code_challenge_method', 'S256'); if (params.scope !== undefined) { url.searchParams.set('scope', params.scope); } return { url: url.toString(), state, codeVerifier, }; } async function exchangeAuthorizationCode(params, options = {}) { return (0, internal_js_1.postTokenRequest)({ grant_type: 'authorization_code', code: params.code, redirect_uri: params.redirectUri, client_id: params.clientId, client_secret: params.clientSecret, code_verifier: params.codeVerifier, }, options); } //# sourceMappingURL=authorization-code.js.map