Secure Your Microsoft .NET and Silverlight Client
Before you add security to your clients, follow the steps in Checklist: Configure Authentication and Authorization to set up security on Kaazing WebSocket Gateway for your client. The authentication and authorization methods configured on the Gateway influence your client's security implementation. For information on secure network connections between clients and the Gateway, see Checklist: Secure Network Traffic with the Gateway.
Before You Begin
This procedure is part of Checklist: Build Microsoft .NET and Silverlight Clients Using Kaazing WebSocket Gateway:
- Set Up Your Development Environment
- Interact with Kaazing WebSocket Gateway Using the WebSocket API
- Interact with Kaazing WebSocket Gateway Using the EventSource API
- Interact with Kaazing WebSocket Gateway Using the ByteSocket API
- Configure Automatic Connection
- Secure Your Microsoft .NET and Silverlight Client
- Display Logs for .NET and Silverlight Clients
- Troubleshoot Your Microsoft .NET and Silverlight Client
To Secure Your Microsoft .NET and Silverlight Client
This section contains the following topics:
- Creating a Basic Challenge Handler
- Managing Log In Attempts
- Negotiate and Register a Location-Specific Challenge Handler
- Using Wildcards to Match Sub-domains and Paths
Authenticating your client involves implementing a challenge handler to respond to authentication challenges from the Gateway. If your challenge handler is responsible for obtaining user credentials, then you will also need to implement a login handler. For more information, see the .NET and Silverlight Client API.
Creating a Basic Challenge Handler
A challenge handler is a constructor used in an application to respond to authentication challenges from the Gateway when the application attempts to access a protected resource. Each of the resources protected by the Gateway is configured with a different authentication scheme (for example, Basic, Application Basic, Application Negotiate, or Application Token), and your application requires a challenge handler for each of the schemes that it will encounter or a single challenge handler that will respond to all challenges. Also, you can add a dispatch challenge handler to route challenges to specific challenge handlers according to the URI of the requested resource.
For information about each authentication scheme type, see Configure the HTTP Challenge Scheme.
Clients with a single challenge handling strategy for all 401 challenges can simply set a specific challenge handler as the default using ChallengeHandlers.Default
. The following is an example of how to implement a single challenge handler for all challenges:
BasicChallengeHandler basicHandler = ChallengeHandlers.Load<BasicChallengeHandler>(typeof(BasicChallengeHandler)); basicHandler.LoginHandler = new LoginHandlerDemo(); ChallengeHandlers.Default = basicHandler;
Note: Instantiate a class using the ChallengeHandlers.load()
method instead of implementing a class directly, such as new DefaultBasicChallengeHandler()
.
The following example shows how to implement a login handler in your Silverlight client:
public class LoginHandlerDemo : LoginHandler { private MainPage parent; public LoginHandlerDemo(MainPage mainPage) { parent = mainPage; } //handle Challenge request from server public PasswordAuthentication GetCredentials() { parent.Dispatcher.BeginInvoke(parent.PopupLoginPage); //wait for user to click OK or Cancel button parent.userInputCompleted.WaitOne(); return parent.Credentials; } }
The following example shows how to implement a login handler in your .NET client:
public class LoginHandlerDemo : LoginHandler { private CoreDemoForm parent; /// <summary> /// constructor /// <para>pass in main form for callback</para> /// </summary> /// <param name="form"></param> public LoginHandlerDemo(CoreDemoForm form) { this.parent = form; } #region LoginHandler Members PasswordAuthentication LoginHandler.GetCredentials() { return parent.AuthenticationHandler(); } #endregion }
Managing Log In Attempts
When it is not possible for the Kaazing WebSocket Gateway client to create a challenge response, the client must return null
to the Gateway to stop the Gateway from continuing to issue authentication challenges.
The following .NET example demonstrates how to stop the Gateway from issuing further challenges.
public delegate void InvokeDelegate(); private const int maxRetries = 2; // max retry number on wrong credentials private int retry = 0; // number of retry /// <summary> /// .Net HTML5 Demo Form /// </summary> public PasswordAuthentication AuthenticationHandler() { PasswordAuthentication credentials = null; if (retry++ >= maxRetries) { return null; // stop authentication when max retries reached } AutoResetEvent userInputCompleted = new AutoResetEvent(false); this.BeginInvoke((InvokeDelegate)(() => { { credentials = new PasswordAuthentication(loginForm.Username, loginForm.Password.ToCharArray()); } else { //user click cancel button to stop the authentication retry = 0; // reset retry counter credentials = null; // return null to stop authentication process } userInputCompleted.Set(); })); // wait user click 'OK' or 'Cancel' on login window this.BeginInvoke((InvokeDelegate)(() => { Log("CONNECTED"); retry = 0; // reset retry counter DisconnectButton.Enabled = true; SendButton.Enabled = true; })); { Log("DISCONNECTED"); retry = 0; // reset retry counter ConnectButton.Enabled = true; DisconnectButton.Enabled = false; SendButton.Enabled = false;
The following Silverlight example demonstrates how to stop the Gateway from issuing further challenges.
// This code is for the main page (e.g. MainPage.xaml.cs) // username and password from Login Popup Page public PasswordAuthentication Credentials; public System.Threading.AutoResetEvent userInputCompleted = new System.Threading.AutoResetEvent(false); private LoginHandlerDemo loginHandler; /// <summary> /// Silverlight HTML5 Demo Main Page /// </summary> // Create handler for authentication BasicChallengeHandler basicHandler = ChallengeHandlers.Load<BasicChallengeHandler>(typeof(BasicChallengeHandler)); loginHandler = new LoginHandlerDemo(this); basicHandler.LoginHandler = loginHandler; ChallengeHandlers.Default = basicHandler; /// Note: you can use DispatchHandler to assign different challengeHandlers for different locations this.Dispatcher.BeginInvoke(() => { Log("CONNECTED"); loginHandler.Retry = 0; /// reset retry counter DisconnectButton.IsEnabled = true; SendButton.IsEnabled = true; }); this.Dispatcher.BeginInvoke(() => { Log("DISCONNECTED"); loginHandler.Retry = 0; /// reset retry counter ConnectButton.IsEnabled = true; DisconnectButton.IsEnabled = false; SendButton.IsEnabled = false; ... // This code is for the security file (e.g. LoginHandlerDemo.cs) public class LoginHandlerDemo : LoginHandler { private MainPage parent; private const int maxRetries = 2; private int retry; public int Retry { get { return retry; } set { retry = value; } } public LoginHandlerDemo(MainPage mainPage) { parent = mainPage; retry = 0; } //handle Challenge request from server public PasswordAuthentication GetCredentials() { if (retry++ >= maxRetries) { return null; // abort authentication process when max retry reached } parent.Dispatcher.BeginInvoke(parent.PopupLoginPage); //wait until user clicks OK or Cancel button parent.userInputCompleted.WaitOne(); return parent.Credentials; } } ...
Negotiate and Register a Location-Specific Challenge Handler
Client applications with location-specific challenge handling strategies can register a DispatchChallengeHandler
object, on which location-specific ChallengeHandler
objects are then registered. The result is that whenever a request matching one of the specific locations encounters a 401 challenge from the server, the corresponding ChallengeHandler
object is invoked to handle the challenge.
The following example creates a challenge handler to handle authentications and uses DispatchChallengeHandler
to assign different challenge handler to each location:
dispatchHandler = ChallengeHandlers.Load<DispatchChallengeHandler>(typeof(DispatchChallengeHandler)); ChallengeHandlers.Default = dispatchHandler; LoginHandler loginHandler = new LoginHandlerDemo(this);
Next, set a loginHandler
for this location:
BasicChallengeHandler basicHandler = ChallengeHandlers.Load<BasicChallengeHandler>(typeof(BasicChallengeHandler)); basicHandler.LoginHandler = loginHandler; dispatchHandler.Register("ws://myserver.com/*", basicHandler);
Add another challenge handler for a second location:
BasicChallengeHandler basicHandler2 = ChallengeHandlers.Load<BasicChallengeHandler>(typeof(BasicChallengeHandler)); basicHandler2.LoginHandler = loginHandler2; dispatchHandler.Register("ws://otherserver.com/*", basicHandler2);
Using Wildcards to Match Sub-domains and Paths
You can use wildcards (“*”) when registering locations using DispatchChallengeHandler
. Some examples of locationDescription
values with wildcards are:
- */ matches all requests to any host on port 80 (default port), with no user information or path specified.
- *.hostname.com:8000 matches all requests to port 8000 on any sub-domain of hostname.com, but not hostname.com itself.
- server.hostname.com:*/* matches all requests to a particular server on any port on any path but not the empty path.