Securing the Controller - XSockets/XSockets.NET-4.0 GitHub Wiki
##Securing the Controller
XSockets.NET does not provide any features for authenticating users. Instead, you integrate the XSockets:NET features into the existing authentication structure for an application. You authenticate users as you would normally in your application, and work with the results of the authentication in your XSockets.NET code. For example, you might authenticate your users with ASP.NET forms authentication, and then in your Controller
, enforce which users or roles are authorized to call a method.
XSockets provides the Authorize attribute to specify which users have access to a controller
or method
. You apply the Authorize attribute to either a controller
or particular methods
in a controller
. Without the Authorize attribute, all public methods on the controller are available to a client that is connected to the controller.
If you want to allow unrestrcited access on some methods you can add the AllowAnonymous
attribute the these methods.
More information about attributes and methods below
Authorize Attribute
This attribute can be set on Controller or Method level. If set at controller level all action methods that do not have the AllowAnonymous
attribute will require authentication.
The authorize attribute can take Roles and Users but if using that you will have to implement your own authentication by overriding OnAuthorization(AuthorizeAttribute authorizeAttribute)
AllowAnonymous Attribute
This attribute can be set on action methods and will then allow anonymous access.
Get FormsAuthentication Ticket
When you have custom authentication you can get the FormsAuthenticationTicket
from this method.
var ticket = GetFormsAuthenticationTicket();
Note: If you do not pass in a cookiename .ASPXAUTH will be used.
Important: If you have separate project you will have to use the same origin to be able to get cookies and also use machine-key in the config to be able to get the AuthCookie. See http://msdn.microsoft.com/en-us/library/system.web.configuration.machinekeysection.compatibilitymode%28v=vs.110%29.aspx if you are using different framework versions in the projects.
Write a custom AuthenticationPipeline
When the socket is connected and the handshake is completed the AuthenticationPipeline
will be called. By default the pipeline will look for a FormsAuthenticationTicket, but you can override this pipline by just implementing a interface XSockets.Core.Common.Socket.IXSocketAuthenticationPipeline
There can only be one pipeline so even if you implement several pipelines only one wil be used.
//using System.Security.Principal;
//using XSockets.Core.Common.Protocol;
//using XSockets.Core.Common.Socket;
//using XSockets.Plugin.Framework.Attributes;
[Export(typeof(IXSocketAuthenticationPipeline))]
public class MyAuthenticationPipeline : IXSocketAuthenticationPipeline
{
public IPrincipal GetPrincipal(IXSocketProtocol protocol)
{
if (protocol.ConnectionContext.User == null)
{
//creating a fake super user ;)
var roles = new string[]{"superman","hulk"};
var userIdentity = new GenericIdentity("David");
protocol.ConnectionContext.User = new GenericPrincipal(userIdentity, roles);
}
return protocol.ConnectionContext.User;
}
}
How to override the OnAuthorization method
The OnAuthorization
method is called for every method on a controller that has authentication. The attribute for the method (or controller) is passed in and we check that
-
That the user is authenticated
-
If the user name is required we verify a match
-
If user name was not required or did not match we check roles
//using System.Linq; //using XSockets.Core.Common.Socket.Attributes; //using XSockets.Core.XSocket; public virtual bool OnAuthorization(IAuthorizeAttribute authorizeAttribute) { try { var user = this.ProtocolInstance.ConnectionContext.User; if (user.Identity.IsAuthenticated) { if (!string.IsNullOrEmpty(authorizeAttribute.Roles) || !string.IsNullOrEmpty(authorizeAttribute.Users)) { if (authorizeAttribute.Users.Split(',').Contains(user.Identity.Name)){ return true; } return authorizeAttribute.Roles.Split(',').Any(user.IsInRole); } return true; } return false; } catch { return false; } }
Example
Based on the Write a custom AuthenticationPipeline
where we added the username Hero
and the roles hulk
, superman
we have the following scenario
//using XSockets.Core.Common.Socket.Attributes;
[Authorize()] //would be valid since we have a authorized `fake` user
[Authorize(Users = "David")] //would be valid
[Authorize(Roles = "batman,robin")] //would be invalid
[Authorize(Roles = "batman,hulk")] //would be valid
How to know when authorization fails
Every time a method on a controller needs authentication the OnAuthorization
method is called (which was overridden in previous sample).
When the OnAuthorization
returns false the OnAuthorizationFailed
event is fired.
//using XSockets.Core.Common.Socket.Event.Arguments;
//using XSockets.Core.XSocket;
//Constructor
public Chat()
{
this.OnAuthorizationFailed += Chat_OnAuthorizationFailed;
}
void Chat_OnAuthorizationFailed(object sender, OnAuthorizationFailedArgs e)
{
Console.WriteLine("Auth Failed: {0},{1}", e.Controller, e.MethodName);
}
The OnAuthorizationFailedArgs
contains information about the controller and the method being called. Information about the user that called the method can be accessed from this.ProtocolInstance.ConnectionContext.User
###How to get information about the client from the ConnectionContext
You can access various information about the connection from the IConnectionContext
. The context is accessible from both Controller and Protocol.
this.ConnectionContext
The context contains information such as:
- Cookies
- Headers
- Querystring
- IPrincipal
- PersistentId
- Environment (is IDictionary<string,object>)
####Getting Cookies
You can get the CookieCollection
by using
var cookies = this.ProtocolInstance.ConnectionContext.CookieCollection;
Note: you will only be able to access cookies if your server is on the same domain as the cookie domain.
####Getting Headers
You can get the headers NameValueCollection
by using
var headers = this.ProtocolInstance.ConnectionContext.Headers;
####Getting Query Strings
You can access the query string NameValueCollection
by using
var querystring = this.ProtocolInstance.ConnectionContext.QueryString;
####Getting the current User (IPrincipal) You can get/set the IPrincipal by using
var principal = this.ProtocolInstance.ConnectionContext.User;
####Getting Environment info When you use custom protocols you can set this to be whatever you need/want.
var env = this.ProtocolInstance.ConnectionContext.Environment;