Implement custom 401 handling for a WebBrowser control - c#

As per this article, I've extended the System.Windows.Forms.WebBrowser class to implement custom error-handling. Mostly, it works.
The problem comes when the browser gets a "401 Unauthorized" response. That kind of response causes the WebBrowser control to display the standard Username / Password dialog. The NavigateError event isn't fired until that dialog is cancelled.
So what can I do to capture the 401 response and handle it in my own custom way?
I assumed there would be something I could do, such as that which I do to capture the NavigateError event, and handle those my own way but I haven't seen anything.
Edit: Solution Found!
The important steps are:
1. The WebBrowser control must first be navigated to a non-secure page ("about:blank" is the typical URL used) in order to avoid KB 320153
2. The host for the WebBrowser control must implement IOleClientSite, IServiceProvider, and IAuthenticate.
3. IServiceProvider.QueryService must handle the IAuthenticate service request with the IAuthenticate implementation, all other service requests can be handled with the INET_E_DEFAULT_ACTION response.
4. IAuthenticate.Authenticate is your custom authentication handler.

implement IAuthenticate and IAuthenticateEx on your webbrowser host. Basically, your IOleClientSite implementation needs to responde IServiceProvider.QueryService, and return an IAuthenticate(Ex) interface (not the managed one, the native one returned from Marshal.GetComInterfaceForObject) when the service is IID_IAuthenticate. For unrecognized service requests, QueryService should return INET_E_DEFAULT_ACTION.
I don't think the WPF webbrowser has extension points for its IOleClientSite implementation. You can try host a Winform webbrowser class which has an overriden CreateWebBrowserSiteBase virtual method that provides the IAuthenticate(Ex) implementation, or write a webbrowser wrapper from the ground up.
This may not work in a Citrix session.

I found that to be able to navigate the site without the Authorization Header getting lost or removed I had to do the following otherwise for each new page the user was prompted again. This solution also does not require the user:password#site syntax to be enabled.
private bool _redirected = false;
private const string BaseUrl = #"http://mySite";
private void Navigate()
{
var helpUrl = BaseUrl;
var authHeader = GetAuthHeader();
_docWindow.Browser.Navigate(helpUrl, string.Empty, null, authHeader);
_docWindow.Browser.Navigating += Browser_Navigating;
}
private string GetAuthHeader()
{
byte[] authData = UnicodeEncoding.UTF8.GetBytes(_userName + ":" + _password);
string authHeader = "Authorization: Basic " + Convert.ToBase64String(authData);
return authHeader;
}
void Browser_Navigating(object sender, System.Windows.Navigation.NavigatingCancelEventArgs e)
{
if (_redirected)
{
_redirected = false;
return;
}
var newPage = BaseUrl + e.Uri.AbsolutePath;
e.Cancel = true;
_redirected = true;
_docWindow.Browser.Navigate(newPage, string.Empty, null, GetAuthHeader());
}

Related

Single session enforcement using signalR/Long polling

I want to implement single session enforcement in my application. Meaning if another login activity found for the same user in different browser/different machine then then first session should get auto logoff. If I use the Ajax polling then unnecessary network traffic will happen. So am planning to use signalR.
For that i tried simple click event. Its working without refreshing the page from browser 1 to browser 2.
I created hubclass and my cshtml as follows
var myHub = $.connection.myHub;
$.connection.hub.logging = true;
$.connection.hub.start();
myHub.client.Postmessage = function (message) {
$('#message1').append('<li><strong>'+ htmlEncode(messaage) + '</li>')
$("btnClick").click(function () (
var message = $('message').val();
myHub.server.helloServer(message);
$('#message1').val('').focus();
External service class to find the session details
private static ISSEnforcementSvc ssEnforcement;
public static SSEnforcementSVC GetSSEnforcementService()
{
if (ssEnforcement == null)
{
var localService GetLocalizationsvc;
var configurationSvc = GetConfigurationSvc();
var cacheSvc = GetCacheSvc();
ssessionEnforcement Service = new
SinglesessionEnforcementService(localService,
configurationSvc,cacheSvc)
}
return ssEnforcement;
}
Please suggest how to implement same way to push the 1st browser to logoff.

The game doesn't load in Instant Games Facebook. How to solve the problem?

I'm posting a Unity game on Facebook. I registered and logged into the developer's account (Facebook for developers - hereinafter ffd). But I have a perpetual download, and I do not know how to fix it. And in the background, behind the download, there is a download from Unity, and the game starts (but the download from Facebook overlaps). I compile the game under WEBGL, archive the folder, and attach it to the Web hosting in ffd - all according to the official documentation. The console Blocked opening in a new window because the request was made in a sandboxed frame whose 'allow-popups' permission is not set. I understand the meaning of the error in general, but I do not know how and where to correct it. I do the authentication itself in this way
// Import statements introduce all the necessary classes for this example.
using Facebook.Unity;
using PlayFab;
using PlayFab.ClientModels;
using UnityEngine;
using LoginResult = PlayFab.ClientModels.LoginResult;
public class PlayfabFacebookAuthExample : MonoBehaviour
{
// holds the latest message to be displayed on the screen
private string _message;
public void Start()
{
SetMessage("Initializing Facebook..."); // logs the given message and displays it on the screen using OnGUI method
// This call is required before any other calls to the Facebook API. We pass in the callback to be invoked once initialization is finished
FB.Init(OnFacebookInitialized);
}
private void OnFacebookInitialized()
{
SetMessage("Logging into Facebook...");
// Once Facebook SDK is initialized, if we are logged in, we log out to demonstrate the entire authentication cycle.
if (FB.IsLoggedIn)
FB.LogOut();
// We invoke basic login procedure and pass in the callback to process the result
FB.LogInWithReadPermissions(null, OnFacebookLoggedIn);
}
private void OnFacebookLoggedIn(ILoginResult result)
{
// If result has no errors, it means we have authenticated in Facebook successfully
if (result == null || string.IsNullOrEmpty(result.Error))
{
SetMessage("Facebook Auth Complete! Access Token: " + AccessToken.CurrentAccessToken.TokenString + "\nLogging into PlayFab...");
/*
* We proceed with making a call to PlayFab API. We pass in current Facebook AccessToken and let it create
* and account using CreateAccount flag set to true. We also pass the callback for Success and Failure results
*/
PlayFabClientAPI.LoginWithFacebook(new LoginWithFacebookRequest { CreateAccount = true, AccessToken = AccessToken.CurrentAccessToken.TokenString },
OnPlayfabFacebookAuthComplete, OnPlayfabFacebookAuthFailed);
}
else
{
// If Facebook authentication failed, we stop the cycle with the message
SetMessage("Facebook Auth Failed: " + result.Error + "\n" + result.RawResult, true);
}
}
// When processing both results, we just set the message, explaining what's going on.
private void OnPlayfabFacebookAuthComplete(LoginResult result)
{
SetMessage("PlayFab Facebook Auth Complete. Session ticket: " + result.SessionTicket);
}
private void OnPlayfabFacebookAuthFailed(PlayFabError error)
{
SetMessage("PlayFab Facebook Auth Failed: " + error.GenerateErrorReport(), true);
}
public void SetMessage(string message, bool error = false)
{
_message = message;
if (error)
Debug.LogError(_message);
else
Debug.Log(_message);
}
public void OnGUI()
{
var style = new GUIStyle { fontSize = 40, normal = new GUIStyleState { textColor = Color.white }, alignment = TextAnchor.MiddleCenter, wordWrap = true };
var area = new Rect(0, 0, Screen.width, Screen.height);
GUI.Label(area, _message, style);
}
}
Here the code is sharpened for PlayFab. It is also registered everywhere there. Facebook and Facebook Instant Games are also connected there. I tried other methods of authentication and login in FB, but no matter what I used, this infinite load is always at 0%. PS Facebook SDK is also integrated into Unity through its official package. If you run a compiled project under WEBGL, then everything is fine, a window pops up about logging in to FB (But swears that the site is not c HTTPS (logically, because the launch is from the locale)).enter image description here

how to dynamically generate HTML code using .NET's WebBrowser or mshtml.HTMLDocument?

Most of the answers I have read concerning this subject point to either the System.Windows.Forms.WebBrowser class or the COM interface mshtml.HTMLDocument from the Microsoft HTML Object Library assembly.
The WebBrowser class did not lead me anywhere. The following code fails to retrieve the HTML code as rendered by my web browser:
[STAThread]
public static void Main()
{
WebBrowser wb = new WebBrowser();
wb.Navigate("https://www.google.com/#q=where+am+i");
wb.DocumentCompleted += delegate(object sender, WebBrowserDocumentCompletedEventArgs e)
{
mshtml.IHTMLDocument2 doc = (mshtml.IHTMLDocument2)wb.Document.DomDocument;
foreach (IHTMLElement element in doc.all)
{
System.Diagnostics.Debug.WriteLine(element.outerHTML);
}
};
Form f = new Form();
f.Controls.Add(wb);
Application.Run(f);
}
The above is just an example. I'm not really interested in finding a workaround for figuring out the name of the town where I am located. I simply need to understand how to retrieve that kind of dynamically generated data programmatically.
(Call new System.Net.WebClient.DownloadString("https://www.google.com/#q=where+am+i"), save the resulting text somewhere, search for the name of the town where you are currently located, and let me know if you were able to find it.)
But yet when I access "https://www.google.com/#q=where+am+i" from my Web Browser (ie or firefox) I see the name of my town written on the web page. In Firefox, if I right click on the name of the town and select "Inspect Element (Q)" I clearly see the name of the town written in the HTML code which happens to look quite different from the raw HTML that is returned by WebClient.
After I got tired of playing System.Net.WebBrowser, I decided to give mshtml.HTMLDocument a shot, just to end up with the same useless raw HTML:
public static void Main()
{
mshtml.IHTMLDocument2 doc = (mshtml.IHTMLDocument2)new mshtml.HTMLDocument();
doc.write(new System.Net.WebClient().DownloadString("https://www.google.com/#q=where+am+i"));
foreach (IHTMLElement e in doc.all)
{
System.Diagnostics.Debug.WriteLine(e.outerHTML);
}
}
I suppose there must be an elegant way to obtain this kind of information. Right now all I can think of is add a WebBrowser control to a form, have it navigate to the URL in question, send the keys "CLRL, A", and copy whatever happens to be displayed on the page to the clipboard and attempt to parse it. That's horrible solution, though.
I'd like to contribute some code to Alexei's answer. A few points:
Strictly speaking, it may not always be possible to determine when the page has finished rendering with 100% probability. Some pages
are quite complex and use continuous AJAX updates. But we
can get quite close, by polling the page's current HTML snapshot for changes
and checking the WebBrowser.IsBusy property. That's what
LoadDynamicPage does below.
Some time-out logic has to be present on top of the above, in case the page rendering is never-ending (note CancellationTokenSource).
Async/await is a great tool for coding this, as it gives the linear
code flow to our asynchronous polling logic, which greatly simplifies it.
It's important to enable HTML5 rendering using Browser Feature
Control, as WebBrowser runs in IE7 emulation mode by default.
That's what SetFeatureBrowserEmulation does below.
This is a WinForms app, but the concept can be easily converted into a console app.
This logic works well on the URL you've specifically mentioned: https://www.google.com/#q=where+am+i.
using Microsoft.Win32;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WbFetchPage
{
public partial class MainForm : Form
{
public MainForm()
{
SetFeatureBrowserEmulation();
InitializeComponent();
this.Load += MainForm_Load;
}
// start the task
async void MainForm_Load(object sender, EventArgs e)
{
try
{
var cts = new CancellationTokenSource(10000); // cancel in 10s
var html = await LoadDynamicPage("https://www.google.com/#q=where+am+i", cts.Token);
MessageBox.Show(html.Substring(0, 1024) + "..." ); // it's too long!
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
// navigate and download
async Task<string> LoadDynamicPage(string url, CancellationToken token)
{
// navigate and await DocumentCompleted
var tcs = new TaskCompletionSource<bool>();
WebBrowserDocumentCompletedEventHandler handler = (s, arg) =>
tcs.TrySetResult(true);
using (token.Register(() => tcs.TrySetCanceled(), useSynchronizationContext: true))
{
this.webBrowser.DocumentCompleted += handler;
try
{
this.webBrowser.Navigate(url);
await tcs.Task; // wait for DocumentCompleted
}
finally
{
this.webBrowser.DocumentCompleted -= handler;
}
}
// get the root element
var documentElement = this.webBrowser.Document.GetElementsByTagName("html")[0];
// poll the current HTML for changes asynchronosly
var html = documentElement.OuterHtml;
while (true)
{
// wait asynchronously, this will throw if cancellation requested
await Task.Delay(500, token);
// continue polling if the WebBrowser is still busy
if (this.webBrowser.IsBusy)
continue;
var htmlNow = documentElement.OuterHtml;
if (html == htmlNow)
break; // no changes detected, end the poll loop
html = htmlNow;
}
// consider the page fully rendered
token.ThrowIfCancellationRequested();
return html;
}
// enable HTML5 (assuming we're running IE10+)
// more info: https://stackoverflow.com/a/18333982/1768303
static void SetFeatureBrowserEmulation()
{
if (LicenseManager.UsageMode != LicenseUsageMode.Runtime)
return;
var appName = System.IO.Path.GetFileName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
Registry.SetValue(#"HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION",
appName, 10000, RegistryValueKind.DWord);
}
}
}
Your web-browser code looks reasonable - wait for something, that grab current content. Unfortunately there is no official "I'm done executing JavaScript, feel free to steal content" notification from browser nor JavaScript.
Some sort of active wait (not Sleep but Timer) may be necessary and page-specific. Even if you use headless browser (i.e. PhantomJS) you'll have the same issue.

Trying to check if disconnected using System.Net.WebRequest

I'm developing a website in asp.net and c# which needs to catch if the user isn't connected when they press a button. So basically, if the user is connected, it will load up the GetList function, and if not a message will appear.
Code so far is...
protected void btnAlphabetical_Click(object sender, EventArgs e)
{
Session["Online"] = 0;
CheckConnect();
if ((int)Session["Online"] == 1) { GetList(); }
if ((int)Session["Online"] == 0) { Page.ClientScript.RegisterClientScriptBlock(Page.GetType(), "alertMessage", "alert('You are currently offline')", true); }
}
protected void CheckConnect()
{
System.Uri Url = new System.Uri("http://www.mywebsite.com/apicture.jpg?" + DateTime.Now);
System.Net.WebRequest WebReq;
System.Net.WebResponse Resp;
WebReq = System.Net.WebRequest.Create(Url);
try
{
Resp = WebReq.GetResponse();
Resp.Close();
WebReq = null;
Session["Online"] = 1;
}
catch
{
WebReq = null;
Session["Online"] = 0;
}
}
Basically, we set our Online session value to 0 and call the CheckConnect function. This gets a jpg that's already on the website and, if it can be loaded, sets our Online variable to 1. If it can't find it, it sets it to 0. When control returns to the main function (a button), we progress depending on what Online is - 1 or 0.
Trouble is, and I'm unsure whether this is more to do with my system settings than anything:
when we're online and getting something that DOES exist it works fine and GetList is fired
when we're online and getting something that DOESN'T exist (an invalid URL) it works fine and our message appears (GetList isn't fired)
HOWEVER, when we're offline and fire it, my browser (IE8) just goes to the regular "diagnose connection settings" screen
Is this my code, or part of IE8 in general? I can't use another browser as it's the one my company uses.
Thanks.
EDIT - the general purpose of this is to be used on mobile devices. The user will load up the page and make changes, then use the button. If connection is lost between the page being loaded and the user pressing the button, I don't want the user to lose their changes.

Using Web API for a Windows Service to Receive Commands and Perform Tasks via Polling?

I have a project where I need to create a windows service that, when instructed via a command, will perform various tasks. This server would run on multiple servers and would effectively perform the same kind of tasks when requested.
For example, I would like to have a Web API service that listens for requests from the servers.
The service running on the server would send a query to Web API every 25 secs or so and pass to it its SERVERNAME. The Web API logic will then look up the SERVERNAME and look for any status updates for various tasks... I.E., if a status for a DELETE command is a 1, the service would delete the folder containing log files... if a status for a ZIP command is a 1, the service would zip the folder containing log files and FTP them to a centralized location.
This concept seems simple enough, and I think I need a nudge to tell me if this sounds like a good design. I'm thinking of using .NET 4.5 for the Windows Service, so that I can use the HttpClient object and, of course, .NET 4.5 for the Web API/MVC project.
Can someone please get me started on what a basic Web API woudld look like provide status updates to the Windows services that are running and issue commands to them...
I'm thinking of having a simple MVC website that folks will have a list of servers (maybe based on a simple XML file or something) that they can click various radio buttons to turn on "DELETE", "ZIP" or whatever, to trigger the task on the service.
I do something similar. I have a main Web API (a Windows Service) that drives my application and has a resource called /Heartbeat.
I also have a second Windows Service that has a timer fire every 30 seconds. Each time the timer fires it calls POST /heartbeat. When the heartbeat request is handled, it goes looking for tasks that have been scheduled.
The advantage of this approach is that the service makes the hearbeat request is extremely simple and never has to be updated. All the logic relating to what happens on a heartbeat is in the main service.
The guts of the service are this. It's old code so it is still using HttpWebRequest instead of HttpClient, but that's trivial to change.
public partial class HeartbeatService : ServiceBase {
readonly Timer _Timer = new System.Timers.Timer();
private string _heartbeatTarget;
public HeartbeatService() {
Trace.TraceInformation("Initializing Heartbeat Service");
InitializeComponent();
this.ServiceName = "TavisHeartbeat";
}
protected override void OnStart(string[] args) {
Trace.TraceInformation("Starting...");
_Timer.Enabled = true;
_Timer.Interval = Properties.Settings.Default.IntervalMinutes * 1000 * 60;
_Timer.Elapsed += new ElapsedEventHandler(_Timer_Elapsed);
_heartbeatTarget = Properties.Settings.Default.TargetUrl;
}
protected override void OnStop() {
_Timer.Enabled = false;
}
private void _Timer_Elapsed(object sender, ElapsedEventArgs e) {
Trace.TraceInformation("Heartbeat event triggered");
try {
var httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(_heartbeatTarget);
httpWebRequest.ContentLength = 0;
httpWebRequest.Method = "POST";
var response = (HttpWebResponse)httpWebRequest.GetResponse();
Trace.TraceInformation("Http Response : " + response.StatusCode + " " + response.StatusDescription);
} catch (Exception ex) {
string errorMessage = ex.Message;
while (ex.InnerException != null) {
errorMessage = errorMessage + Environment.NewLine + ex.InnerException.Message;
ex = ex.InnerException;
}
Trace.TraceError(errorMessage);
}
}
}
You can do it with ServiceController.ExecuteCommand() method from .NET.
With the method you can sand custom command to windows' service.
Then in your service you need to implement ServiceBase.OnCustomCommand() to serve incomming custom command event in service.
const int SmartRestart = 8;
...
//APPLICATION TO SEND COMMAND
service.ExecuteCommand(SmartRestart);
...
//SERVICE
protected override void OnCustomCommand(int command)
{
if (command == SmartRestart)
{
// ...
}
}

Categories