Extending Windows Authentication in ASP.NET MVC 3 Application - asp.net-mvc

after a lot of googling and reading several solutions on how to manage mixed mode authentication in ASP.NET apps, I still have no fitting solution for my problem.
I've got to implement an intranet application for a bunch of different user groups. Until now i've used windows authenthication which was very simple to implement. My problems arise when it comes to authorizing usergroups for special application functionalities.
Using [Authorize(Users = "DOMAIN\\USER")] works great but due to that i have no access to the active directory managament, it is impossible to me to configure rolemanagement in the way I need it for my application.
What I'd like to do is defining custom roles and memberships in addition to the ones that are defined within the active directory (is such an extension possible? e.g. by implementing an own membershipprovider?).
What do you think is the best solution for my problem. Do I really have to implement a complex mixed mode authentication with forms authentication in addition to windows authentication?
Used Technologies:
MS SQL Server 2008
MS VS 2010
ASP.NET MVC 3 - Razor View Engine
Telerik Extensions for ASP.NET MVC
IIS 7 on Windows Server 2008
EDIT (final solution thanks to the help of dougajmcdonald):
After pointing me to use a custom IPrincipal implementation I've found some solutions here and here. Putting everything together I came to the following solution:
1.Create a custom principal implementation:
public class MyPrincipal: WindowsPrincipal
{
List<string> _roles;
public MyPrincipal(WindowsIdentity identity) : base(identity) {
// fill roles with a sample string just to test if it works
_roles = new List<string>{"someTestRole"};
// TODO: Get roles for the identity out of a custom DB table
}
public override bool IsInRole(string role)
{
if (base.IsInRole(role) || _roles.Contains(role))
{
return true;
}
else
{
return false;
}
}
}
2.Integrate my custom principal implementation into the application through extending the "Global.asax.cs" file:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (Request.IsAuthenticated)
{
WindowsIdentity wi = (WindowsIdentity)HttpContext.Current.User.Identity;
MyPrincipal mp = new MyPrincipal(wi);
HttpContext.Current.User = mp;
}
}
3.Use my custom roles for authorization in my application
public class HomeController : Controller
{
[Authorize(Roles= "someTestRole")]
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
}
It works!!! yeah!

I'm not sure if this still applies in MVC, but in Webforms one way to do this would be as follows:
Create a new IPrincipal implementation perhaps extending WindowsPrincipal
In this class, give it a collection of roles (your own custom roles)
Populate those roles, by perhaps getting them from the DB.
Override IsInRole to return true if the role provided is EITHER true from the base call (WindowsAuthentication/Role) OR from your own custom role collection.
This way you can still hook into Principal.IsInRole("MyRole") and also the principal [PrincipalPermission()] annotation.
Hope it helps.
EDIT in answer to q's:
To integrate the principal into the authorisation you need to write your own method for OnAuthenticate in the global.asax for the type of authentication, so I would guess for you, something like this:
void WindowsAuthentication_OnAuthenticate(object sender, WindowsAuthenticationEventArgs e)
{
// ensure we have a name and made it through authentication
if (e.Identity != null && e.Identity.IsAuthenticated)
{
//create your principal, pass in the identity so you know what permissions are tied to
MyCustomePrincipal opPrincipal = new MyCustomePrincipal(e.Identity);
//assign your principal to the HttpContext.Current.User, or perhaps Thread.Current
HttpContext.Current.User = opPrincipal;
}
}
I believe Authorize came in at a later date to the PrincipalPermission, but I'm not too sure as to when/why of the differences I'm afraid :( - sorry!

Related

Authentication with ASP.NET MVC site and ServiceStack API

I am developing a website using ASP.NET MVC with an API using ServiceStack.
Very soon I want to add authentication. The website will have at least two types of users 'service providers' and 'service consumers', although a user could have multiple roles.
I am open to using new MVC Identity, but I want whatever I use to work nicely for both the servicestack API and MVC 'pages' that don't necessarily use the API but should show different content based on login. I do not want to require javascript for login/logout.
I would like the solution to use tokens as I have not used session state anywhere else, but I am open to other options providing they would scale horizontally on a cloud provider (users next request may go to a different instance of back-end).
Anyone have example of an ideal solution?
(N.B: I am not interested in an externally hosted service).
ServiceStack's Authentication can also be used by external ASP.NET Web Frameworks, the ServiceStack and MVC Integration docs shows how you can accept Login credentials from a MVC Controller and register them with ServiceStack:
public ActionResult Login(string userName, string password, string redirect=null)
{
if (ModelState.IsValid)
{
try
{
using (var authService = ResolveService<AuthenticateService>())
{
var response = authService.Authenticate(new Authenticate {
provider = CredentialsAuthProvider.Name,
UserName = userName,
Password = password,
RememberMe = true,
});
// add ASP.NET auth cookie
FormsAuthentication.SetAuthCookie(userName, true);
return Redirect(string.IsNullOrEmpty(redirect) ? "/" : redirect);
}
}
catch (Exception ex)
{
ModelState.AddModelError(string.Empty, ex.Message);
}
}
return View("Index", GetViewModel());
}
The http://mvc.servicestack.net Live Demo shows an example of calling this controller using a standard MVC HTML Form.
Your MVC Controllers can then inherit ServiceStackController to access the Authenticated Users Session and different ServiceStack providers, here are the API's relating to Session and Authentication:
public class ServiceStackController : Controller
{
//...
ISession SessionBag { get; set; }
bool IsAuthenticated { get; set; }
IAuthSession GetSession(bool reload = true);
TUserSession SessionAs<TUserSession>();
void ClearSession();
}
Enable OAuth Providers
Should you need to you can also enable ServiceStack's different OAuth providers which can optionally callback either directly to a ServiceStack Service or
Further to mythz answer I also needed to know if a user was authenticated in a view and the normal Request.IsAuthenticated does not work when your doing above. So I created a CustomWebViewPage (to use this you will have to change *pageBaseType="Your.NameSpace.CustomWebViewPage" in the View folder's Web.config).
public abstract class CustomWebViewPage : WebViewPage
{
private IServiceStackProvider _serviceStackProvider;
public virtual IServiceStackProvider ServiceStackProvider
{
get
{
return _serviceStackProvider ?? (_serviceStackProvider =
new ServiceStackProvider(new AspNetRequest(base.Context, GetType().Name)));
}
}
public virtual bool IsAuthenticated
{
get { return ServiceStackProvider.IsAuthenticated; }
}
}
public abstract class CustomWebViewPage<TModel> : WebViewPage<TModel>
{
//EXACTLY the same as above method...

CurrentPrincipal/User is empty in Web API service

I may be missing something obvious here. I'm new to both MVC and Web API, so I'm working on keeping my head above water.
I have an MVC application that interfaces with a Web API service. Authentication will be handled by a login service developed internally. When working, the MVC client should check if the current user is authenticated. If they're not, then it will redirect to this login service, which is supposed to authenticate the user and update the current user. I then need to be able to access this identity from the Web API service.
I'm operating under the assumption that the current principal (set via Thread.CurrentPrincipal or HTTPContext.Current.User) in the MVC application should be available in my Web API service, but whenever I try to access it from the service, the principal is empty. I've tried accessing the principal from the service using all of the following options, but it's always empty:
RequestContext.Principal
User.Identity
HttpContext.Current.User
Thread.CurrentPrincipal
Here's the basic idea of my code:
MVC Controller:
public ActionResult Index() {
//Just create a test principal here to see if it's available in the service
IPrincipal temp = new GenericPrincipal(new GenericIdentity("myUserName"), new string[]{});
Thread.CurrentPrincipal = temp;
using (var client = new HttpClient()) {
client.BaseAddress = new Uri("myServiceAddress");
HttpResponseMessage response = client.GetAsync("resourceString")).Result;
...Code to deal with result
}
}
Web API Controller:
[HttpGet]
public HttpResponseMessage MyAction() {
if (User.Identity == null || !User.Identity.IsAuthenticated) {
//So sad
} else {
//Do some work
}
}
The current principal is always empty, regardless of how I try to access it.
I think that you're going to need to set both the thread and context principal. Here's what I'm doing:
private static void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null) {
HttpContext.Current.User = principal;
}
}
Part way down This Article it says:
If your application performs any custom authentication logic, you must set the principal on two places:
Thread.CurrentPrincipal. This property is the standard way to set the thread's principal in .NET.
HttpContext.Current.User. This property is specific to ASP.NET.

Web API with Forms authentication and roles

I have a MVC4 web application set up, which uses Forms authentication and Web API for interaction. All API controllers use the [Authorize] attribute.
This was working just fine out of the box, until we started adding role support. Instead of implementing a full-fledged RoleProvider, I added a list of roles to the ticket's UserData, and created the following module:
public class SecurityModule : IHttpModule
{
public void Init(HttpApplication context)
{
var roleManager = (RoleManagerModule)context.Modules["RoleManager"];
roleManager.GetRoles += GetRoles;
}
void GetRoles(object sender, RoleManagerEventArgs e)
{
var user = e.Context.User;
if (user.Identity.IsAuthenticated && !(user is MyCustomPrincipal))
{
var roles = GetRolesFromFormsAuthCookie();
if (roles != null)
e.Context.User = new MyCustomPrincipal(user.Identity, roles,
otherData);
}
e.RolesPopulated = true;
}
}
This works flawlessly for MVC calls. For API, however, even though GetRoles gets called, when it reaches the corresponding method, it's back to GenericPrincipal.
How can I make this work with Web API too? Do I have to create a DelegatingHandler?
I'm also storing some custom data in my Principal, which might be a reason not to rely on just a RoleProvider (since I'd end up with a RolePrincipal), although I could just store that in the request context.
Update: I've now added a DelegatingHandler that does the same as the IHttpModule and sets Thread.CurrentPrincipal. Is this a reasonable approach?
Have you tried to set the Thread.CurrentPrincipal in the HttpModule as well ?. You can also use a Katana handler, which will work for both, ASP.NET MVC and ASP.NET Web API.

start with SimpleMembership in MVC 4 within visual studio 2012

I am creating web app in MVC 4 that is require custom Login, authentication and role based system. My intention is to use SimpleMembership and SimpleRole but struggling to start with and grasp its basic implication within my app.
I have already database say for example DB1.mdf and have created Users table with userId and UserName. I believe i need to initialize simplemembership with existing database??? and i dont want system create database itself if it doesn't find, as it is doing in default internet template created by visual studio 2012.
many thanks in advanced...
SimpleMembership was not developed for extensive customization or integrating to existing databases. To use an existing database you want to create your own membership provider just like you always have in ASP.NET. You can find direction on creating your own membership provider here.
You need to use WebSecurity.InitializeDatabaseFile tell MVC.NET that you already have a database and a Users table. Then, make sure that the provider uses it upon initialization. So create an ActionFilterAttribute that ensures the Simple Membership provider is initialized, for example:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute
{
private static SimpleMembershipInitializer _initializer;
private static object _initializerLock = new object();
private static bool _isInitialized;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Ensure ASP.NET Simple Membership is initialized only once per app start
LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
}
private class SimpleMembershipInitializer
{
public SimpleMembershipInitializer()
{
try
{
if (!WebSecurity.Initialized)
{
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "Users", "UserId", "UserName", autoCreateTables: true);
}
}
catch (Exception ex)
{
throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);
}
}
}
}
Then you will need to register the filter:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new InitializeSimpleMembershipAttribute());
}
}
EDIT:
Thanks to constructive criticism I've removed the code that decorates controllers with the attribute since adding it globally already takes care of the problem. If you decide not to use it globally you will need to decorate other controllers with it since, as explained on this question, you will notice that users may be authenticated with cookies and redirected to other controllers without passing first by your AccountController.
Also, Jon Galloway shows in this post how you can point to your existing database by using:
WebSecurity.InitializeDatabaseFile("SecurityDemo.sdf", "Users", "UserID", "Username", true);

RIA Services: How can I create custom authentication?

I am working with the Silverlight RIA Services and I want to create custom authentication. This appears to be the only thing that has virtually no documentation (I've read through the entire RIAServicesOverview.docx).
Do you know of a way for me to create a customer authentication service? I don't want to use the default ASP.NET membership model. I don't know what interface or abstract class I need to implement - although I did find System.Web.Ria.ApplicationServices.IAuthentication.
Do I need to implement IAuthentication? If so, could you give me some advice on how to go about doing so? These are the following methods:
public User GetUser();
public User Login(string userName, string password, bool isPersistent, string customData);
public User Logout();
public void UpdateUser(User user);
I don't know how I would implement any of these (except for Login) - how could the service possibly know what user is currently logged in in order for Logout() to work?
I've been scouring the web in search of how to do this for hours, and I can't find anything that describes how to create a simple DomainService that can be used for authenticating a user in an "RIA-linked" Silverlight project.
If someone could shed some light on this, I'd be sincerely grateful.
Thanks,
Charles
[EDIT]
I found the RIA Services page on the MSDN Code Gallery. There's a section called Authentication Samples, which links to some great code samples. Check it out if you want to know more about how authentication works within RIA Services.
If you create a "Silverlight Business Application" you'll see how the template implements authentication. (Or just go here and download the template sample project.)
To simplify, here's the process I used:
First, I create a domain service (FooService) that derives from LinqToEntitiesDomainService where FooContext is my entity model. In it I add all the CRUD operations to access my custom DB table and return user profiles.
Next, create a concrete User class on the serverside by deriving from UserBase:
using System.Web.Ria;
using System.Web.Ria.ApplicationServices;
public class User : UserBase
{}
Finally, derive a class from AuthenticationBase and implement the following four methods:
[EnableClientAccess]
public class AuthenticationService : AuthenticationBase<User>
{
private FooService _service = new FooService();
protected override bool ValidateUser(string username, string password)
{
// Code here that tests only if the password is valid for the given
// username using your custom DB calls via the domain service you
// implemented above
}
protected override User GetAuthenticatedUser(IPrincipal pricipal)
{
// principal.Identity.Name will be the username for the user
// you're trying to authenticate. Here's one way to implement
// this:
User user = null;
if (this._service.DoesUserExist(principal.Identity.Name)) // DoesUserExist() is a call
// added in my domain service
{
// UserProfile is an entity in my DB
UserProfile profile = this._service.GetUserProfile(principal.Identity.Name);
user.Name = profile.UserName;
user.AuthenticationType = principal.Identity.AuthenticationType;
}
return user;
}
public override void Initialize(DomainServiceContext context)
{
this._service.Initialize(context);
base.Initialize(context);
}
protected override void Dispose(bool disposing)
{
if (disposing)
this._service.Dispose();
base.Dispose(disposing);
}
}
Here is a complete official example from MS:
http://code.msdn.microsoft.com/Custom-Authentication-96ca3d20
How about implementing the IAuthorization interface?

Resources