DZDEV
// SDK · Sign in with dzbot

Sign in with dzbot

Let your players sign in to your own website or app with their dzbot account. They sign in on your server's dzbot page — linking their game account there if they haven't yet — confirm what your app may do, and land back in your app. You build no login, no account linking and no alt detection: dzbot is where your players are managed anyway.

// SetupSet up

In your server's admin area, open Developers, create an app and open its Sign in with dzbot tab:

The sign-in works on the servers the app is enabled for. The token it produces is bound to the one server the user signed in on.

// PermissionsScopes are access rights

A scope is a dzbot access right, the same keys you see in the group editor — wildcards included: player.shop.*, player.tickets.create, admin.tickets.view. Who the user is (player, main account, linked accounts) needs no scope.

What a token may do is the overlap of three things: the scopes the user confirmed, the app's own rights ceiling, and the user's real rights on the server. A player who confirms admin.* hands over nothing an admin could do, because they cannot do it themselves.

The consent page lists every concrete right a request covers within the ceiling, and exactly those are granted — a wildcard is resolved at that moment, so rights added to dzbot or to the ceiling later need a new sign-in.

Some endpoints stay closed to sign-in tokens whatever their scopes: the account, OAuth and organization areas, faction actions, live rooms, the claim, event sign-ups, impersonation and magic links, the AI Companion and the developer apps themselves (403 app_token_path_not_allowed).

// No backendIn the browser

A public client: no secret, PKCE, tokens in sessionStorage. dzbot is your whole backend.

import { Dzbot, OAuthClient } from '@dzbot/sdk'

const oauth = new OAuthClient({
  clientId: 'your-client-id',
  redirectUri: 'https://your-site.example/auth/callback',
  server: 'your-server',
})

// Sign-in button
location.href = await oauth.authorizeUrl({ scope: ['player.shop.access', 'player.shop.checkout'] })

// On /auth/callback
await oauth.handleCallback(location.href)

const dzbot = new Dzbot({ server: 'your-server', oauth })
const me = await dzbot.me()
console.log(me.player?.name, me.accounts)

// With a backendOn a server

A confidential client with its secret. Give every browser session its own TokenStore (memory for a single process, Redis or your database otherwise) — the store keeps the pending sign-in between redirect and callback and the tokens afterwards.

const oauth = new OAuthClient({
  clientId: process.env.DZBOT_CLIENT_ID,
  clientSecret: process.env.DZBOT_CLIENT_SECRET,
  redirectUri: 'https://your-site.example/auth/callback',
  server: 'your-server',
  store: sessionStore(req), // get(key), set(key, value), remove(key)
})

The starter bot does exactly this with Express.

// SessionsTokens

MethodDoes
authorizeUrl({ scope, state? })The URL to send the user to, on https://<server>.dzbot.de/oauth/authorize
handleCallback(url)Checks state, exchanges the code, stores the tokens. A refused consent throws OAuthError with code access_denied
accessToken()A valid access token, refreshed a minute before it runs out
refresh()Refreshes now; concurrent calls share one request. When dzbot refuses, the user is signed out (login_required)
isSignedIn() / tokens()Whether tokens are stored, and which
revoke()Signs out at dzbot and forgets the tokens

Access tokens live one hour. Refresh tokens rotate on every use and live 30 days for confidential clients, 7 days for public ones. Users see and revoke connected apps on their profile.

// Any languageWithout the SDK

Standard OAuth 2 authorization code with PKCE (S256):

GET https://<server>.dzbot.de/oauth/authorize?response_type=code&client_id=…&redirect_uri=…
    &scope=player.shop.access%20player.tickets.create&state=…&code_challenge=…&code_challenge_method=S256

POST https://api.dzbot.de/oauth/token   (application/x-www-form-urlencoded)
  grant_type=authorization_code&code=…&redirect_uri=…&client_id=…&code_verifier=…[&client_secret=…]
  grant_type=refresh_token&refresh_token=…&client_id=…[&client_secret=…]
→ { token_type, expires_in, access_token, refresh_token, scope }

POST https://api.dzbot.de/oauth/revoke  token=…

Call the API with Authorization: Bearer <access_token> and the X-Server-Subdomain of the server the user signed in on; another server answers 401 token_server_mismatch.