Ersatz Clients

Introduction

The ability for a client to take over the flow that another has initiated is referred to as substitution and clients that are specifically tasked with this as referred to as ersatz clients. We use the cognated word 𝕰𝖗s𝖆𝖙𝖟 (which just means substitute in German) because words like delegate, impersonate, substitute etc. are so overloaded in English that discourse is getting hard -- more time is spent on what the word means in context than anything else. An ersatz client has a very specific, narrow definition and there should be few if any conflicts with other nomenclature.

Definition of an ersatz client

An ersatz client in OA4MP is a specific type of client which may substitute for another in a flow. A client that starts a flow is called the provisioning client. A provisioning client is just a standard client in OA4MP and may be confidential or public.

Definition:If A is the provisioning client and α is an ersatz client we say write A ≻ α read A provisions α or α can substitute for A. When A provisions α, we say the flow has forked to α.

Forking a flow

Prerequisites

The provisioning client, A, must exist. The ersatz client α must exist. A must be set as the provisioner of α. This is normally done by an administrator. The steps then are

  1. Start the flow with A. Get access and refresh tokens for A.
  2. Start an exchange using the credentials of α and a valid refresh or access token from A. The requested token type is either omitted or for an access token.
  3. You will receive a response that includes an access token, refresh token and (if applicable) an id token
  4. At this point, the flow has been forked and you simply use the returned tokens with the credentials for α.

Additional notes:

  • Remeber that ersatz clients cannot start flows, they may only substitute for a provisioning client.
  • The scopes for α are restricted to be at most what A was granted. During the fork, you may downscope. At that point, the resulting scopes become the maximum for α.
  • If you do the fork and request a refresh token as the return type, you will get exactly a refresh token but can just do a regular exchange or refresh to get access tokens as needed. This can be useful if the ersatz client may have a long wait before actually needing an access token.
  • You may fork as freely as you like. Every exchange as per above will fork the flow.
  • OA4MP supports JWTs for authorization which means that A may start a completely automated flow and hand off its work to ersatz clients -- no human intervention is needed. Do read about service clients (OA4MP's name for a client that has sent a public key and can use a JWT).

A not uncommon use is to provision all the permissions for a job with A, then have specific forks for specific permissions (say a fork for read, one for writes, one for managing capabilities, such as starting or stopping jobs.) You could even have dedicated ersatz clients α, β,...

The rest of this document is for more detail or very specialized uses.


Ersatz Client Properties

  • All ersatz relations are explicit and must be set out of band.
  • Ersatz clients cannot start a flow.
  • Ersatz clients can only be substituted at the token exchange (RFC 8693) endpoint.
  • once provisioned, they are simply clients can be used at the refresh endpoint, revoked, introspected etc.
  • Ersatz clients are restricted in their scopes to whatever the provisioning client has and may only downscope.
  • If A ≻ α, then α inherits the configuration (lifetimes, scripts etc.) from A unless explicitly overridden.
  • Provisioning chains may have multiple elements: A ≻ α ≻ β ≻ γ ... which connotes an inheritance chain for abilities, not necessarily a custodial chain. Note especially that α, β and γ are ersatz client. There is exactly one provisioning client as the first element of the chain.
  • ≻ is not an ordering relationship! It is perfectly acceptable to have provision multiple clients, A, B, C and set A ≻ α, B ≻ α, C ≻ α. This is because these relations are set by security policies which may be quite arbitrary.
  • It is perfectly acceptable to have multiple ersatz clients, so A ≻ α, A ≻ β, A ≻ γ.

The token exchange specification has a section on impersonation and delegation semantics. It states in toto

When principal A impersonates principal B, A is given all the rights that B has within some defined rights context and is indistinguishable from B in that context. Thus, when principal A impersonates principal B, then insofar as any entity receiving such a token is concerned, they are actually dealing with B. It is true that some members of the identity system might have awareness that impersonation is going on, but it is not a requirement. For all intents and purposes, when A is impersonating B, A is B within the context of the rights authorized by the token. A's ability to impersonate B could be limited in scope or time, or even with a one- time-use restriction, whether via the contents of the token or an out-of-band mechanism.

What this means is that our substitutions are a form of impersonation and that the trust relations are made out of band. Why not call this impersonation? Because other OAuth systems are free to implement this any way they want, and we want to be clear exactly how we do it.

Note on provisioning chains

You may have ersatz clients substitute for other ersatz clients, so A ≻ α, α ≻ β and β ≻ γ is perfectly fine. This means that A provisions and that α may substitute for it. Later, β may substitute for α, but cannot substitute for A (unless that relation is made explicit). This allows for chains of substitution. We may write this chain more compactly as A ≻ α ≻ β ≻ γ

Creation of Ersatz Clients via the Client Management API

Ersatz clients may be created by an admin client, just like any other. Set the property ersatz_client to true and then set the provisioners. In order to set up the provisioners, you set the property org.oa4mp:/ersatz/provisioners to be either the provisioner ID (i.e., the client ID of the client that an start the flow) or an array of IDs. The zeroth element is always the main provisioner. The list of IDs is

provisionerID ≻ ersatzID_0 ≻ ersatzID_1 ≻ ... ≻ ersatzID_n

All of these must be administered by the same admin client and provision each other in sequence.

If you want the ersatz client to inherit the user metadata (i.e., the id token) then you should set ersatz_inherit_id_token to true. The default is true.

Inheritance etc. (advanced)

Normally if A ≻ α, then A starts the flow and in the token exchange, α presents the access or refresh token to the exchange endpoint. If α is presented for the first time, the flow forks. Note that A cannot resume the flow for α after this point and they become independent, meaning that if A provisions α with an access token, τ, then α exchanges it for τ', A cannot use τ'. On the other hand, if A ≻ β as well then A may provision β with τ too.

Why would you want intermediate ersatz clients?

If you have a very large and complex set of substitutions, you may streamline the flow. Let us say that you have provisioning clients A, B, C, D and you need to have finer grained control. An ersatz client ξ with a specific script (say allowing Lab X) could be used as A ≻ ξ ≻ α and B ≻ ξ ≻ β, (say α and β are used by specific groups within Lab X) so that α and β have consistent behavior but can add their own logic.

Inheritance from provisioners

Clients in OA4MP may set prototypes which are other clients from which they inherit configuration. There may be multiple prototypes (effectively yielding multiple inheritance). Note well that the order you set determines resolution. So if you have 2 prototype clients A and B (in that order) that means that the values of B override the values in A. The values of the base client override all others. This means that you can have a client that is little more than an id and secret which inherits everything else from its prototype, or just sets a single attribute even.

Since provisioning clients can form a prototype hierarchy, there is an attribute for clients namedextendsProvisioners and this means that the provisioners should be used as the prototype hierarchy. If you set this and specify prototypes, then provisioning clients are used first, followed by all specifically set prototypes.