Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Security module for Azure AD #267

Open
tidyui opened this issue May 1, 2018 · 13 comments
Open

Add Security module for Azure AD #267

tidyui opened this issue May 1, 2018 · 13 comments

Comments

@tidyui
Copy link
Member

tidyui commented May 1, 2018

Create security module for Azure AD so that the CMS can be easily integrated into an existing user base

@lestersconyers
Copy link

Is this enhancement for Azure AD + Piranha Identity?

@tidyui
Copy link
Member Author

tidyui commented Oct 4, 2018

I haven't had the time to investigate it yet, but the idea is to.

  1. Implement the ISecurity to authenticate users against Azure AD
  2. If possible, implement admin views to view available users

This isn't really my area of expertise so I need to do some investigation on how it's done. Also the users from the AD needs to be connected with the claims they need for the Piranha Manager, not really sure if this kind of thing can be administered from the Azure AD or if you actually need a local Identity user handling these things and you only use the AD for login.

Do you have any experience in integrating Azure AD with .NET Core?

@lestersconyers
Copy link

Yes, I've integrated Piranha with Azure AD as well. Since you have the manager set up with claims it's relatively straightforward if you augment the claims within startup code. Mapping custom claims in Azure AD is a pain so I would avoid it. Ideally those claims would be augmented by the roles assigned in the manager. I haven't gotten that far yet but would love to help.

@tidyui
Copy link
Member Author

tidyui commented Oct 4, 2018

Well I haven't got that much experience with any of this :) How does it work, are you using the Identity module but with a custom ISecurity implementation that authenticates with Azure AD. Then if the user is authenticated you map it to a local Identity user, and if it doesn't exist you create it? Are you using the standard login view for the manager or are you redirecting to a single-sing-on view from Azure AD?

Regards

@lestersconyers
Copy link

I followed these steps to setup WSFederation using Azure AD. https://docs.microsoft.com/en-us/aspnet/core/security/authentication/ws-federation?view=aspnetcore-2.1#use-ws-federation-without-aspnet-core-identity. So the basics it really had nothing to do with Piranha. I'll fork your examples repo and submit a PR for you to review as a basic example.

@tidyui
Copy link
Member Author

tidyui commented Nov 16, 2019

Hi again @lestersconyers Do you have some code regarding this that you might be willing to share? Got a question regarding this from another user 🙂

@filipelteixeira
Copy link

Hey I’m evaluating using piranha CMS as our new code base for a public website, but it is kind of a requirement to use azure ad for the manager area, if you could share some example code I would pick it from there.
Thanks

@eloekset
Copy link
Contributor

I need support for IdentityServer. Both Azure AD and IdentityServer are OAuth2, so there shouldn't be much of a difference. I'll try it out in my fork and share if I get it up and running. (Also need multi-tenancy BTW, as mentioned in #1132 and related, so I'll see how far I get using Piranha.)

@tidyui
Copy link
Member Author

tidyui commented Jul 19, 2020

@eloekset We really don’t have full multi tenancy planned as this is currently not in the roadmap.

Best regards

@eloekset
Copy link
Contributor

Yes, I saw that. I will have to live in a fork and keep it updated from your master branch. My first goal is OpenID/OAuth2 with IdSrv/Azure AD, and then it shouldn't be too hard to extend all storage with an AccountID or TenantID.

@eloekset
Copy link
Contributor

eloekset commented Aug 7, 2020

I've finally had some time to work on the Azure AD module again, but I'm struggling with an issue that is difficult to figure out, so I created a discussion over in the aspnetcore repo and hope to get some help by someone: dotnet/aspnetcore#24690

@vigouredelaruse
Copy link

vigouredelaruse commented Oct 22, 2020

i've also been trying to authenticate with azure ad b2c and i have this working.

by working i mean

  • anonymous get /manager redirects to /microsoftidentity/account/signin
  • azure ad b2c challenge is displayed, with identity provided by social. logins and azure ad
  • people who aren't in a specifically configured set of azure ad tenants cannot authenticate to /manager - this means the social logins don't get to authenticate at /manager even though if they type /manager they will get an auth challenge
  • on. login from an approved azure ad tenant the manager interface is displayed
  • identitywithseed is being used, to hack around dependency injection issues if there is no identity service registered. this means that in certain circumstances you do see the identitywithseed challange despite my efforts to date. but it's only visible if you're logged in to an approved azure ad tenant. the problem with the identity interface as i see it is the username/password on the interface definition. the impetus. for doing this is to keep passwords out of the database

how i did it

  • if anyone responds here i will tell you. other than that i will publish a blog and update this with the link later. i expect you know i will see you hitting /manager and in the azure risky behaviour analytics
  • essentially i forwarded challenges, loginurls for cookie auth to the oidc scheme registered by microsoftidentity
  • also i wrote an asp.net core iclaimstransformer that supplies the full set of admin claims to approved users. therefore this solution is incomplete, in that there is no mapping mechanism between azure rbac (https://docs.microsoft.com/en-us/azure/role-based-access-control/overview) (b2c groups are a special case https://mrochon.azurewebsites.net/2019/05/06/using-groups-in-azure-ad-b2c/) and piranha rbac- for that matter, i hardcoded the approved. domains, so there. obviously. a production attempt would read such things from asp.net core configuration

a proposed first cut of a solution architecture that requires no code changes to piranha

IMG_0346

the full gist for the claims transformer is here https://gist.github.com/vigouredelaruse/f462a35c3ff5b8028868fd56ea5f9f3d

    public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        if (principal.Identity.IsAuthenticated)
        {
            var p = principal.Identity as ClaimsIdentity;

            var matches = p.Claims.Where(w => w.Value.ToLower().Contains("wizardcontroller.com")).Count();
            var claimCount = p.Claims.Where(c => c.Value.ToLower().Contains("PiranhaAdmin")).Count();

            if (matches > 0 && claimCount <= 1)
            {
                EnsureAdminClaimsForAdminPrincipal(principal);
            }
        }
        return Task.FromResult(principal);
    }

and you register it in startup.cs like this

        services.AddSingleton<IClaimsTransformation, ClaimsTransformer>();

as you can see my proof of concept has the claims mapping logic hardcoded to my specific situation but it's not quite generalized enough for me to deploy. the pieces in the architecture diagram aren't all there, and i'm not quite sure yet the diagram has all the required components. but it's a first cut

additionally i currently have

        // Middleware setup
        app.UsePiranha(options => {
            options.UseManager();
            options.UseTinyMCE();
            options.UseIdentity();

        });

and this bogus rewrite setup that's not actually doing anything useful yet

        var options = new RewriteOptions()
                    //
                    .AddRedirect("'.*login.*'", "/")
                    .AddRewrite(@".*login", "/",
                        skipRemainingRules: true);

for additional service configuration we have

        services.AddAuthentication(options =>
        {
            options.DefaultScheme = "OpenIdConnect";
            options.DefaultChallengeScheme = "OpenIdConnect";
            options.DefaultAuthenticateScheme = "OpenIdConnect";
            options.DefaultSignInScheme = "OpenIdConnect";
        });

        services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAdB2C");

and swinging the hammer to kill the fly we have

        services.Configure<CookieAuthenticationOptions>(
            AzureADDefaults.CookieScheme, options =>
            {
                options.LoginPath = "/microsoftidentity/account/signin";
                options.LogoutPath = "/microsoftidentity/account/signout";
                options.ForwardAuthenticate = "OpenIdConnect";
                options.ForwardChallenge = "OpenIdConnect";
                options.ForwardSignIn = "OpenIdConnect";
                options.ForwardSignOut = "OpenIdConnect";
            });

you'll need sauce that smells similar to this bogus code

        services.Configure<JwtBearerOptions>(
        AzureADDefaults.JwtBearerAuthenticationScheme, options =>
        {
            
            options.TokenValidationParameters.ValidateIssuer = true;
            options.TokenValidationParameters.ValidIssuers = new[]
            {
                $"https://login.microsoftonline.com/tenant/v2.0"
            };

        });

and your version of this

       // as per https://www.josephguadagno.net/2020/06/26/connecting-to-an-api-protected-by-microsoft-identity-platform
        services.AddControllersWithViews(options =>
        {

            //var policy = new AuthorizationPolicyBuilder()
            //    .RequireAuthenticatedUser()
            //    .Build();
            //options.Filters.Add(new AuthorizeFilter(policy));
           
        }).AddMicrosoftIdentityUI();

        // services.AddRazorPages();

        // as per https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-web-app-sign-user-app-configuration?tabs=aspnetcore
        services.AddRazorPages().AddMvcOptions(options =>
        {
         
            //var policy = new AuthorizationPolicyBuilder()
            //              .RequireAuthenticatedUser()
            //              .Build();
            //options.Filters.Add(new AuthorizeFilter(policy));
        }).AddMicrosoftIdentityUI();

and the piece duh resistance

      // Service setup
        services.AddPiranha(options =>
        {
            options.AddRazorRuntimeCompilation = true;

            options.UseBlobStorage(Configuration.GetConnectionString("blobstorage"));
            options.UseImageSharp();
            options.UseManager();
            options.UseTinyMCE();
            options.UseMemoryCache();
            options.UseEF<SQLServerDb>(db =>
                db.UseSqlServer(Configuration.GetConnectionString("piranha")));

            options.UseIdentityWithSeed<IdentitySQLServerDb>(db =>
                db.UseSqlServer(Configuration.GetConnectionString("piranha")),
                identityOpts =>
                {

                },
                cookieOpts =>
                {
                    cookieOpts.LogoutPath = "/microsoftidentity/account/signout";
                    cookieOpts.LoginPath = "/microsoftidentity/account/signin";
                    cookieOpts.ForwardAuthenticate = "OpenIdConnect";
                    cookieOpts.ForwardChallenge = "OpenIdConnect";
                    cookieOpts.ForwardSignIn = "OpenIdConnect";
                    cookieOpts.ForwardSignOut = "OpenIdConnect";
                    cookieOpts.ForwardDefault = "OpenIdConnect";
                });

            options.LoginUrl = "/microsoftidentity/Account/Signin";

for me code complete would approach a solution that doesn't use identitywithseed, and has zero users in the database. for this proof of concept i only have that first admin user created during piranha install, but i won't go to market like that

obviously for piranha with tens of thousands of downloads you might want to expand. the reach to https://auth0.com/

so hopefully you're able to duplicate and better my results for all to consume because the current issue with this setup is

@tedvanderveen
Copy link
Contributor

@vigouredelaruse maybe #1623 can get you going more efficiently?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants