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

[V3] Encode Pending Browser Switch Request as Base64 String #90

Conversation

sshropshire
Copy link
Contributor

@sshropshire sshropshire commented Apr 2, 2024

Summary of changes

  • Rename BrowserSwitchPendingRequest to BrowserSwitchStartResult
  • Support ComponentActivity for BrowserSwitchClient#start() method
  • Rename BrowserSwitchResult to BrowserSwitchParseResult
  • Remove BrowserSwitchInfo helper class

Checklist

  • Added a changelog entry

Authors

List GitHub usernames for everyone who contributed to this pull request.

result.put("shouldNotify", shouldNotifyCancellation);
if (metadata != null) {
result.put("metadata", metadata);
byte[] requestJSONBytes = requestJSON.toString().getBytes(StandardCharsets.UTF_8);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Base 64 encoding is technically optional, though it does make the state string appear to the merchant as an opaque data type to discourage tampering. Base64 encoding also has an added benefit of masking the data to prevent casual observation of browser switch metadata e.g. intercepting an ec-token over a screen share.

@sshropshire sshropshire marked this pull request as ready for review April 2, 2024 18:35
@sshropshire sshropshire requested a review from a team as a code owner April 2, 2024 18:35
*
* @param intent the intent to return to your application containing a deep link result from the
* browser flow
* @param pendingRequestState the {@link BrowserSwitchStartResult.Success} token returned from
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure about the state/token naming here, those don't align in my mind. Maybe startResultDetails or startResultInfo or storedStartResult and remove token from the description?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree the naming can be improved. Choosing the right names for Browser Switch will maximize the Developer Experience and influence the DX in core.

For me the only thing with "details" and "info" that's conflicting is an implication that the string we return can provide information at runtime. Technically it's an opaque string that we don't want merchants to inspect.

We should definitely remove the word token, that's a holdover from what I called it originally. But in a way this "state" is similar to a BT nonce–we don't expect merchants to inspect the value for details because the structure of the data is undefined i.e. doesn't follow semantic versioning.

Could we maybe shorten it to requestState? Or continue to ideate as a group this might be something to get some 👀's on. I'd be interested in seeing how this bubbles up to the features in the Core SDK too that could help us pick the right names.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think storedStartResult makes sense. "result" seems generic enough for a merchant to not want to inspect its value.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I like storedStartResult for this method. The only thing where the naming doesn't scale as nicely is when the result is unpacked, we'd end up with result.storedStartResult.

I'm wondering now if we should just go with something like originalRequest to keep it simple? It'll still be opaque because the type is String, and it would work nicely in both contexts (start() and parse()).

Also reiterating the main reason we make this value a String is because strings work nicely with all Android persistence mechanisms e.g. SharedPrefs, DataStore, etc. We only need the merchant to hold on to this value for us to remove the responsibility of state restoration out of the SDK. We've gotten a good number of inbounds on that in the past, which makes us think that state restoration logic may be unique to each individual merchant app.

Copy link
Contributor

@tdchow tdchow May 21, 2024

Choose a reason for hiding this comment

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

What if we had separate naming for the start() and parse() steps? It might give a better description for each for both steps.

Start - startResultToStore

        when (val result = browserSwitchClient.start(this, browserSwitchOptions)) {
            is BrowserSwitchStartResult.Success ->
                PendingRequestStore.put(this, result.startResultToStore)
            ...
        }

Parse - storedStartResult

       PendingRequestStore.get(this)?.let { storedStartRequest ->
            when (val result = browserSwitchClient.parseResult(intent, storedStartResult)) {
                ...
            }
        }

Copy link
Contributor

Choose a reason for hiding this comment

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

Honestly I still kind of think the pendingRequest naming is a little more clear for what it is - a request that hasn't been completed yet. Maybe we could change the types to Started and Failure to better indicate that the request hasn't completed successfully yet?

   when (val pendingRequest = browserSwitchClient.start(this, browserSwitchOptions)) {
            is BrowserSwitchPendingRequest.Started ->
                PendingRequestStore.put(this, pendingRequest.requestToStore)
            ...
        }

Also maybe something like completeRequest would make more sense than parse to indicate what is required by the merchant?

       PendingRequestStore.get(this)?.let { storedPendingRequest ->
            when (val result = browserSwitchClient.completeRequest(intent, storedPendingRequest)) {
                ...
            }
        }

Can't decide what would be most clear for merchants. Tagging in @scannillo @jaxdesmarais @saperi22 for more opinions

Copy link
Contributor

Choose a reason for hiding this comment

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

I like that suggestion. I think the consumer of Browser Switch could name the return value of browserSwitchClient.start(this, browserSwitchOptions) anything they wanted. But I agree with pendingRequest.

I also think completeRequest() is more clear than parse().

Copy link
Contributor

Choose a reason for hiding this comment

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

Agree on pendingRequest and completeRequest, those both sound like the most clear options for both us and merchants

Copy link
Contributor

Choose a reason for hiding this comment

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

PR for renaming parseResult() to completeRequest(): #101

return new BrowserSwitchRequest(requestCode, Uri.parse(url), metadata, returnUrlScheme, shouldNotify);

@NonNull
static BrowserSwitchRequest fromBase64EncodedJSON(@NonNull String base64EncodedRequest) throws BrowserSwitchException {
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess we should weigh the benefits of base64encoding compared to the extra merchant integration lift of having to handle the Failure type for all browser based flows. I could go either way. What do others think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh yeah def. We can't really enforce JSON tampering though. We may still need the failure scenario for the case where we're unable to reconstruct the original BrowserSwitchRequest.

*
* @param intent the intent to return to your application containing a deep link result from the
* browser flow
* @param pendingRequestState the {@link BrowserSwitchStartResult.Success} token returned from
Copy link
Contributor

Choose a reason for hiding this comment

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

I think storedStartResult makes sense. "result" seems generic enough for a merchant to not want to inspect its value.

Comment on lines +14 to +20
// Links
// Base64 Encode a String in Android: https://stackoverflow.com/a/7360440

// TODO: consider encryption
// Ref: https://medium.com/fw-engineering/sharedpreferences-and-android-keystore-c4eac3373ac7

// TODO: Rename to `BrowserSwitchStartRequest` and remove `BrowserSwitchOptions` in favor this class
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we remove these TODOs before merging the PR in?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree, we may be able to convert these to JIRA tickets.

@sarahkoop
Copy link
Contributor

This PR might need to be closed and re-worked after all the changes in browser switch - @sshropshire can you revert the naming changes or open a new PR with just the base64 encoding work?

@sshropshire
Copy link
Contributor Author

Closing this after discussion with @sarahkoop. Will create a new PR with the Base64 encoding changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants