Clients have the ability to take the non-trivial settings from other clients, called prototypes. Non-trivial settings are those that are greater than equal to zero (if a number, like a token lifetime), non-empty if a string or date. The names of clients are never overwritten. The list of prototypes are processed in order, the values of each override the previous. The current client is the final one processed, then the resulting client is used.
You may set prototypes as a list of identifiers in the client management API. This is the content of the sample prototype-minimal.json configuration bundled with the oidc-cm toolkit
{ "comment":["This is a the minimal request object required for creation: a set of URIs and a name", "This also sets prototypes, so that all the settings will be taken from them." ], "redirect_uris":["https://client.example.org/callback"], "client_name": "My Example", "prototypes": ["client:/ldap_0", "client:/ligo/onboarding"] }
Here a very minimal client (callback, name) is specified and there are two prototypes. All of the settings from these are taken so that in effect, this client is merely an extension of these.
Prototypes do not need to be approved. As a matter of fact, there is a good argument for not approving them if they simply are used by other clients. A final aside is that while this allows for multiple inheritance, the progression is linear and hence the order you specify the clients determines which has priority: Each overrides the one before. This effectively sidesteps certain potential issues with inheritance, such as the Diamond Problem.
If a client is an ersatz client (so it has a provisioner or possibly many) a common situation is that the client should really just inherit everything from the provisioner. In that case, the extendsProvisioners attribute of the client is set to true and any provisioners will be treated as prototypes.