Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Connectors] ConnectorTokenClient improvements #131955

Merged
merged 11 commits into from
May 12, 2022

Conversation

ymao1
Copy link
Contributor

@ymao1 ymao1 commented May 10, 2022

Resolves #131231

Summary

Miscellaneous fixes to connector token client. Left comments on specific items.

Checklist

@ymao1 ymao1 changed the title Connectors/connector token client updates [Connectors] ConnectorTokenClient improvements May 10, 2022
@@ -219,6 +214,24 @@ export class ConnectorTokenClient {
);
return { hasErrors: true, connectorToken: null };
}

if (isNaN(Date.parse(connectorTokensResult[0].attributes.expiresAt))) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If Date.parse(expiresAt) is NaN, then checking whether the token had expired would always return false, causing us to keep using the same access token repeatedly.

return Promise.reject(error);
}
);
axiosInstance.interceptors.response.use(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added response interceptor to check for 4xx errors and delete any stored access tokens for this connector. Previously, if using an access token would result in a 4xx error, we would still continue to use it until it expired.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tested this by:

  • Modifying x-pack/plugins/actions/server/builtin_action_types/servicenow/utils.ts to print out the access token that's used (added console log on line 137)
  • Creating an OAuth servicenow connector via API using QA credentials
  • Testing it using test connector tab. See the access token being logged and see that a connector_token SO is created
  • Perform another test and see that the same access token is used as before
  • Using instructions here https://docs.servicenow.com/bundle/rome-platform-administration/page/administer/security/task/t_RevokeOAuthToken.html to revoke the token
  • Perform another test using test connector tab. See error returned and verify that no more connector_token SO exists for this connector ID
  • Perform another test using test connector tab. See that a new token should have been requested and verify that a new connector_token SO has been created.

@@ -113,6 +114,22 @@ async function sendEmailWithExchange(
Authorization: accessToken,
};

const axiosInstance = axios.create();
axiosInstance.interceptors.response.use(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added response interceptor to check for 4xx errors and delete any stored access tokens for this connector. Previously, if using an access token would result in a 4xx error, we would still continue to use it until it expired.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tested this by:

  • Modifying x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts to print out the access token that's used (added console log on line 108)
  • Creating an MS Exchange email connector (ask for OAuth credentials if needed) with from: responseops@elastictest267.onmicrosoft.com
  • Testing it using test connector tab. See the access token being logged and see that a connector_token SO is created
  • Perform another test and see that the same access token is used as before
  • Update the connector to change the from address to ying@elastictest267.onmicrosoft.com. You will have to reenter the client secret
  • Perform another test using test connector tab. See error returned and verify that no more connector_token SO exists for this connector ID

@@ -134,11 +134,28 @@ export const getAxiosInstance = ({
tokenUrl: `${snServiceUrl}/oauth_token.do`,
connectorTokenClient,
});
if (!accessToken) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Checking for null access token and throwing error

axiosConfig.headers.Authorization = accessToken;
return axiosConfig;
},
(error) => {
Promise.reject(error);
return Promise.reject(error);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Forgot to correctly return the promise rejection 🙈

@@ -72,6 +72,9 @@ export const getOAuthJwtAccessToken = async ({
keyId: jwtKeyId,
});

// Save the time before requesting token so we can use it to calculate expiration
const requestTokenStart = Date.now();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

using the time before requesting the token to calculate the expiration time of the token. previously, the expiration was calculated using a Date.now() inside connectorTokenClient.updateOrReplace, which occurs after the token has been requested and received. If there's any delay in processing between when the token was requested and when it was persisted to the connector_token saved object, it was possible to set an expiration that was past when the token would actually expire.

@@ -62,6 +62,9 @@ export const getOAuthClientCredentialsAccessToken = async ({
}

if (connectorToken === null || Date.parse(connectorToken.expiresAt) <= Date.now()) {
// Save the time before requesting token so we can use it to calculate expiration
const requestTokenStart = Date.now();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

using the time before requesting the token to calculate the expiration time of the token. previously, the expiration was calculated using a Date.now() inside connectorTokenClient.updateOrReplace, which occurs after the token has been requested and received. If there's any delay in processing between when the token was requested and when it was persisted to the connector_token saved object, it was possible to set an expiration that was past when the token would actually expire.

@ymao1 ymao1 self-assigned this May 10, 2022
@ymao1 ymao1 added release_note:skip Skip the PR/issue when compiling release notes Team:ResponseOps Label for the ResponseOps team (formerly the Cases and Alerting teams) Feature:Actions/ConnectorsManagement Issues related to Connectors Management UX v8.3.0 labels May 10, 2022
@ymao1 ymao1 marked this pull request as ready for review May 10, 2022 18:32
@ymao1 ymao1 requested a review from a team as a code owner May 10, 2022 18:32
@elasticmachine
Copy link
Contributor

Pinging @elastic/response-ops (Team:ResponseOps)

@ymao1 ymao1 requested review from cnasikas and azasypkin May 10, 2022 18:32
Copy link
Member

@azasypkin azasypkin left a comment

Choose a reason for hiding this comment

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

Changes in utils.ts LGTM!

@@ -375,6 +416,7 @@ describe('updateOrReplace()', () => {
connectorId: '1',
token: null,
newToken: 'newToken',
tokenRequestDate: Date.now(),
Copy link
Member

Choose a reason for hiding this comment

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

I am curious, how do the test are working with different dates on each run?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch! It looks like the existing unit tests are testing just whether certain values are passed to create and exclude testing the date. I will add a test to ensure that tokenRequestDate is used when it's passed in.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks!

@mikecote mikecote self-requested a review May 11, 2022 20:02
Copy link
Contributor

@mikecote mikecote left a comment

Choose a reason for hiding this comment

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

I finished reviewing the code today, LGTM! Will pull locally and test tomorrow 👍

Copy link
Contributor

@mikecote mikecote left a comment

Choose a reason for hiding this comment

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

Tested locally and changes LGTM!

@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

✅ unchanged

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

cc @ymao1

@ymao1 ymao1 merged commit b932dca into elastic:main May 12, 2022
@kibanamachine kibanamachine added the backport:skip This commit does not require backporting label May 12, 2022
@ymao1 ymao1 deleted the connectors/connector-token-client-updates branch May 12, 2022 16:29
Bamieh pushed a commit to Bamieh/kibana that referenced this pull request May 16, 2022
* Throwing error if service now access token is null. Properly returning rejected promise

* Setting time to calculate token expiration to before the token is created

* Returning null access token if stored access token has invalid expiresAt date

* Adding response interceptor to delete connector token if using access token returns 4xx error

* Adding test for tokenRequestDate

* Handling 4xx errors in the response

* Fixing unit tests

* Fixing types
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:skip This commit does not require backporting Feature:Actions/ConnectorsManagement Issues related to Connectors Management UX release_note:skip Skip the PR/issue when compiling release notes Team:ResponseOps Label for the ResponseOps team (formerly the Cases and Alerting teams) v8.3.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[ResponseOps][Connectors] ConnectorTokenClient improvements
7 participants