Skip to content
TDACorpIDENTITYSign in

Get a client registered

What to prepare and send so an administrator can register your application, and the rules your registration must meet.

On this page

A client is identity's record of your application: its name, the redirect addresses it may be sent back to, the scopes it may ask for, the grants it may use, and the public half of its signing key. Identity only talks to clients it knows. You do not register a confidential client yourself. An administrator does it for you, so your job is to arrive with everything they need.

Who registers it#

TDACorp registers clients for its own products. For an application of yours, ask TDACorp or the administrator of the organization you build for. Either way you send the same information, and you get back a client id.

The account area also has a Developer apps page where a person can register a limited kind of client for themselves with Register an app. That tier is for single-page and native applications, offers only the openid, profile and email scopes, and is In development. It is behind the self_service_oauth_clients flag, which defaults OFF, and by default allows up to 5 apps per person with up to 5 redirect addresses each. Registering needs a personal workspace: a workspace that has been upgraded to an organization (Upgrade to an organization, on the organizations page) is no longer one. It is not the way to register a server-side application. The page itself says an app with a backend that authenticates with a key is registered by an administrator, not here.

Prepare your key#

Identity authenticates your application with a signed assertion (private_key_jwt). There is no client secret to leak: you keep the private key and send only the public half. Ed25519 and P-256 are the accepted key types.

import { generateKeyPairSync } from 'node:crypto'// Run once on your machine. Ed25519 and P-256 are the two key types identity accepts.const { publicKey, privateKey } = generateKeyPairSync('ed25519')// The key id names this key in every client assertion you sign, and in the request to register it.export const kid = 'app-key-1'// Send this and the kid to whoever registers your client. It is public.export const publicJwk = { ...publicKey.export({ format: 'jwk' }), kid }// Keep this in your secret manager. It never leaves your infrastructure.export const privateJwk = privateKey.export({ format: 'jwk' })

Registration refuses a key that carries any private member, so a mistake of sending the wrong half is caught rather than stored. Each key is registered under a key id (kid). If the key itself carries a kid, it must be the same one. Every assertion you sign names that kid in its header.

What to send#

ValueRule
NameFree text, up to 200 characters. A third-party client's name is shown on the consent screen.
Typeweb for a server-side application that can keep a private key, service for a backend that calls other services. See Choosing an integration.
Redirect URIsOne or more. See the rules below.
Post-logout redirect URIsWhere identity may send a person after sign-out. Optional. The same rules apply. See RP-initiated logout.
Back-channel logout URIAn https address of yours that identity calls when a session ends. Optional. See Back-channel logout.
ScopesThe scopes you will ask for, from the list in Scopes and consent. A scope you did not register cannot be requested.
Grantsauthorization_code and refresh_token for a web application, client_credentials and token_exchange for a service. A service may hold no other grant.
Token algorithmEdDSA or ES256, the algorithm identity signs the tokens it issues to your client with. EdDSA is the default and what the SDK expects unless you tell it otherwise.
Public key and kidFrom the script above.
Subject typepublic (the default) or pairwise, fixed at registration. A pairwise client sees a sub that is specific to its sector, so two such clients on different hosts cannot match a person by sub. Third-party clients only. The sector is the host that all the redirect addresses share, so a client whose addresses span hosts, or that uses a loopback host, cannot be pairwise. Access tokens and introspection keep the real sub. See ID token.

Redirect address rules#

Identity compares the address you send in a request to the registered addresses byte for byte, so the registered form has to be exactly what your code produces.

  • It must use https. The one exception is a native client still in its development stage, which may use http on localhost or 127.0.0.1.
  • No wildcard, no fragment, and no user name or password in the address.
  • It must be in canonical form: a lower-case host, no default port and an explicit path. https://app.example.com/api/auth/callback is canonical. https://App.Example.com and https://app.example.com:443/cb are not, and are refused.
  • A trailing slash, a different case in the path or a prefix is a different address.

Defaults you get#

SettingDefault
DPoP requiredOn for any client that holds authorization_code or refresh_token. A service client defaults to off, but a service client must prove a key to get tokens, so ask for DPoP to be required when you register one. See DPoP.
Access token lifetime600 seconds, and never more than 900.
Refresh token lifetime, third-party clientAt most 30 days in total, and a sliding window of at most 7 days. See Refresh tokens.
ConsentShown for a third-party client, skipped for a first-party client.

Which organizations your client can sign people into#

Registration also decides where a person can enter. A client can sign a person in to an organization only if the client is first-party, the organization is the client's own, or your product is installed in that organization and the person is an active member. Every other case is the same access_denied, so it does not reveal whether the organization exists. Resources and exchanged tokens follow the same rule. If a request names no organization and your client cannot enter the person's current one, the sign-in is organization-less: the token carries no tid, unless the client is registered to require an organization, in which case the person is asked to create a workspace first.

Without a registration: a metadata document#

Where the oauth_cimd flag is on (it defaults OFF), a client can skip registration and use an https address as its client_id. That address names a JSON document that identity fetches and checks. It is a request, narrowed, never a grant: no client secret, only none and private_key_jwt for authentication, only the code and refresh grants, only the openid, profile, email and offline_access scopes, DPoP always required, and redirect addresses held to the rules above. The address must be canonical, with a path, no query, no port and at most 512 characters. The consent screen shows the document's name and the address's host. Identity refreshes the document; if it has expired and cannot be refreshed, the client is unknown and its refresh tokens stop working.

What you get back#

You receive a client id. It is public, it is not a secret, and it begins with tdaid_c_. It is the client_id in every request, the iss and sub of your client assertions, and the aud of the ID token and logout tokens identity sends you. Keep the private key and any DPoP key in your secret manager, and keep the client id in configuration.

Registration does not give you a client secret, because none exists.

After registration#

  • Change anything by asking the administrator again: a new redirect address, another scope, a back-channel logout address. A request that uses an unregistered redirect address or scope is refused, and that is by design.
  • Rotate your key by sending a new public key and kid, switching your application to sign with it, and then asking for the old one to be revoked. A client can hold more than one key at a time, and identity picks the one named by the kid in the assertion. A key that is expired or revoked is not accepted.
  • A suspended client cannot sign anyone in. Requests from it are refused with invalid_client or unauthorized_client until it is enabled again.

If a request is refused#

What you seeUsual cause
invalid_clientThe client id is wrong, the client is suspended, no live key matches the assertion's kid, or the assertion does not verify. See Client authentication.
invalid_request mentioning redirect_uriThe address does not match a registered address exactly.
invalid_scopeYou asked for a scope the client is not registered to hold.
unauthorized_clientThe client does not hold the grant you used.

The complete list is in Error codes and Error handling.

Verified against commit a393e09