> ## Documentation Index
> Fetch the complete documentation index at: https://doc.lucidworks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# AEM V2

> The AEM V2 connector retrieves data from an Adobe Experience Manager (AEM) repository and is compatible with AEM version 6.5.

export const schema = {
  "type": "object",
  "title": "AEM",
  "description": "An Apache Sling based connector for AEM",
  "required": ["id", "properties", "pipeline"],
  "properties": {
    "id": {
      "type": "string",
      "title": "Configuration ID",
      "description": "A unique identifier for this Configuration.",
      "minLength": 1,
      "pattern": "^[a-zA-Z0-9_-]+$"
    },
    "pipeline": {
      "type": "string",
      "title": "Pipeline ID",
      "description": "Name of the IndexPipeline used for processing output.",
      "minLength": 1,
      "pattern": "^[a-zA-Z0-9_-]+$"
    },
    "parserId": {
      "type": "string",
      "title": "Parser ID",
      "description": "The Parser to use in the associated IndexPipeline.",
      "pattern": "^[a-zA-Z0-9_-]+$"
    },
    "description": {
      "type": "string",
      "title": "Description",
      "description": "Optional description",
      "hints": ["lengthy"],
      "maxLength": 125
    },
    "modified": {
      "type": "string",
      "title": "Date Modified",
      "description": "The date at which this Configuration was last modified.",
      "hints": ["readonly", "hidden"]
    },
    "created": {
      "type": "string",
      "title": "Date Created",
      "description": "The date at which this Configuration was created.",
      "hints": ["readonly", "hidden"]
    },
    "type": {
      "type": "string",
      "title": "Type",
      "description": "A type ID for this connector.",
      "hints": ["readonly", "hidden"]
    },
    "diagnosticLogging": {
      "type": "boolean",
      "title": "Diagnostic Logging",
      "description": "[Deprecated] Enables verbose diagnostic logging for troubleshooting. May increase log volume. Disabled by default.",
      "default": false
    },
    "collection": {
      "type": "string",
      "title": "Collection ID",
      "description": "The associated content Collection.",
      "hints": ["readonly", "hidden"],
      "minLength": 1,
      "pattern": "^[a-zA-Z0-9_-]+$"
    },
    "type_description": {
      "type": "string",
      "title": "Type Description",
      "default": "An Apache Sling based connector for AEM",
      "hints": ["hidden", "readonly"]
    },
    "category": {
      "type": "string",
      "title": "Category",
      "default": "An Apache Sling based connector for AEM",
      "hints": ["hidden", "readonly"]
    },
    "connector": {
      "title": "Connector Type",
      "description": "Connector type.",
      "minLength": 1,
      "type": "string",
      "hints": ["hidden"]
    },
    "properties": {
      "type": "object",
      "title": "Properties",
      "description": "Plugin specific properties.",
      "required": ["authConfig", "aemBaseUrl"],
      "properties": {
        "aemBaseUrl": {
          "type": "string",
          "title": "AEM URL",
          "description": "Base URL to AEM, e.g. http://localhost:4502",
          "default": "http://localhost:4502",
          "minLength": 1
        },
        "username": {
          "type": "string",
          "title": "AEM username (Deprecated)",
          "description": "Username to use for authentication. The user should have sufficient permissions to read content paths and access Users/Group APIs in case Security Trimming is needed"
        },
        "password": {
          "type": "string",
          "title": "AEM password (Deprecated)",
          "description": "Password to use for authentication.",
          "hints": ["secret"]
        },
        "authConfig": {
          "type": "object",
          "title": "Authentication Settings",
          "description": "Select only one option",
          "required": [],
          "properties": {
            "loginAuthentication": {
              "type": "object",
              "title": "Login Settings",
              "required": [],
              "properties": {
                "username": {
                  "type": "string",
                  "title": "AEM username",
                  "description": "Username to use for authentication. The user should have sufficient permissions to read content paths and access Users/Group APIs in case Security Trimming is needed"
                },
                "password": {
                  "type": "string",
                  "title": "AEM password",
                  "description": "Password to use for authentication.",
                  "hints": ["secret"]
                }
              }
            },
            "oAuth": {
              "type": "object",
              "title": "OAuth Settings",
              "required": [],
              "properties": {
                "accessToken": {
                  "type": "string",
                  "title": "Access Token",
                  "description": "Access Token",
                  "hints": ["secret"]
                },
                "oAuthRefreshToken": {
                  "type": "string",
                  "title": "Refresh Token",
                  "description": "Refresh Token will be used to refresh Access Token",
                  "hints": ["secret"]
                },
                "jwtToken": {
                  "type": "string",
                  "title": "JWT Token",
                  "description": "JWT Token will be used to request new Access Token if Refresh Token is not set",
                  "hints": ["secret"]
                },
                "clientId": {
                  "type": "string",
                  "title": "Client Id",
                  "description": "Client Id"
                },
                "clientSecret": {
                  "type": "string",
                  "title": "Client Secret",
                  "description": "Client Secret",
                  "hints": ["secret"]
                },
                "redirectUri": {
                  "type": "string",
                  "title": "Redirect Uri",
                  "description": "Redirect Uri"
                }
              }
            }
          }
        },
        "allowAllCertificates": {
          "type": "boolean",
          "title": "Allow all HTTPS certificates",
          "description": "If false, security checks will be performed on all SSL/TLS certificate signers and origins. This means self-signed certificates would not be supported.",
          "default": false
        },
        "pageSize": {
          "type": "number",
          "title": "Page Batch Size",
          "description": "Number of documents to fetch per page request. A higher value can make crawling faster, but memory usage is also increased.",
          "default": 100,
          "maximum": 2147483647,
          "exclusiveMaximum": false,
          "minimum": -2147483648,
          "exclusiveMinimum": false,
          "multipleOf": 1
        },
        "nodeDepth": {
          "type": "number",
          "title": "Node Depth",
          "description": "Number of levels you want the query to return",
          "default": 10,
          "maximum": 2147483647,
          "exclusiveMaximum": false,
          "minimum": -2147483648,
          "exclusiveMinimum": false,
          "multipleOf": 1
        },
        "indexChildPathData": {
          "type": "boolean",
          "title": "Index metadata by child path",
          "description": "The metadata associated with subdirectories of the item's path will be indexed.",
          "default": false
        },
        "threadWait": {
          "type": "number",
          "title": "Thread wait (ms)",
          "description": "Time to wait, in milliseconds, between each page request",
          "default": 1000,
          "maximum": 2147483647,
          "exclusiveMaximum": false,
          "minimum": -2147483648,
          "exclusiveMinimum": false,
          "multipleOf": 1
        },
        "incremental": {
          "type": "boolean",
          "title": "Incremental crawl",
          "description": "Incremental crawl doesn't support removal of content",
          "default": false,
          "hints": ["hidden"]
        },
        "paths": {
          "type": "array",
          "title": "Paths to search",
          "description": "AEM paths that will be searched across for content.",
          "default": ["/"],
          "items": {
            "type": "string"
          }
        },
        "excludePathRegexes": {
          "type": "array",
          "title": "Paths excluded",
          "description": "Java regular expressions for paths that should not be fetched",
          "items": {
            "type": "string"
          }
        },
        "aemTypes": {
          "type": "array",
          "title": "AEM types",
          "description": "AEM document type (jcr:primaryType) to include in the index. e.g. cq:Page, dam:Asset",
          "default": ["cq:Page"],
          "items": {
            "type": "string"
          }
        },
        "attachmentTypes": {
          "type": "array",
          "title": "Attachment types",
          "description": "Attachment extensions to index. By default all attachments are indexed.",
          "items": {
            "type": "string"
          }
        },
        "contentIncludePatterns": {
          "type": "array",
          "title": "Content properties include patterns",
          "description": "List of patterns of content properties that should be included in indexed documents.",
          "hints": ["hidden"],
          "items": {
            "type": "string"
          }
        },
        "contentExcludePatterns": {
          "type": "array",
          "title": "Content properties exclude patterns",
          "description": "List of patterns of content properties that should be excluded from indexed documents.",
          "hints": ["hidden"],
          "items": {
            "type": "string"
          }
        },
        "maxSizeBytes": {
          "type": "number",
          "title": "Max file size (bytes)",
          "description": "Maximum size, in bytes, of a document to fetch. If content is larger it will be trimmed to 'maxSizeBytes' size.",
          "default": 4194304,
          "hints": ["advanced"],
          "maximum": 9223372036854776000,
          "exclusiveMaximum": false,
          "minimum": -9223372036854776000,
          "exclusiveMinimum": false,
          "multipleOf": 1
        },
        "requestProperties": {
          "type": "object",
          "title": "Request Options",
          "description": "A set of options for configuring requests to AEM instance.",
          "required": [],
          "properties": {
            "connectTimeout": {
              "type": "number",
              "title": "Connect Timeout (ms)",
              "description": "The timeout in milliseconds until a connection is established.\nA timeout value of zero is interpreted as an infinite timeout. A negative value is interpreted as undefined.",
              "default": 10000,
              "maximum": 2147483647,
              "exclusiveMaximum": false,
              "minimum": -2147483648,
              "exclusiveMinimum": false,
              "multipleOf": 1
            },
            "socketTimeout": {
              "type": "number",
              "title": "Socket Timeout (ms)",
              "description": "The socket timeout (SO_TIMEOUT) in milliseconds, which is the timeout for waiting for data or, put differently, a maximum period inactivity between two consecutive data packets).\nA timeout value of zero is interpreted as an infinite timeout. A negative value is interpreted as undefined.",
              "default": 10000,
              "maximum": 2147483647,
              "exclusiveMaximum": false,
              "minimum": -2147483648,
              "exclusiveMinimum": false,
              "multipleOf": 1
            },
            "requestTimeout": {
              "type": "number",
              "title": "Request Timeout (ms)",
              "description": "The timeout in milliseconds used when requesting a connection from the connection manager.\nA timeout value of zero is interpreted as an infinite timeout. A negative value is interpreted as undefined",
              "default": 10000,
              "maximum": 2147483647,
              "exclusiveMaximum": false,
              "minimum": -2147483648,
              "exclusiveMinimum": false,
              "multipleOf": 1
            }
          },
          "hints": ["advanced"]
        },
        "retryProperties": {
          "type": "object",
          "title": "Retry Options",
          "description": "A set of options for configuring requests retry behavior.",
          "required": [],
          "properties": {
            "maxRetries": {
              "type": "number",
              "title": "Max Retries",
              "description": "If request to AEM fails it will be retried this amount of times",
              "default": 3,
              "maximum": 2147483647,
              "exclusiveMaximum": false,
              "minimum": -2147483648,
              "exclusiveMinimum": false,
              "multipleOf": 1
            },
            "retryDelay": {
              "type": "number",
              "title": "Retry Delay (ms)",
              "description": "Time to wait, in milliseconds, between each retry",
              "default": 1000,
              "maximum": 2147483647,
              "exclusiveMaximum": false,
              "minimum": -2147483648,
              "exclusiveMinimum": false,
              "multipleOf": 1
            }
          },
          "hints": ["advanced"]
        },
        "security": {
          "type": "object",
          "title": "Graph security filtering configuration",
          "required": [],
          "properties": {
            "enabled": {
              "type": "boolean",
              "title": "Enable security trimming",
              "description": "Enable query-time security-trimming",
              "default": true
            }
          }
        }
      }
    },
    "coreProperties": {
      "type": "object",
      "title": "Core Properties",
      "description": "Common behavior and performance settings.",
      "required": [],
      "properties": {
        "fetchSettings": {
          "type": "object",
          "title": "Fetch Settings",
          "description": "System level settings for controlling fetch behavior and performance.",
          "required": [],
          "properties": {
            "numFetchThreads": {
              "type": "number",
              "title": "Fetch Threads",
              "description": "Maximum number of fetch threads; defaults to 5.This setting controls the number of threads that call the Connectors fetch method.Higher values can, but not always, help with overall fetch performance.",
              "default": 5,
              "maximum": 500,
              "exclusiveMaximum": false,
              "minimum": 1,
              "exclusiveMinimum": false,
              "multipleOf": 1
            },
            "indexingThreads": {
              "type": "number",
              "title": "Index Subscription Threads",
              "description": "Maximum number of indexing threads; defaults to 4.This setting controls the number of threads in the indexing service used for processing content documents emitted by this datasource.Higher values can sometimes help with overall fetch performance.",
              "default": 4,
              "maximum": 10,
              "exclusiveMaximum": false,
              "minimum": 1,
              "exclusiveMinimum": false,
              "multipleOf": 1
            },
            "pluginInstances": {
              "type": "number",
              "title": "Number of plugin instances for distributed fetching",
              "description": "Maximum number of plugin instances for distributed fetching. Only specified number of plugin instanceswill do fetching. This is useful for distributing load between different instances.",
              "default": 0,
              "maximum": 500,
              "exclusiveMaximum": false,
              "minimum": 0,
              "exclusiveMinimum": false,
              "multipleOf": 1
            },
            "fetchItemQueueSize": {
              "type": "number",
              "title": "Fetch Item Queue Size",
              "description": "Size of the fetch item queue.Larger values result in increased memory usage, but potentially higher performance.Default is 10k.",
              "default": 10000,
              "hints": ["hidden"],
              "maximum": 500000,
              "exclusiveMaximum": false,
              "minimum": 1,
              "exclusiveMinimum": false,
              "multipleOf": 1
            },
            "fetchRequestCheckInterval": {
              "type": "number",
              "title": "Fetch request check interval(ms)",
              "description": "The amount of time to wait before check if a request is done",
              "default": 15000,
              "hints": ["hidden"],
              "maximum": 500000,
              "exclusiveMaximum": false,
              "minimum": 1000,
              "exclusiveMinimum": false,
              "multipleOf": 1
            },
            "fetchResponseScheduledTimeout": {
              "type": "number",
              "title": "Fetch response scheduled timeout(ms)",
              "description": "The maximum amount of time for a response to be scheduled. The task will be canceled if this setting is exceeded.",
              "default": 300000,
              "maximum": 500000,
              "exclusiveMaximum": false,
              "minimum": 1000,
              "exclusiveMinimum": false,
              "multipleOf": 1
            },
            "fetchResponseCompletedTimeout": {
              "type": "number",
              "title": "Fetch response completion timeout(ms)",
              "description": "The maximum amount of time for a response to be completed. If exceeded, the task will be retried if the job is still running",
              "default": 300000,
              "hints": ["hidden"],
              "maximum": 600000,
              "exclusiveMaximum": false,
              "minimum": 1,
              "exclusiveMinimum": false,
              "multipleOf": 1
            },
            "indexingInactivityTimeout": {
              "type": "number",
              "title": "Indexing inactivity timeout(seconds)",
              "description": "The maximum amount of time to wait for indexing results (in seconds). If exceeded, the job will fail with an indexing inactivity timeout.",
              "default": 86400,
              "maximum": 691200,
              "exclusiveMaximum": false,
              "minimum": 60,
              "exclusiveMinimum": false,
              "multipleOf": 1
            },
            "pluginInactivityTimeout": {
              "type": "number",
              "title": "Plugin inactivity timeout(seconds)",
              "description": "The maximum amount of time to wait for plugin activity (in seconds). If exceeded, the job will fail with a plugin inactivity timeout.",
              "default": 600,
              "maximum": 691200,
              "exclusiveMaximum": false,
              "minimum": 60,
              "exclusiveMinimum": false,
              "multipleOf": 1
            },
            "indexMetadata": {
              "type": "boolean",
              "title": "Index metadata",
              "description": "When enabled the metadata of skipped items will be indexed to the content collection.",
              "default": false
            },
            "indexContentFields": {
              "type": "boolean",
              "title": "Index content fields",
              "description": "When enabled, content fields will be indexed to the crawl-db collection.",
              "default": false
            },
            "asyncParsing": {
              "type": "boolean",
              "title": "Async Parsing",
              "description": "When enabled, content will be indexed asynchronously.",
              "default": false
            }
          }
        },
        "skipConfigValidation": {
          "type": "boolean",
          "title": "Skip Validation",
          "description": "Enable to skip configuration validation when it takes too long and causes timeout issue",
          "default": false
        }
      },
      "hints": ["advanced"]
    }
  },
  "category": "Repository",
  "categoryPriority": 1
};

export const SchemaParamFields = ({schema}) => {
  const sanitize = str => {
    if (typeof str !== "string") return str;
    return str.replace(/^"(.*)"$/s, "$1").replace(/\\/g, "").replace(/"/g, "'");
  };
  const formatDescription = str => {
    const s = sanitize(str);
    return (/[.!?]\)*$/).test(s) ? s : `${s}.`;
  };
  const {description, properties = {}, required: requiredProps = []} = schema;
  const visibleProps = useMemo(() => Object.entries(properties).filter(([, prop]) => !prop.hints?.includes("hidden")), [properties]);
  return <div>
      {description && <p>{formatDescription(description)}</p>}

      {visibleProps.map(([name, prop]) => {
    const isRequired = requiredProps.includes(name);
    const hasDefault = prop.default !== undefined;
    const rawDefault = prop.default;
    const isComplexDefault = hasDefault && (typeof rawDefault === "object" || typeof rawDefault === "string" && (rawDefault.length > 20 || rawDefault.includes('"')));
    const fieldProps = {
      key: name,
      body: prop.title || name,
      type: prop.type,
      ...prop.title && ({
        post: [<><span className="text-stone-400 dark:text-stone-500">API property: </span>{name}</>]
      }),
      ...isRequired && ({
        required: true
      }),
      ...!isComplexDefault && hasDefault ? {
        default: sanitize(String(rawDefault))
      } : {}
    };
    const isObject = prop.type === "object" && prop.properties;
    const isArrayOfObjects = prop.type === "array" && prop.items?.type === "object" && prop.items.properties;
    return <ParamField {...fieldProps}>
            {prop.description && <p>{formatDescription(prop.description)}</p>}

            {isComplexDefault && <div className="flex">
                <p>
                  <strong>Default:</strong>
                </p>
                <pre className="!my-0">
                  <code>
                    {JSON.stringify(rawDefault, null, 2)}
                  </code>
                </pre>
              </div>}

            {isArrayOfObjects && <div className="flex">
              <p>
                <strong>Object attributes:</strong>
              </p>
              <pre className="!my-0">
                <code>
                  {'{\n'}
                  {Object.entries(prop.items.properties).map(([iname, iprop]) => <>
                      {`  ${iname}`}
                      {prop.items?.required?.includes(iname) && <span style={{
      color: 'red'
    }}> required</span>}
                      {`: {\n    display name: ${sanitize(iprop.title || '')}\n    type: ${iprop.type}\n  }\n`}
                    </>)}
                  {'}'}
                </code>
              </pre>
              </div>}

            {isObject && <Expandable title="properties">
                <SchemaParamFields schema={{
      properties: prop.properties,
      required: prop.required
    }} />
              </Expandable>}
          </ParamField>;
  })}
    </div>;
};

export const LwTemplate = ({title = "Key questions to get you started", icon = "sparkles", cta = "Powered by Agent Studio", linkHref = "https://lucidworks.com/demo/?utm_source=docs&utm_medium=referral&utm_campaign=docs_cta_ai"}) => {
  const [isLoaded, setIsLoaded] = useState(false);
  useEffect(() => {
    const timer = setTimeout(() => {
      setIsLoaded(true);
    }, 500);
    return () => clearTimeout(timer);
  }, []);
  return <div className="lw-template-container">
      <Card title={title} icon={icon}>
        {isLoaded && <span dangerouslySetInnerHTML={{
    __html: `<lw-template id="a029c1a9-28be-427e-b0e1-5d918920246a"></lw-template
            >`
  }} />}
        <Link href={linkHref} className="agent-studio-link text-left text-gray-600 gap-2 dark:text-gray-400 text-sm font-medium flex flex-row items-center hover:text-primary dark:hover:text-primary-light group-hover:text-primary group-hover:dark:text-primary-light">Powered by Lucidworks Agent Studio</Link>
      </Card>
    </div>;
};

[localhost link]: http://localhost:3000/docs/fusion-connectors/connectors/aem

[mintlify link]: https://doc.lucidworks.com/docs/fusion-connectors/connectors/aem

[old doc.lw link]: https://doc.lucidworks.com/fusion-connectors/um5omx

<Callout icon="plug" color="#A4C6F7" iconType="solid">
  * **Latest version:** v1.3.0
  * **Compatible with Fusion version:** 5.9.4 and later
</Callout>

<img src="https://mintcdn.com/lucidworks/Au994d8iJwF79HiU/assets/images/connectors/connector-api-flow-aem.png?fit=max&auto=format&n=Au994d8iJwF79HiU&q=85&s=b49407772b76e003142fe4eb9ca1de59" alt="Connector flow" width="4157" height="1915" data-path="assets/images/connectors/connector-api-flow-aem.png" />

The AEM V2 connector supports the following:

* Full crawling and recrawling of pages and assets in Adobe Experience Manager
* Basic authentication
  <Note>
    In Fusion 5, the username and password fields have moved under **Authentication Settings** > **Login Settings**.
  </Note>
* OAuth authentication
* Security trimming to filter results based on user permissions
  * Filter document crawling by including and excluding paths and configuring content properties when setting up the connector.
* Specific wait time between fetch requests to throttle crawls, if necessary
* Optional crawling of child paths

<Accordion title="Configure AEM V2 Connector">
  This document explains how to configure an AEM V2 connector to crawl data in Adobe Experience Manager. This connector is compatible with Fusion 5.5.1 and later.

  <LwTemplate />

  ## Configure AEM Datasource

  1. In Fusion, under Indexing > Datasources, click **Add**, then select **AEM**
  2. Enter a Configuration ID
  3. Enter the AEM URL (the URL used to access the AEM Admin UI) as well as the AEM username and password used to authenticate access to the QueryBuilder JSON Servlet.

  <img src="https://mintcdn.com/lucidworks/VKnUHJXP6sWH55ak/assets/images/5.8/aem-authenticate.png?fit=max&auto=format&n=VKnUHJXP6sWH55ak&q=85&s=94dec34cdc465d041a03fa90680ed643" alt="AEM authentication" width="1856" height="680" data-path="assets/images/5.8/aem-authenticate.png" />

  4. Go to AEM to log in and access CRXDE Lite. In the CRXDE Lite UI, select a path to crawl. Enter this path into Fusion. Click ***Add*** to crawl multiple paths.

  <img src="https://mintcdn.com/lucidworks/VKnUHJXP6sWH55ak/assets/images/5.8/crxde-aem.png?fit=max&auto=format&n=VKnUHJXP6sWH55ak&q=85&s=1c9fe2b01b67e4a97e633d2e4d336fb6" alt="CRXDE path" width="1497" height="1153" data-path="assets/images/5.8/crxde-aem.png" />

  <img src="https://mintcdn.com/lucidworks/VKnUHJXP6sWH55ak/assets/images/5.8/aem-paths.png?fit=max&auto=format&n=VKnUHJXP6sWH55ak&q=85&s=f4e08c826848e1f1796cb4d15806e910" alt="AEM paths to crawl" width="1819" height="370" data-path="assets/images/5.8/aem-paths.png" />

  5. Optional: To exclude paths from the crawl, in Fusion, enter a Java Regular Expression (regex) that represents paths to exclude in the indexed content.

  <img src="https://mintcdn.com/lucidworks/VKnUHJXP6sWH55ak/assets/images/5.8/aem-paths-exclude.png?fit=max&auto=format&n=VKnUHJXP6sWH55ak&q=85&s=c59859533f3b01c581a45acf7e5fee20" alt="paths to exclude" width="1777" height="372" data-path="assets/images/5.8/aem-paths-exclude.png" />

  6. In Fusion, enter the AEM type to crawl. In the CRXDE Lite UI, this is the `jcr:primaryType`. In this example, the AEM connector is configured to crawl the AEM Type `cq:Page`, which represents web content pages.

  <img src="https://mintcdn.com/lucidworks/VKnUHJXP6sWH55ak/assets/images/5.8/crxde-type.png?fit=max&auto=format&n=VKnUHJXP6sWH55ak&q=85&s=6bd814f740c90a25ed56d72264677e89" alt="CRXDE type" width="1393" height="471" data-path="assets/images/5.8/crxde-type.png" />

  <img src="https://mintcdn.com/lucidworks/VKnUHJXP6sWH55ak/assets/images/5.8/aem-types.png?fit=max&auto=format&n=VKnUHJXP6sWH55ak&q=85&s=3530a6ddb7e075d0e8bdff887065dd0b" alt="AEM type" width="1823" height="417" data-path="assets/images/5.8/aem-types.png" />

  7. To index assets with a particular file extension, locate a file type in CRXDE Lite and enter the value of the `jcr:primaryType` into Fusion. In this example, the value of NY\_FairHealth.pdf is `dam:Asset`.

  <img src="https://mintcdn.com/lucidworks/VKnUHJXP6sWH55ak/assets/images/5.8/aem-attachment.png?fit=max&auto=format&n=VKnUHJXP6sWH55ak&q=85&s=a8424537ceda5dd91c110a4ccbb14f8e" alt="AEM attachments" width="1871" height="650" data-path="assets/images/5.8/aem-attachment.png" />

  8. You can choose which content properties to include and exclude from the index. These parameter values are represented by Java regex. For example, to only include properties that start with “jcr” enter `jcr:(.*)`.
  9. In Fusion, click ***Save*** when you’re done configuring the AEM datasource.

  ### Configuration settings

  | Setting                          | Notes                                                                                                                                        |
  | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
  | AEM URL                          | Required. This is the URL used to access the AEM Admin UI.                                                                                   |
  | AEM Username                     | Required. The user should have sufficient permissions to read content paths and access Users/Group APIs in case Security Trimming is needed. |
  | AEM Password                     | Required.                                                                                                                                    |
  | Page Batch Size                  | Number of documents to fetch per page request. A higher value can increase crawling speed but also increases memory usage.                   |
  | Thread wait (ms)                 | Number of milliseconds to wait between fetch requests. This property can be used to throttle a crawl if necessary.                           |
  | Paths to search                  | Required.                                                                                                                                    |
  | Paths that should not be fetched | Java regex for paths that should not be fetched.                                                                                             |
  | AEM Types                        | Required. AEM document type `jcr:primaryType` to include in the index. Examples: `cq:Page`, `dam:Asset`.                                     |
  | Attachment types                 | File extensions to index.                                                                                                                    |
  | Content Property Include Regexes | A list of regex strings of content properties to include in indexed documents. Example: `jcr:.*`.                                            |
  | Content Property Exlude Regexes  | A list of regex strings of content properties to exclude from indexed documents. Example: `sling:.*`                                         |
  | Enable Security Trimming         | Enable this setting for content filtering of results based on the user’s id passed in during query.                                          |
  | Group Mappings                   | AEM user groups mapped to indexed values in the security trimming field which are used to filter content based on user id passed in query.   |
  | Cache Expire Time (m)            | Specifies how long a query is cached in minutes.                                                                                             |

  ## Field data population

  There are multiple sources where AEM data is indexed. The `/bin/querybuilder.json` endpoint data is mandatory and must exist in order for a document to be indexed.

  Note the list of fields that can appear in an indexed document:

  | Field           | Source                                             | Comments                                                                                                                                          |
  | --------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `id`            | `<AEM_URL>/bin/querybuilder.json`                  | Field: path                                                                                                                                       |
  | `content_txt`   | `<AEM_URL>/bin/querybuilder.json`                  | Whole data in text format.                                                                                                                        |
  | `<rest fields>` | `<AEM_URL>/bin/querybuilder.json`                  | All top level fields of JSON object.                                                                                                              |
  | `body_t`        | `<AEM_URL>/crx/de/download.jsp`                    | Used if path ends with one of `Attachment types` OR path does not end with: `/jcr:content`.                                                       |
  | `body_t`        | `<AEM_URL><id>`                                    | Used if there is no `jcr` data. If response status code is something other than 200, Fusion assumes there is no file to download under that path. |
  | `body_t`        | `[content_txt]`                                    | Defaults to `[content_txt]` if `body_t` is empty.                                                                                                 |
  | `parentPage`    | `Id` of document that contains attachment or link. | Populated in case of attachment/link.                                                                                                             |
  | `type`          | File extension of the path.                        | Populated in case of attachment/link.                                                                                                             |
  | `file_size`     | `<AEM_URL>/bin/querybuilder.json`                  | `:jcr:data;` used if `jcr` data is not empty.                                                                                                     |
  | `file_size`     | `<AEM_URL>/bin/querybuilder.json`                  | `dam:size;` used if `jcr` data is empty.                                                                                                          |
</Accordion>

<Tip>
  Check for duplicate data when crawling child paths. For example, if the connector indexes both `cq:Page` and `cq:PageContent` then the results could include duplicated data.
</Tip>

<Tip>
  The v1.3.0 version of this connector is only compatible with Fusion 5.9.4 and later when using security trimming. The v1.3.0 connector version uses [Graph Security Trimming](/docs/5/fusion/reference/config-ref/pipeline-stages/query-stages/security-trimming-graph-query-stage) and not regular security trimming. It is imperative to treat this as a new connector, as configurations do not transfer over due to disparities between newer versions and previous ones. A full crawl is mandatory.
</Tip>

## Prerequisites

Perform these prerequisites to ensure the connector can reliably access, crawl, and index your data.
Proper setup helps avoid configuration or permission errors, so use the following guidelines to keep your content available for discovery and search in Fusion.

### AEM instance

* AEM Author instance must be reachable over HTTP/HTTPS.
* The QueryBuilder JSON API must be enabled. It's typically located at `/bin/querybuilder.json`.
* The JCR Download endpoints must be accessible, such as `/crx/de/download.jsp` or direct node paths.

### Configure an AEM service account

Create a service account with the following:

* Read access to content paths being crawled
* Access to user and group APIs if using security trimming
* Permissions to access the following:
  * Page metadata, such as `cq:Page` or `jcr:content`
  * Binary attachments such as PDFs and DOCX files
  * JCR nodes and properties
  * If using group mapping, access to `/libs/cq/security/userinfo.json` or equivalent endpoints

### Content Paths

* You must define one or more JCR root paths to crawl, such as `/content/SITE_NAME/en`.
* Optionally provide:
  * Exclude path regexes to filter out subtrees
  * Attachment extension types
  * JCR property include/exclude filters

## Authentication

Setting up the correct authentication according to your organization's data governance policies helps keep sensitive data secure while allowing authorized indexing.

The AEM V2 connector supports two modes of authenticating to your AEM instance: basic HTTP and OAuth.
Fusion handles session management as needed, including cookie handling and token renewal.

**Basic HTTP authentication:**

* Provide a standard AEM `username` and `password` with read access to JCR paths.
* If using security trimming, the AEM account also requires read access to the user and groups APIs.

**OAuth 2.0 authentication:**

* Paste in an Access Token and optional Refresh Token.
* If you do not have a pre-obtained token, the connector can fetch a token using JWT authentication.

<Accordion title="AEM connector OAuth authorization">
  The AEM V2 Connector supports OAuth 2.0 authorization with JWT token.

  ## Supported authorization options

  Requests are authorized by including an Access Token in the Authorization header.

  Example:

  ```bash wrap theme={"dark"}
  curl -H 'Authorization: Bearer ACCESS_TOKEN' http://HOST:PORT/content/COMPANY/us/en/community/messaging.html
  ```

  There are three ways the connector can get Access Token:

  * From the datasource configuration
  * From AEM server using Refresh Token
  * From AEM server using JWT token

  Request Access Token, Refresh Token, and JWT token manually and set them in the datasource configuration:

  <img src="https://mintcdn.com/lucidworks/hRHvA40l_Bej4D7e/assets/images/5.10/aem-token-request.png?fit=max&auto=format&n=hRHvA40l_Bej4D7e&q=85&s=8515da2e66e62d075a31b51dea8d909c" alt="AEM token request" width="857" height="216" data-path="assets/images/5.10/aem-token-request.png" />

  Other settings: `Client Id`, `Client Secret`, and `Redirect Uri` can be found in the AEM admin page under **Security > Oauth Clients**:

  <img src="https://mintcdn.com/lucidworks/hRHvA40l_Bej4D7e/assets/images/5.10/aem-client-settings.png?fit=max&auto=format&n=hRHvA40l_Bej4D7e&q=85&s=383b3dec9435309cc18ece1aa029ae01" alt="AEM client settings" width="794" height="415" data-path="assets/images/5.10/aem-client-settings.png" />

  ## Getting Access Token

  1. Open this URL in a browser:
     ```html theme={"dark"}
     http://HOST:PORT/oauth/authorize?response_type=code&client_id=placeholderClientId&client_secret=CLIENT_SECRET&username=admin&password=PASSWORD&scope=offline_access&redirect_uri=REDIRECT_URI
     ```
  2. You are redirected to login page (if you are not logged in):
       <img src="https://mintcdn.com/lucidworks/hRHvA40l_Bej4D7e/assets/images/5.10/aem-login.png?fit=max&auto=format&n=hRHvA40l_Bej4D7e&q=85&s=1ece746efdd22d233d9d853a76bb62aa" alt="AEM login screen" width="1438" height="753" data-path="assets/images/5.10/aem-login.png" />
  3. Logging in redirects you to confirm the authorization. Click **Yes, I authorize this request**.
       <img src="https://mintcdn.com/lucidworks/hRHvA40l_Bej4D7e/assets/images/5.10/aem-login-confirm.png?fit=max&auto=format&n=hRHvA40l_Bej4D7e&q=85&s=b8df40207b961bad155d8339087a5553" alt="AME login confirmation" width="728" height="266" data-path="assets/images/5.10/aem-login-confirm.png" />
  4. You are redirected to the URL provided in `redirect_uri` with parameter `code`: `REDIRECT_URI?code=AUTHORIZATION_CODE`.
  5. Copy the value of the `code` parameter. This is your Authorization Code.
  6. Execute the request to get Access Token:
     ```bash theme={"dark"}
     curl --location --request POST 'http://HOST:PORT/oauth/token?code=AUTHORIZATION_CODE&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&grant_type=authorization_code&redirect_uri=REDIRECT_URI' --header 'Content-Type: application/x-www-form-urlencoded' --header 'Accept: application/json'
     ```
     Example:
     ```bash theme={"dark"}
     curl --location --request POST 'http://34.71.168.50:4502/oauth/token?code=AUTHORIZATION_CODE&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&grant_type=authorization_code&redirect_uri=http://localhost:8080/test' --header 'Content-Type: application/x-www-form-urlencoded' --header 'Accept: application/json'
     ```
     Response:
     ```bash theme={"dark"}
     "access_token":"{placeholderAccessToken}","refresh_token":"{placeholderRefreshToken}","expires_in":3600
     ```

  ## Getting Refresh Token

  To get Refresh Token, follow the same proccess for Access Token, but:

  1. You must include `offline_access` in the scope list.
  2. You must revoke all the previously obtained token. It can be done by clicking **Revoke All Tokens**.

  ## Getting JWT Bearer Token

  1. **Download Private Key** from the AEM Oauth client section. You should have downloaded file **store.p12**.
  2. Run:
     ```bash theme={"dark"}
     openssl pkcs12 -in store.p12 -out store.crt.pem -clcerts -nokeys
     ```
     When asked about password type `notasecret`. You should have generated file named **store.crt.pem**.
  3. Run:
     ```bash theme={"dark"}
     openssl pkcs12 -in store.p12 -passin pass:notasecret -nocerts -nodes -out store.private.key.txt
     ```
     You should have generated file named **store.private.key.txt**.
  4. Create JWT token with the below payload and encrypt it with the private key using RS256:

     ```json theme={"dark"}
     {
     "aud": "http://HOST:PORT/oauth/token",
     "iss": "CLIENT_ID",
     "sub": "USERNAME",
     "exp": <Current time in milliseconds+expiry>,
     "iat": <Current time in milliseconds>,
     "scope": "SCOPE",
     "cty": "code"
     }
     ```

     For example, install pyjwt with `pip install pyjwt` to encrypt with this python script:

     ```python theme={"dark"}
     import jwt
     payload_data = {
     "aud": "http://34.71.168.50:4502/oauth/token",
     "iss": "dp0dtqd9lqpcntvb6t12hrscpa-z1hqkpdg",
     "sub": "admin",
     "exp": 1697840880541,
     "iat": 1697740880541,
     "scope": "offline_access",
     "cty": "code"
     }
     private_key = open('store.private.key.txt', 'r').read()
     token = jwt.encode(
         payload=payload_data,
         key=private_key,
         algorithm='RS256'
     )
     print(token)
     ```
</Accordion>

## Remote connectors

V2 connectors support [running remotely](/docs/fusion-connectors/developers/remote-v2-connectors) in Fusion versions 5.7.1 and later. Refer to **Configure Remote V2 Connectors**.

<Accordion title="Configure Remote V2 Connectors">
  If you need to index data from behind a firewall, you can configure a V2 connector to run remotely on-premises using TLS-enabled gRPC.

  ## Prerequisites

  Before you can set up an on-prem V2 connector, you must configure the egress from your network to allow HTTP/2 communication into the Fusion cloud. You can use a [forward proxy server](#egress-and-proxy-server-configuration) to act as an intermediary between the connector and Fusion.

  The following is required to run V2 connectors remotely:

  * The [plugin zip file and the connector-plugin-standalone JAR](https://plugins.lucidworks.com/).
  * A configured connector backend gRPC endpoint.
  * Username and password of a user with a `remote-connectors` or `admin` role.
  * If the host where the remote connector is running is not configured to trust the server’s TLS certificate, you must configure the file path of the trust certificate collection.

  <Note>If your version of Fusion doesn’t have the `remote-connectors` role by default, you can create one. No API or UI permissions are required for the role.</Note>

  ## Connector compatibility

  Only V2 connectors are able to run remotely on-premises.
  You also need the remote connector client JAR file that matches your Fusion version.
  You can download the latest files at [V2 Connectors Downloads](/docs/fusion-connectors/downloads/v2-connectors-downloads).

  <Note>Whenever you upgrade Fusion, you must also update your remote connectors to match the new version of Fusion.</Note>

  The gRPC connector backend is not supported in Fusion environments deployed on AWS.

  ## System requirements

  The following is required for the on-prem host of the remote connector:

  * (Fusion 5.9.0-5.9.10) JVM version 11
  * (Fusion 5.9.11) JVM version 17
  * Minimum of 2 CPUs
  * 4GB Memory

  Note that memory requirements depend on the number and size of ingested documents.

  ## Enable backend ingress

  In your `values.yaml` file, configure this section as needed:

  ```yaml theme={"dark"}
  ingress:
    enabled: false
    pathtype: "Prefix"
    path: "/"
    #host: "ingress.example.com"
    ingressClassName: "nginx"   # Fusion 5.9.6 only
    tls:
      enabled: false
      certificateArn: ""
      # Enable the annotations field to override the default annotations
      #annotations: ""
  ```

  * Set `enabled` to `true` to enable the backend ingress.
  * Set `pathtype` to `Prefix` or `Exact`.
  * Set `path` to the path where the backend will be available.
  * Set `host` to the host where the backend will be available.
  * In Fusion 5.9.6 *only*, you can set `ingressClassName` to one of the following:
    * `nginx` for Nginx Ingress Controller
    * `alb` for AWS Application Load Balancer (ALB)
  * Configure TLS and certificates according to your CA’s procedures and policies.

    <Note>  TLS must be enabled in order to use AWS ALB for ingress.</Note>

  ## Connector configuration example

  ```yaml theme={"dark"}
  kafka-bridge:
    target: mynamespace-connectors-backend.lucidworkstest.com:443 # mandatory
    plain-text: false # optional, false by default.  
      proxy-server: # optional - needed when a forward proxy server is used to provide outbound access to the standalone connector
      host: host
      port: some-port
      user: user # optional
      password: password # optional
    trust: # optional - needed when the client's system doesn't trust the server's certificate
      cert-collection-filepath: path1

  proxy: # mandatory fusion-proxy
    user: admin
    password: password123
    url: https://fusiontest.com/ # needed only when the connector plugin requires blob store access

  plugin: # mandatory
    path: ./fs.zip
    type: #optional - the suffix is added to the connector id
      suffix: remote
  ```

  ### Minimal example

  ```yaml theme={"dark"}
  kafka-bridge:
    target: mynamespace-connectors-backend.lucidworkstest.com:443

  proxy:
    user: admin
    password: "password123"

  plugin:
    path: ./testplugin.zip
  ```

  ### Logback XML configuration file example

  ```xml theme={"dark"}
  <configuration>
      <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
          <encoder class="com.lucidworks.logging.logback.classic.LucidworksPatternLayoutEncoder">
              <pattern>%d - %-5p [%t:%C{3.}@%L] - %m{nolookups}%n</pattern>
              <charset>utf8</charset>
          </encoder>
      </appender>

      <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
          <file>${LOGDIR:-.}/connector.log</file>
          <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
              <!-- rollover daily -->
              <fileNamePattern>${LOGDIR:-.}/connector-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
              <maxFileSize>50MB</maxFileSize>
              <totalSizeCap>10GB</totalSizeCap>
          </rollingPolicy>
          <encoder class="com.lucidworks.logging.logback.classic.LucidworksPatternLayoutEncoder">
              <pattern>%d - %-5p [%t:%C{3.}@%L] - %m{nolookups}%n</pattern>
              <charset>utf8</charset>
          </encoder>
      </appender>

      <root level="INFO">
          <appender-ref ref="CONSOLE"/>
          <appender-ref ref="FILE"/>
      </root>
  </configuration>
  ```

  ## Run the remote connector

  ```java theme={"dark"}
  java [-Dlogging.config=[LOGBACK_XML_FILE]] \
    -jar connector-plugin-client-standalone.jar [YAML_CONFIG_FILE]
  ```

  The `logging.config` property is optional. If not set, logging messages are sent to the console.

  ## Test communication

  You can run the connector in communication testing mode. This mode tests the communication with the backend without running the plugin, reports the result, and exits.

  ```java theme={"dark"}
  java -Dstandalone.connector.connectivity.test=true -jar connector-plugin-client-standalone.jar [YAML_CONFIG_FILE]
  ```

  ## Encryption

  In a deployment, communication to the connector’s backend server is encrypted using TLS. You should only run this configuration without TLS in a testing scenario. To disable TLS, set `plain-text` to `true`.

  ## Egress and proxy server configuration

  One of the methods you can use to allow outbound communication from behind a firewall is a proxy server. You can configure a proxy server to allow certain communication traffic while blocking unauthorized communication. If you use a proxy server at the site where the connector is running, you must configure the following properties:

  * **Host.** The hosts where the proxy server is running.
  * **Port.** The port the proxy server is listening to for communication requests.
  * **Credentials.** Optional proxy server user and password.

  When you configure egress, it is important to disable any connection or activity timeouts because the connector uses long running gRPC calls.

  ## Password encryption

  If you use a login name and password in your configuration, run the following utility to encrypt the password:

  1. Enter a user name and password in the [connector configuration YAML](#configuration).

  2. Run the standalone JAR with this property:

     ```java theme={"dark"}
     -Dstandalone.connector.encrypt.password=true
     ```

  3. Retrieve the encrypted passwords from the log that is created.

  4. Replace the clear password in the configuration YAML with the encrypted password.

  ## Connector restart (5.7 and earlier)

  The connector will shut down automatically whenever the connection to the server is disrupted, to prevent it from getting into a bad state. Communication disruption can happen, for example, when the server running in the `connectors-backend` pod shuts down and is replaced by a new pod. Once the connector shuts down, connector configuration and job execution are disabled. To prevent that from happening, you should restart the connector as soon as possible.

  You can use Linux scripts and utilities to restart the connector automatically, such as [Monit](https://mmonit.com/monit/).

  ## Recoverable bridge (5.8 and later)

  If communication to the remote connector is disrupted, the connector will try to recover communication and gRPC calls. By default, six attempts will be made to recover each gRPC call. The number of attempts can be configured with the `max-grpc-retries` bridge parameters.

  ## Job expiration duration (5.9.5 only)

  The timeout value for irresponsive backend jobs can be configured with the `job-expiration-duration-seconds` parameter. The default value is `120` seconds.

  ## Use the remote connector

  Once the connector is running, it is available in the Datasources dropdown. If the standalone connector terminates, it disappears from the list of available connectors. Once it is re-run, it is available again and configured connector instances will not get lost.

  ## Enable asynchronous parsing (5.9 and later)

  To separate document crawling from document parsing, enable Tika Asynchronous Parsing on remote V2 connectors.
</Accordion>

Below is an example configuration showing how to specify the file system to index under the `connector-plugins` entry in your `values.yaml` file:

```yaml wrap  theme={"dark"}
additionalVolumes:
- name: fusion-data1-pvc
    persistentVolumeClaim:
    claimName: fusion-data1-pvc
- name: fusion-data2-pvc
    persistentVolumeClaim:
    claimName: fusion-data2-pvc
additionalVolumeMounts:
- name: fusion-data1-pvc
    mountPath: "/connector/data1"
- name: fusion-data2-pvc
    mountPath: "/connector/data2"
```

You may also need to specify the user that is authorized to access the file system, as in this example:

```yaml wrap  theme={"dark"}
securityContext:
    fsGroup: 1002100000
    runAsUser: 1002100000
```

## Learn more

<Accordion title="AEM connector OAuth authorization">
  The AEM V2 Connector supports OAuth 2.0 authorization with JWT token.

  ## Supported authorization options

  Requests are authorized by including an Access Token in the Authorization header.

  Example:

  ```bash wrap theme={"dark"}
  curl -H 'Authorization: Bearer {placeholderAccessToken}' http://HOST:PORT/content/COMPANY/us/en/community/messaging.html
  ```

  There are three ways the connector can get Access Token:

  * From the datasource configuration
  * From AEM server using Refresh Token
  * From AEM server using JWT token

  Request Access Token, Refresh Token, and JWT token manually and set them in the datasource configuration:

  <img src="https://mintcdn.com/lucidworks/hRHvA40l_Bej4D7e/assets/images/5.10/aem-token-request.png?fit=max&auto=format&n=hRHvA40l_Bej4D7e&q=85&s=8515da2e66e62d075a31b51dea8d909c" alt="AEM token request" width="857" height="216" data-path="assets/images/5.10/aem-token-request.png" />

  Other settings: `Client Id`, `Client Secret`, and `Redirect Uri` can be found in the AEM admin page under **Security->Oauth Clients**:

  <img src="https://mintcdn.com/lucidworks/hRHvA40l_Bej4D7e/assets/images/5.10/aem-client-settings.png?fit=max&auto=format&n=hRHvA40l_Bej4D7e&q=85&s=383b3dec9435309cc18ece1aa029ae01" alt="AEM client settings" width="794" height="415" data-path="assets/images/5.10/aem-client-settings.png" />

  ## Getting Access Token

  1. Open this URL in a browser:

     ```html wrap theme={"dark"}
     http://HOST:PORT/oauth/authorize?response_type=code&client_id=placeholderClientId&client_secret=placeholderClientSecret&username=admin&password=PASSWORD&scope=offline_access&redirect_uri=REDIRECT_URI
     ```

  2. You are redirected to login page (if you are not logged in):

       <img src="https://mintcdn.com/lucidworks/hRHvA40l_Bej4D7e/assets/images/5.10/aem-login.png?fit=max&auto=format&n=hRHvA40l_Bej4D7e&q=85&s=1ece746efdd22d233d9d853a76bb62aa" alt="AEM login screen" width="1438" height="753" data-path="assets/images/5.10/aem-login.png" />

  3. Logging in redirects you to confirm the authorization. Click **Yes, I authorize this request**.

       <img src="https://mintcdn.com/lucidworks/hRHvA40l_Bej4D7e/assets/images/5.10/aem-login-confirm.png?fit=max&auto=format&n=hRHvA40l_Bej4D7e&q=85&s=b8df40207b961bad155d8339087a5553" alt="AME login confirmation" width="728" height="266" data-path="assets/images/5.10/aem-login-confirm.png" />

  4. You are redirected to the URL provided in `redirect_uri` with parameter `code`: `<REDIRECT_URI>?code=<AUTHORIZATION_CODE>`.

  5. Copy the value of the `code` parameter. This is your Authorization Code.

  6. Execute the request to get Access Token:

     ```bash wrap theme={"dark"}
     curl --location --request POST 'http://HOST:PORT/oauth/token?code={placeholderAuthorizationCode}&client_id={placeholderClientId}&client_secret={placeholderClientSecret}&grant_type=authorization_code&redirect_uri=<REDIRECT_URI>' --header 'Content-Type: application/x-www-form-urlencoded' --header 'Accept: application/json'
     ```

     Example:

     ```bash wrap theme={"dark"}
     curl --location --request POST 'http://34.71.168.50:4502/oauth/token?code={placeholderAuthorizationCode}&client_id={placeholderClientId}&client_secret={placeholderClientSecret}&grant_type=authorization_code&redirect_uri=http://localhost:8080/test' --header 'Content-Type: application/x-www-form-urlencoded' --header 'Accept: application/json'
     ```

     ```bash wrap theme={"dark"}
     "access_token":"{placeholderAccessToken}","refresh_token":"{placeholderRefreshToken}","expires_in":3600
     ```

  ## Getting Refresh Token

  To get Refresh Token, follow the same process for Access Token, but:

  1. You must include `offline_access` in the scope list.
  2. You must revoke all the previously obtained token. It can be done by clicking **Revoke All Tokens**.

  ## Getting JWT Bearer Token

  1. **Download Private Key** from the AEM Oauth client section.

     You should have downloaded file **store.p12**.

  2. Run:

     ```bash theme={"dark"}
     openssl pkcs12 -in store.p12 -out store.crt.pem -clcerts -nokeys
     ```

     When asked about password type `notasecret`.

     You should have generated file named **store.crt.pem**.

  3. Run

     ```bash wrap theme={"dark"}
     openssl pkcs12 -in store.p12 -passin pass:notasecret -nocerts -nodes -out store.private.key.txt
     ```

     You should have generated file named **store.private.key.txt**.

  4. Create JWT token with the below payload and encrypt it with the private key using RS256:

     ```json theme={"dark"}
     {
      "aud": "http://<HOST>:<PORT>/oauth/token",
      "iss": "<client id>",
      "sub": "<user name>",
      "exp": <Current time in milliseconds+expiry>,
      "iat": <Current time in milliseconds>,
      "scope": "<scope>",
      "cty": "code"
     }
     ```

     For example, install pyjwt to use this python script:

     ```bash theme={"dark"}
     pip install pyjwt
     ```

     ```python theme={"dark"}
     import jwt  

     payload_data = {
      "aud": "http://34.71.168.50:4502/oauth/token",
      "iss": "dp0dtqd9lqpcntvb6t12hrscpa-z1hqkpdg",
      "sub": "admin",
      "exp": 1697840880541,
      "iat": 1697740880541,
      "scope": "offline_access",
      "cty": "code"
     }  

     private_key = open('store.private.key.txt', 'r').read()  

     token = jwt.encode(
        payload=payload_data,
        key=private_key,
        algorithm='RS256'
     )  

     print(token)
     ```
</Accordion>

## Configuration

<SchemaParamFields schema={schema} />
