Identity using Thread.CurrentPrincipal vs HttpContext.Current.User - iis

I know that this topic might have some duplicated questions here but i'm still confused.
I'm having a very weird case with Thread.CurrentPrincipal.Identity and HttpContext.Current.User.Identity.
I have a userIdentity.cs class where i rely on getting the current active user from my token.
Originally my app is an MVC app hosted on IIS on 2 seperate Virtual machines. I was using Thread.CurrentPrincipal.Identity to retrieve the current user from the token claims, and i didn't faced any problems.
However, I've update the code to be compatible with a SPA application built in react. After the update, Thread.CurrentPrincipal.Identity isn't working any more, so i had to do a fallback plan and call HttpContext.Current.User.Identity to retieve the claims.
So the user Identity class updated to be as follwing :
public class UserIdentity : IUserIdentity
{
private IIdentity _identity;
public UserIdentity()
{
_identity = null;
_identity = InitializeClaimsIdentity();
}
private ClaimsIdentity InitializeClaimsIdentity()
{
return Thread.CurrentPrincipal?.Identity != null ?
Thread.CurrentPrincipal.Identity as ClaimsIdentity :
HttpContext.Current.User.Identity as ClaimsIdentity; //HttpContext.Current.User.Identity used for Main SPA
}
public string GetUserId()
{
var userId = GetClaimsIdentity().FindFirst("username")?.Value;
userId = string.IsNullOrEmpty(userId) ? GetClaimsIdentity(forceInit: true).FindFirst("username")?.Value : userId;
return userId;
}
public ClaimsIdentity GetClaimsIdentity(bool forceInit = false)
{
if (_identity == null || forceInit)
{
_identity = InitializeClaimsIdentity();
}
return (ClaimsIdentity)_identity;
}
}
This solutions works perfectly on dev enviroments (on MVC and SPA).
However, after deploying this solution to production,MVC hosted on 2 VMs, and with a significant ammout of users at the same time, claims started to be returned in the wrong way. UserIDs got messed up returning wrong data. When debugging it, I wasn't able to reproduce the case.
When removing HttpContext.Current.User.Identity as ClaimsIdentity as fallback solution, things works like a charm;
If someone can explain to me the main difference between the Thread.CurrentPrincipal and Current.User it would be great.
Plus,how to correctly implement a solution compatible with the MVC and react SPA app ?
Sorry for the long post, and thank you in advance,

Related

Preventing ClaimsTransformation from running on every HTTP request

I have a web application targeting .NET 5.0 and I am trying to implement Windows Authentication along with some policies for authorization. We have a database table from another application that holds info on user roles, so I am using that to set permissions for my users.
I created a ClaimsTransformer class:
ClaimsTransformer.cs
public class ClaimsTransformer : IClaimsTransformation
{
// snip constructor which pulls in my DbContext from DI
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
var id = ((ClaimsIdentity) principal.Identity);
var ci = new ClaimsIdentity(id.Claims, id.AuthenticationType, id.NameClaimType, id.RoleClaimType);
// snip call to DbContext to get user's role from database
if (roleId == 1 || roleId == 7)
{
ci.AddClaim(new Claim("user-role", "admin"));
}
return new ClaimsPrincipal(ci);
}
}
I have my authentication/authorization setup like this:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.AddTransient<IClaimsTransformation, ClaimsTransformer>();
services.AddAuthorization(options =>
{
options.AddPolicy("Admin", policy =>
policy.RequireClaim("user-role", "admin"));
});
// snip rest of method
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// snip unrelated code
app.UseAuthentication();
app.UseAuthorization();
}
My first use of this is to hide a menu in my _Layout.cshtml:
#if ((await AuthorizationService.AuthorizeAsync(User, "admin").Succeeded)
{
// Admin nav link
}
The issue is, since AuthorizeAsync is running on every HTTP request, my ClaimsTransformer also runs each time, hitting the database to check the user's roles on every request. I'd like to avoid this, but I'm not sure of the best way to do so.
Basically, I'd like the system to check the roles only once, when the user is first authenticated. I read that this is what is supposed to happen when using Windows Authentication with IIS, but I am seeing the roles query running on every request when deployed to my IIS server also.
I could easily add a check in my ClaimsTransformer to see if the "user-role" claim exists, and only hit the DB if it is not present, but is there a better way? Should I be overriding something like UserClaimsPrincipalFactory instead of using a ClaimsTransformer?

ASP.NET core WEB API with AD FS authentication

I have small RESTful API written in ASP.NET core and I am looking into how to add authentication to it using Active Directory. I have to use our companies server for authentication using AD but I do not see any tutorials of how to do this.
I guess that JWT authentication is not what I am looking for or I might be wrong and misunderstand something. I am total noob in question of authentication.
I know we have solved that in one of the nodejs project of ours and it was not that straight forward. I would appreciate any help in that matter.
As of today, System.DirectoryServices hasn't been implemented in ASP.NET Core yet, but we could use Novell.Directory.Ldap.NETStandard.
You can install the package via NuGet Novell.Directory.Ldap.NETStandard.
Sample code -
using Novell.Directory.Ldap;
namespace YourNamespace
{
public class LdapAuthenticationService : IAuthenticationService
{
public bool ValidateUser(string domainName, string username, string password)
{
string userDn = $"{username}#{domainName}";
try
{
using (var connection = new LdapConnection {SecureSocketLayer = false})
{
connection.Connect(domainName, LdapConnection.DEFAULT_PORT);
connection.Bind(userDn, password);
if (connection.Bound)
return true;
}
}
catch (LdapException ex)
{
// Log exception
}
return false;
}
}
}

Web API 2 - Authorize authenticated mobile users

I'm trying to secure my Web API 2 with the classic [Autorize] attribute on my controllers. This is successful, now I don't have access to my API ! Great !
I have a Xamarin application, from which I consume the REST services of the app and on which I have a Microsoft Graph Identity provider implemented.
So once I'm logged in the app via Microsoft Graph, I would like to consume the API.
Can anyone point me to the right documentation to do so or explain to me how I should proceed?
I'm guessing there is a way to user the user token provided by the microsoft graph API but I can't see how.
Thank you in advance.
EDIT :
Adding some code from the API after being asked to :
the Startup.Auth.cs :
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = ConfigurationManager.AppSettings["ida:Audience"],
Tenant = ConfigurationManager.AppSettings["ida:Tenant"]
});
}
From the Xamarin app, I log in like this :
public async Task Signin()
{
try
{
if (App.IdentityClientApp != null)
{
var authenticationResult = await App.IdentityClientApp.AcquireTokenAsync(App.ClientScope);
this.UserToken = result.Token;
this.expiration = result.ExpiresOn;
}
else
Debug.WriteLine("Identity client is null");
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
Technically I should provide the token as an AuthenticationHeaderValue with the "Bearer" key.
But I'm not sure that is the right way to go.

Swagger authentication in Azure App Service

In my Azure Mobile .NET backend I want to use Azure Mobile .NET Server Swagger . I'm looking for fast way to hide swagger UI from public access ? Is there any way to provide access only for selected users ?
First a disclaimer: Even if you protect your Swagger UI from public consumption, you are not protecting your APIs from public consumption. You have to assume that everyone knows all of your routes and have the appropriate security in place to protect any requests that may come in.
That being said, there's still not a simple way to do this. Swashbuckle (the piece that adds Swagger to Web API) adds a custom HttpMessageHandler to the /swagger/ui route (as seen here). If you look at the Web API pipeline, you can see that if you specify a custom handler, you can bypass all of the Controller selection, Auth filters, etc. This is what happens here.
Some solutions:
Use an app setting to conditionally call ConfigureSwagger(config) in debug modes only. This would prevent all /swagger routes from making it into production. Or you could use a staging slot and only add it there.
You can wrap the SwaggerUiHandler with something like this Basic Auth MessageHandler. This would prompt the user for basic creds if they went to the /swagger/ui route. See below for my modified version of this code.
Maybe with a little more thought we can come up with a better solution -- I see a couple of issues (here and here) in the Swashbuckle repo that indicate you're not the first one to hit this.
Modified BasicAuthHandler (from here):
Warning: minimally tested (and be sure to change how you verify user/pass)
public class BasicAuthMessageHandler : DelegatingHandler
{
private const string BasicAuthResponseHeader = "WWW-Authenticate";
private const string BasicAuthResponseHeaderValue = "Basic";
public BasicAuthMessageHandler(HttpMessageHandler innerHandler)
{
this.InnerHandler = innerHandler;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
AuthenticationHeaderValue authValue = request.Headers.Authorization;
HttpResponseMessage unauthorizedResponse = request.CreateUnauthorizedResponse();
if (authValue != null && !string.IsNullOrWhiteSpace(authValue.Parameter))
{
Credentials parsedCredentials = ParseAuthorizationHeader(authValue.Parameter);
if (parsedCredentials != null)
{
// TODO: Check that the user/pass are valid
if (parsedCredentials.Username == "user" &&
parsedCredentials.Password == "pass")
{
// If match, pass along to the inner handler
return base.SendAsync(request, cancellationToken);
}
}
}
else
{
// Prompt for creds
unauthorizedResponse.Headers.Add(BasicAuthResponseHeader, BasicAuthResponseHeaderValue);
}
return Task.FromResult(unauthorizedResponse);
}
private Credentials ParseAuthorizationHeader(string authHeader)
{
string[] credentials = Encoding.ASCII.GetString(Convert
.FromBase64String(authHeader))
.Split(
new[] { ':' });
if (credentials.Length != 2 || string.IsNullOrEmpty(credentials[0])
|| string.IsNullOrEmpty(credentials[1])) return null;
return new Credentials()
{
Username = credentials[0],
Password = credentials[1],
};
}
}
Registering with Swagger route
// Do this after calling ConfigureSwagger
ConfigureSwagger(config);
// Remove the swagger_ui route and re-add it with the wrapped handler.
var route = config.Routes["swagger_ui"];
config.Routes.Remove("swagger_ui");
config.Routes.MapHttpRoute("swagger_ui", route.RouteTemplate, route.Defaults, route.Constraints, new BasicAuthMessageHandler(route.Handler));

owin oauth webapi with a dynamic TokenEndpointPath

I've successfully implemented oAuth using OWIN in my WebApi 2 Server with:
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions {
TokenEndpointPath = new PathString("/api/TokenByPassword"),
// ...
});
However, I would like the TokenEndpointPath to be dynamic as I will have multiple databases each with their own account records.
I believe I want something like:
TokenEndpointPath = new PathString("/api/{databaseid}/TokenByPassword");
I don't believe OAuthAuthorizationServerOptions supports this and even if it did - how would I get the databaseid ?
I could implement this in my own WebAPI with AttributeRouting, but then what would be the correct OWIN calls to make in that WebAPI to generate the correct BearerToken?
I found the answer..
Even though the TokenEndpointPath is specified in the OAuthAuthorizationServerOptions, the OAuthAuthorizationServerProvider has a delegate called OnMatchEndpoint. Inside this delegate, you can access the Request.Uri.AbsolutePath of the call and if it matches your criteria, you can then call MatchesTokenEndpoint() in which case OnGrantResourceOwnerCredentials will get called where you again can gain access the the Request.Uri and pick out the {databaseid} and use the correct database to Grant access.
OWIN is very flexible, but not immediately obvious which calls to make when to do what you want when it is something not quite straightforward.
Just to make it clearer, here is the implementation of the function MatchEndpoint of the class that extend OAuthAuthorizationServerProvider, as suggested by David Snipp :
private const string MatchTokenUrlPattern = #"^\/([\d\w]{5})\/token\/?$";
public override async Task MatchEndpoint(OAuthMatchEndpointContext context)
{
var url = context.Request.Uri.AbsolutePath;
if (!string.IsNullOrEmpty(url) && url.Contains("token"))
{
var regexMatch = new Regex(MatchTokenUrlPattern).Match(url);
if (regexMatch.Success)
{
context.MatchesTokenEndpoint();
return;
}
}
await base.MatchEndpoint(context);
}
Be careful on what you do in there because it is called at every request.

Resources