Login SDK from platforms like Facebook allows you to login using WebView when Facebook app is not available. After a success login from their implementation of WebView, it will send back an Intent data which then can be use to get some data like token. (I could be wrong but as far as I remember this is what is happening before)
I am only familiar on Android and what I did before is launching the website using WebView then annotate my define methods using #JavascriptInterface so I can retrieve events and data coming from the site. For sending data I use evaluateJavascript("someJSMethod(data)").
Sample of retrieving events from website using WebView:
webView.addJavascriptInterface(new WebAppInterface() {
#JavascriptInterface
public void sampleCallbacks() {
// Do your stuff
}
}, "JSInterface");
Sample of sending events to website using WebView:
webView.evaluateJavascript("sampleJSMethod(data)", s -> {
});
But you will have to enable JavaScript in WebView settings and there is a lint warning that it can introduce XSS vulnerability, thus I do not know if this is a good practice. On iOS side I read it can do the same but I want to confirm if AppStore will allow it or there is a condition which needs to be met. We only want to use the WebView for registration so it is not technically a WebView app in which PlayStore usually disapprove. Is there a way to replicate this said behavior but using Intent and activity result?
Related
Google has asked me to address https://support.google.com/faqs/answer/9095419 in my Android app, which basically means not to use the JavaScript injection mechanism for a web page loaded via HTTP.
Not using this mechanism (option 1) doesn't work for me. Setting android:usesCleartextTraffic to false also doesn't work, as the app uses non-HTTPS traffic elsewhere. So that leaves me with "you can ensure that any affected WebViews do not load any URLs with HTTP schemes via loadUrl" - which I'm happy to do, as my app only uses file:/// URLs to load content into the WebView, which should be fine security-wise. But how do I need to code the shouldOverrideUrlLoading method so that Google's checker recognizes that I'm using only file:/// URLs?
Note that the question is different from both Remediation for JavaScript Interface Injection Vulnerability (because I'm clear what is being asked) and In Android, JavaScript Interface Injection Vulnerability (because I'm not using HTTP, but file:/// URLs).
Edit: Adding my shouldOverrideUrlLoading method. (This isn't the entire method, but the salient part of it.)
#Override
public boolean shouldOverrideUrlLoading (WebView browser, String url) {
if (url.startsWith("file:///")) {
// This is my web site, so do not override; let my WebView load the page
browser.loadUrl(url);
return true;
}
// Otherwise, the link is not for a page on my site, or is an entirely different kind of URI
// (like tel:, geo: or mailto:), so launch another Activity that handles URLs
act.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
return true;
}
I have not found a way to use file:// URLs with assets in a way that satisfies Google code checker. While this would solve the issue, I'm still not clear how one might need to code it.
What I ended up doing -which solves my immediate problem- is to call a JavaScript method via the WebView.evaluateJavascript method. When called from within WebViewClient.onPageFinished the page has finished loading, so all elements are accessible. While not important for my case, this method can also return a value to the Java code. So while it's not a general replacement for a JavascriptInterface, it addresses some of its uses cases.
We want to use Okta for social login, In web view we are having "shouldOverrideUrlLoading(WebView view, WebResourceRequest request)" or "shouldOverrideUrlLoading(WebView view, String url) " method, in this method we are getting accesstoken,refresh token etc.
But now we want to replace webview with custom tab control, In this custom tab control we are having navigation call back, but not getting required access token,refresh token etc in bundle. also not able to identify which url is loading while redirection.
Is there any solution to get this type of data through custom tab control and to redirect to app after getting data.
I'd strongly suggest taking a look at AppAuth-Android to handle these flows for you. By default, AppAuth uses the Authorization Code Flow with PKCE to return tokens from an IDP following best current practice documented in OAuth 2.0 for Native Apps.
If you're interested - Okta wrote a wrapper around this library called Okta AppAuth, to make these flows easier to handle. See their quickstart for integration instructions.
FINAL EDIT
This was a bug. After filing the report it was addressed.
EDIT
I am trying to get user id in a webview of my facebook messenger bot. It works fine on mobile, yet fails on desktop (web). This should not be the case.
Messenger 2.1 release statement gives the following quote:
Desktop support for Extensions SDK: This new feature will extend functionality across mobile and web, creating a consistent experience across devices. Now, features like user ID and sharing that used to only be accessible on mobile will be available on desktop as well. This also provides developers with an easier way to test and debug when implementing webview and chat extensions.
There are two ways to get the user id with messenger extensions: getUserId() and getContext(). The docs state that getUserId() is not available on desktop, but make no mention of getContext().
Howevew, there is a bug report that states that getContext() call is not yet available on desktop.
The docs mention no other ways of getting user id. How is one supposed to do that?
As a kicker, if you read the original question, you will see that getContext() actually does work on desktop (web), but only if the webview is opened through a link sent directly by the bot.
ORIGINAL
I am working on a Facebook (messenger) bot using webview.
Now the most basic of all tasks is to get the userId.
This is where I hit a big problem. Having investigated it thoroughly, below I present cases and results.
case 1: my app sends me a generic template with a web_url button (that opens the webview) and a share button.
Everything works great. I get the user id.
case 2: I click share from the message in case 1, and share it with myself and my app.
from either message thread (I to myself, or the I to app) getContext() call return error 2018166 Permission not valid to call the SDK API. and askPermission() returns 2018154 Messenger Extensions unexpected error.
case 3: I click share from inside the webview using beginShareFlow() and share with myself and the bot.
same as case 2
case 4: here's the kicker.
activating the webview from case 2 or case 3 from Android, works and gives me the user id.
case 5: sharing to a friend who has interacted with the app before.
When that friend activates the webview, from desktop it fails, from android works.
case 6: sharing to a friend who has not interacted with the bot.
on desktop doesn't work (sharing by button or through beginShareFlow()), it works!
So, after writing all of this out, the pattern is:
on android, webview sharing works as expected: I can get the user id whether they have interacted with the bot befor or not.
on desktop, the only time the webview provides the user id, is when the message with the webview link was sent by the bot. Once it is shared by a user, the webview still opens, but does not provide context.
Just to clarify. I get the user id by using getContext, and not by getUserId which the docs specifically say doesn't work on desktop.
Is there anything I can do about this? I would like my bot's webviews to work both on desktop and mobile.
I can think of a workaround, but it's far from ideal
EDIT 2
As requested, the payload for sharing within webview is:
{
"attachment":{
"type":"template",
"payload":{
"template_type":"generic",
"elements": [{
"title":"Testing webview",
"default_action":{
"type":"web_url",
"webview_height_ratio": "full",
"messenger_extensions": true,
"url":"https://plenty.life/webview"
},
"buttons":[{
"type":"web_url",
"webview_height_ratio": "full",
"messenger_extensions": true,
"url":"https://plenty.life/webview",
"title":"Test"
}]
}]
}
}
}
Try this...
<script>
window.extAsyncInit = function() {
// the Messenger Extensions JS SDK is done loading
console.log("using getUserId in",window.name)
MessengerExtensions.getUserID(function success(uids) {
// User ID was successfully obtained.
var psid = uids.psid;
console.log(psid);
document.getElementById("userId").innerText="your User Id Is "+psid;
}, function error(err, errorMessage) {
// Error handling code
console.log("some Error",err,errorMessage)
});
MessengerExtensions.getContext('<appID>',
function success(result){
console.log("success",result)
},
function error(result){
console.log("error",result)
}
);
};
</script>
For example, user is navigating to google.com in WebView.
Is it possible to authorize him there via Google Account Picker (something like described here https://developers.google.com/android/guides/http-auth) to simplify authorization instead of manually logging in via web form?
Android Web browsers (for example, Google Chrome) are authorizing user via this method).
Part I: Using the Google Plus Services API
If I understand your question correctly, you may be able to achieve what you are trying to do using the Google Plus Services API.
You create your GoogleSignInOptions and then create your GoogleApiClient using these sign-in options. From there, you use the Auth.GoogleSignInApi.getSignInIntent with your GoogleApiClient as the parameter.
This intent should launch a SignInIntent that presents the Google account picker (that will include accounts that have been accessed on the device previously, and the ability to add another account).
Once you get back the GoogleSignInResult, you can verify that the user was authenticated and then create the authentication flow as you would otherwise.
Even included in the Android SDK is the Google SignInButton, which you can use right in your layout instead of having to create a custom button for the sign-in.
Part II: Using WebViewClient
Now, if you are trying to use a WebView to authenticate them, your best bet is to extend the WebViewClient class.
Things you will need: clientId, clientSecret, and clientScope (all of these details will be given for you when you create your application in the Google Developer Console)
First things first, your URL to authorize will probably be as follows: https://accounts.google.com/o/oauth2/auth?response_type=code&clientId={your client id}&state={SOMESTATEINFO}&access_type=offline (access type if you want offline access). This should be the initial URL of your WebView
Next, you will want to modify your extended WebViewClient class. What you will want to do is override the shouldOverrideUrlLoading(WebView webView, String url) method to listen for your redirectURL. Probably the easiest thing to do is to use url.startsWith(<your redirect URL>) to detect this. You can then parse the response. If your response contains error, then it means something went wrong. Otherwise, you should get back two fields in the URL: code and state. If you do not get error back, then return true for shouldOverrideUrlLoading.
Once you get your code, you can create a new GoogleAuthorizationCodeFlow, using your client, scopes, and secrets.
Once you have your flow, you will need a GoogleTokenResponse, which you will be able to get using the code obtained above for your authorization code, using GoogleTokenResponse response = flow.newTokenResponse(<code>).setRedirectUri(<redirectUri>).execute().
Once you have done this, and you have your response, you can get your Credential using flow.createAndStoreCredential(response, null).
And voila, using this Credential, you can authenticate your calls.
Caveats I have not been able to get the WebView to recognize accounts that have been signed into on other web browsers, so the account picker may only show the accounts that have been signed into on the app-specific WebView.
tl;dr It is possible to do this with a WebView and WebViewClient, but it's messy and a little bit more roundabout than using the Google Plus Services API.
This example better illustrates the authorization flow/credential stuff once you get the authorization code and such.
And here's some documentation on the WebViewClient that may be useful as well.
Hope this helps point you in the right direction!
In my android app I've got the access token (valid, stored/retrieved into/from sharedPref). My question is if it's possible load a facebook page, say http://m.facebook.com/House into a webview as the current user?
It looks like a dark magic that the SDK can pickup the user session and use it in a webview by just doing a simple CookieSyncManager calls in the authorization process. Any idea how to do it in my own webview? thanks!
You just have to fetch cookie from default browser or from account setup & then you just have to send it to your application's web-view.