Docs

OAuth

OAuth Quickstart

OAuth apps let an external service identify a Chattr user.

Important limit

Third-party OAuth apps are identity-only. They receive:

  • chattr_user_id
  • nickname
  • name
  • avatar_url

They do not receive delegated user-management access.

Authorization flow

  1. Redirect the user to /oauth/authorize
  2. Receive a temporary code on your redirect URI
  3. Exchange the code at POST /oauth/token

Step 1 — Redirect to authorize

https://chattr.example.com/oauth/authorize?client_id=APP_ID&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback&state=RANDOM_NONCE

Step 2 — Exchange the code

After the user approves, Chattr redirects to your redirect_uri with ?code=TEMP_CODE&state=RANDOM_NONCE. Exchange the code for user identity:

Code examples

cURL

curl -X POST "https://chattr.example.com/oauth/token" \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": APP_ID,
    "client_secret": "YOUR_CLIENT_SECRET",
    "code": "TEMP_CODE",
    "redirect_uri": "https://example.com/callback"
  }'

JavaScript (Node.js)

const CHATTR   = "https://chattr.example.com";
const APP_ID   = 7;
const SECRET   = "YOUR_CLIENT_SECRET";
const REDIRECT = "https://example.com/callback";

// Step 1 — build the authorize URL
const authorizeUrl = `${CHATTR}/oauth/authorize`
  + `?client_id=${APP_ID}`
  + `&redirect_uri=${encodeURIComponent(REDIRECT)}`
  + `&state=RANDOM_NONCE`;

// Redirect the user to authorizeUrl...

// Step 2 — exchange code on your callback endpoint
async function exchangeCode(code) {
  const res = await fetch(`${CHATTR}/oauth/token`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      client_id: APP_ID,
      client_secret: SECRET,
      code,
      redirect_uri: REDIRECT
    })
  });

  const user = await res.json();
  console.log(user.chattr_user_id, user.nickname, user.avatar_url);
  return user;
}

Python

import requests

CHATTR   = "https://chattr.example.com"
APP_ID   = 7
SECRET   = "YOUR_CLIENT_SECRET"
REDIRECT = "https://example.com/callback"

# Step 1 — build the authorize URL
authorize_url = (
    f"{CHATTR}/oauth/authorize"
    f"?client_id={APP_ID}"
    f"&redirect_uri={requests.utils.quote(REDIRECT, safe='')}"
    f"&state=RANDOM_NONCE"
)

# Redirect the user to authorize_url...

# Step 2 — exchange code on your callback endpoint
def exchange_code(code):
    res = requests.post(f"{CHATTR}/oauth/token", json={
        "client_id": APP_ID,
        "client_secret": SECRET,
        "code": code,
        "redirect_uri": REDIRECT
    })

    user = res.json()
    print(user["chattr_user_id"], user["nickname"], user["avatar_url"])
    return user

Go

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
)

const (
	chattr   = "https://chattr.example.com"
	appID    = 7
	secret   = "YOUR_CLIENT_SECRET"
	redirect = "https://example.com/callback"
)

// Step 1 — build the authorize URL
func authorizeURL() string {
	return fmt.Sprintf(
		"%s/oauth/authorize?client_id=%d&redirect_uri=%s&state=RANDOM_NONCE",
		chattr, appID, url.QueryEscape(redirect),
	)
}

// Step 2 — exchange code
func exchangeCode(code string) (map[string]any, error) {
	body, _ := json.Marshal(map[string]any{
		"client_id":     appID,
		"client_secret": secret,
		"code":          code,
		"redirect_uri":  redirect,
	})

	resp, err := http.Post(chattr+"/oauth/token", "application/json", bytes.NewReader(body))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	raw, _ := io.ReadAll(resp.Body)
	var user map[string]any
	json.Unmarshal(raw, &user)
	return user, nil
}

PHP

$chattr   = "https://chattr.example.com";
$appId    = 7;
$secret   = "YOUR_CLIENT_SECRET";
$redirect = "https://example.com/callback";

// Step 1 — build the authorize URL
$authorizeUrl = $chattr . "/oauth/authorize?"
    . http_build_query([
        "client_id"    => $appId,
        "redirect_uri" => $redirect,
        "state"        => "RANDOM_NONCE"
    ]);

// Redirect the user to $authorizeUrl...

// Step 2 — exchange code on your callback endpoint
function exchangeCode(string $code) {
    global $chattr, $appId, $secret, $redirect;

    $ch = curl_init("$chattr/oauth/token");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
        "client_id"     => $appId,
        "client_secret" => $secret,
        "code"          => $code,
        "redirect_uri"  => $redirect
    ]));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $response = curl_exec($ch);
    curl_close($ch);

    $user = json_decode($response, true);
    echo $user["chattr_user_id"] . " " . $user["nickname"];
    return $user;
}

Permissions

Any phone-verified Chattr user can create OAuth apps. Only the app owner can manage (edit or delete) their OAuth apps — no server permission is required.

Response format

{
  "chattr_user_id": 42,
  "nickname": "player1",
  "name": "Player One",
  "avatar_url": "https://chattr.example.com/media/avatars/42.webp"
}