Open PEConn opened 4 years ago
My suggestion is to prefix the demo with cct
, to keep it consistent with the TWA demos, which are prefixed with twa
. Eg: cct-postmessage
.
@andreban @PEConn Any news on postMessage support in TWA? This is an essential (and one of the most requested) feature. See my rationale here: https://github.com/GoogleChromeLabs/svgomg-twa/issues/84.
We don't have an update on postMessage for Trusted Web Activity.
Even though we constantly re-evaluate postMessage for Trusted Web Activity, the overall the goal is to use Web Platform APIs where one is available or planned. Where a capability is not available / planned, we're looking into adding those to the platform.
The advantage here is that by doing it this way we ensure implementations will work the same way across platforms and browsers and avoid a situation where developers need to have multiple codebases. This does indeed take longer than just adding postMessage but is better in the long-term.
Nevertheless, we're looking for feedback for features and capabilities that might not make sense as Web APIs but would make sense as postMessage.
Project Fugu already has issues open for both geofencing and geotracking, the use cases mentioned on the rationale. The Chrome issue for geofencing is crbug.com/898533 and the issue for geotracking is crbug.com/898536
@b1tr0t FYI
The advantage here is that by doing it this way we ensure implementations will work the same way across platforms and browsers and avoid a situation where developers need to have multiple codebases. This does indeed take longer than just adding postMessage but is better in the long-term.
TWA is an Android app, so there is only one codebase (native Android code + Web code).
TWA is not only for Chrome. Other browsers like Firefox may implement some Web APIs unacceptably long or even not implement at all, which lead to bad UX and fragmentation (multiple codebases for different browsers, but that use kludges instead of postMessage).
It is now a year later and Web API's are still limiting development in many ways. If it is a Trusted Web Activity, then it seems to me that Android should trust the website postMessage's completely and expose all of Android's API's, a place you (Google) can exert API control outside of Chrome.
My use case is that I am implementing a Suicide Prevention PWA. To place a call to the national suicide hotline, I have to go through the WebAPIs by rendering html like the following: <a href="tel:18002738255">Call Hotline</a>
. This does issue a proper call prompt to the user, but because you've exerted so much control over the WebAPI, the user has to click the link, perhaps click "Always" or "Just once" to confirm which phone app they want to use, and manually place the call in their phone app. These extra barriers likely cause "call abandonment" that may contribute to suicide. I would much prefer the ability to eagerly and automatically place a call to the hotline without multiple layers of confirmation by calling native code and falling back to the WebAPI if I don't get a proper response in a timely manner.
Don't get me wrong, I'd love a WebAPI to come out with a feature that allows calls to happen instantly, but there have to be countless other use-cases like this that one just won't be able to think of, and while I wait for WebAPIs that probably aren't coming for security reasons, I could've launched my trusted app and hopefully saved some lives.
Please reconsider turning postMessage on.
For your use case, would it not work to have a native Android Activity as part of your TWA? The link on your webpage could then be to a custom scheme that your Activity has an Intent filter for (more on that here).
When your Activity is launched, you can then place your call directly - taking us down to just a single click to place the call (if your app also has the android.permission.CALL_PHONE
permission).
You'll have to add some logic during the launch process to detect whether or not the device you're on can handle phone calls (eg, make sure it's not a tablet) and pass that along to your website.
The presence of a workaround implies the presence of a bug, or at least lack of feature, but thank you nonetheless! I'll give that a try.
@andreban
It's already possible for native and browser to talk, by creating a localhost server on native-side, and connecting to it with websockets from browser side (I did a simple test with Ktor
). It's not hard to abstract it away to behave like a real postMessage
, but needs extra care to be secure and not letting some other website to talk with that server.
Also there are legitimate use cases that cannot be implemented with WebApis. My use-case is to implement native push messages (with all capabilities that android provides, specifically message-style notifications and notifications grouping), and once user clicks on a notification, I need to open a modal without reloading the page (it's a SPA). And I don't like to maintain a completely separate android project for such enhanced user experience.
Right now it's super hard to even share fcm_token
inside native-code with web (native talks to fcm, but browser authenticates the user, both tokens needs to be sent to our server alongside each other). To connect these two there are many solutions, each with it's own issues:
Provide fcm_token
as a query-string: it becomes an issue if an attacker sends a link containing his own fcm_token
to another user. To prevent that we have to check for document.referrer
and make sure fcm_token
will be stripped-off from deep-links once opened and only provided by native code. And yet still it's possible within the app to give such links to other users (it's a messenger) which needs much more care to prevent.
To pass user's authentication token to native-code through intents: Such intents are already android-specific, and needs a lot of edge case handling to check if device is android and browser supports such intents and check if we are running as twa, and it's our own verified pwa (using something like getInstalledRelatedApps
).
Pass fcm_token
through http-headers: not secure on older chrome browsers where another app could do the same without being verified, also since it's a SPA, it needs to be handled through service worker trickeries (handling navigation routes and mirroring the token from request back to response).
And all such messes and workarounds just because there's no postMessage. People are already falling into such security issues because there's no easy and safe way for native and browser to talk.
What's the point of two sides trusting each other, but having no easy means to talk to each other? Someone who needs something, he finds the way and he won't care to have multiple codebases. But through illegitimate means where it could lead to security issues.
Has there been any progress on a demo for using postMessage in CCTs?
Any update on postMessage with CCT? Or any hints on how to get this to work? I feel like maybe I am missing something obvious, but the documentation does not make the order of steps clear.
Wanted to pick this back up - so we have setup up digital assets links and got the verification working but it looks like the post message channel is still not created. Any pointers what else might be missing?
hey all, i'm also just wondering if its as simple as calling window.postMessage()
on the website opened within the CustomTab ?
The way i handled this previously is by calling MyWebView.postMessage()
via a js interface that i injected to the webview but now i'm just planning to use CustomTabs
class MyWebViewJavascriptInterface constructor(
private val webEvents: Channel<MyWebViewBridgeEvent>,
) {
@android.webkit.JavascriptInterface @JvmOverloads
fun postMessage(data: String? = null, origin: String? = null) {
data?.let {
webEvents.trySend(MyWebViewFlowJsonEvents(data = it)).getOrThrow()
}
}
companion object {
const val INTERFACE_NAME = "MyWebView"
}
}
For what's it's worth our team got it working but found it did not work with Samsung Browser or Firefox. Unfortunately using custom tabs has just been an exercise in finding which browsers don't support features.
@anurao can you post a demo solution as to how you achieved communication from native to web and vice versa (if any)? The app we are building will only support chrome and edge for now so if your solution does work it will really benefit us.
Thanks :)
We don't have an update on postMessage for Trusted Web Activity.
Even though we constantly re-evaluate postMessage for Trusted Web Activity, the overall the goal is to use Web Platform APIs where one is available or planned. Where a capability is not available / planned, we're looking into adding those to the platform.
The advantage here is that by doing it this way we ensure implementations will work the same way across platforms and browsers and avoid a situation where developers need to have multiple codebases. This does indeed take longer than just adding postMessage but is better in the long-term.
Nevertheless, we're looking for feedback for features and capabilities that might not make sense as Web APIs but would make sense as postMessage.
Project Fugu already has issues open for both geofencing and geotracking, the use cases mentioned on the rationale. The Chrome issue for geofencing is crbug.com/898533 and the issue for geotracking is crbug.com/898536
@b1tr0t FYI
There is still no movement on the functionality to support geofencing in Chrome in the last three years. This is a requirement for my project. Is there a workaround or any movement on supporting postMessage in TWAs? Please advise.
Hi! Good news, postMessage will be supported in chrome 115. You should be able to start testing in dev channels now. I'll check on docs status.
Hi! Good news, postMessage will be supported in chrome 115. You should be able to start testing in dev channels now. I'll check on docs status.
Thank you for the update. If possible, please direct me to documentation on how to use postMessage in a TWA.
Hi @b1tr0t any update on the postMessage documentation? I see chrome 115 is already out in production.
@RoLfBOT See https://developer.chrome.com/articles/post-message-twa/. š
I am confused by what seems to be a lot of conflicting information on this: is it possible to use postMessage without assetlinks.json?
The blog linked above implies yes.
The post says assetlinks.json is required:
In order for the postMessage to work it requires a valid relationship between a website and the Trusted Web Activity app launching this site, this can be done with Digital Asset Links (DAL) by adding the appās package name in your assetlinks.json
Yeah, but it also says that you can communicate with any other sites š¤
Note that setup on the origin associated with the TWA, it is required to provide an origin for theĀ MessageEvent.originĀ field, butĀ postMessageĀ can be used to communicate with other sites that donāt include the Digital Assets Link. For example, if you ownĀ www.example.comĀ you will have to prove that through DAL but you can communicate with any other websites,Ā www.wikipedia.orgĀ for example.
My confusion comes from the distinction between TWA and Custom tabs.
My understanding is that this issue was to create example code of how to use postMessage with Custom tabs not Trusted Web Activity, and that the Assetlinks is required to validate the relationship for the TWA.
Am I missing something here?
You need Digital Asset Links both for postMessage and for TWAs, and in each case it works slightly differently.
For TWAs, you need a Digital Asset Link relationship set up with the site you want to display in your TWA. If this verification fails (or the user navigates to an unverified origin) the top bar will be shown again.
For postMessage, you need a Digital Asset Link relationship set up with a site in order to provide an origin for the message to come from. So for example, I could:
example.com
.wikipedia.org
.wikipedia.org
a message.The MessageEvent.origin
of the message that Wikipedia gets will be example.com
.
Now, if you're using postMessage for your TWA the distinction doesn't matter too much - you'll be setting up Digital Asset Links with your site anyway, and the messages from the Android app will have your own site as the origin.
But for the Custom Tabs case (or if you're doing something really creative with TWAs), you still need a Digital Asset Link set up so that we can represent your message as coming from an origin that you prove you are associated with.
You need Digital Asset Links both for postMessage and for TWAs, and in each case it works slightly differently.
For TWAs, you need a Digital Asset Link relationship set up with the site you want to display in your TWA. If this verification fails (or the user navigates to an unverified origin) the top bar will be shown again.
For postMessage, you need a Digital Asset Link relationship set up with a site in order to provide an origin for the message to come from. So for example, I could:
- Set up a Digital Asset Link with
example.com
.- Load
wikipedia.org
.- Send
wikipedia.org
a message.The
MessageEvent.origin
of the message that Wikipedia gets will beexample.com
.Now, if you're using postMessage for your TWA the distinction doesn't matter too much - you'll be setting up Digital Asset Links with your site anyway, and the messages from the Android app will have your own site as the origin.
But for the Custom Tabs case (or if you're doing something really creative with TWAs), you still need a Digital Asset Link set up so that we can represent your message as coming from an origin that you prove you are associated with.
Thanks so much for the clarification. That's super helpful.
@PEConn thanks again for the clarification.
My use case is that we are publishing an SDK for android, that opens a website that we (SDK publisher) owns and controls and needs to communicate between the android app and webapp.
Because we don't own the published android app (and hence don't control the publisher key) it seems to me very clear that this will not work for us. Is that your view also?
Do you know of an alternative?
Unfortunately the demo for TWA PostMessage (including the instructions from this link) appears not to work out of the box for me. DAL relationship seems to be ok since the site is displayed without address bar.
Debugging the demo application I noticed that calling requestPostMessageChannel would result in a callback for onRelationshipValidationResult with a false.
If I manually request validation through mSession.validateRelationship(CustomTabsService.RELATION_USE_AS_ORIGIN, TARGET_ORIGIN, null);
I receive a true
validation result but following another requestPostMessageChannel
call I get another false
callback.
I never receive a onMessageChannelReady
call.
Any clues why this setup might be failing?
Unfortunately the demo for TWA PostMessage (including the instructions from this link) appears not to work out of the box for me. DAL relationship seems to be ok since the site is displayed without address bar. Debugging the demo application I noticed that calling requestPostMessageChannel would result in a callback for onRelationshipValidationResult with a false. If I manually request validation through
mSession.validateRelationship(CustomTabsService.RELATION_USE_AS_ORIGIN, TARGET_ORIGIN, null);
I receive atrue
validation result but following anotherrequestPostMessageChannel
call I get anotherfalse
callback. I never receive aonMessageChannelReady
call. Any clues why this setup might be failing?
I'm also having the same issue: the requestPostMessageChannel result is true, the onRelationshipValidationResult is false and onMessageChannelReady is not called.
boolean result = mSession.requestPostMessageChannel(SOURCE_ORIGIN, TARGET_ORIGIN,
boolean result = mSession.requestPostMessageChannel(SOURCE_ORIGIN, TARGET_ORIGIN,
boolean result = mSession.requestPostMessageChannel(SOURCE_ORIGIN, TARGET_ORIGIN,
We have the postMessage demo in here https://developer.chrome.com/docs/android/post-message-twa
However on the function onRelationshipValidationResult, it looks that the result is false which means that it's not validated. Therefore the postMessage cannot be used.
Any idea why or how to debug that ?
@PEConn could you give a hand on that ?
I have my DAL that is set : my TWA doesn't show the address bar.
I followed the article here and set everything up https://developer.chrome.com/docs/android/post-message-twa
But I'm having the same issue as @tushe-tv and @andreas-niemoeller-qhr
I'm also having the same issue as @shunxing and the others, any updates on this?
I finally managed to get it to work, I've changed this:
boolean result = mSession.requestPostMessageChannel(SOURCE_ORIGIN, TARGET_ORIGIN, new Bundle());
to this:
boolean result = mSession.requestPostMessageChannel(TARGET_ORIGIN);
After this change the message channel is created and postMessage works, I don't really understand why Can someone please explain this behavior? @PEConn
@DanGavrielov What value do you have set for TARGET_ORIGIN
? And how are you verifying postMessage
works - are you using the demo pointing to https://peconn.github.io, or are you trying it with an app of your creation?
My understanding between the two postMessage
APIs is the the one with three parameters (source, target, bundle) more closely reflects how the API is intended to work - a postMessage
should have the source defined, so the recipient of the message can verify the request is coming from a known domain.
https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsSession#requestPostMessageChannel(android.net.Uri,android.net.Uri,android.os.Bundle)
https://android-review.googlesource.com/c/platform/frameworks/support/+/2594270
@mattdsteele I am currently researching TWAs as an alternative to WebView for a client, I have set up a tiny Ktor server and testing a demo app i created pointing to it
the TARGET_ORIGIN
is the site's address, basically the URL i am launch through the TWA
After the change I can send and receive data
Further attempts show that this also works:
val result = mSession?.requestPostMessageChannel(targetOrigin, targetOrigin, Bundle())
but this does not:
// i'm using my actual package name in my project
val sourceOrigin = Uri.parse("https://com.my.package.name")
val result = mSession?.requestPostMessageChannel(sourceOrigin, targetOrigin, Bundle())
I do understand the intended API is probably for the TWA to be able to identify a message from the app, but it just doesn't work for me using the code in the demo app, the TWA is presented with no address bar, so the DAL is working correctly but creation of a message channel always fails, and I have no idea why.
@DanGavrielov Thanks for the context. Unfortunately my demo app still fails to send messages to the TWA, either using the requestPostMessageChannel(targetOrigin, targetOrigin, Bundle())
, or requestPostMessageChannel(targetOrigin)
.
In my demo app, I have made some progress; my onRelationshipValidationResult
callback returns a result of true
, and I can invoke postMessage('data', null)
and it returns a value of [RESULT_SUCCESS](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsService#RESULT_SUCCESS()). However, I still can't see postMessage events in the TWA; using the window.addEventListener("message", callback)
code as described on https://developer.chrome.com/docs/android/post-message-twa#communicating_from_the_web
If you're able, would you be able to post your demo app to a new Github repo (with any sensitive data scrubbed), so folks could compare your working example with theirs?
@mattdsteele sure here you go
A simple Ktor server i have built to serve some website to test against: https://github.com/DanGavrielov/Ktor-PWA-Server-Demo/
A simple demo Android TWA application: https://github.com/DanGavrielov/TWA-Application-Demo/
If you use this code don't forget to replace all placeholders throughout the code with you specific information Hope this help :)
Thanks @DanGavrielov! For posterity, I was able to get this working as well with my site's specific information; and it helped me figure out the issue in my codebase (in the web code, you need to add the message
eventListener prior to the Android app calls session.requestPostMessageChannel
; if the listener is added later, it won't capture messages).
@DanGavrielov I followed your demo, but the onRelationshipValidationResult
callback still returns a result of false. Is there anything I need to change?
@aashutoshshr I don't really know, this whole thing was a trial and error thing I don't really understand it make sure you replace all of the placeholders throughout the code with actual values, if it still doesn't work then maybe support from more knowledgeable people is needed
I finally managed to get it to work, I've changed this:
boolean result = mSession.requestPostMessageChannel(SOURCE_ORIGIN, TARGET_ORIGIN, new Bundle());
to this:boolean result = mSession.requestPostMessageChannel(TARGET_ORIGIN);
After this change the message channel is created and postMessage works, I don't really understand why Can someone please explain this behavior? @PEConn
Please use the API that specifies the TARGET_ORIGIN
as the other one will be deprecated soon.
There was a bit missing from the comment on the demo, the SOURCE_ORIGIN
get checked to be a valid origin and it has to either start with http://
or https://
as these are the supported ones for now, see #439, a51ecb7809534aadbc76de73586bc4ece270767f
@SayedElabady The code also worked for me when calling:
session.requestPostMessageChannel(TARGET_ORIGIN, TARGET_ORIGIN, Bundle())
Where TARGET_ORIGIN
is my web url
It didn't work when trying the same code with my package name starting with https://
like so:
val SOURCE_ORIGIN = "https://com.example.package.name"
session.requestPostMessageChannel(SOURCE_ORIGIN, TARGET_ORIGIN, Bundle())
Do you see any issue with how the SOURCE_ORIGIN
is defined?
@mattdsteele I am currently researching TWAs as an alternative to WebView for a client, I have set up a tiny Ktor server and testing a demo app i created pointing to it the
TARGET_ORIGIN
is the site's address, basically the URL i am launch through the TWA After the change I can send and receive dataFurther attempts show that this also works:
val result = mSession?.requestPostMessageChannel(targetOrigin, targetOrigin, Bundle())
but this does not:
// i'm using my actual package name in my project val sourceOrigin = Uri.parse("https://com.my.package.name") val result = mSession?.requestPostMessageChannel(sourceOrigin, targetOrigin, Bundle())
I do understand the intended API is probably for the TWA to be able to identify a message from the app, but it just doesn't work for me using the code in the demo app, the TWA is presented with no address bar, so the DAL is working correctly but creation of a message channel always fails, and I have no idea why.
That worked for me @DanGavrielov
However I still haven't figured out why it should be like that, why does it mention SOURCE_ORIGIN in the documentation... maybe @SayedElabady you could give us a hand for explaining why using TARGET_ORIGIN as SOURCE_ORIGIN is actually working
@mattdsteele Thanks, that worked for me.
Using postMessage is pretty confusing, we should add a demo for it.
(This is for CCTs, not for TWAs)