# CIMD Client Registration Lambda

An overview of the CIMD client registration lambda.

> For the index of this section of the site, see [llms.txt](https://fusionauth.io/docs/llms.txt)

> **PLAN:** This feature requires [an Enterprise plan](https://fusionauth.io/docs/get-started/core-concepts/plans-features.md#enterprise-features).

Available since `1.70.0`

When a [CIMD client](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/client-registration.md#client-id-metadata-documents) logs in, FusionAuth fetches the client's metadata document and registers the client just-in-time. Using this lambda function, you can allow or reject that registration, change the scopes and token settings the client gets, or revoke a client that is already registered.

A registration lambda is the only way to filter which clients may register when a [CIMD domain](https://fusionauth.io/docs/get-started/core-concepts/types/cimd-domains.md) uses the `*` wildcard. Without a lambda that rejects clients you don't recognize, a domain that uses the `*` wildcard accepts every client that publishes a valid metadata document. See [CIMD Security](https://fusionauth.io/docs/operate/secure/cimd-security.md#limit-who-can-register).

The CIMD client registration lambda has access to the following information:

*   the client's metadata document, along with the host and path of its `client_id` and `client_uri` URLs
*   the scope and token settings FusionAuth will apply by default, taken from the CIMD domain
*   the `resource` the client requested
*   metadata about the request, such as the IP address and user agent
*   authentication threats detected for the request, when Advanced Threat Detection is enabled

The lambda doesn't receive the user.

You can use Lambda HTTP Connect to access external resources from this lambda, but you should use external resources sparingly. The lambda runs during the user's login, so any additional latency will impact users.

## When The Lambda Runs

FusionAuth runs the registration lambda after the user logs in, in the following cases:

*   A client registers through the domain for the first time.
*   A user logs in after the client's cached metadata document has expired, and FusionAuth fetches the document again.
*   A registered client requests a `resource` that the lambda hasn't evaluated since the document was last fetched. This can happen during a login or a refresh token request. Each fetch clears the decisions for every other resource, so after a fetch the lambda runs again the first time the client uses each resource.
*   A registered client requests a new `resource` during the domain's **Grace period**, after a fetch of the document has failed. In this case, the lambda evaluates the cached metadata.

The lambda doesn't run while the client's cached document is still valid and the lambda has already evaluated the requested resource. It also doesn't run when a resource server validates a token issued to the client.

## Lambda Structure

All lambda implementations must contain a function with the following signature:

```javascript
function evaluate(result, clientMetadata, context) {
  // Lambda code goes here
}
```

The `evaluate` function must accept the following parameters:

*   `result` - an object used to return the registration decision to FusionAuth. FusionAuth fills it in with the domain's settings before calling the lambda.
*   `clientMetadata` - the read-only metadata from the client's document.
*   `context` - a read-only object containing context for the current request.

`result` is of type `ClientRegistrationLambdaResult`, with the following structure:

```javascript
class ClientRegistrationLambdaResult {
    /**
     * The registration decision. One of ALLOW, DENY, or REVOKE. Defaults to ALLOW.
     * DENY rejects the request. FusionAuth stores nothing, and an existing
     * registration and its refresh tokens are left unchanged.
     * REVOKE deletes the client's registration and its refresh tokens.
     * When the client is registering for the first time, REVOKE rejects the request.
     * @type {string}
     */
    decision;

    /**
     * The Id of the JWT populate lambda that populates the access token and Id token issued to this client.
     * Defaults to the domain's JWT populate lambda. Must be the Id of a JWT populate lambda.
     * Only applies when FusionAuth fetches the document; ignored when the lambda evaluates a new resource.
     * @type {string}
     */
    jwtPopulateLambdaId;

    /**
     * Controls the standard OpenID Connect scopes for this client. Defaults to the domain's provided scope policy.
     * @type {ProvidedScopePolicy}
     */
    providedScopePolicy;

    /**
     * The custom OAuth scopes this client may request. Defaults to the domain's custom scopes.
     * Remove entries to narrow what the client may request, or add entries to grant scopes the domain doesn't define.
     * @type {Array<Scope>}
     */
    scopes;

    /**
     * How long, in seconds, FusionAuth caches the client's metadata document.
     * Defaults to the value FusionAuth derived from the document's Cache-Control header and the domain's settings.
     * FusionAuth limits the value to between 900 seconds and the domain's maximum cache duration.
     * Only applies when FusionAuth fetches the document; ignored when the lambda evaluates a new resource.
     * @type {number}
     */
    ttlInSeconds;
}

class ProvidedScopePolicy {
    /**
     * Each property contains two booleans: `enabled` allows the client to request the scope,
     * and `required` means the user must consent to the scope to complete authorization.
     * @type {{enabled: boolean, required: boolean}}
     */
    address;
    email;
    phone;
    profile;
}

class Scope {
    /**
     * The name of the scope, which the client uses to request it.
     * @type {string}
     */
    name;

    /**
     * A description of the scope for internal use.
     * @type {string}
     */
    description;

    /**
     * The default detail to display on the consent screen if one cannot be found in the theme.
     * @type {string}
     */
    defaultConsentDetail;

    /**
     * The default message to display on the consent screen if one cannot be found in the theme.
     * @type {string}
     */
    defaultConsentMessage;

    /**
     * Whether the user must consent to this scope to complete authorization.
     * @type {boolean}
     */
    required;
}
```

### How FusionAuth Uses Each Result Property

Some properties apply to a single `resource` and others apply to the client's whole registration. FusionAuth stores the `scopes` and `providedScopePolicy` decisions separately for each `resource` the client uses. The `ttlInSeconds` and `jwtPopulateLambdaId` decisions apply to the whole registration.

| Property | Applies to | When FusionAuth uses it |
| --- | --- | --- |
| `decision` | The current request, or the whole registration for `REVOKE` | Every time the lambda runs. `DENY` rejects the request and leaves any existing registration unchanged. `REVOKE` deletes the registration and its refresh tokens. |
| `scopes` | The requested `resource` | Every time the lambda runs. FusionAuth saves the result for that `resource`. |
| `providedScopePolicy` | The requested `resource` | Every time the lambda runs. FusionAuth saves the result for that `resource`. |
| `ttlInSeconds` | The whole registration | Only when FusionAuth fetches the document. |
| `jwtPopulateLambdaId` | The whole registration | Only when FusionAuth fetches the document. |

Because of this, the lambda can't change `ttlInSeconds` or `jwtPopulateLambdaId` when it runs for a new `resource` inside the cache window. In that case, FusionAuth ignores both, and the registration keeps the values it got at the last fetch. The lambda's `scopes` and `providedScopePolicy` still apply to the new `resource`. For more on the cache window, see [CIMD Registration Caching And Lifecycle](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/cimd-registration-lifecycle.md#a-new-resource-inside-the-cache-window).

FusionAuth recalculates a `resource`'s `scopes` and `providedScopePolicy` the first time the client uses that `resource` after each fetch of the document, because a fetch clears them. FusionAuth fetches the document at the first login after the cache window ends. See [When A Registration Is Refreshed](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/cimd-registration-lifecycle.md#when-a-registration-is-refreshed).

`clientMetadata` is an immutable argument of type `ClientMetadata`, with the following structure:

```javascript
class ClientMetadata {
    /**
     * The client's client_id, which is the URL of its metadata document.
     * @type {string}
     */
    clientId;

    /**
     * The host of the client_id URL, such as `app.example.com`.
     * @type {string}
     */
    clientIdHost;

    /**
     * The path of the client_id URL, such as `/oauth/client.json`.
     * @type {string}
     */
    clientIdPath;

    /**
     * The client_name from the document.
     * @type {string}
     */
    clientName;

    /**
     * The client_uri from the document. This is an optional value and may be null.
     * @type {string}
     */
    clientUri;

    /**
     * The host of the client_uri URL. This is an optional value and may be null.
     * @type {string}
     */
    clientUriHost;

    /**
     * The path of the client_uri URL. This is an optional value and may be null.
     * @type {string}
     */
    clientUriPath;

    /**
     * The redirect_uris from the document.
     * @type {Array<string>}
     */
    redirectUris;
}
```

`context` is an immutable argument of type `Context`, with the following structure:

```javascript
class Context {
    /**
     * A list of authentication threats detected for this request. This will only be populated when
     * Advanced Threat Detection is enabled.
     * @type {Set<String>} - Possible values are [BlocklistedIp], [BotDetected], [DormantAccount], [DormantPassword],
     * [ImpossibleTravel], [RecentIdentityChange], [RecentPasswordChange], [SuspiciousUserAgent], [UnrecognizedDevice],
     * or [UntrustedDevice].
     */
    authenticationThreats;

    /**
     * Event information about the current request. This is an optional value and may be null.
     * @type {EventInfo} - See the MFA requirement lambda for details
     */
    eventInfo;

    /**
     * The resource the client requested, which is the value of the `resource` request parameter.
     * @type {string}
     */
    resource;

    /**
     * Provides access to services available to the lambda, including secrets.
     * Use `context.services.secrets.get('<key>')` to retrieve a secret value.
     * @type {Services}
     */
    services;
}
```

For the fields of `eventInfo`, see the `EventInfo` class on the [MFA requirement lambda](https://fusionauth.io/docs/extend/code/lambdas/mfa-requirement.md#lambda-structure) page. For more information on authentication threats, see the [Advanced Threat Detection](https://fusionauth.io/docs/operate/secure/advanced-threat-detection.md) documentation.

### Failure Handling

The lambda fails closed. FusionAuth rejects the client when any of the following happens:

*   The lambda throws an error. FusionAuth writes the error to the Event Log.
*   The lambda sets `decision` to a value other than `ALLOW`, `DENY`, or `REVOKE`. Matching is case-sensitive, but FusionAuth ignores surrounding whitespace.
*   The lambda sets `decision` to `null` or deletes it.
*   The lambda sets `scopes` or `providedScopePolicy` to `null`. To remove every custom scope, set `scopes` to an empty array instead.
*   The lambda sets `jwtPopulateLambdaId` to an Id that doesn't belong to a JWT populate lambda.

Whenever the lambda rejects a client, on purpose or because it failed, FusionAuth writes an Error entry to the Event Log and sends a [CIMD Registration Failed](https://fusionauth.io/docs/extend/events-and-webhooks/events/cimd/cimd-registration-failed.md) event with the reason `lambdaRejected`. Neither one says why the lambda rejected the client. To record the reason, call `console.info` from the lambda, which writes an Event Log entry of type Information.

## Assigning The Lambda

Once a lambda is created, you must assign it to a CIMD domain. Using the FusionAuth admin UI, edit the CIMD domain and find the **Registration lambda** field on the Lambdas tab. The field only lists lambdas of type `CIMD client registration`.

Or if you are using the [CIMD Domain API](https://fusionauth.io/docs/apis/cimd-domains.md), assign the lambda Id to the `domain.registrationLambdaId` field.

## Example Lambdas

The following example allows only clients hosted on `claude.ai` or `chatgpt.com`, which is useful on a CIMD domain that uses the `*` wildcard:

```javascript
function evaluate(result, clientMetadata, context) {
  var allowedHosts = ['claude.ai', 'chatgpt.com'];
  if (!allowedHosts.includes(clientMetadata.clientIdHost)) {
    result.decision = 'DENY';
    console.info('Rejected CIMD client ' + clientMetadata.clientId);
  }
}
```

The following example narrows what one client may request by removing a custom scope and disabling the `phone` scope:

```javascript
function evaluate(result, clientMetadata, context) {
  if (clientMetadata.clientIdHost === 'chatgpt.com') {
    result.scopes = result.scopes.filter(function(scope) {
      return scope.name !== 'reports:write';
    });
    result.providedScopePolicy.phone.enabled = false;
  }
}
```

The following example revokes a specific client. FusionAuth deletes the client's registration and refresh tokens the next time the lambda runs for that client, as described in [When The Lambda Runs](#when-the-lambda-runs):

```javascript
function evaluate(result, clientMetadata, context) {
  var revokedClientIds = ['https://app.example.com/oauth/client.json'];
  if (revokedClientIds.includes(clientMetadata.clientId)) {
    result.decision = 'REVOKE';
  }
}
```

The following example rejects registrations from requests that look automated. This example assumes you are licensed for the [Advanced Threat Detection](https://fusionauth.io/docs/operate/secure/advanced-threat-detection.md) feature:

```javascript
function evaluate(result, clientMetadata, context) {
  if (context.authenticationThreats && context.authenticationThreats.includes('BotDetected')) {
    result.decision = 'DENY';
  }
}
```

Related

[Lifecycle CIMD Registration Caching And Lifecycle Learn how long FusionAuth caches a Client ID Metadata Document, what happens when it expires, and how a CIMD client registration is refreshed or removed. cimdclient id metadata documentoauth client registration](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/cimd-registration-lifecycle.md)

[Lifecycle OAuth Client Registration | Static and CIMD Learn how FusionAuth registers OAuth clients, either ahead of time as Applications or just-in-time from a Client ID Metadata Document. cimdclient id metadata documentmcp](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/client-registration.md)

[Lifecycle Host A Client ID Metadata Document (CIMD) Publish a Client ID Metadata Document so your OAuth client can log in to FusionAuth without being registered ahead of time. cimdclient id metadata documentoauth client registration](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/host-cimd-document.md)

[Operate CIMD Security Learn how FusionAuth protects itself when it fetches a Client ID Metadata Document from a URL a third party controls. cimdclient id metadata documentoauth client registration](https://fusionauth.io/docs/operate/secure/cimd-security.md)
---

## Other pages in Lambdas

> For the full index of this section, see [Extend](https://fusionauth.io/docs/llms-extend.txt).

- [Client Credentials JWT Populate Lambda](https://fusionauth.io/docs/extend/code/lambdas/client-credentials-jwt-populate.md): An overview of the Client Credentials JWT Populate lambda.
- [Lambdas](https://fusionauth.io/docs/extend/code/lambdas.md): An overview of FusionAuth's lambdas, which are pluggable JavaScript code that run at various points in the user lifecycle.
- [JWT Populate Lambda](https://fusionauth.io/docs/extend/code/lambdas/jwt-populate.md): An overview of the JWT populate lambda.
- [Call an API from a Lambda](https://fusionauth.io/docs/extend/code/lambdas/lambda-remote-api-calls.md): An overview of how to make API calls from Lambdas.
- [Login Validation Lambda](https://fusionauth.io/docs/extend/code/lambdas/login-validation.md): An overview of the login validation lambda.
- [MFA Requirement Lambda](https://fusionauth.io/docs/extend/code/lambdas/mfa-requirement.md): An overview of the MFA requirement lambda.
- [Apple Reconcile Lambda](https://fusionauth.io/docs/extend/code/lambdas/reconcile/apple-reconcile.md): An overview of the Apple Reconcile lambda.
- [Epic Games Reconcile Lambda](https://fusionauth.io/docs/extend/code/lambdas/reconcile/epic-games-reconcile.md): An overview of the Epic Games Reconcile lambda.
- [External JWT Reconcile Lambda](https://fusionauth.io/docs/extend/code/lambdas/reconcile/external-jwt-reconcile.md): An overview of the External JWT Reconcile lambda.
- [Facebook Reconcile Lambda](https://fusionauth.io/docs/extend/code/lambdas/reconcile/facebook-reconcile.md): An overview of the Facebook Reconcile lambda.
- [Google Reconcile Lambda](https://fusionauth.io/docs/extend/code/lambdas/reconcile/google-reconcile.md): An overview of the Google Reconcile lambda.
- [HYPR Reconcile Lambda](https://fusionauth.io/docs/extend/code/lambdas/reconcile/hypr-reconcile.md): An overview of the HYPR Reconcile lambda.
- [LDAP Connector Reconcile Lambda](https://fusionauth.io/docs/extend/code/lambdas/reconcile/ldap-connector-reconcile.md): An overview of the LDAP Connector Reconcile lambda.
- [LinkedIn Reconcile Lambda](https://fusionauth.io/docs/extend/code/lambdas/reconcile/linkedin-reconcile.md): An overview of the LinkedIn Reconcile lambda.
- [Nintendo Reconcile Lambda](https://fusionauth.io/docs/extend/code/lambdas/reconcile/nintendo-reconcile.md): An overview of the Nintendo Reconcile lambda.
- [OpenID Connect Reconcile Lambda](https://fusionauth.io/docs/extend/code/lambdas/reconcile/openid-connect-response-reconcile.md): An overview of the OpenID Connect Response Reconcile lambda.
- [SAML v2 Reconcile Lambda](https://fusionauth.io/docs/extend/code/lambdas/reconcile/samlv2-response-reconcile.md): An overview of the SAML v2 Response Reconcile lambda.
- [Sony PlayStation Network Reconcile Lambda](https://fusionauth.io/docs/extend/code/lambdas/reconcile/sony-playstation-network-reconcile.md): An overview of the Sony PlayStation Network Reconcile lambda.
- [Steam Reconcile Lambda](https://fusionauth.io/docs/extend/code/lambdas/reconcile/steam-reconcile.md): An overview of the Steam Reconcile lambda.
- [Twitch Reconcile Lambda](https://fusionauth.io/docs/extend/code/lambdas/reconcile/twitch-reconcile.md): An overview of the Twitch Reconcile lambda.
- [Twitter Reconcile Lambda](https://fusionauth.io/docs/extend/code/lambdas/reconcile/twitter-reconcile.md): An overview of the Twitter Reconcile lambda.
- [Xbox Reconcile Lambda](https://fusionauth.io/docs/extend/code/lambdas/reconcile/xbox-reconcile.md): An overview of the Xbox Reconcile lambda.
- [SAML v2 Populate Lambda](https://fusionauth.io/docs/extend/code/lambdas/samlv2-response-populate.md): An overview of the SAML v2 populate lambda.
- [SCIM Group Request Converter Lambda](https://fusionauth.io/docs/extend/code/lambdas/scim-group-request-converter.md): An overview of the SCIM Group Request Converter lambda.
- [SCIM Group Response Converter Lambda](https://fusionauth.io/docs/extend/code/lambdas/scim-group-response-converter.md): An overview of the SCIM Group Response Converter lambda.
- [SCIM User Request Converter Lambda](https://fusionauth.io/docs/extend/code/lambdas/scim-user-request-converter.md): An overview of the SCIM User Request Converter lambda.
- [SCIM User Response Converter Lambda](https://fusionauth.io/docs/extend/code/lambdas/scim-user-response-converter.md): An overview of the SCIM User Response Converter lambda.
- [Self-Service Registration Validation Lambda](https://fusionauth.io/docs/extend/code/lambdas/self-service-registration.md): An overview of the Self-Service Registration Validation lambda.
- [Test Lambdas](https://fusionauth.io/docs/extend/code/lambdas/testing.md): A guide on how to create and test lambdas with FusionAuth.
- [Token Exchange Policy Lambda](https://fusionauth.io/docs/extend/code/lambdas/token-exchange-policy.md): Reference for the Token Exchange Policy lambda, which decides whether FusionAuth allows a token exchange request.
- [Token Exchange Populate Lambda](https://fusionauth.io/docs/extend/code/lambdas/token-exchange-populate.md): Reference for the Token Exchange Populate lambda, which adds or narrows claims in tokens issued through token exchange.
- [UserInfo Populate Lambda](https://fusionauth.io/docs/extend/code/lambdas/userinfo-populate.md): An overview of the UserInfo populate lambda.
