OAuth 2.0: Login with TalonTodo

This guide describes the authorization code flow for third-party applications.

1. Register an OAuth client

  1. Open the TalonTodo web app and go to Settings, then API.
  2. Create an application: name, optional description, and redirect URIs (exact URLs allowed for callbacks).
  3. Save the client ID and client secret immediately (the secret is shown only once).

2. Authorization endpoint (user consent)

Send the user's browser to the TalonTodo authorize URL (web app), with query parameters:
ParameterValue
response_typecode
client_idYour public client ID
redirect_uriOne of the registered redirect URIs (must match exactly)
scopeSpace-separated scopes (optional; defaults to all allowed scopes)
stateRandom string for CSRF protection (recommended)
Example (replace APP with your app origin, for example https://app.talontodo.com):
https://APP/oauth/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=https%3A%2F%2Fmyapp.com%2Fcallback&scope=tasks%3Aread%20tasks%3Awrite&state=RANDOM_STATE
The user signs in (if needed) and approves or denies access. On approval, the backend returns JSON to the frontend with a redirectUrl your app should navigate to, or you follow the redirect that includes:
https://myapp.com/callback?code=AUTHORIZATION_CODE&state=RANDOM_STATE
On denial:
https://myapp.com/callback?error=access_denied&state=RANDOM_STATE

3. Token endpoint

POST https://API/api/oauth/token
Use Content-Type: application/json or application/x-www-form-urlencoded.

Authorization code exchange

FieldValue
grant_typeauthorization_code
client_idYour client ID
client_secretYour client secret
codeThe code from the redirect
redirect_uriSame redirect URI used in the authorize request
Response (200):
{
  "access_token": "...",
  "refresh_token": "...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "tasks:read tasks:write"
}
Access tokens expire in 1 hour. Refresh tokens expire in 30 days.

Refresh token

FieldValue
grant_typerefresh_token
client_idYour client ID
client_secretYour client secret
refresh_tokenThe refresh token
Each refresh rotates the refresh token (old one is invalidated).

4. Revoke a token

POST https://API/api/oauth/revoke
{ "token": "ACCESS_OR_REFRESH_TOKEN" }

5. Calling APIs

Send the access token on each request:
Authorization: Bearer ACCESS_TOKEN
See the REST API reference for /api/v1/* routes.

Example: Node (fetch)

const tokenRes = await fetch('https://api.example.com/api/oauth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    grant_type: 'authorization_code',
    client_id: process.env.TALON_CLIENT_ID,
    client_secret: process.env.TALON_CLIENT_SECRET,
    code,
    redirect_uri: 'https://myapp.com/callback',
  }),
});
const tokens = await tokenRes.json();

const tasksRes = await fetch('https://api.example.com/api/v1/tasks', {
  headers: { Authorization: `Bearer ${tokens.access_token}` },
});

Security notes

  • Keep client_secret server-side only.
  • Always validate state on callback.
  • Use HTTPS in production.
  • Tokens in browser URLs or iframes can leak via Referer; prefer server-side token exchange and short-lived tokens for widgets.