dotnet / AspNetCore.Docs

Documentation for ASP.NET Core
https://docs.microsoft.com/aspnet/core
Creative Commons Attribution 4.0 International
12.66k stars 25.28k forks source link

Extra details on how to authenticate with Entra ID #33147

Open LapinskasL opened 4 months ago

LapinskasL commented 4 months ago

Description

I've been having severe trouble implementing authentication and authorization in Blazor Hybrid apps.

While the guide has a lot of code, I'm not quite sure how to properly integrate Entra ID into it. I find myself left with a lot of questions.

"What will happen when the token expires?" "Is it automatically renewed when it expires?" "How do I reduce token expiration time so I can test it?" "Why is the AuthorizeView tag working, but not its Roles attribute?" "Why is the [Authorize] attribute not working?"

It would help if the docs used implementation with Entra ID (or provide a sample with it) and also addressed those questions. Coming from the MVC framework, I can't help but feel like there's missing features and the answers to these questions are hard to find.

I've made a post on reddit too if you want to see how far I got: https://www.reddit.com/r/dotnet/comments/1e05wel/how_to_fully_integrate_entra_id_authorization/

Page URL

https://learn.microsoft.com/en-us/aspnet/core/blazor/hybrid/security/?view=aspnetcore-8.0&pivots=maui

Content source URL

https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/blazor/hybrid/security/index.md

Document ID

cb3ca917-28cc-126f-a7ff-3ff00ad30677

Article author

@guardrex

guardrex commented 4 months ago

Hello @LapinskasL ... This article is only about integrating Identity with Blazor (which it calls out in the intro). Tokens/token expiration/token renewal are identity provider subjects covered by the Entra/MS Identity Web docs (or OIDC docs if you use OIDC with Entra) and perhaps .NET MAUI docs/samples. This isn't an article about how to implement Identity generally.

WRT the roles not working with AuthorizeView, it's probably because the role claim is missing or not mapped to the new claims identity properly ... again, something covered in external doc sets (for example, part of the MAUI tutorial series that deals with roles, Entra doc: Configure the role claim, OIDC role claim mapping, etc.).

The .NET MAUI docs point to a tutorial in the External ID doc set's tutorial area :point_right: https://learn.microsoft.com/entra/external-id/customers/tutorial-mobile-app-maui-sign-in-prepare-tenant.

@BethMassi ... These are the potential cross-links that I scared up for an Additional Resources section added to this article. Which of these are these OK? Are there any other resources that I should cross-link?

I'll make checkboxes out of these, and you can edit this and check off the ones I should use if that's fast πŸƒ ..........

LapinskasL commented 4 months ago

@guardrex Thanks for all the info. I was able to gather some bits and improve my code.

However, the information on how to integrate Entra ID auth into a Blazor Hybrid app is scattered across many places. You can see this by the amount of links you needed to provide and how only a potion of information within them is applicable to Blazor Hybrid.

I'd think Microsoft would have a single page dedicated to adding and explaining Blazor Hybrid auth with Entra ID. They're both Microsoft products after all and, ideally for you, I should have minimal trouble guiding myself into a greater dependency on your products. My research tells me I'm not alone in struggling with this specific issue.

That said, I do think atm I have enough figured out to be sufficient for a production environment. It just took really long to put the pieces together.

Also, I am struggling with mapping the role claim name. I don't have the .AddOpenIdConnect() method and having cookie authentication does not make sense.

To be clear, I do appreciate all the help. I understand Blazor Hybrid is new and will take effort and time to improve and provide solid documentation, especially when you have thousands of other people asking you for stuff.

guardrex commented 4 months ago

how to integrate Entra ID auth into a Blazor Hybrid app is scattered across many places

I'm sure @BethMassi just heard your feedback on that. I'm just a worker 🐝 ... a worker πŸ¦–πŸ˜„ ... on Blazor docs. I'm an Aquent contractor limited to working on the Blazor docs in this repo, and management ... such as Beth and Dan Roth ... decide how the overall content is organized, especially when the articles span different documentation teams/people.

I should explain that docs are more compartmentalized than most people think: Azure, Identity, Graph API, MAUI, and Blazor docs are handled by different doc folks/teams, and that can explain why things might seem a little uncoordinated in these layered technology situations.

a single page dedicated to adding and explaining Blazor Hybrid auth with Entra ID

It can be accomplished to an extent ... a basic use case ... in a tutorial or in an article that explains how a provided sample app works. However, it's impossible to cover security these days in a single reference article because the technologies are so complex and have so many features covering so many scenarios. When a tutorial (or sample+article) is placed, the author has to make a lot of choices about what scenarios/features to include, and leaving something out, such as roles, does often generate a request for additional coverage.

WRT to roles and Blazor Hybrid ...

The first part of the coverage is this ...

https://learn.microsoft.com/entra/external-id/customers/tutorial-desktop-maui-role-based-access-control#receive-groups-and-roles-claims-in-net-maui

BTW @BethMassi on that lead-in text to the code snippet, readers might fare better with just being given the full class, the full method, or a diff block.

... and that refers to making changes in the context of this ...

https://learn.microsoft.com/entra/external-id/customers/tutorial-desktop-app-maui-sign-in-sign-out#handle-the-claimsview-data

The second piece is in our article here ...

https://learn.microsoft.com/en-us/aspnet/core/blazor/hybrid/security/?view=aspnetcore-8.0&pivots=maui

... in spots where the claims identity is created (new ClaimsIdentity()).

I'll see about getting roles via Entra ID working here, and then I'll see if I can get that into this Blazor article. I might be able to work on this today. I just need to see if anything else is pressing at the moment.

guardrex commented 4 months ago

I was just reviewing the issues, and a 'please add roles' request came in for the BWA+OIDC sample app, too ...

https://github.com/dotnet/AspNetCore.Docs/issues/32512

It's quite a common request due to the ubiquity of role use in production .NET apps.

Note to self πŸ¦–

The prereqs of the third article (my first link in the preceding comment) are ...

BUT ... The first one is Part 3 of the series ☝️ ... and it makes it clear that you must complete Part 2 before that ...

https://learn.microsoft.com/en-us/entra/external-id/customers/tutorial-desktop-app-maui-sign-in-prepare-app

BUT AND™ πŸ˜„ ... Part 2 relies on Part 1 and the second article ☝️ requires the tenant to have been created in the first place, so it also relies on Part 1.

I think what I'll end up floating for the Blazor doc is an ordered reading/cross-link list before/after whatever code sample(s) provided.

guardrex commented 4 months ago

Versioning doesn't seem to be in use ...

In the Additional information window, choose .NET 7.0 and select Create.

Cross-ref: https://learn.microsoft.com/en-us/entra/external-id/customers/tutorial-desktop-app-maui-sign-in-prepare-app

Because of this approach of setting the app's namespace via project creation ...

In the Configure your new project window, Project name must be set to SignInMaui. Update the Solution name to sign-in-maui and select Next.

... it makes it more challenging to implement the guidance in an existing app. It might be better to just have the user update the namespaces of the added files.

NRTs not in use, so static analysis throws quite a bit.

There's no AppShell.xaml file in my MAUI app.

Unfortunately, I've run into too many errors that I don't recognize and can't resolve, so I won't be able to adopt an in-place adoption of Identity.

I'll try the sample app next.

No 🎲🎲 OOB. The sample targets 7.0, which throws unsupported errors. Even updating to target 8.0 and using the 8.0 SDK, it throws ...

Sometimes, my preview VS πŸ’₯ during preview, and I don't have access to the "nightly" VS builds. I've tried to set the SDK via a global.json to 8.0.300, but that's not working.

guardrex commented 4 months ago

@BethMassi ... In a couple of hours, I wasn't able to either get an 8.0 app based on the MAUI tutorial or using the sample app up and running. Even if the blocking errors could be resolved for either approach, the NRT static analysis is throwing a fit all over the place with the code provided. There's no AppShell.xaml file, so I wonder now if we missed a tutorial update at 8.0's release. I'll proceed with the links, but I'll need to circle around to additional roles coverage later after I get a working auth-enabled MAUI app running. I'll try again next week with this.

LapinskasL commented 4 months ago

Appreciate all the updates so far. It was definitely quite a challenge to get Blazor Hybrid auth to work on my end.

I have a lot going on currently but if I can get to it, I will remake a sample app in my own time and show you how I have it working. Maybe it will give you some ideas.

guardrex commented 4 months ago

I've had trouble in prior years with preview VS and preview SDK. I'm documenting 9.0 via the preview SDK these days. Sometimes, my problems here are related to my VS breaking during a transition to a new framework, not the project. However, having the sample app and guidance at 7.0 can be problematic when I'm basing my test app on 8.0, and having NRTs blowing up all over the place isn't helpful either. One thing I might do on my next attempt is go back to the command line (dotnet commands), which avoid VS troubles.

Yes, if you put up a repro app, I'll pull that down and see if it will compile here. I hope to come back to this issue sometime next week.

BethMassi commented 4 months ago

@guardrex I can take a look at this next week as well. I don't have an Entra tenant at the moment to test.

guardrex commented 4 months ago

I managed to get the sample app running πŸŽ‰.

However, I'm now stuck on the guidance to place a non-standard entry into the Redirect URLs.

msal{APP ID}://auth

What?! That's not a valid URL. The Azure portal doesn't like it.

Oh, I see! It's a mobile/desktop thing ...

Mobile and Desktop applications option

Ok ... this what happens when you get a web dev in here 🀣.

guardrex commented 4 months ago

πŸŽ‰

Working now! I can take it forward into some roles testing. I'll pick back up with this on Monday. Still tho ... the code is ... mmmmmm ... how can I put it without offending anyone??? ... πŸ€” ... the code is ....... very interesting! πŸ˜†

image

guardrex commented 4 months ago

I set up User and Admin roles and assigned the Admin role to a user. When adding the claim type, I can see that the roles claim arrives when a user is given a role in Entra. So far, so good!

image

I think on Monday I'm going to flip back over to the Maui-Blazor app because this auth example has no Blazor in it. I should be able to get the tutorial app up and running with auth now that I can see this auth app working here ... and then I'll take a stab at getting the auth rolled in.

guardrex commented 4 months ago

... and the code provided isn't going to fly for multiple roles (or groups/wids for that matter to cover Azure security groups and built-in roles).

The code is ...

var roleClaim = token.Claims.FirstOrDefault(c => c.Type == "roles")?.Value;

if (!string.IsNullOrEmpty(roleClaim))
{
    // If the role claim exists, add it to the IdTokenClaims
    IdTokenClaims = new List<string> { roleClaim };
}

Here's the user with User and Admin roles, a wids claim (b0f54661-2d74-4c50-afa3-1ec803f12efe) for the Entra built-in "Billing Administrator" role assignment, and a groups claim for an O365 security group that I created (173a9880-af52-47e7-b549-5fd5cd84e030). I don't recall off the top of my head what the other groups and wids claims are, but I think I can look them up.

image

@BethMassi ... I'll suggest some better code than that when I flesh out the Blazor bits, hopefully on Monday.

Ideally, the code will show how to capture ALL of the roles claims, groups claims, and wids claims that arrive with some cross-links (or text) on getting the extra claims to come down to the app (btw in case anyone isn't aware, it's via setting "groupMembershipClaims": "All" in the app's manifest). The GUID of any security group is displayed out in the open in the Azure portal as soon as you create the group. Entra's built-in roles' GUID values are here :point_right: https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/permissions-reference. When I get the app roles, groups, and Entra roles assigned to the claims identity, the <AuthorizeView> should light up in Blazor. πŸ€žπŸ€πŸ˜„

guardrex commented 4 months ago

Here's the answer on one, so we'll want to hold on to this to add an explanation of what it is ... and I'd like to add a remark on it to the Blazor WASM article, which covers a lot of this stuff ...

https://learn.microsoft.com/en-us/answers/questions/678108/what-b79fbf4d-3ef9-4689-8143-76b194e85509-in-wids

I can't find out what the groups GUID 69ff516a-b57d-4697-a429-9de4af7b5609 is. We need to get an πŸ™ Azure/Entra god πŸ™ to find out what that represents. I think it's another Azure/Entra internal type of thing not germane to the coverage really. Ultimiately, devs are going to create auth policies out of these GUIDs, and they only need to know the GUIDs for the built-in roles (via that table) and the security/distribution groups (via what the Azure portal tells them when they create the group).

guardrex commented 4 months ago

UPDATE (8/1): I was sidetracked for a few days, but I hope to get back to this tomorrow (Friday).

LapinskasL commented 4 months ago

[EDIT by guardrex to add code fencing to a tag]

The code is ...

var roleClaim = token.Claims.FirstOrDefault(c => c.Type == "roles")?.Value;

if (!string.IsNullOrEmpty(roleClaim))
{
    // If the role claim exists, add it to the IdTokenClaims
    IdTokenClaims = new List<string> { roleClaim };
}

@guardrex Hey thanks a lot for all the work. I'm just low on personal time for this but still have some time to look at it at work.

In the code I quoted, I wonder have you figured out how to set the default role claim for roles? The <Authorize> and [Authorize] have a Roles property, and UserPrincipal has an IsInRole extension method.

I suspect the reason is because the claim name it checks is http://schemas.microsoft.com/ws/2008/06/identity/claims/role, while the claim name sent to the app is, as you know, roles.

guardrex commented 4 months ago

I haven't made it back here yet. Items came up overnight that I needed to address today.

figured out how to set the default role claim for roles

I don't think that's going to matter because the role claims are already using a type name of "roles" looking at my earlier research☝️. I have "roles," "groups," and "wids" on the Hybrid side. AFAICT at this point, the challenge is getting the Blazor app (BlazorWebView/auth state provider) to receive the claims. I'll try to get back to this issue for further research as soon as I can ... I hope on Monday.

guardrex commented 3 months ago

Made it back for another look πŸ˜….

@LapinskasL ... UPDATED: I can't get an IAccount established with the code that you provided. I don't think I should spend much time with it because ........................

@BethMassi ... This issue is likely going to require a PU-provided sample for doc updates, and I assume it would be based on the Hybrid-BWA sample. Even if @LapinskasL's approach based on the (incomplete†) docs is correct, the PU usually makes specific choices about what they want to show/explain that probably aren't going to exactly match what @LapinskasL has.

incomplete†: @LapinskasL did well getting as far as he did with this content. Quite a lot is left out from looking at the code he posted relative to the article. I can fix the article based on what you/they want to show.

I understand that you'll be OOF soon, so I'll wait until you return if you want to address this later.

BethMassi commented 3 months ago

@guardrex sorry for the delay! I haven't been able to look at this. Adding @JeremyLikness to this thread to see if he has any suggestions while I'm OOF.

LapinskasL commented 3 months ago

@guardrex I updated that reddit post of mine with more new code. It seems I needed a few changes to the Routes.razor file to make the [Authorize] attribute and <AuthorizeView>, <Authorized>, <NotAuthorized> tags work.

The Roles attributes for the attribute and tags still don't function though, but I can use policies in the views which makes the code cleaner.

guardrex commented 3 months ago

I'm punting it to the product unit for resolution because I couldn't get the guidance to work myself.

BTW ... You must code-fence markup :point_up: to get it to show up in GH comments.

LapinskasL commented 3 months ago

I'm not sure what you're asking me to do in the last sentence.

guardrex commented 3 months ago

Use backticks around inline code to get tags to show up in GH comments. I'll edit your post :point_up: to add them.

guardrex commented 1 month ago

πŸ¦– NOTE TO SELF πŸ¦–

See discussion πŸ‘‰ https://github.com/dotnet/AspNetCore.Docs/issues/33851#issuecomment-2417016350

guardrex commented 1 month ago

UPDATE (10/24) πŸ“£ ... I failed in my attempt to get Entra auth (MSAL) integrated into the MauiBlazorWeb app 😒. I was stopped cold by this problem and just couldn't get the 😈😈😈 resolved in spite of using latest packages (Microsoft.Identity.Client v4.66.1). A member of engineering with more experience and intelligence 🧠 will need to provide the sample, and then I can write content based on its configuration. We've been chatting about this offline. I don't have an ETA yet for the new sample, but I'll remark here (or Beth will) when there's new information.