Skip to main content
With an OAuth app, your internal portal can read a member’s notes on their behalf without each member issuing an API key. The member signs in to Tiro and passes a consent screen once, and the app then acts only within the permissions that member already has.

When should you use an OAuth app?

OAuth apps support the Authorization Code flow only, and PKCE is required. There is no client_credentials flow for obtaining a token without a user, so use an API key for server-to-server integrations.

Step 1. Register an OAuth app

An organization Admin or Developer registers the app at Tiro Platform. OAuth apps are available in the organization scope only.
1

Open the OAuth apps screen

In the Tiro Platform side menu, open your organization’s [OAuth apps] menu and press [Register OAuth app].
2

Enter the app details

Enter the app name, between 1 and 10 Redirect URIs, and the scopes the app will request from users. Each Redirect URI must start with https:// (except local-development loopback HTTP), and can’t contain user info or a fragment after #. For each authorization request, the app selects one redirect_uri from the registered list.
3

Store the Client ID and Client Secret

Right after registration, the screen shows the Client ID and Client Secret. The Client Secret is shown this one time and can’t be retrieved again. Store it somewhere safe, such as a server environment variable, right away.
Redirect URIs use https:// by default. For local development only, you can register http://localhost, http://127.0.0.1, http://[::1], and ports on those addresses. Register separate apps for local development and production so development URLs don’t enter production settings. The redirect_uri in an authorization request must exactly match one URL in the registered list. Tiro compares the complete string, including the port, query string, and trailing slash.
The [OAuth apps] menu also appears on workspace screens, but it only opens in the organization scope. If you don’t see the menu, ask your account manager or partners@theplato.io and we’ll issue an app for you.
Once you close the registration screen, the Client Secret can’t be viewed again. If you lose it, get a new one from the refresh icon at the right of that app’s row in the OAuth apps list. The current secret stops working the moment you regenerate, so be ready to update the servers that use the app. Discarding an app also disconnects every user connection made through it.

Scopes you can request

An OAuth app can request only the permissions a user is able to delegate. Organization management scopes (organization_member:*, workspace:*, session:write) can’t be delegated and are available to organization API keys only. What the app actually reaches is the overlap between the scopes the user consented to and the permissions that user holds in Tiro. Workspaces and notes the user can’t see stay out of reach no matter which scopes were granted. Send the user from your app to the Tiro authorization page. First generate a 43 to 128 character code_verifier using only ASCII letters, digits, -, ., _, and ~. Encoding 32 cryptographically random bytes as unpadded Base64URL produces a 43-character code_verifier. Then hash that string as UTF-8 bytes with SHA-256 and send the unpadded Base64URL encoding of the digest as code_challenge. Sending a hex string or the raw digest bytes gets rejected in the authorization request.
After the user signs in with their Tiro account, reviews the app name and requested scopes on the consent screen, and approves, code and state come back to your Redirect URI.

Validate state on the callback

Check state before you exchange the token. Store the value you generated for the authorize request in the user’s session, compare it with the value that comes back on the callback, then clear the stored value. If it’s missing or different, stop the request right there. Skipping this check allows login CSRF: an authorization the attacker started gets attached to the victim’s session, connecting them to an account they know nothing about.
Members of organizations using SSO sign in through their corporate IdP as usual before the consent screen. The consent screen is scoped to that member’s Tiro account, and the app receives that account’s permissions only.

Step 3. Exchange the code for a token

Turn the validated code into an access token from your server. The examples below read the authorization code from the callback and the code_verifier from step 2 as environment variables. Handle the Client Secret on the server only and never put it in browser or app code. An authorization code works exactly once, so sending it twice is rejected with invalid_grant.
Client authentication works with both HTTP Basic and body parameters (client_id, client_secret).
expires_in is the remaining lifetime in seconds and varies with the app’s configuration. Don’t hardcode the value; use whatever comes back in the response. Apps that also receive a refresh_token should follow the refresh procedure below, and apps that don’t can send the user back to the authorization page before the token expires.

Step 4. Call the API

Put the access token in the Authorization header exactly as you would an API key. The endpoints you can call and the response formats match what’s covered in the API Overview.
The notes in the response are the ones the consenting user can see in the app. It’s the same result as calling with that user’s account API key.

Step 5. Refresh the token

If the token exchange response includes a refresh_token, you can get a new access token without asking the user to consent again.
  • A refresh token works exactly once. When the response carries a new refresh token, discard the previous value and store the new one.
  • Sending a refresh token that was already used is treated as theft, and every token on that user connection is invalidated. You then have to ask the user to consent again.
  • A connection left unrefreshed for a long time expires. Each refresh pushes the expiry further out, so connections in steady use stay alive.
  • Refreshes are rejected once the app has been discarded or the user has disconnected it.
If a network problem leaves you without a response to a refresh request, don’t send the same refresh token again. The server may have already rotated it. Clearing the stored token and prompting the user to reauthorize is the safer move.

Disconnect

When a user stops using the app, revoke the refresh token to disconnect. Access tokens already issued expire on their own once expires_in passes.

Server metadata

The endpoints and supported methods are published in the standard metadata document.

Common errors


Related pages: Authentication · Integrate at the organization level · API Overview