Key lock by connector ID and profile UID#257968
Merged
lorenabalan merged 7 commits intolb/poc/ears-authfrom Mar 24, 2026
Merged
Key lock by connector ID and profile UID#257968lorenabalan merged 7 commits intolb/poc/ears-authfrom
lorenabalan merged 7 commits intolb/poc/ears-authfrom
Conversation
## Summary State client was generating a PKCE code longer than recommended 128 characters. This should fix it.
5 tasks
szwarckonrad
approved these changes
Mar 18, 2026
Contributor
There was a problem hiding this comment.
It might be worth updating actionsMock.createServices() to return properly typed mocks for ConnectorTokenClientContract, so downstream consumers don't all need the same cast boilerplate.
Contributor
💔 Build Failed
Failed CI Steps
Test Failures
Metrics [docs]
History
|
lorenabalan
added a commit
that referenced
this pull request
Mar 31, 2026
## Summary Closes elastic/search-team#12949 ### Changes: * introduced `getStoredTokenWithRefresh` to avoid duplicate logic between ears and non-ears authz; the only real difference was how we refreshed * introduced "strategies" for axios instances to avoid too many `if ears` branches in how we instantiate the instance * added `xpack.actions.ears.url` config that points to the relevant base EARS url;⚠️ this needs to be populated in ES3 (and hosted) deployments, like we did https://github.com/elastic/serverless-gitops/blob/f51db9f789fe9ce1ea668e27970a4468b37ef7c2/services/kibana-controller/values/qa/default.yaml#L63 * added 2 new folders `lib/ears` and `lib/axios_auth_strategies` but some files import from outer folder `lib`. I just compromised for that over moving files and making the diff larger, though I'm still not sure if it's the best folder split. The `lib` folder is quite unwieldy. 😅 * [Remove leftover capitalize](89beff8) - see [thread](#253695 (comment)) * #257968 **++ Thank you CodeRabbit:** * fixed interceptor logic for authz code grant & ears flow, so that on retry & successful refresh, we _also_ update the headers on the axiosInstance, for future requests ([Update axiosInstance headers for future requests](09f6ae7)) * fixed `useBasicAuth` logic in authz code grant in [Fix useBasicAuth propagation](b395e81); connectors that specify `tokenEndpointAuthMethod: 'client_secret_post'` will now correctly send credentials in the request body instead of always defaulting to HTTP Basic Auth when refreshing tokens via `getToken`. ### Other considerations * 🚫 `revoke` workflow kept out of scope of this PR↔️ see #255701 (review) * ✨ We could maybe add nicer UI component to render for `ears` authz type in the form (connector flyout); not a huge fan of the fact that `scope` are treated like secrets and they reset to default after clicking `Save` * 👉 We should raise a follow-up PR to set EARS URL in ES3 deployments in the `xpack.actions.ears.url` config (if everyone agrees on the name) * **Serverless**: elastic/serverless-gitops#81614 * **ECH**: elastic/cloud#153459 * ❓ We have NO separate rate limits between EARS and normal Authz Grant flow. Should we? I would say no because ultimately we reach 3rd party provider so we can't have higher rate limits than vanilla flow. The name of the current config `auth.oauth_authorization_code.rate_limits` kind of implies there would be more rate limits per auth types. ## Testing Enable EARS auth on Google Drive connector by adding the following lines to `auth.type` in `src/platform/packages/shared/kbn-connector-specs/src/specs/google_drive/google_drive.ts`: ```typescript { type: 'ears', defaults: { provider: 'google', scope: 'https://www.googleapis.com/auth/drive.readonly', }, }, ``` Update your `kibana.dev.yaml` to contain the following lines: ```yaml server.publicBaseUrl: http://localhost:5601 xpack.actions.ears.url: https://elastic-auth-redirect-service.eu-west-1.aws.svc.qa.elastic.cloud uiSettings: overrides: 'workflows:ui:enabled': true logging: loggers: - name: plugins.actions level: debug appenders: [default] ``` Start ES ```shell yarn es serverless --projectType elasticsearch_search --kill ``` Start Kibana ```shell yarn start --serverless=es ``` Try to create & authorise a Google Drive connector. Then run a workflow such as ```yaml version: '1' name: 'sources.google_drive.search' description: Search for files in Google Drive using Google's query syntax tags: ['agent-builder-tool'] enabled: true triggers: - type: manual inputs: - name: query type: string description: "Google Drive search query. Examples: name contains 'budget' and trashed=false | fullText contains 'quarterly report' and mimeType='application/pdf' | 'me' in owners and modifiedTime > '2024-01-01' | mimeType='application/vnd.google-apps.folder' and trashed=false. Operators: contains, =, !=, <, >, <=, >=. Combine with 'and'/'or'. String values use single quotes. Add 'and trashed=false' to exclude trashed files." - name: pageSize type: number required: false description: Number of results to return (default 250, max 1000) - name: pageToken type: string required: false description: "Pagination token. Pass the 'nextPageToken' value from a previous response to get the next page. When nextPageToken is absent in the response, there are no more results." - name: orderBy type: string required: false description: "Sort order: 'createdTime', 'createdTime desc', 'modifiedTime', 'modifiedTime desc', 'name', or 'name desc'" steps: - name: search_files type: google_drive.searchFiles connector-id: <ENTER YOUR CONNECTOR ID HERE!> with: query: "${{inputs.query}}" pageSize: ${{inputs.pageSize}} pageToken: "${{inputs.pageToken}}" orderBy: "${{inputs.orderBy}}" ``` ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - we have separate elastic/search-team#13469 to track this - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) - [x] Review the [backport guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing) and apply applicable `backport:*` labels. --------- Co-authored-by: Sean Story <sean.story@elastic.co> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Julian Gernun <17549662+jcger@users.noreply.github.com> Co-authored-by: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Christos Nasikas <xristosnasikas@gmail.com> Co-authored-by: Dennis Tismenko <dennis.tismenko@elastic.co>
jeramysoucy
pushed a commit
to jeramysoucy/kibana
that referenced
this pull request
Apr 1, 2026
## Summary Closes elastic/search-team#12949 ### Changes: * introduced `getStoredTokenWithRefresh` to avoid duplicate logic between ears and non-ears authz; the only real difference was how we refreshed * introduced "strategies" for axios instances to avoid too many `if ears` branches in how we instantiate the instance * added `xpack.actions.ears.url` config that points to the relevant base EARS url;⚠️ this needs to be populated in ES3 (and hosted) deployments, like we did https://github.com/elastic/serverless-gitops/blob/f51db9f789fe9ce1ea668e27970a4468b37ef7c2/services/kibana-controller/values/qa/default.yaml#L63 * added 2 new folders `lib/ears` and `lib/axios_auth_strategies` but some files import from outer folder `lib`. I just compromised for that over moving files and making the diff larger, though I'm still not sure if it's the best folder split. The `lib` folder is quite unwieldy. 😅 * [Remove leftover capitalize](elastic@89beff8) - see [thread](elastic#253695 (comment)) * elastic#257968 **++ Thank you CodeRabbit:** * fixed interceptor logic for authz code grant & ears flow, so that on retry & successful refresh, we _also_ update the headers on the axiosInstance, for future requests ([Update axiosInstance headers for future requests](elastic@09f6ae7)) * fixed `useBasicAuth` logic in authz code grant in [Fix useBasicAuth propagation](elastic@b395e81); connectors that specify `tokenEndpointAuthMethod: 'client_secret_post'` will now correctly send credentials in the request body instead of always defaulting to HTTP Basic Auth when refreshing tokens via `getToken`. ### Other considerations * 🚫 `revoke` workflow kept out of scope of this PR↔️ see elastic#255701 (review) * ✨ We could maybe add nicer UI component to render for `ears` authz type in the form (connector flyout); not a huge fan of the fact that `scope` are treated like secrets and they reset to default after clicking `Save` * 👉 We should raise a follow-up PR to set EARS URL in ES3 deployments in the `xpack.actions.ears.url` config (if everyone agrees on the name) * **Serverless**: elastic/serverless-gitops#81614 * **ECH**: elastic/cloud#153459 * ❓ We have NO separate rate limits between EARS and normal Authz Grant flow. Should we? I would say no because ultimately we reach 3rd party provider so we can't have higher rate limits than vanilla flow. The name of the current config `auth.oauth_authorization_code.rate_limits` kind of implies there would be more rate limits per auth types. ## Testing Enable EARS auth on Google Drive connector by adding the following lines to `auth.type` in `src/platform/packages/shared/kbn-connector-specs/src/specs/google_drive/google_drive.ts`: ```typescript { type: 'ears', defaults: { provider: 'google', scope: 'https://www.googleapis.com/auth/drive.readonly', }, }, ``` Update your `kibana.dev.yaml` to contain the following lines: ```yaml server.publicBaseUrl: http://localhost:5601 xpack.actions.ears.url: https://elastic-auth-redirect-service.eu-west-1.aws.svc.qa.elastic.cloud uiSettings: overrides: 'workflows:ui:enabled': true logging: loggers: - name: plugins.actions level: debug appenders: [default] ``` Start ES ```shell yarn es serverless --projectType elasticsearch_search --kill ``` Start Kibana ```shell yarn start --serverless=es ``` Try to create & authorise a Google Drive connector. Then run a workflow such as ```yaml version: '1' name: 'sources.google_drive.search' description: Search for files in Google Drive using Google's query syntax tags: ['agent-builder-tool'] enabled: true triggers: - type: manual inputs: - name: query type: string description: "Google Drive search query. Examples: name contains 'budget' and trashed=false | fullText contains 'quarterly report' and mimeType='application/pdf' | 'me' in owners and modifiedTime > '2024-01-01' | mimeType='application/vnd.google-apps.folder' and trashed=false. Operators: contains, =, !=, <, >, <=, >=. Combine with 'and'/'or'. String values use single quotes. Add 'and trashed=false' to exclude trashed files." - name: pageSize type: number required: false description: Number of results to return (default 250, max 1000) - name: pageToken type: string required: false description: "Pagination token. Pass the 'nextPageToken' value from a previous response to get the next page. When nextPageToken is absent in the response, there are no more results." - name: orderBy type: string required: false description: "Sort order: 'createdTime', 'createdTime desc', 'modifiedTime', 'modifiedTime desc', 'name', or 'name desc'" steps: - name: search_files type: google_drive.searchFiles connector-id: <ENTER YOUR CONNECTOR ID HERE!> with: query: "${{inputs.query}}" pageSize: ${{inputs.pageSize}} pageToken: "${{inputs.pageToken}}" orderBy: "${{inputs.orderBy}}" ``` ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - we have separate elastic/search-team#13469 to track this - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) - [x] Review the [backport guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing) and apply applicable `backport:*` labels. --------- Co-authored-by: Sean Story <sean.story@elastic.co> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Julian Gernun <17549662+jcger@users.noreply.github.com> Co-authored-by: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Christos Nasikas <xristosnasikas@gmail.com> Co-authored-by: Dennis Tismenko <dennis.tismenko@elastic.co>
paulinashakirova
pushed a commit
to paulinashakirova/kibana
that referenced
this pull request
Apr 2, 2026
## Summary Closes elastic/search-team#12949 ### Changes: * introduced `getStoredTokenWithRefresh` to avoid duplicate logic between ears and non-ears authz; the only real difference was how we refreshed * introduced "strategies" for axios instances to avoid too many `if ears` branches in how we instantiate the instance * added `xpack.actions.ears.url` config that points to the relevant base EARS url;⚠️ this needs to be populated in ES3 (and hosted) deployments, like we did https://github.com/elastic/serverless-gitops/blob/f51db9f789fe9ce1ea668e27970a4468b37ef7c2/services/kibana-controller/values/qa/default.yaml#L63 * added 2 new folders `lib/ears` and `lib/axios_auth_strategies` but some files import from outer folder `lib`. I just compromised for that over moving files and making the diff larger, though I'm still not sure if it's the best folder split. The `lib` folder is quite unwieldy. 😅 * [Remove leftover capitalize](elastic@89beff8) - see [thread](elastic#253695 (comment)) * elastic#257968 **++ Thank you CodeRabbit:** * fixed interceptor logic for authz code grant & ears flow, so that on retry & successful refresh, we _also_ update the headers on the axiosInstance, for future requests ([Update axiosInstance headers for future requests](elastic@09f6ae7)) * fixed `useBasicAuth` logic in authz code grant in [Fix useBasicAuth propagation](elastic@b395e81); connectors that specify `tokenEndpointAuthMethod: 'client_secret_post'` will now correctly send credentials in the request body instead of always defaulting to HTTP Basic Auth when refreshing tokens via `getToken`. ### Other considerations * 🚫 `revoke` workflow kept out of scope of this PR↔️ see elastic#255701 (review) * ✨ We could maybe add nicer UI component to render for `ears` authz type in the form (connector flyout); not a huge fan of the fact that `scope` are treated like secrets and they reset to default after clicking `Save` * 👉 We should raise a follow-up PR to set EARS URL in ES3 deployments in the `xpack.actions.ears.url` config (if everyone agrees on the name) * **Serverless**: elastic/serverless-gitops#81614 * **ECH**: elastic/cloud#153459 * ❓ We have NO separate rate limits between EARS and normal Authz Grant flow. Should we? I would say no because ultimately we reach 3rd party provider so we can't have higher rate limits than vanilla flow. The name of the current config `auth.oauth_authorization_code.rate_limits` kind of implies there would be more rate limits per auth types. ## Testing Enable EARS auth on Google Drive connector by adding the following lines to `auth.type` in `src/platform/packages/shared/kbn-connector-specs/src/specs/google_drive/google_drive.ts`: ```typescript { type: 'ears', defaults: { provider: 'google', scope: 'https://www.googleapis.com/auth/drive.readonly', }, }, ``` Update your `kibana.dev.yaml` to contain the following lines: ```yaml server.publicBaseUrl: http://localhost:5601 xpack.actions.ears.url: https://elastic-auth-redirect-service.eu-west-1.aws.svc.qa.elastic.cloud uiSettings: overrides: 'workflows:ui:enabled': true logging: loggers: - name: plugins.actions level: debug appenders: [default] ``` Start ES ```shell yarn es serverless --projectType elasticsearch_search --kill ``` Start Kibana ```shell yarn start --serverless=es ``` Try to create & authorise a Google Drive connector. Then run a workflow such as ```yaml version: '1' name: 'sources.google_drive.search' description: Search for files in Google Drive using Google's query syntax tags: ['agent-builder-tool'] enabled: true triggers: - type: manual inputs: - name: query type: string description: "Google Drive search query. Examples: name contains 'budget' and trashed=false | fullText contains 'quarterly report' and mimeType='application/pdf' | 'me' in owners and modifiedTime > '2024-01-01' | mimeType='application/vnd.google-apps.folder' and trashed=false. Operators: contains, =, !=, <, >, <=, >=. Combine with 'and'/'or'. String values use single quotes. Add 'and trashed=false' to exclude trashed files." - name: pageSize type: number required: false description: Number of results to return (default 250, max 1000) - name: pageToken type: string required: false description: "Pagination token. Pass the 'nextPageToken' value from a previous response to get the next page. When nextPageToken is absent in the response, there are no more results." - name: orderBy type: string required: false description: "Sort order: 'createdTime', 'createdTime desc', 'modifiedTime', 'modifiedTime desc', 'name', or 'name desc'" steps: - name: search_files type: google_drive.searchFiles connector-id: <ENTER YOUR CONNECTOR ID HERE!> with: query: "${{inputs.query}}" pageSize: ${{inputs.pageSize}} pageToken: "${{inputs.pageToken}}" orderBy: "${{inputs.orderBy}}" ``` ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - we have separate elastic/search-team#13469 to track this - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) - [x] Review the [backport guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing) and apply applicable `backport:*` labels. --------- Co-authored-by: Sean Story <sean.story@elastic.co> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Julian Gernun <17549662+jcger@users.noreply.github.com> Co-authored-by: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Christos Nasikas <xristosnasikas@gmail.com> Co-authored-by: Dennis Tismenko <dennis.tismenko@elastic.co>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Addresses #253695 (comment)
Previously, the mutex used to serialise concurrent token refreshes was keyed only on
connectorId. In per-user (authMode: 'per-user') mode each user holds an independent token, so this caused unnecessary serialisation: a refresh for User A would block User B even though they operate on completely separate stored tokens.The lock key is now
connectorId:profileUidforper-usermode andconnectorIdforsharedmode. Concurrent requests from different users on the same connector now run in parallel, while concurrent requests from the same user remain correctly serialised.