Search code examples
asp.net.netwindows-authenticationnancy

NancyFx and Windows Authentication


I want to use NancyFx for an intranet web app. All the documentation and forums only mention Forms and Basic authentication. Anyone successfully use Nancy with Windows Authentication?

There's also something called Nancy.Authentication.Stateless but I can't see what that does (looks like it's for use in Apis).


Solution

  • I used this in an internal project recently - I don't really like it, and it ties you to asp.net hosting, but it did the job:

    namespace Blah.App.Security
    {
        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Security.Principal;
        using System.Web;
    
        using Nancy;
    
        public static class SecurityExtensions
        {
            public static string CurrentUser
            {
                get
                {
                    return GetIdentity().Identity.Name;
                }
            }
    
            public static bool HasRoles(params string[] roles)
            {
                if (HttpContext.Current != null && HttpContext.Current.Request.IsLocal)
                {
                    return true;
                }
    
                var identity = GetIdentity();
    
                return !roles.Any(role => !identity.IsInRole(role));
            }
    
            public static void RequiresWindowsAuthentication(this NancyModule module)
            {
                if (HttpContext.Current != null && HttpContext.Current.Request.IsLocal)
                {
                    return;
                }
    
                module.Before.AddItemToEndOfPipeline(
                    new PipelineItem<Func<NancyContext, Response>>(
                        "RequiresWindowsAuthentication",
                        ctx =>
                            {
                                var identity = GetIdentity();
    
                                if (identity == null || !identity.Identity.IsAuthenticated)
                                {
                                    return HttpStatusCode.Forbidden;
                                }
    
                                return null;
                            }));
            }
    
            public static void RequiresWindowsRoles(this NancyModule module, params string[] roles)
            {
                if (HttpContext.Current != null && HttpContext.Current.Request.IsLocal)
                {
                    return;
                }
    
                module.RequiresWindowsAuthentication();
    
                module.Before.AddItemToEndOfPipeline(new PipelineItem<Func<NancyContext, Response>>("RequiresWindowsRoles", GetCheckRolesFunction(roles)));
            }
    
            private static Func<NancyContext, Response> GetCheckRolesFunction(IEnumerable<string> roles)
            {
                return ctx =>
                    {
                        var identity = GetIdentity();
    
                        if (roles.Any(role => !identity.IsInRole(role)))
                        {
                            return HttpStatusCode.Forbidden;
                        }
    
                        return null;
                    };
            }
    
            private static IPrincipal GetIdentity()
            {
                if (System.Web.HttpContext.Current != null)
                {
                    return System.Web.HttpContext.Current.User;
                }
    
                return new WindowsPrincipal(WindowsIdentity.GetCurrent());
            }
    
            public static Func<NancyContext, Response> RequireGroupForEdit(string group)
            {
                return ctx =>
                    {
                        if (ctx.Request.Method == "GET")
                        {
                            return null;
                        }
    
                        return HasRoles(group) ? null : (Response)HttpStatusCode.Forbidden;
                    };
            }
        }
    }
    

    It bypasses all the security checks if it's coming from local (for testing), which is probably a bad idea, but it's a behind the firewall thing so it isn't an issue for this.

    Wouldn't suggest you use it verbatim, but might point you in the right direction :)