Skip to content
TDACorpIDENTITYSign in

Installations for app developers

Which organizations your client can sign a person into, what tdaid_tenant does, when a token has no tid, and why every refusal looks the same.

On this page

Status: Available

The entry rule applies to every client and needs no flag. Installing a product into an organization is an operator action (TDACorp's tooling and admin interface), not an API your product calls.

An organization decides which products may act inside it. That decision is an installation: a record that your product is installed in one organization. The entry rule is the single check identity applies wherever a client would choose or reuse an organization: at sign-in, when a code is redeemed, when a refresh token is used, when a token is exchanged, and when a resource is requested. This page is what it means for the code you write. The person-facing side is in Organizations.

The rule#

A client may enter an organization if any one of these holds:

  1. The client is first-party (registered by TDACorp for its own products).
  2. The organization is the client's own.
  3. The organization has an active installation of the product the client belongs to.

A client that belongs to no product and is not first-party fails closed. A suspended installation does not count: either side can suspend it, the organization or the product's side, and it stays suspended while either side still holds it (installations.ts:27, installations.ts:339). To the caller, a suspended installation and no installation are the same thing.

To sign a person in, identity applies one more condition in the same predicate: the person must hold an active membership in the organization (installations.ts:373). A live membership is an active one that has not expired.

First-party products are installed automatically in a new organization with their full permission set (installations.ts:224). Your product is not, unless TDACorp has set it up that way.

Preselecting an organization: tdaid_tenant#

Add tdaid_tenant to the pushed authorization request to sign the person in to a specific organization. The value is the organization's canonical lower-case identifier, not its slug, and anything else is invalid_request. With the SDK, pass tenant to startAuthorization, or give the route helpers authorization.tenant for a fixed organization or getTenant(request) to choose one for each sign-in (store the organization's id when it installs your product, and never copy it unchecked from the query). A named organization wins over whichever organization the person last used.

If the request names no organization, identity looks at the one the person's session is in, and the outcomes are:

Your clientThe session's organizationResult
Can enter itanyThe token is for that organization: it carries tid and tslug.
Cannot enter it, and is not registered to require an organizationanyThe sign-in goes ahead without an organization. The token has no tid, and no organization policy or membership is read.
Registered to require an organizationnone selectedThe person is sent to the sign-in's workspace step to pick or create a workspace. Under prompt=none the request fails with access_denied.

These come from decideTenant and resolveTenant. Whether a client requires an organization is part of its registration; see Register a client.

Code that reads the token must therefore handle a missing tid. A tenantless token reaches only first-party audiences when exchanged.

One refusal for every reason#

When a named organization is refused, the answer is access_denied, always the same one: the account has no active membership in the selected tenant. The product is not installed, the organization does not exist, the person is not a member, the membership is suspended: your client cannot tell them apart, by design, so a product cannot probe which organizations exist or who belongs to them. It is the same under prompt=none. Do not build a message that says which cause it was, because you cannot know it.

Show the person a neutral screen ("you cannot sign in to that workspace with this app") and offer to choose another organization.

Resources are checked too#

When a request carries a resource (see Resource indicators), each one must be a registered resource, and unless your client is first-party it must belong to your client's own product. Once the organization is known, that resource's product must also be installed there, or the request fails with invalid_target, this resource is not available to this client. This check runs after the membership check, so only a member learns anything from it.

An installation that ends later#

The rule is re-applied each time a token is minted, not only at sign-in. If the installation is suspended or removed after a person signed in, a refresh, a code redemption or a token exchange that was for that organization fails with invalid_grant instead of issuing a token. Access tokens already issued live out their lifetime. Treat invalid_grant on a refresh as the end of that grant: send the person back through sign-in.

The same rule decides token exchange: the audience's client must be able to enter the subject's organization, or the exchange fails with invalid_target. See Service to service.

The decision API#

The decision API applies the installation rule to your service as well: it answers only for organizations where your product has an active installation, and clamps every answer to what the organization consented to for your product. See Authorization decision API.

Verified against commit ca46b57