Here's what I want to have figured out : I'm using a webview to access a feature of a site that requires authentication. Therefore whenever that particular link is loaded the login page is displayed . Is is possible to somehow authenticate silently so that the particular feature is displayed directly ? Perhaps by using the "webView.setHttpAuthUsernamePassword" (which does not seem to work for me, or I don't do it right) or by making some POST or GET before I load the page , or other possibility ?
On logging in the server is simply supposed to send me a cookie.
That method only works for HTTP Basic Auth, not for form-based login.
Your best bet would be to use the onLoadResource method of the WebViewClient to detect that the page is about to load, then override the content of the WebView using loadData with a chunk of HTML/js that posts to the login form with the proper parameters, then detect the successful cookie return and forward on to the original url.
Depending on the site it may be messy: you may have to set the proper referer headers or CSRF tokens and other things of that nature.
Good luck.
See, you could do the task this way, I am not sure if this is what you want:
#Override
public void onReceivedHttpAuthRequest(
WebView view,
HttpAuthHandler handler,
String host,
String realm) {
String currentUrl = yourWebView.getUrl();
if(currentUrl.equals("www.yourwebsite.com")){
handler.proceed("username","password");
}
}
Related
I have an ASP.NET MVC/Web API backend where I have implemented a Forms Authentication for my Phonegap app. The login is executed by sending the users credentials via jQuery Ajax call like this:
$.ajax({
type: "POST",
url: "/api/authentication/login",
data: JSON.stringify({ Username: username, Password: password }),
contentType: "application/json; charset=utf-8",
dataType: "TEXT",
statusCode: {
200: function (response, status, xhr) {
// successfully authenticated
Backbone.history.navigate("/", { trigger: true });
}
}
});
The backends login method looks like this:
[ActionName("login")]
[AllowAnonymous]
public LoginResult Login(LoginCredentials credentials)
{
// doing all kinds of things here
// if valid credentials
FormsAuthentication.SetAuthCookie(loginID, true);
return loginResult;
}
I have this in my Web.config:
<authentication mode="Forms">
<forms
name=".ASPXAUTH"
loginUrl="/login"
defaultUrl="/home"
protection="All"
slidingExpiration="true"
timeout="525600"
cookieless="UseCookies"
enableCrossAppRedirects="false"
requireSSL="true"
>
</forms>
</authentication>
Now the problem with Android here is that the cookie is properly set and it does work on my authorized methods after the login, but sometimes (often) when I close the app and open it again, I'm no longer logged in. The cookie isn't there anymore, I can not see it in the request. This should not happen because I have set the timeout to 525600. I have noticed that this problem often occurs when I close the app immediately after login. In other hand if I log out and then log in without closing the app, the cookie is saved properly.
But, if I get the cookie to stick, most of the time the logout behaves strangely as well. This is how I do the logout request:
$.ajax({
type: "POST",
url: "/api/authentication/logout",
data: "{}",
contentType: "application/json; charset=utf-8",
dataType: "text"
success: function (response) {
// successfully logged out
Backbone.history.navigate("api/login", { trigger: true });
}
});
The backend:
[ActionName("logout")]
[AllowAnonymous]
public String Logout()
{
FormsAuthentication.SignOut();
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, "");
cookie.Expires = DateTime.Now.AddYears(-1);
HttpContext.Current.Response.Cookies.Add(cookie);
return "home";
}
Now similar to the problem with the login, the logout first seems to be successful and the cookie is no longer sent with any requests. But when I close the app and open it again, the cookie is back and I'm logged in again. I can see that the cookie has the same value as the one I thought I just removed by setting its expiration time to the past.
I have tried all kinds of tricks, like:
extra reloads after the login/logout (location.reload())
executing the logout/login request multiple times
executing request to other methods after the login/logout
1-10 second timeout between the login/logout request and the reload
all kinds of variations of the above
The authentication works as intended on iOS and Windows Phone. The problem occurs only on Android (tested on KitKat and Lollipop). No problem on the Android emulator, but on real devices and Visual Studios Android emulator this happens all the time.
I don't know in which direction to go from here. Is there something in the Android WebView that could cause this kind of behavior? Is there something else I could test out? Please help!
I'm more than happy to give more information if needed.
EDIT:
Inspired by Fabian's comment, I changed the logout method to this:
FormsAuthentication.SignOut();
HttpCookie cookie = HttpContext.Current.Response.Cookies[FormsAuthentication.FormsCookieName];
cookie.Expires = DateTime.Now.AddYears(-1);
HttpContext.Current.Response.Cookies.Clear();
HttpContext.Current.Response.Cookies.Add(cookie);
return "home";
Instead of creating a new cookie, I used the one in the response. It did not work.
I also tried something I found from here: http://techblog.dorogin.com/2013/01/formsauthentication-gotcha-with-signout.html That also did no difference, the path was not the problem. Still looking for a solution.
ANOTHER EDIT:
Still not able to find a solution for this. I had to make a horrible workaround.
Login: I make two reloads after the login and then a request to
a dummy method. This seems to work every time.
Logout: I use a flag placed in localStorage to determine if the user has logged out and perform a logout in the startup. This always removes the cookie correctly.
I'm not happy with these hacks and I'm still hoping for a better solution.
PhoneGap loads files from file:// protocol. Unfortunately, cross origin requests are not allowed and unless you open cross origin requests from all hosts *, this problem will not resolve.
There are multiple ways this can be fixed but they are really long.
Load Html from http://
Load entire website from web server instead of local storage. This removes all issues with cross origin requests. Benefit is you don't need to publish new version of app when you change UI. But you will have to implement very powerful caching and first time opening app will take longer time.
Intercept http:// and deliver local files
As you know, phonegap simply uses WebView, in all platforms, you can simply override Url protocol to inject files from your app's local storage. This will be faster, and browser will think that it is loading html from same resource.
Setup OAuth + custom header for authentication
Redirect to a login page hosted at your website say http://domain.com/api/login
After successful login, use PhoneGap localStorage (not browser's localStorage) to store authorization.
Navigate to your local html pages from app and for each json api request you send to server, send authorization header as separate header in ajax request.
Setup a Authorization module, where you can manually authorize asp.net request if your authorization was sent through custom header in http request
I believe I have found the solution. The Phonegap version on your config.xml file is cli-5.1.1, which includes Android Phonegap version 4.0.2 according to the documentation.
The problem with the versions is it seems the Android Phonegap team eventually fixed the cookie storage problem on version 5.2.0. It can be found in release notes as:
CB-10896 We never enabled cookies on the WebView proper
Therefore, updating your Phonegap to latest version should solve the problem.
According to MSDN:
The FormsAuthentication.SignOut method removes the
forms-authentication ticket information from the cookie.
And that's all you need to log the user out. You don't need to expire or remove your cookie itself. Simply change your Logout() to:
[ActionName("logout")]
[AllowAnonymous]
public String Logout()
{
FormsAuthentication.SignOut();
return "home";
}
I have a problem using loadUrl after postUrl.
First I use postUrl to login to a website and after that I want to load another page from the same website with loadUrl. But after using loadUrl Im asked again to login.
Probably you need to handle cookie with WebView.
You need to store the COOKIE returned by the POST request (i.e., the login request). Then, whenever you send a GET request, you need to pass along the cookie so the sever knows you've already logged in.
I did not found an acceptable solution for rewriting all urls referenced in an WebView of an Android-App.
I mean all urls!
Not just those from HTTP-GET Methods. I also need to rewrite urls with HTTP-POST, PUT, DELETE, HEAD methods and urls inside Javascript(with all http methods).
Why I want to do this? I want to make an app, accessing intranet content from outside. Let's say intranet is located on https://myintranet.com
Let's say I have web resources https://myintranet.com/resources/whatever.html. If I am logged in my desktop-machine in the firm I can access the resource directly.
Now we want to go mobile. Therefore we do have a proxy, which supports https and basic authentication. We now accessing a resource with https://myproxy.com/myInternalMapperToIntranet/resources/whatever.html
What did I try:
I tried to set the Web view a WebViewClientoverriding
public WebResourceResponse shouldInterceptRequest (WebView view, String url)
look here Unfortunately this only works for HTTP-GET requests but not for HTTP POST requests.
And a WebViewClient overriding
public boolean shouldOverrideUrlLoading (WebView view, String url)
same thing here, HTTP Posts mapped to a send Button or JavaScript do not provoking a call to one of those methods.
In my case the most easiest way would be, to get the HttpClient of the WebView, and intercepting all requests this urls wants to make and rewriting the url to the one with the Proxy-content.
I simply could define a regex and replace the Url. But it seems, this is not possible in Android. A Collegue did this on a iOS-App, but on Android it seems currently not possible. I found a possible solution #API 21 with:
public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request)
the WebResourceRequests encapsulates the Http Method, so it smells for the possibility to intercept all httpMethods. Unfortunately I have to support API 17, and may not use this API.
I cant believe that this technical requirement cant be realized with standard android components. Please prove me the opposite!
Does anybody knows alternatives?
Doing the whole rewriting in the back-end for many simultaneous users is currently no alternative. It has to be done client-side on the App due performance issues.
TIA Luke
I don't have much experience with development in Android. I am working on app, which can communicate with Facebook, Twitter, LinkedIn and Google+.
Every social network has own wrapper, which handles communication between Android app and social network. Facebook communication is already done. I have problems with Twitter communication. Implemetations, which I have found on internet use twitter4j library and internet browser for logging. Then you must read response data from browser.
For example this: Twitter Test App
My issue is reading this response data from browser. Logging and authorizing in browser goes without problems. Tutorials like I mentioned above use usually onNewIntent() Activity method for dealing with browser response. But I cannot use this method, because it is used for different purposes. I don't know if it's posible to do this without any other Activity apart from MainActivity or I need to create another Activity in wrapper, which will handle whole Twitter communication.
Thanks for any help.
I'm really not sure why so many examples for twitter4j on the internet go through intents and custom schemes to handle the callback URL. I find this awkward for most scenarios. I also dislike leaving the application to go to the browser to login.
Instead, try using a WebView to load the page. Then you can attach a WebViewClient which can detect the callback URL. You won't have to deal with changing the manifest, etc. too. You might do something like this, perhaps inside onCreateView in a DialogFragment:
mWebView = new WebView(context);
mWebView.setWebViewClient(new WebViewClient() {
#Override
public void onLoadResource(WebView view, String url) {
if (url.startsWith(CALLBACK_URL)) {
Uri uri = Uri.parse(url);
String denied = uri.getQueryParameter("denied");
if (denied != null) {
// handle denied
} else {
// handle authenticated
}
}
super.onLoadResource(view, url);
}
});
and then you can load the authentication URL in onViewCreated (if you're doing this in a DialogFragment):
Twitter twitter = new TwitterFactory().getInstance();
twitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
RequestToken reqToken = twitter.getOAuthRequestToken();
mWebView.loadUrl(reqToken.getAuthenticationURL());
I have created an android app that is using a custom-rolled authentication method by calling a web service (webapi on .net mvc 4) with HttpClient. When the user is authenticated, the action method on the server is setting the forms authentication cookie and then returns a user model back to the android client. That user model contains the internal user id and a few other properties.
After the user is authenticated, I'm opening up a WebView on android to serve up a viewport for some simple navigation elements. That WebView needs to be handed the authentication cookie from the webapi call in the previous step. I assume that the forms authentication cookie is being held in the HttpClient (though I can't confirm this) and if so, is there a way to pass that cookie over to the WebView so the web page that is served up in the WebView knows who the user is based on the first step?
If this isn't possible, can someone guide me with some steps to make this work.
TIA
This looks like a very similar problem. Set a cookie to a webView in Android.
Hopefully this can assist you.