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.
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.
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.
Step 2. Get user consent
Send the user from your app to the Tiro authorization page. First generate a 43 to 128 charactercode_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
Checkstate 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 validatedcode 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_id, client_secret).
Step 4. Call the API
Put the access token in theAuthorization 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.
Step 5. Refresh the token
If the token exchange response includes arefresh_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.
Disconnect
When a user stops using the app, revoke the refresh token to disconnect. Access tokens already issued expire on their own onceexpires_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