Background
XBuilder currently depends on our Casdoor fork for account sign-in and token issuance. The fork has diverged from upstream Casdoor and includes XBuilder-specific sign-in behavior.
This proposal is about replacing Casdoor with a first-party account system named XBuilder Account. The goal is not to build a generic Auth0 or Casdoor-style identity platform. The goal is to provide the account and identity foundation for XBuilder products.
Proposed direction
Build XBuilder Account as XBuilder's first-party account system. It should serve xbuilder.com and first-party sibling apps, use the existing user model as the account principal, and remove Casdoor concepts from the runtime model after migration.
XBuilder Account should be responsible for account identity, sign-in methods, account sessions, first-party app SSO, Account OAuth endpoints, account-management APIs, and admin audit logs. It should not manage product authorization data such as roles, plans, capabilities, quotas, memberships, seats, workspaces, or product permissions.
Goals
- Replace Casdoor with XBuilder Account
- Use the existing
user as the account principal
- Support third-party identity sign-in
- Support users that only have third-party identities
- Support admin-created users
- Support admin-managed password credentials
- Support username/password sign-in only for users that already have an admin-managed password
- Support hosted sign-in and SSO with account sessions, logout, and session revocation
- Support first-party sibling apps sharing the same XBuilder account identity
- Support platform-managed apps for first-party app SSO
- Support first-party apps using Account-issued token model or App-owned session model
- Provide an XBuilder Account admin module in XBuilder Admin Console under
xbuilder.com/admin/
- Keep XBuilder Account separate from XBuilder Authorization
- Support admin audit logging for account and authorization administration
Non-goals
- Public username/password registration
- Public password setup or password reset for ordinary users
- A generic multi-tenant identity platform
- An arbitrary public third-party app platform
- Self-service app registration
- Managing XBuilder or sibling app product authorization data such as roles, plans, capabilities, quotas, memberships, seats, workspaces, or product permissions
- Centralized management or synchronous revocation of app-owned product sessions
- User deletion or user status management
- Persisting provider access tokens, provider refresh tokens, or full raw provider profile payloads
- Preserving Casdoor user IDs, groups, or other Casdoor concepts in the new runtime model
Core concepts
user: the XBuilder account principal
identity_provider: platform configuration for a supported third-party identity provider
user_identity: a third-party identity bound to a user
user_password_credential: optional password credential managed by admins
user_session: account session managed by XBuilder Account for hosted sign-in and SSO continuity
app: platform-managed OAuth client configuration for first-party app SSO
app_secret: OAuth client credential used by confidential apps during backend token exchange
app_grant: authorization relationship between a user and an app
auth_flow: short-lived temporary state owned by XBuilder Account for provider callbacks, provider credential handoff, and OAuth authorization code flow, such as provider redirect state, pushed authorization request, authorization code, PKCE challenge, and one-time provider code consumption records
audit_log: admin and security audit events
app is the product and management resource name. The OAuth protocol layer should still use standard client concepts and parameter names such as client_id, client_secret, redirect_uri, request_uri, state, code, grant_type, code_challenge, and code_verifier.
XBuilder Account should reuse the existing user model, but Account APIs should only expose account-owned user fields such as id, username, displayName, and avatar. Product fields such as description, roles, plan, capabilities, statistics, and product-specific fields belong to XBuilder product APIs or XBuilder Authorization.
auth_flow defines lifecycle and security semantics, not physical storage. Provider redirect state, pushed authorization request, authorization code, PKCE challenge, and provider code consumption records should be short-lived. One-time states must be one-time use. Failed provider callbacks, failed token exchanges, and repeated submissions must not leave replayable valid state.
User sign-in behavior
Ordinary users should not be able to register with username/password.
By default, ordinary users sign in through third-party identities. The first batch of external identity providers should be:
wechat
qq
github
apple
google
x
Admins can create users. Admins can also set or clear a user's password. Only users with an admin-managed password can sign in with username/password.
A user's sign-in methods are third-party identities and admin-managed password credentials. Admins can remove any third-party identity and clear any password credential. A user may have no sign-in methods after these operations. In that state, the account remains present but the user cannot sign in until an admin sets a password credential again.
Removing a sign-in method does not automatically revoke existing account sessions. Session revocation is a separate operation.
Third-party identity
Provider identities should be matched by stable provider subject, not by email or username. When sharing account identity across first-party apps, XBuilder Account should prefer provider subjects that are stable across provider apps or clients.
Examples:
- WeChat:
unionid
- QQ:
unionid
- GitHub: numeric user ID
- Apple:
sub
- Google:
sub
- X: user ID
Apple should be handled as an OIDC-style provider. Account linking must use Apple's stable sub, not email, because Apple users may use private relay email addresses.
WeChat and QQ unionid availability depends on provider configuration, app binding relationships, and authorization results. openid is a provider app or client scoped identifier. It may be stored only as an auxiliary identifier and must not be used as the principal subject for unified account identity across first-party apps. If openid is stored, the corresponding subject namespace must also be stored.
If a WeChat or QQ sign-in result does not contain unionid, XBuilder Account should not silently create a cross-app account identity from openid. Account creation should rely on a verified cross-app stable provider subject. A provider-scoped identity may only be linked through a user-confirmed account-linking flow.
user_identity should store the provider, stable provider subject, subject namespace when needed, and the linked user.id. Provider username, display name, avatar, and similar metadata may be stored for display and troubleshooting. They should not be treated as authoritative XBuilder user fields.
Provider access tokens and refresh tokens should not be persisted. Full raw profile payloads should not be persisted. XBuilder Account should not act as a token vault for third-party API integrations. Product capabilities that need access to provider APIs should model, authorize, and store their own provider tokens separately.
External identity provider configuration should be managed by deployment configuration or server-controlled configuration. It is not part of the XBuilder Admin Console management scope.
First-party apps and hosted sign-in
First-party apps are apps registered and managed by the XBuilder platform. xbuilder.com should be treated as a first-party app using XBuilder Account, not as the account system itself. Sibling apps under *.xbuilder.com should also be supported as first-party apps.
Apps are platform-managed OAuth client configurations. They define redirect URI allowlists, allowed origins, client type, status, and credentials for first-party app SSO.
First-party apps share the same XBuilder account identity, but product authorization is managed by each app or by the corresponding product authorization system. XBuilder Account is responsible only for the account and SSO boundary, such as account existence, app status, redirect URI allowlists, credential checks, and token or code validity. App roles, memberships, workspaces, seats, and product permissions are product authorization concerns. They are not managed by XBuilder Account and should not be encoded in Account-issued app-scoped OAuth tokens.
First-party app product API integration should support two models:
- Account-issued token model: the app uses an Account-issued app-scoped OAuth token as its product API Bearer credential. The app backend validates the token through token introspection, verifies that the token is bound to the current app, and can read account fields from Account API when the token has the required Account API scope
- App-owned session model: the app backend uses XBuilder Account SSO to obtain stable
user.id, then creates, validates, refreshes, and revokes its own app session
Account-issued token model is appropriate for apps that do not want to maintain their own product session state and whose product requests happen in user-initiated request paths. The app frontend may hold the Account-issued app-scoped OAuth token. The app backend should treat that token as the product API credential for that app, validate it on product requests, extract stable user.id, and then apply its own product authorization logic.
App-owned session model is appropriate for apps that are independently deployed or operated, need their own refresh, logout, or revocation semantics, need to call XBuilder Account on behalf of a user outside the immediate frontend request path, or should not depend on XBuilder Account token validation for every product request. App-owned sessions reference stable user.id and carry the app's own authorization context. They are not independent accounts and are not sessions managed by XBuilder Account. XBuilder Account should not store or revoke app-owned sessions.
Regardless of the model, product authorization remains owned by the app or the corresponding product authorization system. Account sessions, Account-issued app-scoped OAuth tokens, and app sessions should not encode roles, memberships, workspaces, seats, or product permissions.
First-party app SSO should use OAuth 2.0 authorization code flow. The stable user-facing sign-in entry is account.xbuilder.com/sign-in, not XBuilder Account APIs. Hosted sign-in handles account resolution, user creation, third-party identity binding, app callback, and hosted interactions such as profile completion, account-linking confirmation, and identity conflict handling.
Public and confidential apps must both use PKCE in authorization code flow. Public apps should not rely on app secrets. Confidential apps may also use app secrets as OAuth client credentials for backend token exchange. Secret values should only be returned when they are created.
Sibling app frontends should normally integrate their own backend's OAuth facade and then talk only to their own backend after sign-in. They should not depend on non-OAuth XBuilder Account APIs as their stable integration surface. Sibling apps should use user.id as the stable account reference. Mutable fields such as username, displayName, and avatar may be cached by sibling apps for display, but XBuilder Account remains the authoritative source for these account fields.
Hosted sign-in and provider credential handoff
XBuilder Account should serve hosted sign-in at account.xbuilder.com/sign-in. Hosted sign-in should use account.xbuilder.com/api/* as a same-origin API facade that forwards to api.xbuilder.com/account/*. api.xbuilder.com/account/* remains the authoritative XBuilder Account API origin.
account.xbuilder.com/api/* is a same-origin entry point for Account Web, not a security boundary. XBuilder Account APIs should not relax authentication or authorization because a request came through the facade. The facade should not change OAuth, Bearer token, or account session cookie authentication semantics.
Hosted sign-in supports two ways to acquire provider credentials:
- Hosted provider acquisition: hosted sign-in redirects the user to the provider authorize page, and the provider callback returns the provider credential to XBuilder Account
- Provider credential handoff: the client obtains a short-lived provider code from a platform runtime or provider SDK before entering hosted sign-in, and passes it through the OAuth authorization request using XBuilder extension parameters such as
xbuilder_provider and xbuilder_provider_code
Provider credential handoff is intended for clients that can obtain a short-lived provider code before opening hosted sign-in, including WeChat Mini Programs and native apps using platform runtime or provider SDK APIs. For native iOS and Android apps, hosted sign-in should still be opened through the system browser or system authentication session, such as ASWebAuthenticationSession or Chrome Custom Tabs. They should not use embedded WebView.
Provider credential handoff only replaces the upstream provider web authorize stage. It does not replace the XBuilder Account sign-in flow. Regardless of whether the provider credential comes from hosted provider acquisition or handoff, XBuilder Account should use the same account resolution, user creation, and third-party identity binding logic. If profile completion, account-linking confirmation, or identity conflict handling is required, the interaction should happen inside hosted sign-in.
Only short-lived, one-time, authorization-code-like provider credentials should be passed through provider credential handoff. Allowed examples include WeChat Mini Program wx.login() code, Apple authorization code, and Google server auth code. Provider access tokens, provider refresh tokens, ID tokens, session_key, account session tokens, app access tokens, app refresh tokens, app secrets, and other long-lived secrets must not be passed through URL parameters, postMessage, or untrusted iframes.
Provider credential handoff errors should be reported through the PAR or authorize flow as OAuth errors. Expired, consumed, provider-mismatched, or unknown provider codes must not produce usable request_uri values. XBuilder Account should consume provider codes atomically with recording their consumption state.
OAuth and Account API boundary
XBuilder Account OAuth endpoints should live under api.xbuilder.com/account/oauth/*. They should contain OAuth and OAuth RFC extension protocol endpoints only, and should issue Account-issued app-scoped OAuth tokens.
Account-issued app-scoped OAuth token subject is stable user.id. Token client is the concrete app. The proposed Account API scope is account:user:read, which authorizes GET /account/user only. It should not express product authorization state or any app's product API permissions.
Account identity provider endpoints
GET /account/identity-providers
GET /account/identity-providers/{provider}/authorize
GET /account/identity-providers/{provider}/callback
POST /account/identity-providers/{provider}/callback
- These endpoints are provided by XBuilder Account backend and are primarily used by hosted sign-in
GET /account/identity-providers returns identity providers available for hosted sign-in in the current app context
GET /account/identity-providers/{provider}/authorize starts provider redirect when hosted sign-in did not receive a provider code through provider credential handoff
- Provider callback should support both GET and POST because the HTTP method depends on provider response mode, such as Sign in with Apple's
form_post
- Provider callback should rely on provider redirect state for callback correlation and CSRF protection
Account OAuth endpoints
POST /account/oauth/par
GET /account/oauth/authorize
POST /account/oauth/token
POST /account/oauth/introspect
POST /account/oauth/revoke
- OAuth protocol parameters should use standard names such as
client_id, redirect_uri, request_uri, state, code, grant_type, code_challenge, and code_verifier
- Confidential clients should use
client_secret_basic authentication
- Public clients should use
client_id in token exchange or revocation requests that need client identification
POST /account/oauth/par creates pushed authorization requests and can carry provider credential handoff parameters. The returned request_uri is an opaque, short-lived, single-use reference, not a dereferenceable URL
GET /account/oauth/authorize is a PAR-only OAuth authorization endpoint. It accepts client_id and request_uri; authorization request parameters such as response_type, redirect_uri, scope, state, and code_challenge must be pushed through POST /account/oauth/par first
POST /account/oauth/token is used for authorization code exchange and refresh token exchange
POST /account/oauth/introspect is RFC 7662 token introspection and should only be callable by authenticated app backends
POST /account/oauth/revoke revokes Account-issued app-scoped OAuth tokens or refresh tokens
Current account endpoints
GET /account/user
PATCH /account/user
GET /account/user/identities
POST /account/session
GET /account/session
DELETE /account/session
GET /account/sessions
DELETE /account/sessions
DELETE /account/sessions/{sessionID}
GET /account/user returns account-owned user fields. Hosted sign-in may authenticate with an account session cookie. App backends may authenticate with an Account-issued app-scoped OAuth token that has account:user:read
PATCH /account/user is only for Account Web authenticated with an account session cookie
GET /account/user and PATCH /account/user should not expose or update XBuilder product fields such as description
GET /account/user/identities returns the current user's third-party identities
account:user:read should not authorize GET /account/user/identities, account session endpoints, mutation endpoints, or admin endpoints
POST /account/session is called by Account Web to submit sign-in credentials. The backend validates the credentials before creating the current account session
- Account session endpoints manage hosted sign-in account sessions, not app-owned sessions
XBuilder Account admin endpoints
GET /admin/account/users
POST /admin/account/users
GET /admin/account/users/{userID}
PATCH /admin/account/users/{userID}
PUT /admin/account/users/{userID}/password
DELETE /admin/account/users/{userID}/password
GET /admin/account/users/{userID}/identities
DELETE /admin/account/users/{userID}/identities/{identityID}
GET /admin/account/users/{userID}/sessions
DELETE /admin/account/users/{userID}/sessions
DELETE /admin/account/sessions/{sessionID}
GET /admin/account/apps
POST /admin/account/apps
GET /admin/account/apps/{appID}
PATCH /admin/account/apps/{appID}
PUT /admin/account/apps/{appID}/status
GET /admin/account/apps/{appID}/secrets
POST /admin/account/apps/{appID}/secrets
DELETE /admin/account/apps/{appID}/secrets/{secretID}
- Apps should not have
DELETE semantics
- Apps should be taken out of service by updating app status so audit records, historical grants, and token relationships keep their context
XBuilder Authorization admin endpoints
GET /admin/authorization/users/{userID}
PATCH /admin/authorization/users/{userID}
- These endpoints share the XBuilder Admin API namespace, but they are not XBuilder Account APIs
- They manage XBuilder Authorization inputs for a user
- Writable fields are
roles and plan
capabilities and quota policies are derived by XBuilder Authorization and should be read-only
Admin audit endpoints
- These endpoints share the XBuilder Admin API namespace, but they are not XBuilder Account APIs
- They may include audit events from account, authorization, and other admin modules
Token and session strategy
XBuilder Account uses opaque tokens.
Account session tokens, authorization codes, Account-issued app-scoped OAuth tokens, and refresh tokens should be high-entropy random secrets. Token values should not encode user fields, product authorization state, or other mutable business state.
XBuilder Account should not issue JWTs. User identity and mutable account state should be resolved from server-side state and account APIs.
Hosted sign-in should use a __Host- prefixed cookie on account.xbuilder.com to maintain the account session for sign-in page state and SSO continuation. The cookie must use HttpOnly, Secure, SameSite=Lax, and Path=/, and must not set Domain. Account sessions should not be exposed to app frontends and should not be used as product API credentials.
Product API requests should use server-revocable opaque credentials. Account-issued token model uses Account-issued app-scoped OAuth tokens. App-owned session model uses credentials owned by the app backend, usually app access tokens and app refresh tokens.
Product API credential transport should be defined by each app. Bearer access tokens are the recommended default. Product API credentials must not be passed through URLs, postMessage, or untrusted iframes.
Access tokens should be short-lived, server-revocable, and should not encode user fields or product authorization state. Refresh tokens should outlive access tokens, be stored as server-side hashes, and rotate after each use. Reuse of a rotated refresh token should be treated as a replay signal and should revoke the corresponding token family or grant.
App frontends should refresh access tokens before they expire. If a product request receives 401, the frontend should refresh and retry the original request at most once. Concurrent refresh attempts in the same frontend runtime should be merged. Multi-tab web sessions should coordinate refresh and token updates to avoid concurrent use of the same rotating refresh token. If refresh fails, the frontend should clear local product API tokens and return to hosted sign-in.
OAuth token revocation should revoke Account-issued app-scoped OAuth tokens or refresh tokens. app_grant validity should be determined by the related token family and app status. Revoking an app_grant should revoke that grant's refresh token family and active access tokens, or disable the corresponding app. Account session revocation should affect hosted sign-in and subsequent SSO. App-owned session logout, refresh, and revocation are owned by the corresponding app backend.
Admin roles and admin surface
xbuilder.com/admin/ should be XBuilder Admin Console, not a frontend dedicated to XBuilder Account. It may host XBuilder Account, XBuilder Authorization, assets, courses, and other product admin modules.
The XBuilder Account admin permission identifier is accountAdmin.
accountAdmin grants access to the XBuilder Account admin module. It covers user management, admin-managed passwords, identity viewing and removal, account session revocation, app management, and app secret management.
The XBuilder Authorization admin permission identifier is authorizationAdmin.
authorizationAdmin grants access to the XBuilder Authorization admin module. It covers user roles, plan, derived capabilities, and quota policies.
Granting and evaluating accountAdmin and authorizationAdmin is part of XBuilder Authorization. accountAdmin does not manage XBuilder product authorization data such as roles, plans, capabilities, or quotas. Admins that need to manage both account and authorization data should have both permissions.
The first administrator should be provisioned through deployment configuration, migration scripts, or an operations command that is only available during initialization. If XBuilder Authorization is unavailable or cannot evaluate permissions, Admin APIs must not allow the request.
Admin audit logs should record account, authorization, and other admin module operations. Viewing audit logs is not part of the XBuilder Account admin module itself. The XBuilder Admin API should control visible audit events through backend RBAC.
The XBuilder Account admin module should cover:
- User list and user details
- User creation
- Setting and clearing admin-managed passwords
- Viewing and removing user identities
- Viewing and revoking user account sessions
- App management
- App secret creation and deletion
XBuilder product authorization data may appear in the same XBuilder Admin Console and on the same user detail page. It is not part of XBuilder Account.
The admin console frontend can live in the open-source frontend repository and be deployed at xbuilder.com/admin/.
The main frontend may expose an Admin Console entry point for users with admin permissions.
Relationship with product authorization
XBuilder Account is responsible for authentication, account identity, account sessions, and first-party app SSO. It does not centrally manage product authorization for first-party apps.
XBuilder roles, plans, capabilities, quotas, memberships, seats, and other product permissions belong to the product authorization system. Sibling apps should also have their own authorization models. XBuilder Account may provide stable user.id values to these systems, but it should not manage product authorization data or encode mutable authorization state in Account-issued app-scoped OAuth tokens.
Security boundary
The admin frontend is not a security boundary. Loading the admin console in a browser does not grant admin permissions. Admin APIs must be protected by backend RBAC and audit logging. api.xbuilder.com/admin/* should be restricted at ingress. Ingress restrictions can reduce exposure but cannot replace backend authorization checks.
Session tokens, refresh tokens, authorization codes, app secrets, and other security credentials must have enough entropy. request_uri values should be unguessable, short-lived, one-time use, and bound to the app. Authorization codes should be short-lived, one-time use, and bound to app, redirect URI, and PKCE. Authorization responses must return the original OAuth state for app validation. App secret values should only be returned when they are created.
XBuilder Account must validate redirect URI allowlists with exact string matching, with no prefix, wildcard, or path-prefix matching. It must also validate PKCE during token exchange. Apps must validate OAuth state and authorization requests. Provider identity linking must be based on stable provider subject, not email, username, or display fields.
App backends that accept Account-issued app-scoped OAuth tokens as product API credentials must validate token active status, token subject, and token client/app. Product API credentials must not be shared across apps.
Account sessions should only be used for hosted sign-in and SSO continuity. They should not be passed through URLs, postMessage, or untrusted iframes, and should not be directly read or persisted by app frontends.
Account Web mutation endpoints that authenticate with cookies should validate Origin or use equivalent CSRF protection.
Native iOS and Android apps should use the system browser or system authentication session for hosted sign-in, such as ASWebAuthenticationSession or Chrome Custom Tabs. They should not use embedded WebView. Restricted runtimes such as WeChat Mini Programs can use provider credential handoff to pass short-lived provider codes to hosted sign-in. They should not pass long-lived tokens through URLs or postMessage.
Migration direction
The migration should be one-time rather than gradual.
Casdoor should be treated only as a migration data source for users, identities, password credential information, and existing XBuilder product authorization data.
Historical WeChat or QQ identities that only have openid and no unionid must not be used for automatic cross-app account merging. They may be migrated as provider-scoped identities with subject namespace to the existing linked user, or upgraded with unionid before migration if they need to act as cross-app principal identities.
Existing XBuilder product authorization data such as roles and plans should be handled during the same one-time migration. After migration, this data should be managed by XBuilder Authorization. It should not be managed by XBuilder Account and should not be encoded in Account-issued app-scoped OAuth tokens.
After migration, runtime dependencies on Casdoor should be removed.
Casdoor-derived identity identifiers should only be used as one-time migration mapping keys. They should not remain part of the runtime account model.
Frontend migration should remove dependencies on Casdoor SDKs, Casdoor JWTs, and frontend token decoding for profile data. spx-gui may keep the Bearer request model, but the Bearer value should become a product API token.
Background
XBuilder currently depends on our Casdoor fork for account sign-in and token issuance. The fork has diverged from upstream Casdoor and includes XBuilder-specific sign-in behavior.
This proposal is about replacing Casdoor with a first-party account system named XBuilder Account. The goal is not to build a generic Auth0 or Casdoor-style identity platform. The goal is to provide the account and identity foundation for XBuilder products.
Proposed direction
Build XBuilder Account as XBuilder's first-party account system. It should serve
xbuilder.comand first-party sibling apps, use the existingusermodel as the account principal, and remove Casdoor concepts from the runtime model after migration.XBuilder Account should be responsible for account identity, sign-in methods, account sessions, first-party app SSO, Account OAuth endpoints, account-management APIs, and admin audit logs. It should not manage product authorization data such as roles, plans, capabilities, quotas, memberships, seats, workspaces, or product permissions.
Goals
useras the account principalxbuilder.com/admin/Non-goals
Core concepts
user: the XBuilder account principalidentity_provider: platform configuration for a supported third-party identity provideruser_identity: a third-party identity bound to auseruser_password_credential: optional password credential managed by adminsuser_session: account session managed by XBuilder Account for hosted sign-in and SSO continuityapp: platform-managed OAuth client configuration for first-party app SSOapp_secret: OAuth client credential used by confidential apps during backend token exchangeapp_grant: authorization relationship between auserand anappauth_flow: short-lived temporary state owned by XBuilder Account for provider callbacks, provider credential handoff, and OAuth authorization code flow, such as provider redirect state, pushed authorization request, authorization code, PKCE challenge, and one-time provider code consumption recordsaudit_log: admin and security audit eventsappis the product and management resource name. The OAuth protocol layer should still use standard client concepts and parameter names such asclient_id,client_secret,redirect_uri,request_uri,state,code,grant_type,code_challenge, andcode_verifier.XBuilder Account should reuse the existing
usermodel, but Account APIs should only expose account-owned user fields such asid,username,displayName, andavatar. Product fields such asdescription,roles,plan,capabilities, statistics, and product-specific fields belong to XBuilder product APIs or XBuilder Authorization.auth_flowdefines lifecycle and security semantics, not physical storage. Provider redirect state, pushed authorization request, authorization code, PKCE challenge, and provider code consumption records should be short-lived. One-time states must be one-time use. Failed provider callbacks, failed token exchanges, and repeated submissions must not leave replayable valid state.User sign-in behavior
Ordinary users should not be able to register with username/password.
By default, ordinary users sign in through third-party identities. The first batch of external identity providers should be:
wechatqqgithubapplegooglexAdmins can create users. Admins can also set or clear a user's password. Only users with an admin-managed password can sign in with username/password.
A user's sign-in methods are third-party identities and admin-managed password credentials. Admins can remove any third-party identity and clear any password credential. A user may have no sign-in methods after these operations. In that state, the account remains present but the user cannot sign in until an admin sets a password credential again.
Removing a sign-in method does not automatically revoke existing account sessions. Session revocation is a separate operation.
Third-party identity
Provider identities should be matched by stable provider subject, not by email or username. When sharing account identity across first-party apps, XBuilder Account should prefer provider subjects that are stable across provider apps or clients.
Examples:
unionidunionidsubsubApple should be handled as an OIDC-style provider. Account linking must use Apple's stable
sub, not email, because Apple users may use private relay email addresses.WeChat and QQ
unionidavailability depends on provider configuration, app binding relationships, and authorization results.openidis a provider app or client scoped identifier. It may be stored only as an auxiliary identifier and must not be used as the principal subject for unified account identity across first-party apps. Ifopenidis stored, the corresponding subject namespace must also be stored.If a WeChat or QQ sign-in result does not contain
unionid, XBuilder Account should not silently create a cross-app account identity fromopenid. Account creation should rely on a verified cross-app stable provider subject. A provider-scoped identity may only be linked through a user-confirmed account-linking flow.user_identityshould store the provider, stable provider subject, subject namespace when needed, and the linkeduser.id. Provider username, display name, avatar, and similar metadata may be stored for display and troubleshooting. They should not be treated as authoritative XBuilder user fields.Provider access tokens and refresh tokens should not be persisted. Full raw profile payloads should not be persisted. XBuilder Account should not act as a token vault for third-party API integrations. Product capabilities that need access to provider APIs should model, authorize, and store their own provider tokens separately.
External identity provider configuration should be managed by deployment configuration or server-controlled configuration. It is not part of the XBuilder Admin Console management scope.
First-party apps and hosted sign-in
First-party apps are apps registered and managed by the XBuilder platform.
xbuilder.comshould be treated as a first-party app using XBuilder Account, not as the account system itself. Sibling apps under*.xbuilder.comshould also be supported as first-party apps.Apps are platform-managed OAuth client configurations. They define redirect URI allowlists, allowed origins, client type, status, and credentials for first-party app SSO.
First-party apps share the same XBuilder account identity, but product authorization is managed by each app or by the corresponding product authorization system. XBuilder Account is responsible only for the account and SSO boundary, such as account existence, app status, redirect URI allowlists, credential checks, and token or code validity. App roles, memberships, workspaces, seats, and product permissions are product authorization concerns. They are not managed by XBuilder Account and should not be encoded in Account-issued app-scoped OAuth tokens.
First-party app product API integration should support two models:
user.id, then creates, validates, refreshes, and revokes its own app sessionAccount-issued token model is appropriate for apps that do not want to maintain their own product session state and whose product requests happen in user-initiated request paths. The app frontend may hold the Account-issued app-scoped OAuth token. The app backend should treat that token as the product API credential for that app, validate it on product requests, extract stable
user.id, and then apply its own product authorization logic.App-owned session model is appropriate for apps that are independently deployed or operated, need their own refresh, logout, or revocation semantics, need to call XBuilder Account on behalf of a user outside the immediate frontend request path, or should not depend on XBuilder Account token validation for every product request. App-owned sessions reference stable
user.idand carry the app's own authorization context. They are not independent accounts and are not sessions managed by XBuilder Account. XBuilder Account should not store or revoke app-owned sessions.Regardless of the model, product authorization remains owned by the app or the corresponding product authorization system. Account sessions, Account-issued app-scoped OAuth tokens, and app sessions should not encode roles, memberships, workspaces, seats, or product permissions.
First-party app SSO should use OAuth 2.0 authorization code flow. The stable user-facing sign-in entry is
account.xbuilder.com/sign-in, not XBuilder Account APIs. Hosted sign-in handles account resolution, user creation, third-party identity binding, app callback, and hosted interactions such as profile completion, account-linking confirmation, and identity conflict handling.Public and confidential apps must both use PKCE in authorization code flow. Public apps should not rely on app secrets. Confidential apps may also use app secrets as OAuth client credentials for backend token exchange. Secret values should only be returned when they are created.
Sibling app frontends should normally integrate their own backend's OAuth facade and then talk only to their own backend after sign-in. They should not depend on non-OAuth XBuilder Account APIs as their stable integration surface. Sibling apps should use
user.idas the stable account reference. Mutable fields such asusername,displayName, andavatarmay be cached by sibling apps for display, but XBuilder Account remains the authoritative source for these account fields.Hosted sign-in and provider credential handoff
XBuilder Account should serve hosted sign-in at
account.xbuilder.com/sign-in. Hosted sign-in should useaccount.xbuilder.com/api/*as a same-origin API facade that forwards toapi.xbuilder.com/account/*.api.xbuilder.com/account/*remains the authoritative XBuilder Account API origin.account.xbuilder.com/api/*is a same-origin entry point for Account Web, not a security boundary. XBuilder Account APIs should not relax authentication or authorization because a request came through the facade. The facade should not change OAuth, Bearer token, or account session cookie authentication semantics.Hosted sign-in supports two ways to acquire provider credentials:
xbuilder_providerandxbuilder_provider_codeProvider credential handoff is intended for clients that can obtain a short-lived provider code before opening hosted sign-in, including WeChat Mini Programs and native apps using platform runtime or provider SDK APIs. For native iOS and Android apps, hosted sign-in should still be opened through the system browser or system authentication session, such as
ASWebAuthenticationSessionor Chrome Custom Tabs. They should not use embedded WebView.Provider credential handoff only replaces the upstream provider web authorize stage. It does not replace the XBuilder Account sign-in flow. Regardless of whether the provider credential comes from hosted provider acquisition or handoff, XBuilder Account should use the same account resolution, user creation, and third-party identity binding logic. If profile completion, account-linking confirmation, or identity conflict handling is required, the interaction should happen inside hosted sign-in.
Only short-lived, one-time, authorization-code-like provider credentials should be passed through provider credential handoff. Allowed examples include WeChat Mini Program
wx.login()code, Apple authorization code, and Google server auth code. Provider access tokens, provider refresh tokens, ID tokens,session_key, account session tokens, app access tokens, app refresh tokens, app secrets, and other long-lived secrets must not be passed through URL parameters,postMessage, or untrusted iframes.Provider credential handoff errors should be reported through the PAR or authorize flow as OAuth errors. Expired, consumed, provider-mismatched, or unknown provider codes must not produce usable
request_urivalues. XBuilder Account should consume provider codes atomically with recording their consumption state.OAuth and Account API boundary
XBuilder Account OAuth endpoints should live under
api.xbuilder.com/account/oauth/*. They should contain OAuth and OAuth RFC extension protocol endpoints only, and should issue Account-issued app-scoped OAuth tokens.Account-issued app-scoped OAuth token subject is stable
user.id. Token client is the concreteapp. The proposed Account API scope isaccount:user:read, which authorizesGET /account/useronly. It should not express product authorization state or any app's product API permissions.Account identity provider endpoints
GET /account/identity-providersreturns identity providers available for hosted sign-in in the current app contextGET /account/identity-providers/{provider}/authorizestarts provider redirect when hosted sign-in did not receive a provider code through provider credential handoffform_postAccount OAuth endpoints
client_id,redirect_uri,request_uri,state,code,grant_type,code_challenge, andcode_verifierclient_secret_basicauthenticationclient_idin token exchange or revocation requests that need client identificationPOST /account/oauth/parcreates pushed authorization requests and can carry provider credential handoff parameters. The returnedrequest_uriis an opaque, short-lived, single-use reference, not a dereferenceable URLGET /account/oauth/authorizeis a PAR-only OAuth authorization endpoint. It acceptsclient_idandrequest_uri; authorization request parameters such asresponse_type,redirect_uri,scope,state, andcode_challengemust be pushed throughPOST /account/oauth/parfirstPOST /account/oauth/tokenis used for authorization code exchange and refresh token exchangePOST /account/oauth/introspectis RFC 7662 token introspection and should only be callable by authenticated app backendsPOST /account/oauth/revokerevokes Account-issued app-scoped OAuth tokens or refresh tokensCurrent account endpoints
GET /account/userreturns account-owned user fields. Hosted sign-in may authenticate with an account session cookie. App backends may authenticate with an Account-issued app-scoped OAuth token that hasaccount:user:readPATCH /account/useris only for Account Web authenticated with an account session cookieGET /account/userandPATCH /account/usershould not expose or update XBuilder product fields such asdescriptionGET /account/user/identitiesreturns the current user's third-party identitiesaccount:user:readshould not authorizeGET /account/user/identities, account session endpoints, mutation endpoints, or admin endpointsPOST /account/sessionis called by Account Web to submit sign-in credentials. The backend validates the credentials before creating the current account sessionXBuilder Account admin endpoints
DELETEsemanticsXBuilder Authorization admin endpoints
rolesandplancapabilitiesand quota policies are derived by XBuilder Authorization and should be read-onlyAdmin audit endpoints
Token and session strategy
XBuilder Account uses opaque tokens.
Account session tokens, authorization codes, Account-issued app-scoped OAuth tokens, and refresh tokens should be high-entropy random secrets. Token values should not encode user fields, product authorization state, or other mutable business state.
XBuilder Account should not issue JWTs. User identity and mutable account state should be resolved from server-side state and account APIs.
Hosted sign-in should use a
__Host-prefixed cookie onaccount.xbuilder.comto maintain the account session for sign-in page state and SSO continuation. The cookie must useHttpOnly,Secure,SameSite=Lax, andPath=/, and must not setDomain. Account sessions should not be exposed to app frontends and should not be used as product API credentials.Product API requests should use server-revocable opaque credentials. Account-issued token model uses Account-issued app-scoped OAuth tokens. App-owned session model uses credentials owned by the app backend, usually app access tokens and app refresh tokens.
Product API credential transport should be defined by each app. Bearer access tokens are the recommended default. Product API credentials must not be passed through URLs,
postMessage, or untrusted iframes.Access tokens should be short-lived, server-revocable, and should not encode user fields or product authorization state. Refresh tokens should outlive access tokens, be stored as server-side hashes, and rotate after each use. Reuse of a rotated refresh token should be treated as a replay signal and should revoke the corresponding token family or grant.
App frontends should refresh access tokens before they expire. If a product request receives 401, the frontend should refresh and retry the original request at most once. Concurrent refresh attempts in the same frontend runtime should be merged. Multi-tab web sessions should coordinate refresh and token updates to avoid concurrent use of the same rotating refresh token. If refresh fails, the frontend should clear local product API tokens and return to hosted sign-in.
OAuth token revocation should revoke Account-issued app-scoped OAuth tokens or refresh tokens.
app_grantvalidity should be determined by the related token family and app status. Revoking anapp_grantshould revoke that grant's refresh token family and active access tokens, or disable the corresponding app. Account session revocation should affect hosted sign-in and subsequent SSO. App-owned session logout, refresh, and revocation are owned by the corresponding app backend.Admin roles and admin surface
xbuilder.com/admin/should be XBuilder Admin Console, not a frontend dedicated to XBuilder Account. It may host XBuilder Account, XBuilder Authorization, assets, courses, and other product admin modules.The XBuilder Account admin permission identifier is
accountAdmin.accountAdmingrants access to the XBuilder Account admin module. It covers user management, admin-managed passwords, identity viewing and removal, account session revocation, app management, and app secret management.The XBuilder Authorization admin permission identifier is
authorizationAdmin.authorizationAdmingrants access to the XBuilder Authorization admin module. It covers user roles, plan, derived capabilities, and quota policies.Granting and evaluating
accountAdminandauthorizationAdminis part of XBuilder Authorization.accountAdmindoes not manage XBuilder product authorization data such as roles, plans, capabilities, or quotas. Admins that need to manage both account and authorization data should have both permissions.The first administrator should be provisioned through deployment configuration, migration scripts, or an operations command that is only available during initialization. If XBuilder Authorization is unavailable or cannot evaluate permissions, Admin APIs must not allow the request.
Admin audit logs should record account, authorization, and other admin module operations. Viewing audit logs is not part of the XBuilder Account admin module itself. The XBuilder Admin API should control visible audit events through backend RBAC.
The XBuilder Account admin module should cover:
XBuilder product authorization data may appear in the same XBuilder Admin Console and on the same user detail page. It is not part of XBuilder Account.
The admin console frontend can live in the open-source frontend repository and be deployed at
xbuilder.com/admin/.The main frontend may expose an Admin Console entry point for users with admin permissions.
Relationship with product authorization
XBuilder Account is responsible for authentication, account identity, account sessions, and first-party app SSO. It does not centrally manage product authorization for first-party apps.
XBuilder roles, plans, capabilities, quotas, memberships, seats, and other product permissions belong to the product authorization system. Sibling apps should also have their own authorization models. XBuilder Account may provide stable
user.idvalues to these systems, but it should not manage product authorization data or encode mutable authorization state in Account-issued app-scoped OAuth tokens.Security boundary
The admin frontend is not a security boundary. Loading the admin console in a browser does not grant admin permissions. Admin APIs must be protected by backend RBAC and audit logging.
api.xbuilder.com/admin/*should be restricted at ingress. Ingress restrictions can reduce exposure but cannot replace backend authorization checks.Session tokens, refresh tokens, authorization codes, app secrets, and other security credentials must have enough entropy.
request_urivalues should be unguessable, short-lived, one-time use, and bound to the app. Authorization codes should be short-lived, one-time use, and bound to app, redirect URI, and PKCE. Authorization responses must return the original OAuth state for app validation. App secret values should only be returned when they are created.XBuilder Account must validate redirect URI allowlists with exact string matching, with no prefix, wildcard, or path-prefix matching. It must also validate PKCE during token exchange. Apps must validate OAuth state and authorization requests. Provider identity linking must be based on stable provider subject, not email, username, or display fields.
App backends that accept Account-issued app-scoped OAuth tokens as product API credentials must validate token active status, token subject, and token client/app. Product API credentials must not be shared across apps.
Account sessions should only be used for hosted sign-in and SSO continuity. They should not be passed through URLs,
postMessage, or untrusted iframes, and should not be directly read or persisted by app frontends.Account Web mutation endpoints that authenticate with cookies should validate
Originor use equivalent CSRF protection.Native iOS and Android apps should use the system browser or system authentication session for hosted sign-in, such as
ASWebAuthenticationSessionor Chrome Custom Tabs. They should not use embedded WebView. Restricted runtimes such as WeChat Mini Programs can use provider credential handoff to pass short-lived provider codes to hosted sign-in. They should not pass long-lived tokens through URLs orpostMessage.Migration direction
The migration should be one-time rather than gradual.
Casdoor should be treated only as a migration data source for users, identities, password credential information, and existing XBuilder product authorization data.
Historical WeChat or QQ identities that only have
openidand nounionidmust not be used for automatic cross-app account merging. They may be migrated as provider-scoped identities with subject namespace to the existing linked user, or upgraded withunionidbefore migration if they need to act as cross-app principal identities.Existing XBuilder product authorization data such as roles and plans should be handled during the same one-time migration. After migration, this data should be managed by XBuilder Authorization. It should not be managed by XBuilder Account and should not be encoded in Account-issued app-scoped OAuth tokens.
After migration, runtime dependencies on Casdoor should be removed.
Casdoor-derived identity identifiers should only be used as one-time migration mapping keys. They should not remain part of the runtime account model.
Frontend migration should remove dependencies on Casdoor SDKs, Casdoor JWTs, and frontend token decoding for profile data.
spx-guimay keep the Bearer request model, but the Bearer value should become a product API token.