After you register an OAuth client, your application uses its settings—including its client ID, client secret, and registered redirect URI—to implement the authorization flow. As shown in the OAuth overview, your application sends the user to Lunch Money, Lunch Money returns an authorization code to your callback, and your application exchanges that code for tokens.
This guide walks through that process for a Confidential web client. Your server handles the callback and token exchange and keeps the client secret and refresh tokens secure. If you are building a standalone mobile or desktop application, read the native application guidance instead.
OAuth libraries handle much of the protocol work for you, including generating PKCE values, building authorization requests, validating callback state, and exchanging authorization codes. Choose a well-maintained OAuth 2.0 client library for your language and application framework. Look for support for:
state generation and validation; andFor Node.js and TypeScript, openid-client is one maintained option. Despite its name, using it does not mean that Lunch Money supports OpenID Connect identity scopes, ID tokens, or UserInfo.
Your OAuth library needs to know where to send the user for authorization, where to exchange codes for tokens, and which protocol features Lunch Money supports. Instead of configuring each value separately, give the library the Lunch Money issuer:
https://api.lunchmoney.dev
Then have it load the OAuth authorization-server metadata document:
https://api.lunchmoney.dev/.well-known/oauth-authorization-server
The metadata document is a machine-readable description of Lunch Money's authorization, token, and revocation endpoints, supported client-authentication methods, and PKCE method. Using it allows your library to obtain the current OAuth configuration without your application assembling endpoint URLs itself.
/oauth/authorize, /oauth/token, and /oauth/revoke. These are useful when debugging, but use metadata discovery when your library supports it.On your server, generate:
state value bound to the user's session;code_verifier; andS256 code_challenge.Redirect the browser to the discovered authorization endpoint with response_type=code, your client_id, exact redirect_uri, state, code_challenge, and code_challenge_method=S256.
scope. If your OAuth library includes it, Lunch Money ignores the requested value and uses the client's complete registered scope set. An authorization request cannot narrow or expand that set; changing scopes requires a replacement client.The callback receives either code and state, or an OAuth error and the original state. Before exchanging a code:
state with the one-time value stored in the initiating session;Treat access_denied as a normal user decision. Do not log the callback query string.
Send a form-encoded request to the discovered token endpoint with grant_type=authorization_code, the code, exact redirect_uri, and original code_verifier. A confidential client authenticates with HTTP Basic using its client ID and secret. Do this server-side.
Store access_token, token_type, granted scope, and expiration metadata securely. If a refresh_token is returned, keep it server-side as a high-value credential.
Send the access token in the header:
GET /v2/me HTTP/1.1
Host: api.lunchmoney.dev
Authorization: Bearer YOUR_ACCESS_TOKEN
The operation must be allowed by the token's scope and by the user's access to the selected budgeting account. See the scope catalog.
Use the response's expires_in value to schedule renewal slightly before expiry. Read token lifecycle and recovery before implementing refresh, logout, or revocation.