Sign in with Starchild

Add Starchild OAuth login to a web app and verify every protected request on your server.

Before you start

You need:

  • A Starchild account
  • A web app with a browser client and a backend
  • The exact origin where the client runs, such as https://app.example.com

Create an OAuth app at iamstarchild.com/oauth-apps. Enter the exact allowed origin and copy the generated client ID.

Allowed origins contain only the scheme, host, and optional port. Do not include a path, query string, or trailing slash. Production origins must use HTTPS. For local development, an origin such as http://localhost:3000 is supported.

Install the SDK

For React, Vue, Vite, webpack, or another bundler:

npm install starchild-auth-sdk
import { StarchildAuth } from 'starchild-auth-sdk'

For plain HTML, load the UMD build:

<script src="https://unpkg.com/starchild-auth-sdk/dist/starchild-auth.umd.cjs"></script>

If unpkg is unavailable, use the npmmirror UMD build or self-host the file.

A plain <script> tag must use starchild-auth.umd.cjs. The ESM file, starchild-auth.js, requires a bundler or <script type="module">.

Initialize login

<button id="login-btn">Sign in with Starchild</button>
<button id="logout-btn" hidden>Log out</button>
<p id="status"></p>

<script>
const loginButton = document.getElementById('login-btn')
const logoutButton = document.getElementById('logout-btn')
const status = document.getElementById('status')

const auth = new StarchildAuth({
  clientId: 'YOUR-CLIENT-ID',
  onLogin: ({ userInfo }) => {
    status.textContent = `Signed in as ${userInfo.agentName}`
    loginButton.hidden = true
    logoutButton.hidden = false
  },
  onLogout: () => {
    status.textContent = ''
    loginButton.hidden = false
    logoutButton.hidden = true
  },
  onTokenRefreshFailed: () => {
    status.textContent = 'Your session expired. Sign in again.'
    loginButton.hidden = false
    logoutButton.hidden = true
  },
})

loginButton.addEventListener('click', async () => {
  try {
    await auth.login()
  } catch (error) {
    status.textContent = 'Login did not finish. Allow popups and try again.'
  }
})

logoutButton.addEventListener('click', () => auth.logout())
</script>

Call login() directly from a click or another user gesture. Browsers may block the login window if it opens during page load or after an unrelated asynchronous action.

onLogin is the single signed-in entry point. It runs after a completed login and after the SDK restores an existing session on page load.

Call your backend

Read the current access token when each request is made:

const response = await fetch('/api/me', {
  headers: {
    Authorization: `Bearer ${auth.getToken()}`,
  },
  cache: 'no-store',
})

Do not cache the access token in application state for later reuse. The SDK refreshes short-lived access tokens automatically.

Verify every token on your server

Your backend must treat browser-provided identity data as untrusted. Extract the bearer token and verify it with Starchild:

GET https://ai-api.iamstarchild.com/v1/oauth/userinfo
Authorization: Bearer <access-token>

A valid token returns the user's Starchild identity. Reject missing, invalid, expired, or unverifiable tokens with 401 Unauthorized.

import json
import urllib.error
import urllib.request

USERINFO_URL = "https://ai-api.iamstarchild.com/v1/oauth/userinfo"

def verify_starchild_token(token):
    if not token:
        return None

    request = urllib.request.Request(
        USERINFO_URL,
        headers={"Authorization": "Bearer " + token},
    )

    try:
        with urllib.request.urlopen(request, timeout=8) as response:
            payload = json.loads(response.read().decode("utf-8"))
    except (urllib.error.HTTPError, urllib.error.URLError, TimeoutError, ValueError):
        return None

    data = payload.get("data", payload) if isinstance(payload, dict) else {}
    user_id = data.get("userInfoId") or data.get("user_info_id") or data.get("id")
    if not user_id:
        return None

    return {
        "user_id": str(user_id),
        "display_name": data.get("agentName") or data.get("agent_name") or "",
        "avatar": data.get("agentAvatar") or data.get("agent_avatar") or "",
    }

Use userInfoId as the stable key for ownership, quotas, and application records. Agent names and avatars are display fields and can change.

For payments, one-time actions, and permission checks, fail closed when verification is unavailable. A temporary verification failure must never grant access.

Session behavior

  • Session restoration is enabled by default with autoLogin: true.
  • The SDK stores its refresh token in localStorage under starchild_rt_{clientId}.
  • Access tokens refresh every 12 minutes by default and when the tab becomes visible.
  • Tabs using the same client ID share the browser session.
  • auth.logout() clears the local session and revokes it server-side.
  • onTokenRefreshFailed runs when the session cannot be refreshed.

Because the browser stores session material in localStorage, keep a strict Content Security Policy, avoid unsafe inline third-party scripts, sanitize rendered content, and treat any cross-site scripting issue as an account-security issue.

API reference

Constructor options

Option Default Purpose
clientId Required OAuth client ID from the OAuth Apps panel
onLogin None Runs after login and session restoration
onLogout None Runs after logout
onTokenRefresh None Receives the new access token after refresh
onTokenRefreshFailed None Runs when the session cannot be refreshed
autoLogin true Restores a saved session during initialization
refreshInterval 720000 Refresh interval in milliseconds

Methods

login() · logout() · isLoggedIn() · getToken() · getUserInfo() · refreshToken() · destroy()

User information

type UserInfo = {
  userInfoId: string
  agentName: string
  agentAvatar: string
}

Troubleshooting

Problem What to check
Popup blocked Call login() directly from a click handler
Origin mismatch Match the registered origin exactly, with no path or trailing slash
StarchildAuth is not defined Use the UMD build with a plain <script> tag
onLogin runs during page load This is normal session restoration; set autoLogin: false to disable it
Refresh repeatedly fails The app may have been revoked, the session may have expired, or the network may be unavailable
CDN unavailable Use npmmirror or self-host the UMD file

Launch checklist

  • [ ] Register the exact production origin
  • [ ] Keep separate client IDs for local development and production when practical
  • [ ] Trigger login() from a user gesture
  • [ ] Drive signed-in UI from onLogin
  • [ ] Read auth.getToken() at request time
  • [ ] Verify every protected request on the server
  • [ ] Key application data by userInfoId
  • [ ] Sanitize user-controlled display fields
  • [ ] Fail closed when identity verification fails
  • [ ] Review Content Security Policy and cross-site scripting protections