Identity.IsAuthenticated always true on azure - asp.net-mvc-4

I'm currently working on a website with the ASP.net MVC framework for my own amusement and I decided
to stop working with a local database and to publish my web application on azure with it's associated database.
Now I have a strange issue with authentication. Identity.IsAuthenticated is always true.
At first I got the following error :
A claim of type 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier' or 'http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider' was not present on the provided ClaimsIdentity. To enable anti-forgery token support with claims-based authentication, please verify that the configured claims provider is providing both of these claims on the ClaimsIdentity instances it generates. If the configured claims provider instead uses a different claim type as a unique identifier, it can be configured by setting the static property AntiForgeryConfig.UniqueClaimTypeIdentifier.
Without understanding too much I found a solution which was to configure the AntiForgery token in the global.cs file
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;
At the point the error stopped showing but the user identified don't exist in the AspNetUsers table. Of course when I try to get info from the user in a section where authentification is needed the application crashes since no entry exists.
On my layout, I have a section of code to display some data if the user is authenticated
#if (User.Identity.IsAuthenticated)
{
using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm", #class = "navbar-right" }))
{
#Html.AntiForgeryToken()
<ul class="nav navbar-nav navbar-right">
<li>
#Html.ActionLink("Hello " + User.Identity.GetUserName() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" })
</li>
<li>Log off</li>
</ul>
}
}
The User.Identity.GetUserName() always returns live.com#[MyAzureAccount]#outlook.com and it's impossible for me to logoff.
This seems like an identity issue but as I didn't touch the code generated when I started the project and as it works in local I would've expected it to work.

The solution was simply to download the publishing files from azure instead of filling by hand the fields for publishing. I don't really know why it worked but it solved the problem.

Related

.NET CORE and ADFS no Claims Available

Migrating to .NET Core 3 from a 4.6 project and I'm not 100% sure I am implementing things properly.
I followed the steps in this article, making sure to configure startup.cs following the code sample under the "Use WS-Federation without ASP.NET Core Identity" section.
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/ws-federation?view=aspnetcore-3.0
Login seems to be working in that I'm redirected to MS login and sent back to my app with the expected cookies, for example MSISAuthenticated. But, user.identity.isauthenticated is always false and I have no claims available. Is this expected behavior? Perhaps I'm not configuring things properly? Ideally I'd like to be able to check if a user is authenticated and access the claims.
I've come across a number of articles about adding policies based on groups, but how would [Authorize (Policy="SomeGroup")] even work if no claims are available?
ConfigureServices Code:
enter image description here
Configure Code:
enter image description here
Controller Action:
public IActionResult Index()
{
var identity = (ClaimsIdentity)User.Identity;
ViewBag.Claims = identity.Claims;
return View();
}
View Code:
#using System.Security.Claims;
#{
ViewBag.Title = "Home Page";
IEnumerable<Claim> claims = (IEnumerable<Claim>)ViewBag.Claims;
}
#if (User.Identity.IsAuthenticated)
{
<div class="jumbotron">
<h1>Successful Sign On!</h1>
</div>
<div class="row">
<div class="col-md-12">
<h2>WS Federation Services Claims</h2>
#foreach (Claim claim in claims)
{
<p>
<b>#(claim.Type.ToString())</b>
<br />
#(claim.Value.ToString()) (type: #(claim.ValueType.ToString()))
<hr />
</p>
}
</div>
</div>
}
else
{
<div class="jumbotron">
<h1>SSO Test</h1>
<p class="lead">To sign in using Microsoft's single sign-on service, click the button below.</p>
<p>Sign in ยป</p>
</div>
}
perhaps the fact is that you are not send the desired ResourceUrl to ADFS. Then ADFS considers the default resource and issues a token without claims.
See more info on 3 step in "High level AD FS authentication flow"
enter link description here
AD FS identifies the resource which the client wants to access through
the resource parameter passed in the auth request. If using MSAL
client library, then resource parameter is not sent. Instead the
resource url is sent as a part of the scope parameter: scope =
[resource url]//[scope values e.g., openid].

How to get user's first name or last name from #context.User?

I created a Server side Blazor application using Windows authentication. And it created the following file.
LoginDisplay.razor
<AuthorizeView>
Hello, #context.User.Identity.Name!
</AuthorizeView>
However, it shows "DOMAIN\username". Is it a way to show the user first name?
I tried to print all the claim types.
#foreach(var c in context.User.Claims)
{
<p>#c.Value #c.Type</p>
}
However, there is only one type with name in it. (with value of DOMAIN\username)
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
You can also use it as below using the Directory Services (install using NuGet).
_Imports.razor
#using System.DirectoryServices.AccountManagement
LoginDisplay.razor
<AuthorizeView>
#{
var pcontext = new PrincipalContext(ContextType.Domain, "XYZ.net", "DC=XYZ,DC=net");
var principal = UserPrincipal.FindByIdentity(pcontext, context.User.Identity.Name);
}
Hello, #principal.GivenName #principal.Surname!
</AuthorizeView>
You have to go out to AD for that information. But connecting to AD in ASP.NET Core isn't straight-forward.
If you are only going to run this on a Windows server, then you can install Microsoft.Windows.Compatibility from NuGet, and use DirectoryEntry to bind directly to the object using its SID. The SID is available in context.User.Identity.User.Value.
<AuthorizeView>
#{
var identity = (WindowsIdentity) context.User.Identity;
var user = new DirectoryEntry($"LDAP://<SID={identity.User.Value}>");
//Add any other attributes you want to read to this list
user.RefreshCache(new [] { "givenName" });
}
Hello, #user.Properties["givenName"].Value!
</AuthorizeView>
Other attributes you might be interested in:
sn: Last name
displayName: How their name is displayed in Outlook, for example. Often this is "Last, First"
I try this in blazor web assembly. and API using asp.net core 6 and its works as i expected
here is the code
List<Claim> claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier,user.Id.ToString()),
new Claim(ClaimTypes.Email,user.Email),
new Claim(ClaimTypes.Name,user.FirstName + " " + user.LastName),
new Claim(ClaimTypes.Surname,user.LastName)
};
I just return the Name with Firstname then +"give space here"+ give Lastname
and it gives me results using
#context.User.Identity.Name
Hi! You're logged in with Vikas Admin.
Admin is the last name of the user.
Thankyou

NullReferenceException when accessing #User.Identity.Name

We are running an ASP net core 2.1 website. In the header of the page, we want to display the name of the current user. The variable is retrieved through the windows authentication scheme using the #User.Identity.Name reference and is assembled like this: ZH_MB\rohzeh, where ZH_MB is the domain and rohzeh the windows AD user. Because we just want the username, we split both loike this:
<p class="pull-right navbar-text"><span class="glyphicon glyphicon-user"></span> #User.Identity.Name.Split("\\")[1]</p>
Now when we run this code, we get the following error:
An unhandled exception occurred while processing the request.
NullReferenceException: Object reference not set to an instance of an
object. AspNetCore.Views_Shared__Layout.b__46_1() in
_Layout.cshtml, line 111
Line 111 is the code above. When I run it without the split part like this:
<p class="pull-right navbar-text"><span class="glyphicon glyphicon-user"></span> #User.Identity.Name</p>
It works just fine, except for the domain information that I don't want.
When i surround this code with a try/catch block, it works as expected, showing only the user name:
#try
{
<p class="pull-right navbar-text"><span class="glyphicon glyphicon-user"></span> #User.Identity.Name.Split("\\")[1]</p>
}
catch (NullReferenceException) { }
Any idea what the problem might be? My initial idea was: the information is just not ready when the page is rendered. But in this case, the second line of could should provide the same error.
It seems
your user is not authenticated yet or Name is null
or you didn't install ASP.NET Core/.NET Core: Runtime & Hosting Bundle for IIS
or you didn't configure all needed to use Windows Authentication
or forwarding Windows Authentication is set to false for some reasons
You can set it this way or others:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<IISOptions>(options => options.ForwardWindowsAuthentication = true);
// Add framework services.
services.AddMvc();
}
Thanks, the answer of Chris Pratt did the trick. I could replace the try/catch with the "?" and the username is shown corrently.

Supporting Individual User Accounts AND Organizational Accounts in MVC5 / ASP.Net Identity 2

I've created an ASP.Net MVC5 application, in which I have configured (and have working fine) Individual User Accounts via Google, Facebook, etc.
What I'd like to do is also support authentication against Azure Active Directory (Organizational Accounts). This would be for internal staff to be able to logon to the app as administrators.
All existing information/guides/documentation I've found typically deals with using one or the other. How would I enable them both together?
If there needs to be a separate logon form for each type of user, that would not be an issue.
EDIT:
I was looking at the Application configuration within Azure Active Directory portal, and notice that they define an "OAUTH 2.0 AUTHORIZATION ENDPOINT". Can MVC5 be configured within Startup.Auth.cs to use this?
I managed to implement this by doing the following:
First, adding a reference to the Microsoft.Owin.Security.OpenIdConnect Nuget package.
Second, configuring it in my Startup.Auth.cs:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = "From the Azure Portal (see below)",
Authority = "https://login.windows.net/<domain>.onmicrosoft.com",
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = (ctx) =>
{
if (ctx.Request.Path.Value.EndsWith("ExternalLogin"))
{
string appBasePathUrl = ctx.Request.Scheme + "://" + ctx.Request.Host + ctx.Request.PathBase;
ctx.ProtocolMessage.RedirectUri = appBasePathUrl + "/";
ctx.ProtocolMessage.PostLogoutRedirectUri = appBasePathUrl;
}
else
{
ctx.State = NotificationResultState.Skipped;
ctx.HandleResponse();
}
return Task.FromResult(0);
}
},
Description = new AuthenticationDescription
{
AuthenticationType = "OpenIdConnect",
Caption = "SomeNameHere"
}
});
Third, I setup the application in the Azure Portal (classic):
Fourth, I added a separate logon page for admin users:
#using (Html.BeginForm("ExternalLogin", "Home"))
{
#Html.AntiForgeryToken()
<div class="ui basic segment">
<div class="ui list">
<div class="item">
<button type="submit" name="provider" value="OpenIdConnect" class="left floated huge ui button social">
<i class="windows icon"></i>
<span>My Org Name</span>
</button>
</div>
</div>
</div>
}
Fifth, the ExternalLogin action doesn't need to change - we just let OWIN middleware redirect us to the external login page. The flow would then direct the user back to the ExternalLoginCallback action.
Finally, in the ExternalLoginCallback action, I check the incoming claims to determine that the login was via Azure AD, and instead of calling into ASP.NET Identity, I construct my own ClaimsIdentity, which has all my (application specific) claim information which my application recognises as an admin user.
Now, admin users navigate to https://example.com/admin, click the login button, are redirected to the Azure AD login, and windup back at the application as an admin user.
Your best bet would be to leverage Azue AD Access Control Services (ACS) and setup the Identity Providers to include Azure AD, Facebook, et al. See the documentation here: http://azure.microsoft.com/en-us/documentation/articles/active-directory-dotnet-how-to-use-access-control/
ACS can indeed be used, however as you have already implemented Google/Facebook signin I recommend that you directly integrate with Azure AD instead of going through an intermediate STS like ACS/thinktecture.
If your app' signin experience involves the user clicking on "Signin with Google/Signin with Facebook" stickers - you can add "Signin with your company' account." (there's even a recommended branding style: http://msdn.microsoft.com/en-us/library/azure/dn132598.aspx
If you app performs realm discovery and forwards the user to the appropriate IdP (employing a text box saying something like "Enter your email address to sign in") - then you could add matching for your company name email addresses and forward those users to AAD.
In both cases, your application will issue an SSO request to SSO endpoint of AAD: https://login.windows.net/{company domain name/id}/{wsfed/saml/oauth2}. If you're using .Net, WSFed, this should see you through: http://msdn.microsoft.com/en-us/library/azure/dn151789.aspx. Look for the code:
SignInRequestMessage sirm = FederatedAuthentication.WSFederationAuthenticationModule.CreateSignInRequest("", HttpContext.Request.RawUrl, false);
result = Redirect(sirm.RequestUrl.ToString());
There's also an OpenIdConnect sample here: http://msdn.microsoft.com/en-us/library/azure/dn151789.aspx

Using https in combination with #Html.BeginForm

I have a mvc4 view form with some fields, let's say:
User name
Surname
Age
Code
below an example:
#{
var actionURL = "https://" + Request.Url.Host + Request.Url.PathAndQuery;
using (Html.BeginForm("AddUser", "Configure", FormMethod.Post), new { #action = actionURL })
{
...
#Html.TextBoxFor(model => model.UserViewModel.Name, null, new { id = "UserName" })
#Html.PasswordFor(model => model.UserViewModel.UserCode, new { id = "UserCode" })
...
<input id="submitUser" type="submit" value="Send" />
...
}
}
ConfigureController.cs:
[RequireHttps]
public ActionResult AddUser(UserViewModel model)
{
// Encrypt user code and store it in database
// Store in database the rest of user information as-is, without encrypting
}
Code field should be encrypted once user info is submited through internet but other fields are not necessary. Then once I recieve user info in the controller, I encrypt the code field and store it in the database. Other user information is stored in database as-is.
I want to encrypt the user code in server side not in client side.
My problem is that once submit button is clicked an error is thrown, a page is shown saying:
Ensure web address https://... is correct
Search the page with you search engine
Update/Refresh the page in a few minutes
Ensure TLS/SSL protocols are enabled by going to Tools->Internet Options->Advanced options->Configuration->Security
I have ensured that TLS and SSL protocols are enabled: SSL 3.0 and TLS 1.0 are enabled.
What's wrong? and how to send code encrypted to the mvc4 controller? I do not want to make changes in web.config.
I have tried other solutions like explained below, but nothing works for me:
building own action filter: ASP.NET MVC RequireHttps
using form action instead of html.beginform:
How to change POST to SSL URL in MVC 4
but nothing works...