How to implement External Authentication with local claims in mvc 5 - asp.net

I am working on a mvc 5 application and have configured WS Federation to authenticate using an external IP. However based on the functionality of the application, the users's roles need to be managed by application itself. An administrator of the application will allow different level of access to the application from an admin area of the application. and since i am working with ASP.NET Identity for the first time i am lost on how to move forward from here.
Do I need to implement a custom ClaimsAuthenticationManager for this?
Do I also need to implement a UserManager to create local list of users to be able to manage their Claims?
Any direction/help is appreciated.
Thanks,

You can add external claims in your override of CreateUserIdentityAsync.
This will add just Roles from your Federated login:
public override async Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
var externalIdentity =
await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
var localIdentity = await user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
foreach (var item in externalIdentity.Claims.Where(x => x.Type == ClaimTypes.Role))
localIdentity.AddClaim(new Claim(ClaimTypes.Role, item.Value));
return localIdentity;
}

Related

how to tell services.AddAuthorization where is my custom user and role table is

I just build my user and role policy table and a table for connecting these to gather but how to tell services.AddAuthorization to looking for which policy in which table.
I had read the document of Microsoft Role-based authorization but they don't use custom user and role table I even don't know how to ask my question I confused
I mean how did it know were looking for Administrator in this picture
I just build my user and role policy table and a table for connecting
these to gather but how to tell services.AddAuthorization to looking
for which policy in which table.
By default, in the official Microsoft document (about Role-based or Policy-based authorization), it uses the Asp.net core Identity to manage the user and role.
From you description, I assume you also use the Asp.net Identity page to login and logout, right? If that is the case, since you are using a custom user and role table, in the Login.cshtml.cs file, after user login successfully, you could query this table based on the login user's email and get the user's role. Then add the role claims to the current user. After that, you can create policy based on the claims.
You can check the following sample code:
In the Login.cshtml.cs page:
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
//find current user.
var user = await _userManager.FindByEmailAsync(Input.Email);
//based on user information to query the user and role policy table. Here I set the user role directly.
var userrole = "User";
if (user.UserName.Contains("aa"))
{
userrole = "Admin";
}
//add claims to current user.
await _userManager.AddClaimAsync(user, new Claim(ClaimTypes.Role, userrole));
var claimsPrincipal = await _signInManager.CreateUserPrincipalAsync(user);
await _signInManager.RefreshSignInAsync(user);
_logger.LogInformation("User logged in.");
return LocalRedirect(returnUrl);
}
In the ConfigureServices method, create a policy based on the claims.
services.AddAuthorization(options =>
{
options.AddPolicy("RequiredAdmin", policy =>
policy.RequireClaim(ClaimTypes.Role, "Admin"));
});
Then, in the Configure method, add the following code:
app.UseAuthentication();
app.UseAuthorization();
and apply the policy to the action method:
[Authorize(Policy = "RequiredAdmin")]
public IActionResult Privacy()
{
return View();
}
The result as below: The User aa is Admin role, and the bb is User role.
Besides, here are some relate articles, you can refer them:
Policy-based authorization in ASP.NET Core
Policy-Based And Role-Based Authorization In ASP.NET Core 3.0 Using Custom Handler
Cookie Authentication In ASP.NET Core(if you don't use Asp.net core Identity, you can refer this article and configure the policy).

Port over existing MVC user authentication to Azure functions

I have an old web application which is using ASP.net with the build in cookie based authentication which has the standard ASP.net SQL tables for storing the users credentials.
This is currently running as an Azure web app, but I was toying with the idea of trying to go serverless as per this example creating a ReactJs SPA hosting on blob storage to try and keep costs down and also improve performance without breaking the bank.
https://learn.microsoft.com/en-us/azure/architecture/reference-architectures/serverless/web-app
I was wondering if it is possible to port over the existing ASP.net authentication to Azure functions, to instead return a JWT (JSON Web Token) which could be passed back in the headers to handle authenticated requests.
When I have tried this in the past I have failed misserably, so I was wondering if anyone knows if it is possible?
I've seen this article, which seems to talk about Azure functions doing authentication, but with Azure AD, which I don't think is right for what I need.
https://blogs.msdn.microsoft.com/stuartleeks/2018/02/19/azure-functions-and-app-service-authentication/
The answer is kind of. What I mean by this is that you can use your existing database and many of the same libraries, but you can't port over the code configuration. The default authentication for Functions is either 1) The default API tokens or 2) one of the EasyAuth providers baked into App Services which is in the guide you linked. Currently, any other solution you'll need to setup yourself.
Assuming you go with the JWT option, you'll need to turn off all of the built-in authentication for Functions. This includes setting your HttpRequest functions to AuthorizationLevel.Anonymous.
At a basic level You'll need to create two things. A function to issue tokens, and either a DI service or a custom input binding to check them.
Issuing tokens
The Functions 2.x+ runtime is on .NET Core so I'm gong to borrow some code from this blog post that describes using JWTs with Web API. It uses System.IdentityModel.Tokens.Jwt to generate a token, which we could then return from the Function.
public SecurityToken Authenticate(string username, string password)
{
//replace with your user validation
var user = _users.SingleOrDefault(x => x.Username == username && x.Password == password);
// return null if user not found
if (user == null)
return null;
// authentication successful so generate jwt token
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
return tokenHandler.CreateToken(tokenDescriptor);
}
Validating Tokens
There are several guides out there for validating JWT within Azure Functions. I like this one from Ben Morris: https://www.ben-morris.com/custom-token-authentication-in-azure-functions-using-bindings/ (source code). It describes authenticating with either a custom input binding or with DI. Between the two, DI is the preferred option, unless there is a specific reason you need to use a binding. Here again, its the Microsoft.IdentityModel.JsonWebTokens and System.IdentityModel.Tokens.Jwt libraries that you'll need to do the bulk of the work.
public class ExampleHttpFunction
{
private readonly IAccessTokenProvider _tokenProvider;
public ExampleHttpFunction(IAccessTokenProvider tokenProvider)
{
_tokenProvider = tokenProvider;
}
[FunctionName("ExampleHttpFunction")]
public IActionResult Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "example")] HttpRequest req, ILogger log)
{
var result = _tokenProvider.ValidateToken(req);
if (result.Status == AccessTokenStatus.Valid)
{
log.LogInformation($"Request received for {result.Principal.Identity.Name}.");
return new OkResult();
}
else
{
return new UnauthorizedResult();
}
}
}

Implementing active authentication using ADFS

I am working on the authentication with Active Directory using ADFS.
While searching, I got few articles to accomplish this requirement, but they are suggesting to redirect the Login page of application to Login page of ADFS and then come back.
Redirecting to ADFS Login page is not suggested as per user experience.
Can anyone help me to find out the solution to authenticate with active directory using ADFS behind the scene ? So, everything will be handled by application code, not by ADFS login page.
Please advise.
Please let me know if you have any concern or query or if you need more information.
The reason those articles suggest you redirect (using WS-Federation protocol) to the ADFS login page is because it allows you to set up federation to other identity providers (allow an external company' employees to use their own credentials to log in to your application).
What you want can be done using the WS-Trust protocol, but you'll give up (or have to implement yourself) the possibility to federate.
ADFS exposes endpoints like /adfs/services/trust/13/usernamemixed that you can talk to to get a security token. Something like below should get you going.
public class UserNameWSTrustBinding : WS2007HttpBinding
{
public UserNameWSTrustBinding()
{
Security.Mode = SecurityMode.TransportWithMessageCredential;
Security.Message.EstablishSecurityContext = false;
Security.Message.ClientCredentialType = MessageCredentialType.UserName;
}
}
private static SecurityToken GetSamlToken(string username, string password)
{
var factory = new WSTrustChannelFactory(new UserNameWSTrustBinding(), "https://yourdomain.com/adfs/services/trust/13/UsernameMixed")
{
TrustVersion = TrustVersion.WSTrust13
};
factory.Credentials.UserName.UserName = username;
factory.Credentials.UserName.Password = password;
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
AppliesTo = new EndpointReference("https://yourdomain.com/yourservice"),
KeyType = KeyTypes.Bearer
};
var channel = factory.CreateChannel();
return channel.Issue(rst);
}

WCF RIA Services Form Authentication ServiceContext.User.Identity.Name Empty

We are using WCF Ria Services with silverlight 5 project.For authentication we are using Custom membership provider.WCF Ria Service in RIA Services class library.
Client side authentication running.We access current user name with WebContext.Current.User.Name.But server side ServiceContext.User empty.If we use [RequireAuthentication] attr. in DomainServices return always Access Denied.
How Can we push WebContext.Current.user to ServiceContext.user.I read several document and tutorial but every test fail.
Code examples :
CustomMembershipProvider.cs:
public class CustomMembershipProvider : MembershipProvider {
public override bool ValidateUser(string username, string password)
{
using (var context = new TimEntities())
{
var user = context.User.FirstOrDefault(u => u.Username == username &&
u.Password == password);
return user != null;
}
}
}
AuthenticationDomainService:
[EnableClientAccess]
public class AuthenticationDomainService : AuthenticationBase<AuthUser>
{}
public class AuthUser : UserBase
{}
App.Xaml.Cs:
var webContext = new WebContext();
var formsAuth = new FormsAuthentication();
var authContext = new AuthenticationDomainContext();
formsAuth.DomainContext = authContext;
webContext.Authentication = formsAuth;
ApplicationLifetimeObjects.Add(webContext);
I was having the same problem, but I have finally tracked down the answer. I had been messing about trying to expose SOAP endpoints, so I could access the same RIA services from a phone app. As part of that, I had added the following line in the App constructor:
WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);
Get rid of that, and you should have the user name available again. SOAP endpoints still seem to be exposed to the phone app as well.
At first you have to configure Forms authentication for your hosting website regardless whether you use WCF RIA Service or not. Moreover, Forms authentication have to be installed and enabled on IIS.
Then you have to configure ASP.NET membership in order to use your CustomMembershipProvider class.

using asp.net authentication with custom authentication

Here is my Scenario.
I have authentication web-services exposed by another domain. Now I want user name and password to be sent to that external domain for authentication. and when user is authenticated (returned true), I want the ASP.net to take that authentication further and let the user in and provide me all the asp.net standard utilities accessible, like currentuser, Isauthorized, Roles etc, for the user, authenticated. I hope this make sense.
This is not a problem. You have a variety of options available to you. One approach is to blend Forms Authentication with your own security model.
The basic idea is to let Forms Auth create and manage a ticket (in the form of an encrypted ticket) for the logged-in user. The ticket is used to determine whether or not someone is logged in, and who they are. You can then mix in any additional security related logic on top of that.
To process the login request, you just have a controller and action like you normally would. Note: in the example below, I am making some assumptions about LoginViewModel, the service you are using to authenticate, and the object it returns if any. You'll have to sub in your actual logic.
public ActionResult Login(LoginViewModel model)
{
// make sure the user filled out the login fields correctly
if (!ModelState.IsValid) return View(model);
// authenticate the user here
var authenticatedUser = AuthorizeUserUsingRemoteWebService(model.Username, model.Password);
if (authenticatedUser.IsAuthenticated)
{
// create forms auth ticket cookie and redirect to the home page
FormsAuthentication.SetAuthCookie(authenticatedUser.Username);
return RedirectToAction("Index", "Home");
}
// authentication failed, so show the login page again
return View(model);
}
In addition to that, you may have an HTTP module that handles the AuthenticateRequest event. Your module will be registered after the Forms Auth HTTP module, so it will have already processed whether or not the user is logged in. What you want to do is look up additional information if they are logged in, to get roles and such.
public class CustomAuthHttpModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.AuthenticateRequest += new EventHandler(OnAuthenticateRequest);
}
void OnAuthenticateRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
HttpContext context = appObject.Context;
// user isn't logged in, so don't do anything else
if (!context.User.Identity.IsAuthenticated) return;
// look up the roles for the specified user, returning the role names as an array of strings
string[] roles = LookupUserRolesFromWebService(context.User.Identity.Name);
// replace the current User principal with a new one that includes the roles we discovered for that user.
context.User = new GenericPrincipal(new GenericIdentity(context.User.Identity.Name), roles);
}
}
You'll register the HTTP module in your web.config:
<httpModules>
<add name="CustomAuthHttpModule"
type="MyAssembly.CustomAuthenticationModule, MyAssembly" />
</httpModules>
You can now use the User object in your MVC controllers and views, the AuthenticatedAttribute, etc.
However, I'd recommend that you cache the results of looking up a user's roles so you don't hammer your web service. I'll leave that up to you.
You can you use Security Token Service for your application. Setup a Windows Identity Foundation SDK and find examples in sdk directory (for me it is "C:\Program Files (x86)\Windows Identity Foundation SDK\v4.0\Samples\End-to-end\Federation for Web Apps"). One of them ( named "Federation for Web Apps") implement your case for AD authentication.

Resources