> For the complete documentation index, see [llms.txt](https://docs.1kosmos.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.1kosmos.com/integrations/identity-verification/okta-identity-verification-for-account-management/creating-an-oidc-client-for-okta-in-1kosmos.md).

# Creating an OIDC client for Okta in 1Kosmos

***

1. Log in to your tenant as a Community Administrator.
2. Navigate to **Applications > Add Application** (or click **Add an application** from the Manage Applications page).
3. Scroll down and click **Add Integration** under the **OIDC** tile.
4. In the **Create new OIDC Integration** page, fill in the following:
   * **Application name** — Descriptive name, for example: `Okta IDV for Account Recovery`
   * **Grant Type** — Authorization Code
   * **Sign-In Redirect URI** — Format: `https://{okta-domain}/idp/identity-verification/callback` Example: `https://company.okta.com/idp/identity-verification/callback`
   * **Scopes** — The following scopes are required:
     * `profile` — Required for user profile claims (enabled by default)
     * `openid` — Required for OIDC authentication (enabled by default)
     * `identity_assurance` — **Required** — enables the identity verification flow
     * `idv_flow_{id}` — **Required** — specifies which workflow to trigger. Replace `{id}` with the workflow access code.
       * Example: if access code is `abc123`, scope is `idv_flow_abc123`
       * Example: if access code is `verify-user-2024`, scope is `idv_flow_verify-user-2024`
5. Configure the Claims Transformation Script (see below).
6. Click **Create**. A Client ID and Client Secret are automatically assigned.

#### Scopes Behavior

When **identity\_assurance** is selected, the related `idv_flow_{id}` scope is automatically populated, enabled by default, and non-editable. When **identity\_assurance** is unselected, `idv_flow_{id}` is automatically removed.

#### Enabling and Configuring the Claims Transformation Script

1. In the OIDC application configuration, navigate to **Claims Transformation**
2. Toggle **Returns a custom JSON payload** to ON — a sample script appears in the code editor
3. The script receives these inputs:
   * `user` — user session attributes (e.g., firstname, lastname, email)
   * `ialData` — identity assurance and authenticator data
   * `mapping` — claim mappings derived from selected scopes
   * `workflowSummary` *(optional)* — verification workflow results
4. The sample script is commented out by default. To activate it:
   * Select all commented lines (starting with `//`)
   * **macOS:** `Cmd+/` | **Windows:** `Ctrl+/`
   * Or manually remove `//` from the beginning of each line
5. After uncommenting, you will be prompted for OTP verification (Email or SMS). Enter the 6-digit OTP to confirm and save.

**Note:** The Run feature in the script editor validates syntax and behavior in isolation — it does not execute the full end-to-end identity flow. Use it to check for errors before saving.

#### Supported Claims for Identity Proofing

The current identity proofing flow supports comparison of the following claims only:

* `given_name` (First Name)
* `family_name` (Last Name)
* `birthdate` (Date of Birth)

Okta may send additional claims depending on its configuration, but only the three attributes above are processed for identity proofing purposes.

#### Sample Claims Transformation Script

```javascript
function mapClaims({ user = {}, ialData = {}, userDocuments = [], mapping = [], workflowSummary = null, errorContext = null }) {
  const claims = {};

  // Helper function to get nested values (e.g., 'aliases.alias1')
  const getNestedValue = (obj, path) => {
    return path.split('.').reduce((nestedData, nestedKey) => nestedData && nestedData[nestedKey], obj);
  };

  // Process each claim in the mapping (skip if mapping is not provided for IDV flow)
  if (mapping && mapping.length > 0) {
    for (const { claim_name, attribute_name, attribute_type, value_type, value } of mapping) {
      let attrValue;

      if (value_type === 'null') {
        claims[claim_name] = null;
        continue;
      }

      if (value_type === 'static') {
        claims[claim_name] = value;
        continue;
      }

      if (attribute_type === 'session') {
        if (attribute_name === 'aal') {
          attrValue = ialData.aal;
        } else {
          attrValue = getNestedValue(user, attribute_name);
        }
        if (value_type === 'default') {
          attrValue = attrValue || value;
        }
      }
      else if (attribute_type === 'ledger') {
        if (attribute_name === 'ial') {
          attrValue = ialData.ial;
        } else {
          attrValue = (ialData.authenticator_data && ialData.authenticator_data[attribute_name]) ||
                      (ialData.pon_data && ialData.pon_data[attribute_name]);
        }
        if (value_type === 'default') {
          attrValue = attrValue || value;
        }
      }
      else if (attribute_type === 'identity') {
        const attributeSegments = attribute_name.split('.');
        if (attributeSegments.length === 2) {
          const [docType, property] = attributeSegments;
          const document = userDocuments.find((doc) => doc.type === docType);
          if (document) {
            attrValue = document.data[property];
          }
        }
        else if (attributeSegments.length === 3) {
          const [docType, nestedObj, property] = attributeSegments;
          const document = userDocuments.find((doc) => doc.type === docType);
          if (document) {
            attrValue = document.data[nestedObj] && document.data[nestedObj][property];
          }
        }
        if (value_type === 'default') {
          attrValue = attrValue || value;
        }
      }

      if (attrValue === undefined) {
        throw new Error(`${attribute_type}.${attribute_name} cannot be prepared, because data does not contain this attribute`);
      }

      claims[claim_name] = typeof attrValue === 'object' ? JSON.stringify(attrValue) : attrValue;
    }
  }

  const createVerifiedClaims = () => {
    const expectedClaims = workflowSummary?.metaData?.claims?.id_token?.verified_claims?.[0]?.claims || {};
    const personInfo = workflowSummary?.summary?.person_info || {};

    if (errorContext) {
      const failedClaims = {};
      if (expectedClaims.given_name) {
        failedClaims.given_name = expectedClaims.given_name?.fuzzy ? { value: null, fuzzy: true } : null;
      }
      if (expectedClaims.family_name) {
        failedClaims.family_name = expectedClaims.family_name?.fuzzy ? { value: null, fuzzy: true } : null;
      }
      if (expectedClaims.birthdate) {
        failedClaims.birthdate = expectedClaims.birthdate?.fuzzy ? { value: null, fuzzy: true } : null;
      }
      return [{
        verification: {
          assurance_level: "FAILED",
          time: new Date().toISOString(),
          trust_framework: "IDV-DELEGATED",
          verification_process: "idv-session-" + Date.now()
        },
        claims: failedClaims
      }];
    }

    if (Object.keys(expectedClaims).length === 0) {
      return [];
    }

    let allClaimsVerified = true;
    const verifiedClaimsObj = {};

    const buildVerifiedClaim = (actualValue, expectedClaim) => {
      if (actualValue) {
        if (expectedClaim?.fuzzy) {
          return { value: actualValue, fuzzy: true };
        } else {
          return actualValue;
        }
      } else {
        allClaimsVerified = false;
        if (expectedClaim?.fuzzy) {
          return { value: null, fuzzy: true };
        } else {
          return null;
        }
      }
    };

    if (expectedClaims.given_name) {
      verifiedClaimsObj.given_name = buildVerifiedClaim(personInfo.firstName, expectedClaims.given_name);
    }
    if (expectedClaims.family_name) {
      verifiedClaimsObj.family_name = buildVerifiedClaim(personInfo.lastName, expectedClaims.family_name);
    }
    if (expectedClaims.birthdate) {
      verifiedClaimsObj.birthdate = buildVerifiedClaim(personInfo.dob, expectedClaims.birthdate);
    }

    const assuranceLevel = allClaimsVerified ? "VERIFIED" : "FAILED";

    return [{
      verification: {
        assurance_level: assuranceLevel,
        time: new Date().toISOString(),
        trust_framework: "IDV-DELEGATED",
        verification_process: "idv-session-" + Date.now()
      },
      claims: verifiedClaimsObj
    }];
  };

  if (workflowSummary || errorContext) {
    claims.verified_claims = createVerifiedClaims();
  }

  return claims;
}

Log in to your tenant as a Community Administrator.Navigate to Applications > Add Application (or click Add an application from the Manage Applications page).Scroll down and click Add Integration under the OIDC tile.In the Create new OIDC Integration page, fill in the following:Application name — Descriptive name, for example: Okta IDV for Account RecoveryGrant Type — Authorization CodeSign-In Redirect URI — Format: https://{okta-domain}/idp/identity-verification/callback Example: https://company.okta.com/idp/identity-verification/callbackScopes — The following scopes are required:profile — Required for user profile claims (enabled by default)openid — Required for OIDC authentication (enabled by default)identity_assurance — Required — enables the identity verification flowidv_flow_{id} — Required — specifies which workflow to trigger. Replace {id} with the workflow access code.Example: if access code is abc123, scope is idv_flow_abc123Example: if access code is verify-user-2024, scope is idv_flow_verify-user-2024Configure the Claims Transformation Script (see below).Click Create. A Client ID and Client Secret are automatically assigned.
Scopes Behavior
When identity_assurance is selected, the related idv_flow_{id} scope is automatically populated, enabled by default, and non-editable. When identity_assurance is unselected, idv_flow_{id} is automatically removed.

Enabling and Configuring the Claims Transformation Script
In the OIDC application configuration, navigate to Claims TransformationToggle Returns a custom JSON payload to ON — a sample script appears in the code editorThe script receives these inputs:user — user session attributes (e.g., firstname, lastname, email)ialData — identity assurance and authenticator datamapping — claim mappings derived from selected scopesworkflowSummary (optional) — verification workflow resultsThe sample script is commented out by default. To activate it:Select all commented lines (starting with //)macOS: Cmd+/ | Windows: Ctrl+/Or manually remove // from the beginning of each lineAfter uncommenting, you will be prompted for OTP verification (Email or SMS). Enter the 6-digit OTP to confirm and save.
Note: The Run feature in the script editor validates syntax and behavior in isolation — it does not execute the full end-to-end identity flow. Use it to check for errors before saving.
Supported Claims for Identity Proofing
The current identity proofing flow supports comparison of the following claims only:
given_name (First Name)family_name (Last Name)birthdate (Date of Birth)
Okta may send additional claims depending on its configuration, but only the three attributes above are processed for identity proofing purposes.
Sample Claims Transformation Script
function mapClaims({ user = {}, ialData = {}, userDocuments = [], mapping = [], workflowSummary = null, errorContext = null }) {  const claims = {};  // Helper function to get nested values (e.g., 'aliases.alias1')  const getNestedValue = (obj, path) => {    return path.split('.').reduce((nestedData, nestedKey) => nestedData && nestedData[nestedKey], obj);  };  // Process each claim in the mapping (skip if mapping is not provided for IDV flow)  if (mapping && mapping.length > 0) {    for (const { claim_name, attribute_name, attribute_type, value_type, value } of mapping) {      let attrValue;      if (value_type === 'null') {        claims[claim_name] = null;        continue;      }      if (value_type === 'static') {        claims[claim_name] = value;        continue;      }      if (attribute_type === 'session') {        if (attribute_name === 'aal') {          attrValue = ialData.aal;        } else {          attrValue = getNestedValue(user, attribute_name);        }        if (value_type === 'default') {          attrValue = attrValue || value;        }      }      else if (attribute_type === 'ledger') {        if (attribute_name === 'ial') {          attrValue = ialData.ial;        } else {          attrValue = (ialData.authenticator_data && ialData.authenticator_data[attribute_name]) ||                      (ialData.pon_data && ialData.pon_data[attribute_name]);        }        if (value_type === 'default') {          attrValue = attrValue || value;        }      }      else if (attribute_type === 'identity') {        const attributeSegments = attribute_name.split('.');        if (attributeSegments.length === 2) {          const [docType, property] = attributeSegments;          const document = userDocuments.find((doc) => doc.type === docType);          if (document) {            attrValue = document.data[property];          }        }        else if (attributeSegments.length === 3) {          const [docType, nestedObj, property] = attributeSegments;          const document = userDocuments.find((doc) => doc.type === docType);          if (document) {            attrValue = document.data[nestedObj] && document.data[nestedObj][property];          }        }        if (value_type === 'default') {          attrValue = attrValue || value;        }      }      if (attrValue === undefined) {        throw new Error(`${attribute_type}.${attribute_name} cannot be prepared, because data does not contain this attribute`);      }      claims[claim_name] = typeof attrValue === 'object' ? JSON.stringify(attrValue) : attrValue;    }  }  const createVerifiedClaims = () => {    const expectedClaims = workflowSummary?.metaData?.claims?.id_token?.verified_claims?.[0]?.claims || {};    const personInfo = workflowSummary?.summary?.person_info || {};    if (errorContext) {      const failedClaims = {};      if (expectedClaims.given_name) {        failedClaims.given_name = expectedClaims.given_name?.fuzzy ? { value: null, fuzzy: true } : null;      }      if (expectedClaims.family_name) {        failedClaims.family_name = expectedClaims.family_name?.fuzzy ? { value: null, fuzzy: true } : null;      }      if (expectedClaims.birthdate) {        failedClaims.birthdate = expectedClaims.birthdate?.fuzzy ? { value: null, fuzzy: true } : null;      }      return [{        verification: {          assurance_level: "FAILED",          time: new Date().toISOString(),          trust_framework: "IDV-DELEGATED",          verification_process: "idv-session-" + Date.now()        },        claims: failedClaims      }];    }    if (Object.keys(expectedClaims).length === 0) {      return [];    }    let allClaimsVerified = true;    const verifiedClaimsObj = {};    const buildVerifiedClaim = (actualValue, expectedClaim) => {      if (actualValue) {        if (expectedClaim?.fuzzy) {          return { value: actualValue, fuzzy: true };        } else {          return actualValue;        }      } else {        allClaimsVerified = false;        if (expectedClaim?.fuzzy) {          return { value: null, fuzzy: true };        } else {          return null;        }      }    };    if (expectedClaims.given_name) {      verifiedClaimsObj.given_name = buildVerifiedClaim(personInfo.firstName, expectedClaims.given_name);    }    if (expectedClaims.family_name) {      verifiedClaimsObj.family_name = buildVerifiedClaim(personInfo.lastName, expectedClaims.family_name);    }    if (expectedClaims.birthdate) {      verifiedClaimsObj.birthdate = buildVerifiedClaim(personInfo.dob, expectedClaims.birthdate);    }    const assuranceLevel = allClaimsVerified ? "VERIFIED" : "FAILED";    return [{      verification: {        assurance_level: assuranceLevel,        time: new Date().toISOString(),        trust_framework: "IDV-DELEGATED",        verification_process: "idv-session-" + Date.now()      },      claims: verifiedClaimsObj    }];  };  if (workflowSummary || errorContext) {    claims.verified_claims = createVerifiedClaims();  }  return claims;}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.1kosmos.com/integrations/identity-verification/okta-identity-verification-for-account-management/creating-an-oidc-client-for-okta-in-1kosmos.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
