Xamarin way to open self signed certificate webpage in UIWebView - ios

As the question title described, I want to open a self signed webpage within an UIWebview (Xamarin.iOS)
By default, self signed webpages do not load in an UIWebView.
Important requirements for the solution:
It should be accepted by Apple when I want to submit the app to the Apple app store (so a custom NSUrlRequest does not fit).
It should load css and javascript properly.
I found a possible solution on stackoverflow but this is for native iOS.
https://stackoverflow.com/a/11664147
I was also wondering if the solution described above requires to login using a NSUrlConnectionDelegate.
The desired solution should be that the user can fill-in the credentials by himself using the UIWebView.
Could someone provide the Xamarin solution for this? I tried it by myself but couldn't get it work.
Thanks in advance for your help.

I know this is quite an old post, but it was quite an interesting question, so I had to take a go at at. So if you still need it (most likely not) or if anyone finds this post, here are a ported version of the native UIWebView with support for self signed. It can be used as a regular UIWebView with the exception that it takes a hostname as additional parameter, which should be the hostname of the page where certificate check should be disabled.
public class InsecureWebView : UIWebView, INSUrlConnectionDataDelegate, IUIWebViewDelegate
{
public InsecureWebView(string baseUrl) : base()
{
Setup (baseUrl);
}
public InsecureWebView(CoreGraphics.CGRect rect, string baseUrl) : base(rect)
{
Setup (baseUrl);
}
public InsecureWebView(NSCoder coder, string baseUrl) : base(coder)
{
Setup (baseUrl);
}
public InsecureWebView(NSObjectFlag t, string baseUrl) : base(t)
{
Setup (baseUrl);
}
public InsecureWebView(IntPtr handler, string baseUrl) : base(handler)
{
Setup (baseUrl);
}
string baseUrl = null;
bool authenticated;
NSUrlRequest failedRequest;
private void Setup(string baseUrl)
{
this.Delegate = this;
this.baseUrl = baseUrl;
}
[Foundation.Export ("webView:shouldStartLoadWithRequest:navigationType:")]
public bool ShouldStartLoad (UIKit.UIWebView webView, Foundation.NSUrlRequest request, UIKit.UIWebViewNavigationType navigationType)
{
var result = authenticated;
if (!authenticated) {
failedRequest = request;
NSUrlConnection.FromRequest (request, this);
}
return result;
}
[Foundation.Export ("connection:willSendRequestForAuthenticationChallenge:")]
public void WillSendRequestForAuthenticationChallenge (NSUrlConnection connection, NSUrlAuthenticationChallenge challenge)
{
if (challenge.ProtectionSpace.AuthenticationMethod == NSUrlProtectionSpace.AuthenticationMethodServerTrust) {
var baseUrl = new NSUrl (this.baseUrl);
if (challenge.ProtectionSpace.Host == baseUrl.Host) {
challenge.Sender.UseCredential (NSUrlCredential.FromTrust (challenge.ProtectionSpace.ServerSecTrust), challenge);
}
}
challenge.Sender.ContinueWithoutCredential (challenge);
}
[Foundation.Export ("connection:didReceiveResponse:")]
public void DidReceivedResponse(NSUrlConnection connection, NSUrlResponse response)
{
authenticated = true;
connection.Cancel ();
LoadRequest (failedRequest);
}
}

Related

Xamarin iOS: Pass credentials over http

I am trying to pass credentials over http on my xamarin iOS project but its not working.
public partial class WebView : UIViewController
{
protected override void ViewDidLoad()
{
webView.LoadRequest(new NSUrlRequest(new NSUrl("https:example.com")));
webView.AllowsBackForwardNavigationGestures = true;
webView.NavigationDelegate = new WebViewDelegate(this);
}
}
public class WebViewDelegate : WKNavigationDelegate, INSUrlConnectionDataDelegate
{
public override void DidReceiveAuthenticationChallenge(WKWebView webView, NSUrlAuthenticationChallenge challenge, Action<NSUrlSessionAuthChallengeDisposition, NSUrlCredential> completionHandler)
{
//base.DidReceiveAuthenticationChallenge(webView, challenge, completionHandler);
completionHandler(NSUrlSessionAuthChallengeDisposition.PerformDefaultHandling, new NSUrlCredential("username", "password", NSUrlCredentialPersistence.ForSession));
Console.WriteLine("We are authenticated");
return;
}
}
can anyone advise how to resolve this issue ?
Update:
DidReceiveAuthenticationChallenge is being called and is being called continuously in a loop. Instead of getting the expected page , I am getting a blank page and it just says "Authenticate"
From Apple's doc on PerformDefaultHandling:
Use the default handling for the challenge as though this delegate method were not implemented. The provided credential parameter is ignored.
Try UseCredential instead:
var crendential = new NSUrlCredential("user", "pass", NSUrlCredentialPersistence.ForSession);
completionHandler(NSUrlSessionAuthChallengeDisposition.UseCredential, crendential);

Loading more than URL in The same browser using jxbrowser

I'am using jxbrowser,
This code loads one URL in one browser and saves it's page .
public class JxBrowserDemo {
public JxBrowserDemo(String url) {
Browser browser = new Browser();
browser.addLoadListener(new LoadAdapter() {
#Override
public void onFinishLoadingFrame(FinishLoadingEvent event) {
if (event.isMainFrame()){
String filePath = "G:\\Test\\index"+System.currentTimeMillis()+".html";
String dirPath = "G:\\Test\\resources";
event.getBrowser().saveWebPage(filePath, dirPath, SavePageType.COMPLETE_HTML);
}
}
});
browser.loadURL(url);
if(!browser.isLoading())
{
browser.dispose();
}
}
public static void main(String args[])
{
JxBrowserDemo jxBrowserDemo=new JxBrowserDemo("www.google.com");
}
}
could I load more than URL in the same browser and save it's pages in Local Path ?
Thanks In advance ............
You can load one by one as many URLs as you want and save these web pages by the saveWebPage() method in the same browser.
You should completely load the web page, invoke the saveWebPage() method, wait until the web page is saved, and then repeat these actions with the next URL.
The dispose() method should be invoked when you don't need to perform any actions with this browser instance.

UrlActionResult in asp.net MVC

I want to know is there any open source action result code available for asp.net mvc in which i can pass a URL and it will response out the content.
Just like there are some inbuilt Action results
FileResult
FileStreamResult
I want to build a custom Action Result in which i can pass a URL (basically an mp3 url) which will get downloaded in memory and the content will be streamed to the current executing response.
It should also support resume download if supported by server and client. Thats a must for me.
public UrlActionResult DownloadUrl(string url)
{
return new UrlActionResult("http://www.example.com/audiofile.mp3");
}
For a basic scenario you could use something like this:
public class DownloadResult : ActionResult
{
private readonly FileStreamResult _fileStreamResult;
public DownloadResult(string url, string contentType)
{
using (var myWebClient = new WebClient())
{
var myStream = myWebClient.OpenRead(url);
_fileStreamResult = new FileStreamResult(myStream, contentType);
}
}
public override void ExecuteResult(ControllerContext context)
{
_fileStreamResult.ExecuteResult(context);
}
}
However if you want to do the resume download it becomes a lot more complicated. Here is a great article with example code.
If you don't need to hide the url just use a redirect. Using a redirect with spare you server bandwidth:
public ActionResult DownloadUrl(string url)
{
return new Redirect("http://www.example.com/audiofile.mp3");
}

How do I convert an HttpRequestBase into an HttpRequest object?

inside my ASP.NET MVC controller, I've got a method that requires an HttpRequest object. All I have access to is an HttpRequestBase object.
Is there anyway I can somehow convert this?
What can/should I do??
You should always use HttpRequestBase and HttpResponseBase in your application as opposed to the concrete versions which are impossible to test (without typemock or some other magic).
Simply use the HttpRequestWrapper class to convert as shown below.
var httpRequestBase = new HttpRequestWrapper(Context.Request);
Is it your method, so you can re-write it to take HttpRequestBase? If not, you can always get the current HttpRequest from HttpContext.Current.HttpRequest to pass on. However, I often wrap access to the HttpContext inside a class like mentioned in ASP.NET: Removing System.Web Dependencies for better unit testing support.
You can just use
System.Web.HttpContext.Current.Request
The key here is that you need the full namespace to get to the "correct" HttpContext.
I know it's been 4 years since this question was asked, but if this will help somebody, then here you go!
(Edit: I see that Kevin Hakanson already gave this answer...so hopefully my response will help those people who just read answers and not comments.) :)
To get HttpRequest in ASP.NET MVC4 .NET 4.5, you can do the following:
this.HttpContext.ApplicationInstance.Context.Request
Try to use/create a HttpRequestWrapper using your HttpRequestBase.
Typically when you need to access the HttpContext property in a controller action, there is something you can do better design wise.
For example, if you need to access the current user, give your action method a parameter of type IPrincipal, which you populate with an Attribute and mock as you wish when testing. For a small example on how, see this blog post, and specifically point 7.
There is no way to convert between these types.
We had a similar case. We rewrote our classes/web services methods so that they use HttpContextBase, HttpApplicationStateBase, HttpServerUtilityBase, HttpSessionStateBase... instead of the types of close name without the "Base" suffix (HttpContext, ... HttpSessionState). They are a lot easier to handle with home-made mocking.
I feel sorry you couldn't do it.
This is an ASP.Net MVC 3.0 AsyncController which accepts requests, converts the inbound HttpRequestBase MVC object to a System.Web.HttpWebRequest. It then sends the request asynchronously. When the response comes back, it converts the System.Web.HttpWebResponse back into an MVC HttpResponseBase object which can be returned via the MVC controller.
To answer this question explicitly, I guess you'd only be interested in the BuildWebRequest() function. However, it demonstrates how to move through the whole pipeline - converting from BaseRequest > Request and then Response > BaseResponse. I thought sharing both would be useful.
Through these classes, you can have an MVC server which acts as a web proxy.
Hope this helps!
Controller:
[HandleError]
public class MyProxy : AsyncController
{
[HttpGet]
public void RedirectAsync()
{
AsyncManager.OutstandingOperations.Increment();
var hubBroker = new RequestBroker();
hubBroker.BrokerCompleted += (sender, e) =>
{
this.AsyncManager.Parameters["brokered"] = e.Response;
this.AsyncManager.OutstandingOperations.Decrement();
};
hubBroker.BrokerAsync(this.Request, redirectTo);
}
public ActionResult RedirectCompleted(HttpWebResponse brokered)
{
RequestBroker.BuildControllerResponse(this.Response, brokered);
return new HttpStatusCodeResult(Response.StatusCode);
}
}
This is the proxy class which does the heavy lifting:
namespace MyProxy
{
/// <summary>
/// Asynchronous operation to proxy or "broker" a request via MVC
/// </summary>
internal class RequestBroker
{
/*
* HttpWebRequest is a little protective, and if we do a straight copy of header information we will get ArgumentException for a set of 'restricted'
* headers which either can't be set or need to be set on other interfaces. This is a complete list of restricted headers.
*/
private static readonly string[] RestrictedHeaders = new string[] { "Accept", "Connection", "Content-Length", "Content-Type", "Date", "Expect", "Host", "If-Modified-Since", "Range", "Referer", "Transfer-Encoding", "User-Agent", "Proxy-Connection" };
internal class BrokerEventArgs : EventArgs
{
public DateTime StartTime { get; set; }
public HttpWebResponse Response { get; set; }
}
public delegate void BrokerEventHandler(object sender, BrokerEventArgs e);
public event BrokerEventHandler BrokerCompleted;
public void BrokerAsync(HttpRequestBase requestToBroker, string redirectToUrl)
{
var httpRequest = BuildWebRequest(requestToBroker, redirectToUrl);
var brokerTask = new Task(() => this.DoBroker(httpRequest));
brokerTask.Start();
}
private void DoBroker(HttpWebRequest requestToBroker)
{
var startTime = DateTime.UtcNow;
HttpWebResponse response;
try
{
response = requestToBroker.GetResponse() as HttpWebResponse;
}
catch (WebException e)
{
Trace.TraceError("Broker Fail: " + e.ToString());
response = e.Response as HttpWebResponse;
}
var args = new BrokerEventArgs()
{
StartTime = startTime,
Response = response,
};
this.BrokerCompleted(this, args);
}
public static void BuildControllerResponse(HttpResponseBase httpResponseBase, HttpWebResponse brokeredResponse)
{
if (brokeredResponse == null)
{
PerfCounters.ErrorCounter.Increment();
throw new GriddleException("Failed to broker a response. Refer to logs for details.");
}
httpResponseBase.Charset = brokeredResponse.CharacterSet;
httpResponseBase.ContentType = brokeredResponse.ContentType;
foreach (Cookie cookie in brokeredResponse.Cookies)
{
httpResponseBase.Cookies.Add(CookieToHttpCookie(cookie));
}
foreach (var header in brokeredResponse.Headers.AllKeys
.Where(k => !k.Equals("Transfer-Encoding", StringComparison.InvariantCultureIgnoreCase)))
{
httpResponseBase.Headers.Add(header, brokeredResponse.Headers[header]);
}
httpResponseBase.StatusCode = (int)brokeredResponse.StatusCode;
httpResponseBase.StatusDescription = brokeredResponse.StatusDescription;
BridgeAndCloseStreams(brokeredResponse.GetResponseStream(), httpResponseBase.OutputStream);
}
private static HttpWebRequest BuildWebRequest(HttpRequestBase requestToBroker, string redirectToUrl)
{
var httpRequest = (HttpWebRequest)WebRequest.Create(redirectToUrl);
if (requestToBroker.Headers != null)
{
foreach (var header in requestToBroker.Headers.AllKeys)
{
if (RestrictedHeaders.Any(h => header.Equals(h, StringComparison.InvariantCultureIgnoreCase)))
{
continue;
}
httpRequest.Headers.Add(header, requestToBroker.Headers[header]);
}
}
httpRequest.Accept = string.Join(",", requestToBroker.AcceptTypes);
httpRequest.ContentType = requestToBroker.ContentType;
httpRequest.Method = requestToBroker.HttpMethod;
if (requestToBroker.UrlReferrer != null)
{
httpRequest.Referer = requestToBroker.UrlReferrer.AbsoluteUri;
}
httpRequest.UserAgent = requestToBroker.UserAgent;
/* This is a performance change which I like.
* If this is not explicitly set to null, the CLR will do a registry hit for each request to use the default proxy.
*/
httpRequest.Proxy = null;
if (requestToBroker.HttpMethod.Equals("POST", StringComparison.InvariantCultureIgnoreCase))
{
BridgeAndCloseStreams(requestToBroker.InputStream, httpRequest.GetRequestStream());
}
return httpRequest;
}
/// <summary>
/// Convert System.Net.Cookie into System.Web.HttpCookie
/// </summary>
private static HttpCookie CookieToHttpCookie(Cookie cookie)
{
HttpCookie httpCookie = new HttpCookie(cookie.Name);
foreach (string value in cookie.Value.Split('&'))
{
string[] val = value.Split('=');
httpCookie.Values.Add(val[0], val[1]);
}
httpCookie.Domain = cookie.Domain;
httpCookie.Expires = cookie.Expires;
httpCookie.HttpOnly = cookie.HttpOnly;
httpCookie.Path = cookie.Path;
httpCookie.Secure = cookie.Secure;
return httpCookie;
}
/// <summary>
/// Reads from stream into the to stream
/// </summary>
private static void BridgeAndCloseStreams(Stream from, Stream to)
{
try
{
int read;
do
{
read = from.ReadByte();
if (read != -1)
{
to.WriteByte((byte)read);
}
}
while (read != -1);
}
finally
{
from.Close();
to.Close();
}
}
}
}
It worked like Kevin said.
I'm using a static method to retrieve the HttpContext.Current.Request, and so always have a HttpRequest object for use when needed.
Here in Class Helper
public static HttpRequest GetRequest()
{
return HttpContext.Current.Request;
}
Here in Controller
if (AcessoModel.UsuarioLogado(Helper.GetRequest()))
Here in View
bool bUserLogado = ProjectNamespace.Models.AcessoModel.UsuarioLogado(
ProjectNamespace.Models.Helper.GetRequest()
);
if (bUserLogado == false) { Response.Redirect("/"); }
My Method UsuarioLogado
public static bool UsuarioLogado(HttpRequest Request)

ASP.Net MVC - forms authentication using an external URL

Our organization has a central solution for forms authentication. I am trying to implement an ASP.Net MVC app that uses this external URL - and it worked till RC! was released...
Here's what's happening
In an ActionAttribute Extension
I check for s session var
if not found
check for a request data chuck
if found, set the session var
if not found - redirect to external URL
if found
continue.
The trouble is that till I updated to RC1, this worked. Since then, so many requests are being sent to the external URL that it detects a DoS attack and shuts me out!
I removed the redirection code and replaced it with the web.config changes for Forms Auth - and the same thing happened...
Why not use Microsoft Geneva instead of attempting to roll your own authentication provider?
CODE:
public class MyAuthenticate : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Session["user"] == null)
{
using (Authenticator dp = new Authenticator())
{
MyUser mu;
string data = string.Empty;
try
{
data = filterContext.HttpContext.Request["Data"];
}
catch { };
if (!string.IsNullOrEmpty(data))
{
mu = dp.Redeem(data);
if (mu.authenticated)
{
filterContext.HttpContext.Session.Clear();
AuthenticatedUser user = new AuthenticatedUser(mu);
filterContext.HttpContext.Session.Add("user", user);
FormsAuthentication.SetAuthCookie(user.UserId, false);
}
else
{
filterContext.HttpContext.Response.Redirect("MY EXTERNAL URL GOES HERE!!");
}
}
else
{
filterContext.HttpContext.Response.Redirect("MY EXTERNAL URL GOES HERE!!");
}
}
}
base.OnActionExecuting(filterContext);
}
}
}
I resolved this issue by creating a static dictionary of requesting IPs, and dropping duplicate requests from the same IP. Not a very nice solution - so if anyone figures out a better solution - let me know.

Resources