-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
[Connectors] ConnectorTokenClient
improvements
#131955
Conversation
…g rejected promise
… token returns 4xx error
…ors/connector-token-client-updates
ConnectorTokenClient
improvements
@@ -219,6 +214,24 @@ export class ConnectorTokenClient { | |||
); | |||
return { hasErrors: true, connectorToken: null }; | |||
} | |||
|
|||
if (isNaN(Date.parse(connectorTokensResult[0].attributes.expiresAt))) { |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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.
Pinging @elastic/response-ops (Team:ResponseOps) |
There was a problem hiding this 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(), |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
There was a problem hiding this 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 👍
There was a problem hiding this 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!
💚 Build SucceededMetrics [docs]
History
To update your PR or re-run it, just comment with: cc @ymao1 |
* 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
Resolves #131231
Summary
Miscellaneous fixes to connector token client. Left comments on specific items.
Checklist