Token Model
How administrator sessions and `user_token` work together.
Downcity uses an administrator session token to manage Federation, and user_token to let end-user clients call services.
Create city / request user_tokenuser_token, and stores provider keys internally.user_token and calls services.No access to administrator credentialsuser_token
user_token is the call credential used by the end user or product. FederationAdmin requests it from Federation using bureau_id + user_id.
bureau_iduser_idmetadatattl
Product configuration supplies bureau_id when login starts. After login, service calls only carry
user_token; Federation reads bureau_id from the verified Token. The client never touches provider
keys or administrator session tokens.
User Tokens use one downcity:user audience. Federation and Bureau Servers validate the same audience;
a Bureau Server additionally compares the Token's bureau_id with its machine-bound Bureau ID.
bureau_id always remains the original product-provided value and is never composed into or normalized
as an audience.
Administrator session token
Use the administrator ID and password configured by fed deploy to sign in at /v1/admin/login. The response contains a time-limited administrator session token. Pass that token to FederationAdmin for trusted backend operations such as creating Bureaus, managing env, and requesting Federation to issue user_token. Passwords are stored as PBKDF2 digests; no administrator secret key is generated or provisioned as an environment variable.
const admin = new FederationAdmin({
base_url: "https://base.example.com",
credential: administrator_session_token,
});
const bureau = await admin.bureaus.create({
name: "Chrome Extension",
server_url: "https://bureau.example.com",
});Multi-city reuse
Many clients can belong to the same Bureau. When issuing a token, write bureau_id explicitly:
const user = await admin.service("accounts").action("tokens/issue").invoke({
bureau_id: bureau.bureau_id,
user_id: "user_123",
metadata: {
plan: "pro",
},
ttl: "7d",
});
console.log(user.user_token);