I am trying to set up an Android app where I can access URL's behind arbitrary proxies or HTTP authentications. That is, the app won't know immediately if a URL needs authentication and will repond to an authentication request by asking the user for credentials, the same way it does for the Android Browser.
I recently worked out how to request user authentication for a WebView, responding to authentication requests from the browser, bringing up a dialog, and advancing the user to the destination page. I am looking to do the same with HttpClient.
The basic process, as I see it, is to:
Perform the request via HttpClient.execute.
If not 401 or 407 with proper headers, trigger a "done" callback.
Otherwise...
Pop up a dialog asking for the username and password.
Set the http credentials in the HTTP client via HttpClient.getCredentialsProvider().setCredentials.
Return to step 1 until the the user clicks cancel or the server gives up. In that case, trigger a "failed" callback with the last response received.
Am I on the right track or has someone already implemented something similar? I would hate to be reinventing the wheel on this.
You should try the authentication example on the apache site
httpclient.getCredentialsProvider().setCredentials(
new AuthScope("localhost", 443),
new UsernamePasswordCredentials("username", "password"));
The direct link to the java file is http://hc.apache.org/httpcomponents-client-ga/httpclient/examples/org/apache/http/examples/client/ClientAuthentication.java
The reactive approach described above did work. Responding to an arbitrary 401 or 407 request is effective with the caveat that you need to suppor each authentication scheme you expect to encounter so something like UsernamePasswordCredentials won't work for NTLM.
Related
I have set up third party OAuth authentication on my Node.js server using passport.js.
It works well from browser. The flow is the following:
Get request to myserver.com/auth/instagram
Passport redirects me to instagram website to login
I type my credentials
I am redirected to the callback url, which is my server.com/auth/instagram/callback
I do further processing and finally res.send(myAccessToken) to the client.
I am having troubles implementing this in Android. To type third-party credential, a browser page will be needed. But then how am I going to get the final result in my app?
Option 1
Start the browser.
Intent i = new Intent(ACTION_VIEW);
i.setData(Uri.parse("https://myserver.com/auth/instagram"));
startActivity(i);
Issues:
I cannot easily set proper headers needed for my server.
I cannot programmatically read the final response! The action is given to the browser app, which goes to the end and will never notify my app. The user is shown a blank page with myAccessToken printed.
Option 2
Connect using some http library.
final Request request = createRequest()
.url("https://myserver.com/auth/instagram")
.get()
.build();
Response response = client.newCall(request).execute();
Issues:
Does not work! Even if I set okhttp to follow redirects, I get a 200 response pointing to some instagram url. Of course the user is never prompted his instagram credentials, because no browser ever opened.
How to deal with this?
Notes:
I have used instagram as an example, the third party service is not relevant to the question. I am looking for a general solution to receive the result callback in my Android app.
I am not willing to implement client-side auth. I am willing to trigger steps 1.-5. from my Android app and receive the final result in a Java callback.
I have an application where authentication happens through a passive client. Basically based on server information, a browser will be launched and it will show a login screen. Once the user enters login credentials, the further handling of cookies and session is done in shouldOverrideUrlLoading.
The issue is coming with authentication when I am connecting to web application servers in a clustered environment. When user connects to first server, it shows him login screen and user enters the details, server authenticates, but during session handling in shouldOverrideUrlLoading, my code connects to the same server with same url, but the response from the server comes that user has not been authenticated, while he has already done authentication.
So to differentiate between different servers, we use JSESSIONID to identify server.
I get the original JSESSIONID that was used on the first URL, but when the second URL is fired, my code use JSESSIONID and other cookies from the first URL in the request of second URL. To fire second URL, i use org.apache.http.impl.client.DefaultHttpClient.execute method.
I am not sure what I am missing to get the response from server that user is already authenticated.
I resolved this issue. There was an issue with cookie version, I was using while building a HTTP context for second request.
BasicClientCookie cookie = new BasicClientCookie(name,value);
// cookie.setVersion(1);
cookie.setDomain(host);
cookie.setPath("/");
cookie.setSecure(true);
cookieJar.addCookie(cookie);
I commented version for cookie and then it recognized request to send to same cluster member which was authenticated in first request.
So I want to login to a webpage which uses Basic Authentication to login.
If I send HTTP Get with the Authorization Header, I get the successful response as HTML. But afterwards, it asks me again, to login, because it didn’t remember that I’m already logged in.
So my question is:
How can I save this information, that I have already been logged in?
I know, I already asked that question before, but since no one answered any more on the other thread my last hope was to open another.
The other thread: Android doesnt notice when i do an WWW-Authenticate
EDIT:
So what i wanna do is:
Intent browserIntent = new Intent(Intent.ACTION_VIEW,Uri.parse("The Website with the Authentication")); // Here, i want to add the request header
handler.startActivity(browserIntent);
But i have no idea how, because, the only thing you can send the intent is the Url (and no request headers).
Hope this makes things clearer.
Basic authentication doesn’t save state on the server end. Every request requires that you include the login information.
What happens normally is that:
The browser makes a request for a URL, lacking any authentication.
The server returns code 401, that this URL requires basic authentication.
The browser asks (either the user, or some database of its own) for the username and password. If it asked the user for the password, the browser then stores the username/password in an internal database, keying it against this URL.
For every further request for this URL, the browser includes that username and password in the HTTP GET request.
So the basic answer is, the server doesn’t remember that you have already logged in. The browser remembers that. Since you’re writing the browser, it’s up to you to have your application remember the username/password combination for that URL. Whatever you are doing now after you receive a 401 response, you simply always do for that URL. You don’t even need to wait for the 401 response.
So basically i need my android app to connect to a web service using a url as such
"http://username:password#0.0.0.0" aka basic authentication.
obviously the username and password are checked by the web app before allowing access and otherwise doesn't allow the request.
my issue is that all the methods i try always say unauthorised (response code 401) regardless of what combination of classes and methods ive used to try and connect to the the url.
The web app in question is designed to return things only is un/pw clears otherwise it returns nothing, the web app and un/pw etc have all be checked and cleared.
so does anyone no the correct way to send a request to a url like that and have it work correctly?
android api8 btw
UPDATE
Turns out my issue is due to the web app using NTLM windows authentication which is not supported directly by androids/apache http library, investigating appropriate workarounds now
Here's some code form a really old project of mine. I used basic auth for some web service, and this worked at the time. I'm not sure if there are updated api's since then (this was Android 1.6), but it should still work.
HttpGet request = new HttpGet();
request.setURI(new URI(url));
UsernamePasswordCredentials credentials =
new UsernamePasswordCredentials(authUser, authPass);
BasicScheme scheme = new BasicScheme();
Header authorizationHeader = scheme.authenticate(credentials, request);
request.addHeader(authorizationHeader);
Basically, Basic HTTP auth is a simple hash of the user and password. The browser allows you to stuff these values in the url, but it actually does the work of adding the basic auth header to your request.
I have an issue with wifi connectivity when a browser form must be completed before internet access is granted. The user (if using a browser) is redirected to a login form requiring them to login or sign up for wifi access.
The users are not using a browser but a request from my app to the server is redirected to a login form making the server unreachable. Is there a graceful way of detecting this behaviour and at least warning the user of the issue..
In many cases the mobile network is available and switching off wifi is all that is required.
An HTTP redirect has a specific response code (well, several specific response codes, from 300 to 307). You could read the responseCode before assuming that you have a connection and handle the failure from that point.
A redirect should also have a Location value in the response header, so you could conceiveably capture that value and launch a WebView, passing Location as the URL, giving the user access to the login form. This would get a little complex, though, depending on how the session is validated - if there's a cookie, for instance, you'd have to extract it from the form response and store it for future requests from your native code. Often these sessions employ the MAC address, in which case you wouldn't have to do anything else (apart from handle a successful login and try your request again).
I hope that's helpful.