Android WebView - with authenticated proxy - java

I am currently trying to debug an Android App built around WebView. The development network environment that I am tasked to deal with (not my choice, it is an 'enterprisey' security decision) is WPA WiFi + proxy server + proxy authentication.
While the instructions on a very helpful previous answer were great, I'm trying to find a way to configure both proxy host:port and username:password.
My constraints are:
Phone is not rooted - trying to reproduce a customer-reported bug, would rather not deviate from typical customer setup
Running a Samsung Galaxy S on Froyo
Built against 2.1
Android apps aren't my usual thing, again not my choice, so if I'm blatantly missing details, be nice ;)

With WebView android proxy configuration, for basic scheme preemptive proxy authentication,
Starting from Android 2.2, the extra header can be set for authentication. The following can add a header for webView's http request:
public void loadUrl(WebView view, String url, String proxyUserName, String proxyPassword){
UsernamePasswordCredentials creds= new UsernamePasswordCredentials(proxyUserName, proxyPassword);
Header credHeader = BasicScheme.authenticate(creds, "UTF-8", true);
Map<String, String> header = new HashMap<String, String>();
header.put(credHeader.getName(), credHeader.getValue());
view.loadUrl(url, header);
}
For older version, the preemptive proxy authentication can be set on mProxyUserName and mProxyPassword in android.webkit.Network by reflection:
public void loadUrl(WebView view, String url, String proxyUserName, String proxyPassword){
try{
Class networkClass = Class.forName("android.webkit.Network");
if (networkClass != null) {
Object networkObj = invokeMethod(networkClass, "getInstance", new Object[]{view.getContext()}, Context.class);
if (networkObj != null) {
Field mProxyUserName = obj.getClass().getDeclaredField("mProxyUserName");
mProxyUserName.setAccessible(true);mProxyUserName.set(networkObj, proxyUserName);
Field mProxyPassword = obj.getClass().getDeclaredField("mProxyPassword");
mProxyPassword.setAccessible(true);mProxyPassword.set(networkObj, proxyPassword);
}
}
}catch(Exception e){
e.printStackTrace();
}
view.loadUrl(url);
}
When you load a new url, both loadUrl() must need to call again. That is very important.
Therefore, a custom WebViewClient should be used to override shouldOverrideUrlLoading(WebView view, String url)
class ProxyAuthWebViewClient extends WebViewClient {
String proxyUserName;
String proxyPassword;
public ProxyAuthWebViewClient(String proxyUserName, String proxyPassword){
this.proxyUserName = proxyUserName;
this.proxyPassword = proxyPassword;
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
loadUrl(view, url, proxyUserName, proxyPassword);
return true ;
}
}
And set the WebViewClient on your webView:
webView.setWebViewClient(new ProxyAuthWebViewClient("user", "password"));

Related

Wiremock proxy record feature - can it replace Host or IP in responses?

I am recording rest api's with wiremock... in my case for SharePoint.
So I set up a recorder:
java -jar wiremock-standalone-2.18.0.jar
Now I go to http://localhost:8080/__admin/recorder/ and I enable recording for my http://sharepointhost.
Now I make some requests to sharepoint rest apis through http://localhost:8080.
But the rest api responses still reference the http://sharepointhost.
Is there a way to turn on some sort of reverse proxy or URL pattern string replace so I can avoid this issue? What is the way to do that in my case? Do I need to use the Java variety of the recorder instead of using the standalone?
WireMock supports "Extensions." And there are some pre-packaged extension types called "Transformers."
There is an extension type that allows you to intercept responses of http requests. Here you can then replace contents of responses.
See http://wiremock.org/docs/extending-wiremock/
I created a GitHub repository with a response body URL rewrite extension:
https://github.com/nddipiazza/wiremock-response-body-url-rewriter
public class ResponseBodyUrlRewriteTransformer extends ResponseTransformer {
final int wiremockPort;
final String wiremockBindAddress;
final private List<String> urlsToReplace;
public ResponseBodyUrlRewriteTransformer(String wiremockBindAddress, int wiremockPort, List<String> urlsToReplace) {
this.urlsToReplace = urlsToReplace;
this.wiremockBindAddress = wiremockBindAddress;
this.wiremockPort = wiremockPort;
}
private String replaceUrlsInBody(String bodyText) {
for (String urlToReplace : urlsToReplace) {
bodyText = bodyText.replaceAll(Pattern.quote(urlToReplace),
"http://" + wiremockBindAddress + ":" + wiremockPort);
}
return bodyText;
}
#Override
public Response transform(Request request, Response response, FileSource files, Parameters parameters) {
if (response.getStatus() == 200) {
ContentTypeHeader contentTypeHeader = response.getHeaders().getContentTypeHeader();
if (contentTypeHeader != null && contentTypeHeader.mimeTypePart().contains("xml")) {
return Response.response()
.body(replaceUrlsInBody(response.getBodyAsString()))
.headers(response.getHeaders())
.status(response.getStatus())
.statusMessage(response.getStatusMessage())
.fault(response.getFault())
.chunkedDribbleDelay(response.getChunkedDribbleDelay())
.fromProxy(response.isFromProxy())
.build();
}
}
return response;
}
#Override
public String getName() {
return "ResponseBodyUrlRewriteTransformer";
}
}
Yes. You can launch WireMock as a proxy with automatic record mode. The command you need is this:
java -jar wiremock-standalone-2.18.0.jar --port 8787 --print-all-network-traffic --verbose --enable-browser-proxying --record-mappings
The important params there are enable-browser-proxying and record-mappings
The proxy is running on port 8787 and you have to configure your browser to use proxy localhost:8787
Now you can browse any web site, and all the trafic will be recorded.

The best way to intercept a WebView request in Android

I am using a WebView in my app in which I must intercept requests. I am currently using the follwing code to do it.
public WebResourceResponse shouldInterceptRequest (WebView view, String url) {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestProperty("User-Agent", userAgent);
String mime;
if (url.lastIndexOf('.') > url.lastIndexOf('/')) {
String ext = url.substring(url.lastIndexOf('.') + 1);
mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext);
} else {
mime = "text/html";
}
return new WebResourceResponse(mime, "UTF-8", conn.getInputStream());
}
Above code works fine in most cases, but no all. For example when I try to login to Outlook, it just shows that my email or password is incorrect, I have also seen other cases in which requests get broken, but everything works fine if I remove shouldInterceptRequest.
Is there any better way that the one I am currently using to intercept requests?
There are two issues with you code
Incorrect extension detection
For example, when the code try to get resource extension for this URL:
https://login.live.com/login.srf?wa=wsignin1.0&rpsnv=12&ct=1442476202&rver=6.4.6456.0&wp=MBI_SSL_SHARED&wreply=https:%2F%2Fmail.live.com%2Fdefault.aspx%3Frru%3Dinbox&lc=1033&id=64855&mkt=en-us&cbcxt=mai
It will return aspx%3Frru%3Dinbox&lc=1033&id=64855&mkt=en-us&cbcxt=mai which is wrong. There is special method for getting extension from the URL: getFileExtensionFromUrl()
According to documentation method MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext) may return null. In this case your code set wrong mime type for the page.
Here is the method code that take into account both these issues
#Override
public WebResourceResponse shouldInterceptRequest(WebView view,
String url) {
String ext = MimeTypeMap.getFileExtensionFromUrl(url);
String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext);
if (mime == null) {
return super.shouldInterceptRequest(view, url);
} else {
HttpURLConnection conn = (HttpURLConnection) new URL(
url).openConnection();
conn.setRequestProperty("User-Agent", userAgent);
return new WebResourceResponse(mime, "UTF-8",
conn.getInputStream());
}
}

openning http connection behind proxy which requires authentication but does not return 407

Our applet is behind Microsoft ISA Server which has integrated proxy authentication.Isa Proxy server returns http 405(NOT 407) for connections which has no authentication credentials on it.There for my java.net.Authenticator class does not get called. how can i authenticate my connections to the proxy server in this situation?
Applet is signed and compiled with java1.6. URLConnection class is used for the connections.
I can see two approaches to working around this problem and neither is really ideal. First, I'm guessing that you've verified that sending the request with the authorization information does not result in a 405 response code? If the answer is yes, you can try setting the Proxy-authorization header in the request as a header:
URL url = new URL("http://location");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Proxy-authorization", authorizationValue);
The format of that header will depend upon the authorization scheme that is required by the proxy server, so you'll have to do some research for your particular scenario.
The second approach involves subclassing an internal JDK class to spoof the response code to force the normal proxy authentication path. First, here is the subclass:
public class HttpURLConnection extends sun.net.www.protocol.http.HttpURLConnection {
#Override
public int getResponseCode() throws IOException {
int code = super.getResponseCode();
if (code == HTTP_BAD_METHOD) {
code = HTTP_PROXY_AUTH;
responseCode = code;
}
return code;
}
}
Of course, this will mask any actual 405 responses so it could have unintended consequences. Telling the URL object to use this requires a subclass of java.net.URLStreamHandlerFactory:
public class URLStreamHandlerFactory extends java.net.URLStreamHandlerFactory {
#Override
URLStreamHandler createURLStreamHandler(String protocol) {
if (!protocol.equals("http")) {
return null;
} else {
return new java.net.URLStreamHandler {
protected String proxy;
protected int proxyPort;
public Handler () {
proxy = null;
proxyPort = -1;
}
public Handler (String proxy, int port) {
this.proxy = proxy;
this.proxyPort = port;
}
#Override
protected java.net.URLConnection openConnection(URL u) throws IOException {
return openConnection(u, (Proxy)null);
}
#Override
protected java.net.URLConnection openConnection(URL u, Proxy p) throws IOException {
return new HttpURLConnection(u, p, this);
}
#Override
protected int getDefaultPort() {
return 80;
}
}
}
}
}
Then you can use this object by calling URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory()); somewhere in initialization code. I found this site and this site useful for looking at how the core Java classes work. If you need to support HTTPS then you will need to make similar changes for that protocol.
Hopefully one of these solutions could be useful for you. I'm not completely sure that the latter approach will work inside an Applet's security constraints. The former should though. It is also possible that this might be easier to do with a different HTTP library such as Apache HttpComponents if you are able to use it.

How to store cookies, and reuse them in another session

I am trying to authenticate a user from a web page,
store the cookies and load an rss-feed from a different web page as the authenticated user.
Im using a webView with this WebViewClient which loads an RSS-link when the user has authenticated himself (url is finished loading): - This does not redirect until after user logs in and presses another link.. How can I redirect straight after login?
class LinkWebViewClient extends WebViewClient
{
//Callback method for when the url is finished loading
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
{
view.loadUrl(url);
view.loadUrl("https://something.com/todays-rssfeeds");
return true;
}
#Override
public void onPageFinished(WebView view, String url) {
CookieSyncManager.getInstance().sync();
};
}
Main Problem is when i try to load the second page, i have to re-authenticate myself because it is not the same session..
Any suggestions to how i can solve these problems?
These are my Cookie settings:
/
/ use cookies to remember a logged in status
CookieSyncManager.createInstance(this);
CookieManager cookieManager = CookieManager.getInstance();
//After Login
List<Cookie> cookies = httpClient.getCookieStore().getCookies();
if (!cookies.isEmpty()) {
for (int i = 0; i < cookies.size(); i++) {
cookie = cookies.get(i);
}
}
Cookie sessionCookie = cookie;
if(sessionCookie != null)
{
String cookieString = sessionCookie.getName() +"="+sessionCookie.getValue()+"; domain="+sessionCookie.getDomain();
cookieManager.setCookie(myUrl, cookieString);
CookieSyncManager.getInstance().sync();
}
All help appreciated! Thanks
Cookies are tied to domain, but if these pages share authentication system (unlikely, I think), you could manually pass session identifier to the second page. If not, the question is how that second page should authorize user?

Spring Security & Facebook OAuth 2.0 Integration with Graph API

Please, at least pseudo (but from working environment not "maybe this should work") application context and controller/filter that will authenticate and/or auto-register Facebook users.
This link: http://blog.kadirpekel.com/2009/11/09/facebook-connect-integration-with-spring-security/ will not do. Actually I will put minus point to anyone who will post it as answer. I spend 2 hours with the thing and I didn't get it to work. I ended bit more bolder and feeling more stupid than usual after this endeavor :-(
I would really like to see OAuth 2.0 solution for facebook connect. And restrict the use of Facebook JavaScript API to absolute minimum.
Following link shows about what I need:
http://www.richardnichols.net/2010/06/implementing-facebook-oauth-2-0-authentication-in-java/
Please post only code to this question. I already got all the advice I can handle.
UPDATE
I have servlet solution and posted answer here if anyone is interested:
Facebook Connect example in JSP (tomcat)
Here's an MVC implementation of facebook OAuth 2.0
The code's in C# and hopefully its similarity with java helps you out.
Controller(Entry point):Controller(in MVC) is the point in the code where the control reaches after someone clicks on the login link.
public ActionResult Authenticate()
{
var oauthFacebook = new FacebookOAuth();
if (Request["code"] == null)
{
//Redirect the user to Facebook for authorization.
Response.Redirect(oauthFacebook.AuthorizationLinkGet());
}
else
{
//Get the access token and secret.
oauthFacebook.AccessTokenGet(Request["code"]);
if (oauthFacebook.Token.Length > 0)
{
//We can now make our api calls
var user = oauthFacebook.GetAttributes();
}
}
}
FacebookOAuth Class
public class FacebookOAuth : Oauth
{
public FacebookOAuth()
{
Authorize = "https://graph.facebook.com/oauth/authorize";
AccessToken = "https://graph.facebook.com/oauth/access_token";
CallbackUrl = "http://<YourURLHere>/Authenticate";
AttributesBaseUrl = "https://graph.facebook.com/me/?access_token=";
ConsumerKey = ConfigurationManager.AppSettings["FacebookConsumerKey"];//Ur Consumer Key goes here
ConsumerSecret = ConfigurationManager.AppSettings["FacebookConsumerSecret"];//Ur Consumer secret goes here
Provider = "Facebook";
}
public override string AuthorizationLinkGet()
{
return
string.Format(
"{0}?client_id={1}&redirect_uri={2}&scope=email,user_education_history,user_location,user_hometown",
Authorize, ConsumerKey, CallbackUrl);
}
public User GetAttributes()
{
string attributesUrl = string.Format("{0}{1}", AttributesBaseUrl, Token);
string attributes = WebRequest(Method.Get, attributesUrl, String.Empty);
var FacebookUser = new JavaScriptSerializer().Deserialize<FacebookUser>(attributes);
return new User()
{
FirstName = FacebookUser.first_name,
MiddleName = FacebookUser.middle_name,
LastName = FacebookUser.last_name,
Locale = FacebookUser.locale,
UserEmail = FacebookUser.email,
AuthProvider = Provider,
AuthToken=Token
};
}
}
OAuth baseclass(Class from which FacebookOAuth derives)
public abstract class Oauth
{
#region Method enum
public enum Method
{
Get,
Post,
Delete
} ;
#endregion
protected string AccessToken;
protected string AttributesBaseUrl;
protected string Authorize;
protected string CallbackUrl;
protected string ConsumerKey;
protected string ConsumerSecret;
public string Provider { get; protected set; }
public string Token { get; set; }
public virtual string AuthorizationLinkGet()
{
return
string.Format(
"{0}?client_id={1}&redirect_uri={2}&scope=publish_stream,email,user_education_history,user_location",
Authorize, ConsumerKey, CallbackUrl);
}
public void AccessTokenGet(string authToken)
{
Token = authToken;
string accessTokenUrl = string.Format("{0}?client_id={1}&redirect_uri={2}&client_secret={3}&code={4}",
AccessToken, ConsumerKey, CallbackUrl, ConsumerSecret, authToken);
string response = WebRequest(Method.Get, accessTokenUrl, String.Empty);
if (response.Length > 0)
{
//Store the returned access_token
NameValueCollection qs = HttpUtility.ParseQueryString(response);
if (qs["access_token"] != null)
{
Token = qs["access_token"];
}
}
}
public string WebRequest(Method method, string url, string postData)
{
StreamWriter requestWriter;
string responseData = string.Empty;
var webRequest = System.Net.WebRequest.Create(url) as HttpWebRequest;
if (webRequest != null)
{
webRequest.Method = method.ToString();
webRequest.ServicePoint.Expect100Continue = false;
webRequest.Timeout = 20000;
if (method == Method.Post)
{
webRequest.ContentType = "application/x-www-form-urlencoded";
//POST the data.
requestWriter = new StreamWriter(webRequest.GetRequestStream());
try
{
requestWriter.Write(postData);
}
finally
{
requestWriter.Close();
}
}
responseData = WebResponseGet(webRequest);
}
return responseData;
}
public string WebResponseGet(HttpWebRequest webRequest)
{
StreamReader responseReader = null;
string responseData;
try
{
responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream());
responseData = responseReader.ReadToEnd();
}
finally
{
if (webRequest != null) webRequest.GetResponse().GetResponseStream().Close();
if (responseReader != null) responseReader.Close();
}
return responseData;
}
}
I actually just finished my non-javascript, implementation of the Facebook Graph API Authentication last night. I was a gargantuan pain in the a**, but it works and it's working fairly well.
I used the example from the link you posted above as a starting point, as well as, the code from here as a starting point. I had to write my own implementation of their FacebookGraphAuthenticationProvider and their FacebookGraphAuthenticationFilter, but now it works the way I want it to.
You need to create implementations of both of these files, put your filter in the filter chain, and create a implementation of the Spring Security UserDetailsService that the Provider can use to manage your user account information. I have some code on my machine at home that I can send you via email if you like.
Here are the steps I had to use to get the authentication to work:
Get an "code" for a user, this is done by making the following call: https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&redirect_uri=YOUR_URL&scope=email,read_stream (The scope is all the permissions you want to request from FB). This call will create an "authentication code" which will then be sent back to your "redirect_uri" (which I stated as http://{my fb app registered domain}/j_spring_security_authentication_check.
Once you have this "code", you need to make a call within your AuthenticationProvider that will retrieve an access_token for your user's session: this URL looks like: https://graph.facebook.com/oauth/access_token? client_id=YOUR_APP_ID&redirect_uri=YOUR_URL&client_secret=YOUR_APP_SECRET&code=THE_CODE_FROM_ABOVE. You have to make sure your "redirect_uri" is the same as the one you did in #1. You'll make the above call using something like Apache's HttpClient, or the like.
Now with this access_token (which comes in the body of above response), you can get your user's profile information with the following URL: https://graph.facebook.com/me?access_token={ACCESS_TOKEN from above). The response will be in JSON. You can also use the access_token with all of the graph API to post status, pictures, etc.
I have some code at home that has my full implementation that I would be happy to share.
I hope this helps at least a bit. I suggest using the Spring Social app to get started with posting status, pictures, wall stuff, etc. This will be a good place to start looking at FB-Spring interaction.

Categories

Resources