Skip to content

Delegated platform applications

A platform app lets a project operate as a third-party service provider for customer-authorized accounts. It is not platform-console SSO, a chat account, or a generic resource. A first-party social login uses its own app credential; a delegated social authorizer instead names platform_app and the authorized account app_id.

config.providerDelegated social providersTicket / customer authorization
weixin_tpweixin_mp, weixin_miniComponent ticket; customer authorization link; account-specific message callback
wecom_tpwecomSuite ticket; installation link; corp-specific message callback
alipay_isvalipay, alipay_miniRSA-signed ISV gateway, merchant app_auth_token; no pushed platform ticket
dingtalk_isvdingtalk, dingtalk_appSuite ticket and corp authorization pushes; no generated customer authorization URL
douyin_tpdouyin_mini onlyThird-party mini-program ticket, authorization link and rotating delegated credentials
kuaishou_tpkuaishou_mini onlyProvider-specific push scheme/key format, authorization link and rotating delegated credentials

A Douyin/Kuaishou mini-program platform is not delegated web-login support. A WeChat component cannot stand in for weixin_open, and WeCom is not interchangeable with WeChat. Config validation checks these pairings. Support here does not promise every provider API, merchant payment feature, publishing workflow, app-market approval or arbitrary event-to-script automation.

You need a registered/approved provider application with the applicable capabilities, HTTPS public callbacks reachable by that provider, stable public instance URL, and project editor membership (owner, admin, developer). This page does not perform or prove provider registration. For intranet deployments, do not assume a private callback becomes reachable merely because Viat accepted its URL.

Management routes:

RouteMeaning
GET/POST /projects/{slug}/platformsList/create apps
GET/PUT/DELETE .../platforms/{name}Read/update/delete app
GET .../platforms/{name}/authorizationsList customer authorizations
DELETE .../platforms/{name}/authorizations/{authorizer_ref}Delete local authorization and evict account-token cache
POST .../platforms/{name}/authorize-urlNo request body; mint fresh customer URL where supported; returns {url,redirect_uri}

These reads also require editor membership. They are not anonymous callback endpoints. Provider callbacks are public protocol routes with provider signature/encryption verification, not platform bearer management.

Create requires {name,config,secret}. name is the project-local app identifier; use a path-safe fixture name such as fixture-component. config must include provider, app_id; app_id nonblank <=128 bytes. Secret is required on creation even though its DTO field is optional. Provider cannot change on update; name is the route identifier, not an update field.

Config fieldDefault / validation
providerRequired one of the six literals above
app_idRequired component appid/suite id/ISV application id
alipay_public_keyAlipay ISV required verification RSA public key; public PEM/base64
post_authorize_redirect_uriOmitted/null = Viat completion page; supplied URL must parse
api_base, auth_baseProvider defaults; optional test/egress origin overrides, HTTPS or loopback HTTP
legacy_api_baseDingTalk legacy tmp_auth_code gateway override; default https://oapi.dingtalk.com; same origin validation

For WeChat/WeCom/DingTalk/Douyin/Kuaishou, secret needs nonblank app_secret (<=1024 bytes), token callback signature token (<=128 bytes), encoding_aes_key. Ordinary EncodingAESKey is 43-character provider material; Kuaishou uses base64 of 32 bytes, not that same text format. For Alipay use app_private_key (valid RSA PEM/base64) and config public key; no ticket/token/EncodingAESKey requirement. Never copy real callback tokens or private keys into examples.

Source-derived unexecuted request body for POST /projects/demo/platforms:

{"name":"fixture-component","config":{"provider":"weixin_tp","app_id":"fixture-component-appid","post_authorize_redirect_uri":"https://app.example.invalid/authorized"},"secret":{"app_secret":"REPLACE_WITH_PROVIDER_SECRET","token":"REPLACE_WITH_CALLBACK_TOKEN","encoding_aes_key":"REPLACE_WITH_VALID_PROVIDER_AES_KEY"}}

Placeholders intentionally do not constitute a valid registration/cryptographic key. Submit provider-issued values only in an approved live setup, not this documentation exercise.

Update PUT .../{name}: omitted/null config keeps it, supplied config replaces the whole object; omitted/null secret keeps it. Supplied secret merges nonblank fields over stored fields; blank strings keep old values. Thus rotating app_secret does not blank the callback token/AES key. There is no “blank means erase secret” operation. Update evicts cached app tokens so old credentials are not reused. Changing provider requires a different app, not a partial secret patch.

App responses return id, name, provider, config, uses_ticket, has_ticket, nullable ticket_at, callback_url, nullable message_callback_url, has_authorize_url, nullable authorized_url, created_at, updated_at. They do not expose secret blobs or ticket strings. Copy the returned URLs, preserving deployment base prefixes and escaped names:

  • Provider push/verification: BASE/p/demo/_platform/fixture-component/callback (GET/POST).
  • WeChat account message callback: same root /callback/$APPID$.
  • WeCom corp message callback: same root /callback/$CORPID$.
  • Customer return where supported: same root /authorized (GET).
  1. Create the app with matching provider credentials. Register its returned callback and provider-specific account callback template in the provider console.
  2. Complete provider callback verification. Ticket-based providers need a real pushed ticket; has_ticket:false means waiting, not a working authorization flow. Alipay uses_ticket:false is normal.
  3. For providers with has_authorize_url:true, request a fresh authorization URL and send the customer there. Preauthorization codes expire; don’t permanently publish a generated link. DingTalk instead authorizes via app market/in-app activation/provider-console deployment QR and sends the result as a push; its authorize-url call fails with a diagnostic.
  4. Customer return/push stores account authorization and encrypted credential, or marks revocation. Signed state checks protect the applicable public return flows; do not manufacture return query parameters.
  5. Inspect authorizations and only then create a compatible social authorizer with {provider,app_id:<authorized account>,platform_app:<name>}. This authorizer still needs a role and published endpoint admission.
  6. Perform an opt-in real login using provider-issued code; static config/has_ticket is not proof customer SSO works.

Authorization list items: id, authorizer_ref, kind, nullable display_name, scopes array, meta object, status (active/revoked), authorized_at, nullable revoked_at, updated_at. Credentials and encrypted credential bytes are omitted. An active row reflects authorization state held by Viat, not a guarantee every provider scope or downstream request will succeed.

Delegated customer refresh/authorization material stays platform-side. Douyin/Kuaishou rotating single-use refresh credentials cannot be copied to two independent refreshers safely. Do not pollute logs with full upstream payloads. Social Alipay mini-program phone collection can still need the mini-program’s own AES key even when ISV credentials supply gateway access.

Provider revocation events mark local authorizations revoked and invalidate their use. Deleting a local authorization is local cleanup, not a provider-side customer deauthorization request. Deleting an app is blocked (409 platform.in_use detail) while social authorizers still reference it; repoint/delete those deliberately first. App deletion removes local state and caches; it does not cancel the upstream app registration or customer contract.

Rotate nonblank secret fields in place, validate callback verification and fresh delegated login, then retire upstream old material in the provider-approved order. Callback-token/AES rotation may require coordinated provider changes; a temporarily mismatched push configuration is not fixed by disabling verification. For offboarding, disable dependent login entry points, revoke/deauthorize at the provider as required, delete local authorization/app dependencies, and separately remove project users/roles if intended.

Check family mismatch, app ID, key encoding, stable callback origin, provider scopes, first ticket, active customer authorization and provider network/trust before retrying. Invalid config = 400, duplicate name = conflict, missing app = 404, noneditor = 403; signature/decryption failures must not be treated as accepted pushes. A callback HTTP acknowledgement is not proof an end user has successfully logged in or a message reached a script.

No provider registration, authorization, notification, ticket injection, SSO, network call or remote cleanup was executed. Verification here is source inspection only. Evidence: crates/viat-api/src/platform/{mod.rs,handlers.rs,weixin_tp.rs,wecom_tp.rs,alipay_isv.rs,dingtalk_isv.rs,douyin_tp.rs,kuaishou_tp.rs}, crates/viat-api/src/{social/mod.rs,handlers/authorizers.rs,lib.rs}. Authorizers owns the complete social config; existing resource pages own resource credentials.