Developer Documentation

Everything you need to add TinTorch to your app - from a one-click login button to full OpenID Connect and the Subscriptions API.

What you can build

Overview

TinTorch Account is a standard OpenID Connect provider. Any compliant OIDC/OAuth 2.0 client library can integrate with it - point your library at the discovery document and it auto-configures every endpoint below. Tokens are signed with RS256 and verifiable via the JWKS endpoint.

Issuerhttps://account.tintorch.com
Discoveryhttps://account.tintorch.com/.well-known/openid-configuration
Authorizationhttps://account.tintorch.com/sso/authorize
Tokenhttps://account.tintorch.com/api/sso/token
UserInfohttps://account.tintorch.com/api/sso/userinfo
JWKShttps://account.tintorch.com/api/sso/jwks

Scopes

Request the scopes your app needs. openid is always required; add offline_access to receive a refresh token.

openidVerify your identity
profileYour name, username, photo, locale and timezone
emailYour email address and verification status
phoneYour phone number
organizationsThe workspaces you belong to and your roles
offline_accessStay signed in and refresh access without re-authenticating

How the flow works

  1. 1. Redirect the user to the authorization endpoint with your client_id, redirect_uri, response_type=code, a scope, a random state, and (for public clients) a PKCE code_challenge.
  2. 2. The user signs in and consents. TinTorch redirects back to your redirect_uri with a code and your state.
  3. 3. Your server exchanges the code at the token endpoint for an access_token, id_token, and (with offline_access) a refresh_token.
  4. 4. Verify the id_token signature against the JWKS, or call the UserInfo endpointwith the access token to get the user's profile.

Sign in and open the Developer section to register an app and get a client_id and client_secret.

Sign in

Quickstart

The fastest way to add TinTorch login: drop in one script tag and a button. We open a small popup, the user confirms, and you get their profile back - no backend, no redirects, no library. This uses a public (PKCE) client, so there is no client secret to manage.

1Register a public client

In the Developer section, create an app as a public client (PKCE, no secret) and add this exact redirect URI:

Redirect URI
https://account.tintorch.com/sso/popup-callback

2Drop in the button

Add the script and any element with data-tintorch-login. The script is served from TinTorch, so it always stays up to date.

index.html
<script src="https://account.tintorch.com/tintorch.js" data-client-id="YOUR_CLIENT_ID"></script>

<button data-tintorch-login>Sign in with TinTorch</button>

<script>
  window.addEventListener("tintorch:login", (e) => {
    const { user, accessToken } = e.detail;
    console.log("Signed in:", user); // { sub, name, email, picture, ... }
    // ...create a session, redirect, etc.
  });
  window.addEventListener("tintorch:error", (e) => {
    console.error("Login failed:", e.detail.message);
  });
</script>

3Or trigger it from your own code

Prefer your own button? Call TinTorch.signIn() from a click handler - it resolves with the signed-in user.

programmatic
<script src="https://account.tintorch.com/tintorch.js"></script>
<script>
  document.querySelector("#login").addEventListener("click", async () => {
    try {
      const { user, accessToken, idToken } = await TinTorch.signIn({
        clientId: "YOUR_CLIENT_ID",
        scope: "openid profile email",
      });
      console.log(user);
    } catch (err) {
      console.error(err.message); // "popup_closed", "popup blocked", ...
    }
  });
</script>

4React / SPA

Load the script once and call the global from any component.

LoginButton.tsx
import { useEffect, useCallback } from "react";

const SRC = "https://account.tintorch.com/tintorch.js";

export function LoginButton() {
  useEffect(() => {
    if (document.querySelector(`script[src="${SRC}"]`)) return;
    const s = document.createElement("script");
    s.src = SRC;
    document.head.appendChild(s);
  }, []);

  const onClick = useCallback(async () => {
    const { user, accessToken } = await window.TinTorch.signIn({
      clientId: "YOUR_CLIENT_ID",
    });
    // ...send accessToken to your backend to create a session
  }, []);

  return <button onClick={onClick}>Sign in with TinTorch</button>;
}

Security: the popup uses OAuth 2.0 authorization code + PKCE with a per-request state nonce. The token is exchanged from the browser (public client) - for higher-trust apps, send the returned accessToken to your backend and verify it against the JWKS, or use the Next.js / Node.js server-side flows instead.

Most libraries auto-configure from the discovery document at https://account.tintorch.com/.well-known/openid-configuration.

Workspaces

A user belongs to one or more workspaces (their personal one, plus any team they own or were invited to). Read them with the signed-in user's access token to build your own workspace switcher - including the image the owner uploaded in TinTorch Account, so the icon matches everywhere.

List the user's workspaces
curl https://account.tintorch.com/api/organizations \
  -H "Authorization: Bearer USER_ACCESS_TOKEN"

# Response:
# {
#   "organizations": [
#     {
#       "id": "clx…",
#       "slug": "acme-1a2b3c",
#       "name": "Acme Inc.",
#       "image": "https://…/workspaces/clx….png",
#       "personal": false,
#       "role": "ADMIN",
#       "memberCount": 7
#     }
#   ]
# }

The same list (minus memberCount) ships in the organizations claim when you request that scope, so a token you already hold may be enough. Membership itself is managed in TinTorch Account - workspace owners invite people by email and the invitation is accepted there.

Subscriptions

If your app is a first-party product with plans, start a subscription from your backend using the signed-in user's access token. We resolve your product from the token's client, so a user can only be subscribed to your plans. Free plans subscribe immediately; paid plans return a hosted checkout url to redirect the user to.

Start a subscription
curl -X POST https://account.tintorch.com/api/subscriptions/checkout \
  -H "Authorization: Bearer USER_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "plan": "pro",
        "successUrl": "https://your-app.com/billing/done",
        "cancelUrl": "https://your-app.com/billing"
      }'

# Response:
#   { "url": "https://.../checkout", "message": null }   # paid → redirect to url
#   { "url": null, "message": "Subscribed to ..." }      # free → already active

Body: plan (the plan key) or planId; optional organizationId(defaults to the user's workspace) and successUrl/cancelUrl. The token must include the organizations scope context; plans are billed through whichever gateway each plan is configured with.