-
Notifications
You must be signed in to change notification settings - Fork 3
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
Add Salesforce B2C support #228
Conversation
$client_auth_url = sprintf( '%s/shopper/auth/v1/organizations/%s/oauth2/token', $endpoint, $organization_id ); | ||
|
||
// Even though we're using a refresh token, authentication is still required to receive a new secret | ||
$client_credentials = base64_encode( sprintf( '%s:%s', $client_id, $client_secret ) ); |
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 think this is a bit silly - in order to refresh our auth token using a refresh token, we still need to pass in our client ID and secret. From what I can tell, this is due to the OAuth spec. See the "Client Authentication" section on Refreshing Access Tokens:
If the client was issued a secret, then the client must authenticate this request.
Because we receive a secret in the initial grant, we still need it again here.
It makes me wonder - is it really necessary to save and reuse the refresh token? We'll still sending client credentials every 30 minutes anyway, why not just use the client_credentials
grant every time and remove this function entirely?
The current setup seems like the "correct" flow, but the refresh token feels pointless.
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 believe the primary reason to use the refresh token is because you're generating revokable tokens on the Authorization Server. So imagine we used the client credentials to get new tokens every time instead of just refreshing the one... and then the user goes in to manage their issued tokens and revoke some... and they see a list of 10,000 tokens because we've created a new one on every refresh.
$data_source_config = $this->get_data_source()->to_array(); | ||
|
||
return sprintf( | ||
'%s/search/shopper-search/v1/organizations/%s/product-search?siteId=RefArchGlobal&q=%s', |
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.
Parameters currently passed directly in the URL string should be passed somewhere cleaner, if possible.
@@ -50,6 +50,11 @@ export const AddDataSourceDropdown = () => { | |||
label: SUPPORTED_SERVICES_LABELS.shopify, | |||
value: 'shopify', | |||
}, | |||
{ | |||
icon: HttpIcon, |
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.
Note we're still using the HttpIcon
here instead of something more specific. This should be fixed.
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'm ok with merging this and getting another PR for some design updates if needed.
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 pushed a commit that includes an icon, however the icon is licensed under the MIT license so we should get it replaced with something custom probably.
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.
The icon came from here.
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 switched to the icon provided by Salesforce in their brand guide, though it isn't sizing well for our use case. You might be able to try again. I had to remove a bunch of stuff from the SVG contents to get it to work at all.
I pushed a small commit to rename |
I think we can abstract this some more for using OAuth with other services, but we can do that in a PR when we add another one. This is a great start. A little more work is necessary to "productionize" this just like we need to do with the other data sources, but this looks pretty good. |
This PR adds support for Salesforce B2C product search and selection by ID:
Additionally, this adds support for the Salesforce B2C data source type in remote data settings:
Missing bits
This could be reviewed as-is, but I'd like to document some deficiencies and fragilities with the current code.
get_request_headers()
should be modified to return aarray|WP_Error
in case of runtime failure. Currently if authentication fails inSalesforceB2CAuth::generate_token()
, we know that the subsequent request will fail, but we have no way to report an error without throwing. Instead, the request will fail and return a different error. We should update the inherited funtion signature and all implementors ofget_request_headers()
to return thearray|WP_Error
type. If an error occurs, fail early inQueryRunner::get_request_details()
and return a better error message.Other integrations that do important logic in
get_request_headers()
may also be updated to return an early error on auth failure.There are no tests for this new data source.
Currently we use an authentication fall-through for requests that looks like this:
There's a fallback for expired tokens, but if we have a saved authentication token that appears to be valid, but it still fails during the request, we won't be able to re-authenticate and try again. The auth token is a signed JWT with a known expiration time, so this is unlikely to fail unless there's a clock issue or something. However if it does fail we don't have a way to go back and re-run the whole request from scratch.
Other authenticated endpoints like the Sheets API are also susceptible to the same thing. I think any solution would require an architecture change that allows a parent in the process to report an auth error to
HttpQueryContext
and re-run. I'm not sure if this is something we need to solve in this PR, but it's worth considering.There's not currently a "list" block associated with SalesforceB2C, and I'm not sure if that will be possible. From what I could find there's an endpoint for
getProducts
, but this requires passing in explicit IDs. There's also aproductSearch
that we use in the search block, but this requires a search term. We may need to ask for a category ID or something before listing.I'll add a handful of notes in code below.
Testing
Breakpoints
I found the easiest way to test the auth flow was to set breakpoints in
inc/Integrations/SalesforceB2C/Auth/SalesforceB2CAuth.php
'sgenerate_token()
function.Token expiry
The
access_token
andrefresh_token
expire times are really long (30 minutes and 30 days, respectively), so I also modified the$*_token_expires_in
variables on these lines to a much shorter time:remote-data-blocks/inc/Integrations/SalesforceB2C/Auth/SalesforceB2CAuth.php
Line 150 in 79f5f75
remote-data-blocks/inc/Integrations/SalesforceB2C/Auth/SalesforceB2CAuth.php
Line 197 in 79f5f75
For example:
Clearing cache
To clear cache, run: