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 browser client, a backend, and the exact origin where the client runs. Create an OAuth app from iamstarchild.com → More → OAuth Apps → Create App. The direct OAuth Apps page may also resolve directly.
Enter:
- Name, required.
- Allowed Origin(s), required. Register the third-party page origin only, including scheme and port, with no path, query, hash, or trailing slash. Register every local origin you use, such as
http://localhost:5173andhttp://localhost:3333. Do not registerhttp://localhost:6066; that is the Starchild web app's own local port and is already whitelisted server-side. - Scopes, selecting only what the app needs.
- System Prompt, optional. It customizes the Agent's behaviour for chat apps.
Scopes and approval
| Scope | Access | Approval |
|---|---|---|
profile |
Name, avatar, user ID, and guest status | Automatic |
chat |
Agent conversation, threads, containers, skills, media, jobs, wallet reads, and WebSockets | Admin review |
credit:read |
Credit balance and history | Admin review |
credit:write |
Top-ups, gift cards, and Points exchange; includes credit:read |
Admin review |
Selecting only profile gives the app a Client ID immediately. Any other scope enters admin review, and the Client ID is issued after approval. Origins from approved and active apps are added to the API CORS allowlist roughly every five minutes. Wait for that refresh after changing an origin.
Install the SDK
npm install starchild-auth-sdk
# or: yarn add starchild-auth-sdk
# or: pnpm add starchild-auth-sdk
For plain HTML, load the UMD build. It exposes window.StarchildAuth, the constructor itself:
<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. The ESM build, starchild-auth.js, requires a bundler or <script type="module">.
Initialize login
The default sign-in scope is profile. Add chat, credit:read, or credit:write only after the OAuth app is approved for those scopes.
<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',
scope: 'profile', // add approved scopes only when needed
onLogin: ({ accessToken, refreshToken, expiresIn, userInfo }) => {
// userInfo = { userInfoId, agentName, agentAvatar, isGuest }
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) {
if (error?.message?.includes('cancelled')) {
status.textContent = 'Sign-in was cancelled.'
} else if (error?.message?.includes('blocked')) {
status.textContent = 'The popup was blocked. Allow popups and try again.'
} else {
status.textContent = 'Login did not finish. Try again.'
}
}
})
logoutButton.addEventListener('click', () => auth.logout())
</script>
Call login() directly from a click or another user gesture. onLogin receives { accessToken, refreshToken, expiresIn, userInfo }. The SDK also calls it when it restores an existing session on page load.
Guest accounts and account binding
There is no loginAsGuest(). Guests and permanent accounts use the same auth.login() flow. A user can choose continue-as-guest in the Starchild popup, which returns userInfo.isGuest === true.
Binding a permanent login method must happen on Starchild. Do not build a guest-binding page in your app:
const user = await auth.fetchUserInfo()
if (auth.isGuest() || user?.isGuest) {
const newTab = auth.bindAccount()
if (!newTab) window.location.href = auth.getBindAccountUrl()
}
After the user binds Google, X, Email, Phone, or Wallet on Starchild, the next fetchUserInfo() or token refresh should show isGuest: false. Decide explicitly whether guests may perform payments or other irreversible actions. If they may not, require binding first.
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
Treat browser 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 identity. Reject missing, invalid, expired, or unverifiable tokens with 401 Unauthorized. Token refresh and logout use https://go-api.iamstarchild.com/v1/oauth/{refresh,logout}.
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 "",
"is_guest": bool(data.get("isGuest") or data.get("is_guest")),
}
Use userInfoId as the stable key for ownership, quotas, and application records. A 403 with { "detail": "Insufficient scope: …" } means the token lacks a requested scope. That differs from a 401 invalid or expired token. Surface the distinction and let the user re-authorize with the required scope.
For payments, one-time actions, and permission checks, fail closed when verification is unavailable.
Session behavior
- Access tokens last 15 minutes and refresh automatically every 12 minutes and on
visibilitychange. - Refresh tokens last 7 days and are stored in
localStorageunderstarchild_rt_{clientId}. autoLogin: truerestores a saved session on page load.auth.logout()clears the local session and revokes it throughPOST /v1/oauth/logouton go-api.- Prefer
refreshToken()and automatic refresh.getRefreshToken()exposes the in-memory copy of the value already stored locally. Treat it like a password: never log it, send it to third parties, or put it in a URL.
Keep a strict Content Security Policy, avoid unsafe third-party inline scripts, sanitize rendered content, and treat cross-site scripting as an account-security issue.
Next steps
Once the OAuth app is approved for the relevant scopes, the same SDK can access chat, threads, containers, skills, media, scheduled jobs, wallet reads, WebSockets, and credits. The Starchild Auth SDK API reference covers those namespaces, error handling, endpoints, and local testing.
Troubleshooting
| Problem | What to check |
|---|---|
| Popup blocked | Call login() directly from a click handler. |
| Origin mismatch | Match the third-party page origin exactly, including scheme and port. Never use localhost:6066 for the third-party app. |
| CORS fails on a new origin | Check allowed origins, approval and active status, then allow roughly five minutes for the CORS refresh. |
onLogin does not fire |
The user may have closed or rejected the popup. Check the login error message for cancelled or blocked. |
403 Insufficient scope: … |
Re-authorize with the required approved scope. |
userInfo.isGuest === true |
Use auth.bindAccount() to open Starchild Linked accounts. Do not build your own binding flow. |
| 401 after refresh | The 7-day refresh token expired. Ask the user to sign in again. |
Node or script cannot call login() |
Popup login requires a real browser and a user gesture. Log in in the browser, then use the access token for script requests. |
Launch checklist
- [ ] Request only the scopes you need.
- [ ] Register production and every local origin, never
localhost:6066. - [ ] Handle
userInfo.isGuestand offerauth.bindAccount(). - [ ] Catch scope errors and distinguish them from expired tokens.
- [ ] Keep refresh-token output out of logs, URLs, and third-party backends.
- [ ] Verify every protected request server-side and fail closed.
- [ ] Key application data by
userInfoId.
For constructor options and the full method and namespace reference, continue to the Starchild Auth SDK API reference.
Current SDK version
These examples are written against starchild-auth-sdk 0.4.1 and the starchild-auth integration guide 1.11.0.
User information
type UserInfo = {
userInfoId: string
agentName: string
agentAvatar: string
isGuest: boolean
}
Constructor options
| Option | Default | Purpose |
|---|---|---|
clientId |
Required | OAuth Client ID |
scope |
profile |
Space-separated scopes |
origin |
https://iamstarchild.com |
Login and account-binding site |
apiBase |
https://go-api.iamstarchild.com/v1 |
Token endpoints |
chatApiBase |
https://ai-api.iamstarchild.com |
Userinfo and Agent REST APIs |
clawdApiBase |
https://preview.iamstarchild.com |
Chat stream, jobs, and model endpoints |
clawdWsBase |
wss://preview.iamstarchild.com |
Sync, terminal, and metrics WebSockets |
creditApiBase |
https://credit.iamstarchild.com |
Credits API |
autoLogin |
true |
Restore a saved session |
refreshInterval |
720000 |
Refresh interval in milliseconds |
onLogin / onLogout / onTokenRefresh / onTokenRefreshFailed |
None | Session lifecycle callbacks |
Defaults point at production. Override them only when running the full Starchild backend locally.
Methods
login() · logout() · isLoggedIn() · getToken() · getRefreshToken() · getUserInfo() · fetchUserInfo() · isGuest() · getBindAccountUrl() · bindAccount() · refreshToken() · destroy()
For the 60+ chat, thread, container, skills, media, jobs, wallet, and credits methods, use the dedicated API reference.
API errors
JSON helpers throw the exported StarchildAuthError on non-2xx responses. It includes status, code, detail, path, insufficientScope, and response. sendMessage() and reconnectStream() return a raw Response, so check .ok yourself.
import { StarchildAuthError } from 'starchild-auth-sdk'
try {
await auth.credit.getBalance()
} catch (error) {
if (error instanceof StarchildAuthError && error.insufficientScope) {
await auth.login()
}
}
API reference
For all other methods, grouped aliases, endpoint paths, SSE events, fly-force-instance-id, server-side tokens, and the browser-versus-Node local-testing matrix, see the Starchild Auth SDK API reference.
Keep this page focused
This page is the sign-in path. The linked API reference carries the full Agent surface so a developer can get a working login first without searching through every chat and credits method.