Skip to content

Conversation

@andrewkroh
Copy link
Member

@andrewkroh andrewkroh commented Aug 13, 2025

Proposed commit message

Added configurable oauth_endpoint_params for greater flexibility. An
example use case is to allow overriding the default grant_type to
support using providing a static refresh_token.

Checklist

  • I have reviewed tips for building integrations and this pull request is aligned with them.
  • I have verified that all data streams collect metrics or logs.
  • I have added an entry to my package's changelog.yml file.
  • I have verified that Kibana version constraints are current according to guidelines.
  • I have verified that any added dashboard complies with Kibana's Dashboard good practices

Related issues

Screenshots

Screenshot 2025-08-13 at 15 21 42

Note

The above configuration treats the refresh_token as static configuration. If the server returns a new refresh_token it a response the oauth client will not use it.


Request tracer log showing the oauth request.

{
  "log.level": "debug",
  "@timestamp": "2025-08-13T18:42:20.704Z",
  "message": "HTTP request",
  "transaction.id": "JLSRFNTSCTDHG-1",
  "url.original": "http://svc-o365-cel:8080/test-grant-type-refresh-token-client-id/oauth2/v2.0/token",
  "url.scheme": "http",
  "url.path": "/test-grant-type-refresh-token-client-id/oauth2/v2.0/token",
  "url.domain": "svc-o365-cel",
  "url.port": "8080",
  "url.query": "",
  "http.request.method": "POST",
  "http.request.header": {
    "Authorization": [
      "Basic dGVzdC1jZWwtY2xpZW50LWlkOnRlc3QtY2VsLWNsaWVudC1zZWNyZXQ="
    ],
    "Content-Type": [
      "application/x-www-form-urlencoded"
    ]
  },
  "user_agent.original": "",
  "http.request.body.content": "grant_type=refresh_token&refresh_token=refresh_token_123&scope=https%3A%2F%2Fmanage.office.com%2F.default",
  "http.request.body.truncated": false,
  "http.request.body.bytes": 105,
  "http.request.mime_type": "application/x-www-form-urlencoded",
  "ecs.version": "1.6.0"
}

Agent policy:

  - id: cel-o365-53d0dfa0-8026-44a7-85fc-abc9a604a5ed
    name: o365-1
    revision: 1
    type: cel
    use_output: default
    meta:
      package:
        name: o365
        version: 2.22.0
    data_stream:
      namespace: default
    package_policy_id: 53d0dfa0-8026-44a7-85fc-abc9a604a5ed
    streams:
      - id: cel-o365.audit-53d0dfa0-8026-44a7-85fc-abc9a604a5ed
        data_stream:
          dataset: o365.audit
          type: logs
        interval: 3m
        auth.oauth2:
          client.id: b
          client.secret: ${SECRET_0}
          provider: azure
          scopes:
            - https://manage.office.com/.default
          endpoint_params:
            grant_type: refresh_token
            refresh_token: '1234'
          token_url: https://login.microsoftonline.com/a/oauth2/v2.0/token

Testing locally

  1. Download the package zip:
  1. Navigate to the Integration page and upload.
1 2 3 4 5

@andrewkroh andrewkroh added Integration:o365 Microsoft Office 365 enhancement New feature or request Team:Security-Service Integrations Security Service Integrations team [elastic/security-service-integrations] labels Aug 13, 2025
Added configurable `oauth_endpoint_params` for greater flexibility.  An
example use case is to allow overriding the default grant_type to
support using providing a static refresh_token.
@andrewkroh andrewkroh force-pushed the o365/feat/oauth-endpoint-param-yaml branch from bc71970 to 5517605 Compare August 13, 2025 19:37
@andrewkroh andrewkroh changed the title feat(o365): add support for configurable oauth2 endpoint params (#123) feat(o365): add support for configurable oauth2 endpoint params Aug 13, 2025
@elastic-vault-github-plugin-prod

🚀 Benchmarks report

To see the full report comment with /test benchmark fullreport

Copy link
Contributor

@efd6 efd6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit and query, otherwise LGTM

Comment on lines +21 to +48
# Refresh token to get access token: https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow#refresh-the-access-token
- path: /test-cel-tenant-id/oauth2/v2.0/token
methods: [ POST ]
query_params:
scope: 'https://manage.office.com/.default'
refresh_token: refresh_token_123
grant_type: refresh_token
request_headers:
Authorization:
- "Basic dGVzdC1jZWwtY2xpZW50LWlkOnRlc3QtY2VsLWNsaWVudC1zZWNyZXQ="
Content-Type:
- "application/x-www-form-urlencoded"
responses:
- status_code: 200
headers:
Content-Type:
- "application/json"
body: |-
{{ minify_json `
{
"access_token": "CELtoken",
"token_type": "Bearer",
"expires_in": 2,
"scope": "https%3A%2F%2Fgraph.microsoft.com%2Fmail.read",
"refresh_token": "AwABAAAAvPM1KaPlrEqdFSBzjqfTGAMxZGUTdM0t4B4...",
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctOD..."
}
` }}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this down below the default /test-cel-tenant-id/oauth2/v2.0/token rule.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When order matters we have the more general cases later, out of necessity.

Here order doesn't matter for matching. I think it makes sense have this new one at the top with the other odd case (the o365audit input) then have the main cases together underneath.

Copy link
Member Author

@andrewkroh andrewkroh Aug 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6df4cc6 (reverted in 5dcfe97)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw Chris's comments after I addressed Dan's first round of comments. Will leave this as it is.

Comment on lines +66 to +67
default: |
grant_type: client_credentials
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this get picked up on upgrade?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the upgrade works well.

When I added a policy for the old version, then upgraded the package, the policy was unchanged.

Then upgrading the policy did pick up the new setting and default value and generated the same endpoint_params settings.

Diff from before policy upgrade to after policy upgrade:

--- elastic-agent.yml	2025-08-14 15:41:19.496659216 +0200
+++ elastic-agent(1).yml	2025-08-14 15:42:11.519298684 +0200
@@ -1,22 +1,22 @@
 id: elastic-agent-managed-ep
-revision: 2
+revision: 3
 outputs:
   default:
     type: elasticsearch
     hosts:
       - 'https://elasticsearch:9200'
     ssl.ca_trusted_fingerprint: 1055FA6AFC9C3459CAC7B743130935B3B96D04019D291F833B2C9A44934F5EF1
     preset: latency
 fleet:
   hosts:
     - 'https://fleet-server:8220'
 output_permissions:
   default:
     _elastic_agent_monitoring:
       indices: []
     _elastic_agent_checks:
       cluster:
         - monitor
     0e4eb51a-bf09-4f59-8336-bb7acf268a97:
       indices:
         - names:
@@ -24,47 +24,47 @@
           privileges:
             - auto_configure
             - create_doc
 agent:
   download:
     sourceURI: 'https://artifacts.elastic.co/downloads/'
   monitoring:
     enabled: false
     logs: false
     metrics: false
     traces: false
   features: {}
   protection:
     enabled: false
     uninstall_token_hash: GN9RlDrrdlgGuTzSIKwi4Tb1dPPl8q7MuKh34xdgeVo=
     signing_key: >-
       MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpJjCJPBpxIXkk5txcGxVJ4JPguzHOGTnnjd8+Ts/ntxhWUaeOrrH/BkQ7CjytKjcDACT6V1+HQ8CpONpCn3KMA==
 inputs:
   - id: cel-o365-0e4eb51a-bf09-4f59-8336-bb7acf268a97
     name: o365-1
-    revision: 1
+    revision: 2
     type: cel
     use_output: default
     meta:
       package:
         name: o365
-        version: 2.21.1
+        version: 2.22.0
     data_stream:
       namespace: default
     package_policy_id: 0e4eb51a-bf09-4f59-8336-bb7acf268a97
     streams:
       - id: cel-o365.audit-0e4eb51a-bf09-4f59-8336-bb7acf268a97
         data_stream:
           dataset: o365.audit
           type: logs
         interval: 3m
         auth.oauth2:
           client.id: testclientid
           client.secret: '${SECRET_0}'
           provider: azure
           scopes:
             - 'https://manage.office.com/.default'
           endpoint_params:
             grant_type: client_credentials
           token_url: 'https://token.example.com/testtenantid/oauth2/v2.0/token'
         resource.url: 'https://manage.office.com'
         resource.timeout: 60s
@@ -77,26 +77,26 @@
           want_more: false
           base:
             tenant_id: testtenantid
             list_contents_start_time: 1h
             batch_interval: 1h
             maximum_age: 167h55m
             content_types: >-
               Audit.AzureActiveDirectory, Audit.Exchange, Audit.SharePoint,
               Audit.General, DLP.All
         redact:
           fields:
             - base.tenant_id
         program: "state.base.content_types.split(\",\").map(content_type_raw,\n\tcontent_type_raw.trim_space()\n).map(content_type,\n\trequest(\n\t\t\"POST\",\n\t\tstate.url.trim_right(\"/\") + \"/api/v1.0/\" + state.base.tenant_id + \"/activity/feed/subscriptions/start?\" +\n\t\t{\n\t\t\t\"contentType\": [content_type],\n\t\t\t\"PublisherIdentifier\": [state.base.tenant_id],\n\t\t}.format_query()\n\t).do_request().as(start_subs_resp,\n\t\tstart_subs_resp.Body.decode_json().as(start_subs_resp_body,\n\t\t\t(\n\t\t\t\thas(start_subs_resp_body.status) && start_subs_resp_body.status == \"enabled\" ||\n\t\t\t\thas(start_subs_resp_body.error) && has(start_subs_resp_body.error.code) &&\n\t\t\t\tstart_subs_resp_body.error.code == \"AF20024\"\n\t\t\t) ?\n\t\t\t\t// When start-subscription API returns success or if already started subscription,\n\t\t\t\tduration(state.base.batch_interval).as(batch_interval,\n\t\t\t\t\tmin(duration(\"24h\"), batch_interval).as(batch_interval,\n\t\t\t\t\t\trequest(\n\t\t\t\t\t\t\t\"GET\",\n\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\tstate.want_more && has(state.?cursor.content_types_state_as_list) &&\n\t\t\t\t\t\t\t\tsize(state.cursor.content_types_state_as_list.filter(e, e.content_type == content_type)) > 0 &&\n\t\t\t\t\t\t\t\tstate.cursor.content_types_state_as_list.filter(e, e.content_type == content_type)[0].next_page != \"\"\n\t\t\t\t\t\t\t) ?\n\t\t\t\t\t\t\t\t// if NextPageUri exists\n\t\t\t\t\t\t\t\tstate.cursor.content_types_state_as_list.filter(e, e.content_type == content_type)[0].next_page\n\t\t\t\t\t\t\t:\n\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\thas(state.?cursor.content_types_state_as_list) &&\n\t\t\t\t\t\t\t\tsize(state.cursor.content_types_state_as_list.filter(e, e.content_type == content_type)) > 0\n\t\t\t\t\t\t\t) ?\n\t\t\t\t\t\t\t\t// if NextPageUri does not exist, but content_type_state_created_at exists in state\n\t\t\t\t\t\t\t\tstate.cursor.content_types_state_as_list.filter(e, e.content_type == content_type).as(content_type_state,\n\t\t\t\t\t\t\t\t\tcontent_type_state[0].content_created_at.as(content_type_state_created_at,\n\t\t\t\t\t\t\t\t\t\t// if saved time inside state is more than 7 days old, then change it to 7 days.\n\t\t\t\t\t\t\t\t\t\tmax(\n                      // Enforce the maximum age limit.\n\t\t\t\t\t\t\t\t\t\t\tnow() - duration(state.base.maximum_age),\n\t\t\t\t\t\t\t\t\t\t\tcontent_type_state_created_at.parse_time(time_layout.RFC3339)\n\t\t\t\t\t\t\t\t\t\t).as(state_created_at_calc,\n\t\t\t\t\t\t\t\t\t\t\tstate.url.trim_right(\"/\") + \"/api/v1.0/\" + state.base.tenant_id + \"/activity/feed/subscriptions/content?\" +\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\"contentType\": [content_type],\n\t\t\t\t\t\t\t\t\t\t\t\t\"PublisherIdentifier\": [state.base.tenant_id],\n\t\t\t\t\t\t\t\t\t\t\t\t\"startTime\": [string(min(now() - duration(\"1s\"), state_created_at_calc + duration(\"1s\")))],\n\t\t\t\t\t\t\t\t\t\t\t\t\"endTime\": [string(min(now(), state_created_at_calc + batch_interval))],\n\t\t\t\t\t\t\t\t\t\t\t}.format_query()\n\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t:\n\t\t\t\t\t\t\t\t// initial run when no cursor state exists i.e., polling from initial_interval\n\t\t\t\t\t\t\t\tstate.url.trim_right(\"/\") + \"/api/v1.0/\" + state.base.tenant_id + \"/activity/feed/subscriptions/content?\" +\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"contentType\": [content_type],\n\t\t\t\t\t\t\t\t\t\"PublisherIdentifier\": [state.base.tenant_id],\n\t\t\t\t\t\t\t\t\t\"startTime\": [string(min(now() - duration(\"1s\"), now() - duration(state.base.list_contents_start_time)))],\n\t\t\t\t\t\t\t\t\t\"endTime\": [string(min(now(), now() - duration(state.base.list_contents_start_time) + batch_interval))],\n\t\t\t\t\t\t\t\t}.format_query()\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t).do_request().as(list_contents_resp,\n\t\t\t\t\tlist_contents_resp.Body.decode_json().as(list_contents_resp_body,\n\t\t\t\t\t\t(\n\t\t\t\t\t\t\ttype(list_contents_resp_body) != map && size(list_contents_resp_body) > 0 &&\n\t\t\t\t\t\t\thas(list_contents_resp_body[0].contentUri) && list_contents_resp_body[0].contentUri != \"\" &&\n\t\t\t\t\t\t\thas(list_contents_resp_body[0].contentCreated) && list_contents_resp_body[0].contentCreated != \"\"\n\t\t\t\t\t\t) ?\n\t\t\t\t\t\t\t// contents exist to consume\n\t\t\t\t\t\t\tlist_contents_resp_body.map(l1,\n\t\t\t\t\t\t\t\t(has(l1.contentExpiration) && l1.contentExpiration.parse_time(time_layout.RFC3339) >= now()) ?\n\t\t\t\t\t\t\t\t\trequest(\"GET\", l1.contentUri).do_request().as(content_resp,\n\t\t\t\t\t\t\t\t\t\t(has(content_resp.StatusCode) && content_resp.StatusCode == 200 && size(content_resp.Body) > 0) ?\n\t\t\t\t\t\t\t\t\t\t\tcontent_resp.Body.decode_json().map(content_resp_body,\n\t\t\t\t\t\t\t\t\t\t\t\tcontent_resp_body.with({\"copy\": {\"o365audit\": content_resp_body}})\n\t\t\t\t\t\t\t\t\t\t\t).map(content_resp_body_with_copy,\n\t\t\t\t\t\t\t\t\t\t\t\tcontent_resp_body_with_copy.copy\n\t\t\t\t\t\t\t\t\t\t\t).flatten().drop_empty().as(contents,\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"events_per_content_type\": contents,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"content_type\": content_type,\n\t\t\t\t\t\t\t\t\t\t\t\t\t// if 'contentCreated' is older than the maximum age, change it to the maximum age.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"content_created_at\": {\"temp\": list_contents_resp_body}.collate(\"temp.contentCreated\").max().as(temp_max,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t(temp_max.parse_time(time_layout.RFC3339) > now() - duration(state.base.maximum_age)) ?\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttemp_max\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(now() - duration(state.base.maximum_age)).format(time_layout.RFC3339)\n\t\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"next_page\": (has(list_contents_resp.?Header.NextPageUri) && list_contents_resp.Header.NextPageUri.size() > 0) ?\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t(list_contents_resp.Header.NextPageUri[0])\n\t\t\t\t\t\t\t\t\t\t\t\t\t: (has(list_contents_resp.?Header.Nextpageuri) && list_contents_resp.Header.Nextpageuri.size() > 0) ?\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t(list_contents_resp.Header.Nextpageuri[0])\n\t\t\t\t\t\t\t\t\t\t\t\t\t:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t// keep fetching more if (nextpageuri exists) or\n\t\t\t\t\t\t\t\t\t\t\t\t\t// (the end of the request range is one batch interval or longer before the current time)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"want_more_content\": has(list_contents_resp.Header) &&\n\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\thas(list_contents_resp.Header.NextPageUri) && list_contents_resp.Header.NextPageUri.size() > 0 ||\n\t\t\t\t\t\t\t\t\t\t\t\t\t\thas(list_contents_resp.Header.Nextpageuri) && list_contents_resp.Header.Nextpageuri.size() > 0 ||\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tlist_contents_resp.Request.URL.parse_url().RawQuery.parse_query().?endTime[0].optMap(endTime,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  (now() - endTime.parse_time(time_layout.RFC3339)) >= duration(state.base.batch_interval)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t).orValue(false)\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t:\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\"events_per_content_type\": [],\n\t\t\t\t\t\t\t\t\t\t\t\t\"content_type\": content_type,\n\t\t\t\t\t\t\t\t\t\t\t\t\"content_created_at\": {\"temp\": list_contents_resp_body}.collate(\"temp.contentCreated\").max().as(temp_max,\n\t\t\t\t\t\t\t\t\t\t\t\t\t(temp_max.parse_time(time_layout.RFC3339) > now() - duration(state.base.maximum_age)) ?\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttemp_max\n\t\t\t\t\t\t\t\t\t\t\t\t\t:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t(now() - duration(state.base.maximum_age)).format(time_layout.RFC3339)\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t\t\"next_page\": (has(list_contents_resp.?Header.NextPageUri) && list_contents_resp.Header.NextPageUri.size() > 0) ?\n\t\t\t\t\t\t\t\t\t\t\t\t\t(list_contents_resp.Header.NextPageUri[0])\n\t\t\t\t\t\t\t\t\t\t\t\t: (has(list_contents_resp.?Header.Nextpageuri) && list_contents_resp.Header.Nextpageuri.size() > 0) ?\n\t\t\t\t\t\t\t\t\t\t\t\t\t(list_contents_resp.Header.Nextpageuri[0])\n\t\t\t\t\t\t\t\t\t\t\t\t:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t:\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"events_per_content_type\": [],\n\t\t\t\t\t\t\t\t\t\t\"content_type\": content_type,\n\t\t\t\t\t\t\t\t\t\t\"content_created_at\": {\"temp\": list_contents_resp_body}.collate(\"temp.contentCreated\").max().as(temp_max,\n\t\t\t\t\t\t\t\t\t\t\t(temp_max.parse_time(time_layout.RFC3339) > now() - duration(state.base.maximum_age)) ?\n\t\t\t\t\t\t\t\t\t\t\t\ttemp_max\n\t\t\t\t\t\t\t\t\t\t\t:\n\t\t\t\t\t\t\t\t\t\t\t\t(now() - duration(state.base.maximum_age)).format(time_layout.RFC3339)\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\"next_page\": (has(list_contents_resp.?Header.NextPageUri) && list_contents_resp.Header.NextPageUri.size() > 0) ?\n\t\t\t\t\t\t\t\t\t\t\t(list_contents_resp.Header.NextPageUri[0])\n\t\t\t\t\t\t\t\t\t\t: (has(list_contents_resp.?Header.Nextpageuri) && list_contents_resp.Header.Nextpageuri.size() > 0) ?\n\t\t\t\t\t\t\t\t\t\t\t(list_contents_resp.Header.Nextpageuri[0])\n\t\t\t\t\t\t\t\t\t\t:\n\t\t\t\t\t\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t:\n\t\t\t\t\t\t\t// contents does not exist, or is empty array\n\t\t\t\t\t\t\tlist_contents_resp.Request.URL.parse_url().RawQuery.parse_query().as(reqQuery,\n\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"events_per_content_type\": (size(list_contents_resp_body) == 0) ? [] : [list_contents_resp_body],\n\t\t\t\t\t\t\t\t\t\t\"content_type\": content_type,\n\t\t\t\t\t\t\t\t\t\t\"content_created_at\": (\n\t\t\t\t\t\t\t\t\t\t\thas(list_contents_resp.StatusCode) && has(reqQuery.endTime) &&\n\t\t\t\t\t\t\t\t\t\t\tlist_contents_resp.StatusCode == 200 && reqQuery.endTime.size() > 0\n\t\t\t\t\t\t\t\t\t\t) ?\n\t\t\t\t\t\t\t\t\t\t\t(reqQuery.endTime[0])\n\t\t\t\t\t\t\t\t\t\t: (has(reqQuery.startTime) && reqQuery.startTime.size() > 0) ?\n\t\t\t\t\t\t\t\t\t\t\t(reqQuery.startTime[0])\n\t\t\t\t\t\t\t\t\t\t: has(\n\t\t\t\t\t\t\t\t\t\t\tstate.cursor.content_types_state_as_list.filter(e,\n\t\t\t\t\t\t\t\t\t\t\t\te.content_type == content_type\n\t\t\t\t\t\t\t\t\t\t\t)[0].content_created_at\n\t\t\t\t\t\t\t\t\t\t) ?\n\t\t\t\t\t\t\t\t\t\t\tstate.cursor.content_types_state_as_list.filter(e,\n\t\t\t\t\t\t\t\t\t\t\t\te.content_type == content_type\n\t\t\t\t\t\t\t\t\t\t\t)[0].content_created_at\n\t\t\t\t\t\t\t\t\t\t:\n\t\t\t\t\t\t\t\t\t\t\tstring(now() - duration(state.base.list_contents_start_time)),\n\t\t\t\t\t\t\t\t\t\t\"next_page\": \"\",\n\t\t\t\t\t\t\t\t\t\t\"want_more_content\": (\n\t\t\t\t\t\t\t\t\t\t\thas(list_contents_resp.StatusCode) && has(reqQuery.endTime) &&\n\t\t\t\t\t\t\t\t\t\t\tlist_contents_resp.StatusCode == 200 && reqQuery.endTime.size() > 0 &&\n\t\t\t\t\t\t\t\t\t\t\treqQuery.endTime[0].parse_time(time_layout.RFC3339).as(endTime,\n\t\t\t\t\t\t\t\t\t\t\t  (now() - endTime) >= duration(state.base.batch_interval)\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t:\n\t\t\t\t// When start-subscription API produces error, such as Authentication error.\n\t\t\t\t[\n\t\t\t\t\t{\n\t\t\t\t\t\t\"events_per_content_type\": [{\n\t\t\t\t\t\t  \"error\": {\n\t\t\t\t\t\t    \"code\": string(start_subs_resp.StatusCode),\n\t\t\t\t\t\t    \"id\": string(start_subs_resp.Status),\n\t\t\t\t\t\t    \"message\": content_type + \": \" + start_subs_resp_body.?error.code.optFlatMap(code,\n\t\t\t\t\t\t      start_subs_resp_body.?error.message.optMap(message,\n\t\t\t\t\t\t        code + \" \" + message\n\t\t\t\t\t\t      )\n\t\t\t\t\t\t    ).orValue(start_subs_resp_body)\n\t\t\t\t\t\t  }\n\t\t\t\t\t\t}],\n\t\t\t\t\t\t\"content_type\": content_type,\n\t\t\t\t\t\t\"content_created_at\": (has(state.cursor) && has(state.cursor.content_types_state_as_list)) ?\n\t\t\t\t\t\t\tstate.cursor.content_types_state_as_list.filter(e, e.content_type == content_type)[0].content_created_at\n\t\t\t\t\t\t:\n\t\t\t\t\t\t\tstring(now() - duration(state.base.list_contents_start_time)),\n\t\t\t\t\t\t\"next_page\": \"\",\n\t\t\t\t\t\t\"want_more_content\": false,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t)\n\t)\n).flatten().drop_empty().as(events_list,\n\tevents_list.collate(\"events_per_content_type\").as(events,\n\t  events.filter(e, has(e.?error.message)).as(error_events,\n\t    error_events.size() > 0 ?\n\t      // If there are errors, return a single merged error event and discard any other results\n\t      {\n\t        \"url\": state.url,\n\t        \"base\": state.base,\n\t        \"events\": {\n\t          \"error\": {\n\t            \"code\": error_events.collate(\"error.code\").as(codes, codes.zip(codes)).keys().as(codes, codes.size() == 1 ? codes[0] : dyn(codes)),\n\t            \"id\": error_events.collate(\"error.id\").as(ids, ids.zip(ids)).keys().as(ids, ids.size() == 1 ? ids[0] : dyn(ids)),\n\t            \"message\": error_events.collate(\"error.message\").join(\"; \"),\n\t          },\n\t        },\n\t        \"want_more\": false,\n\t        ?\"cursor\": state.?cursor,\n\t      }\n\t    :\n\t      {\n\t        \"url\": state.url,\n\t        \"base\": state.base,\n\t        \"events\": events,\n\t        \"want_more\": events_list.collate(\"want_more_content\").filter(e, e == true).size() > 0,\n\t        \"cursor\": {\n\t          \"content_types_state_as_list\": events_list.drop([\"events_per_content_type\"]),\n\t        },\n\t      }\n\t  )\n\t)\n)"
         tags:
           - preserve_original_event
           - forwarded
           - o365-cel
         publisher_pipeline.disable_host: true
 signed:
   data: >-
-    eyJpZCI6ImVsYXN0aWMtYWdlbnQtbWFuYWdlZC1lcCIsImFnZW50Ijp7ImZlYXR1cmVzIjp7fSwicHJvdGVjdGlvbiI6eyJlbmFibGVkIjpmYWxzZSwidW5pbnN0YWxsX3Rva2VuX2hhc2giOiJHTjlSbERycmRsZ0d1VHpTSUt3aTRUYjFkUFBsOHE3TXVLaDM0eGRnZVZvPSIsInNpZ25pbmdfa2V5IjoiTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFcEpqQ0pQQnB4SVhrazV0eGNHeFZKNEpQZ3V6SE9HVG5uamQ4K1RzL250eGhXVWFlT3JySC9Ca1E3Q2p5dEtqY0RBQ1Q2VjErSFE4Q3BPTnBDbjNLTUE9PSJ9fSwiaW5wdXRzIjpbeyJpZCI6ImNlbC1vMzY1LTBlNGViNTFhLWJmMDktNGY1OS04MzM2LWJiN2FjZjI2OGE5NyIsIm5hbWUiOiJvMzY1LTEiLCJyZXZpc2lvbiI6MSwidHlwZSI6ImNlbCJ9XX0=
+    eyJpZCI6ImVsYXN0aWMtYWdlbnQtbWFuYWdlZC1lcCIsImFnZW50Ijp7ImZlYXR1cmVzIjp7fSwicHJvdGVjdGlvbiI6eyJlbmFibGVkIjpmYWxzZSwidW5pbnN0YWxsX3Rva2VuX2hhc2giOiJHTjlSbERycmRsZ0d1VHpTSUt3aTRUYjFkUFBsOHE3TXVLaDM0eGRnZVZvPSIsInNpZ25pbmdfa2V5IjoiTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFcEpqQ0pQQnB4SVhrazV0eGNHeFZKNEpQZ3V6SE9HVG5uamQ4K1RzL250eGhXVWFlT3JySC9Ca1E3Q2p5dEtqY0RBQ1Q2VjErSFE4Q3BPTnBDbjNLTUE9PSJ9fSwiaW5wdXRzIjpbeyJpZCI6ImNlbC1vMzY1LTBlNGViNTFhLWJmMDktNGY1OS04MzM2LWJiN2FjZjI2OGE5NyIsIm5hbWUiOiJvMzY1LTEiLCJyZXZpc2lvbiI6MiwidHlwZSI6ImNlbCJ9XX0=
   signature: >-
-    MEYCIQClOWUzH0+o4RJRE2G+p7bOqGS8i83VzQtpkTfiol2ScQIhAOteM98FDe4uljczqdF895gmLC1tx3zbIZLPu/kzbmkK
+    MEYCIQDOfHRC6t93mFQWhUQFk+g+3JilN4lGFCfrbWhl3KpwAQIhAIggITS9TqSi1NAEVLhKEYgjc1+YT7HxA12RXMfPbn1q
 secret_references:
   - id: PyrOqJgBmkat5LOAnC-h
 namespaces: []

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I tested a 2.21.0 -> 2.22.0 upgrade. The agent policy received the default value upon upgrade.

Copy link
Contributor

@chrisberkhout chrisberkhout left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! This is working in production.

@andrewkroh andrewkroh marked this pull request as ready for review August 14, 2025 13:59
@andrewkroh andrewkroh requested a review from a team as a code owner August 14, 2025 13:59
@elasticmachine
Copy link

Pinging @elastic/security-service-integrations (Team:Security-Service Integrations)

…oauth2/v2.0/token rule"

This reverts commit 6df4cc6.

chrisberkhout said:

> Here order doesn't matter for matching. I think it makes sense have this
> new one at the top with the other odd case (the o365audit input) then
> have the main cases together underneath.
@elasticmachine
Copy link

💚 Build Succeeded

History

@elastic-sonarqube
Copy link

@andrewkroh andrewkroh merged commit 4e996b8 into elastic:main Aug 14, 2025
9 checks passed
@elastic-vault-github-plugin-prod

Package o365 - 2.22.0 containing this change is available at https://epr.elastic.co/package/o365/2.22.0/

robester0403 pushed a commit to robester0403/integrations that referenced this pull request Aug 14, 2025
…tic#14924)

Added configurable `oauth_endpoint_params` for greater flexibility.  An
example use case is to allow overriding the default grant_type to
support using providing a static refresh_token.
tehbooom pushed a commit to tehbooom/integrations that referenced this pull request Nov 19, 2025
…tic#14924)

Added configurable `oauth_endpoint_params` for greater flexibility.  An
example use case is to allow overriding the default grant_type to
support using providing a static refresh_token.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request Integration:o365 Microsoft Office 365 Team:Security-Service Integrations Security Service Integrations team [elastic/security-service-integrations]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants