MVC Authentication - Easiest Way - c#

I have looked at ASP.NET Identity and it looks really complex and difficult to follow. Basically what I want to know is the easiest way to authorize a user on login so the [Authorize] data annotation will allow them through.

Follow these steps:
Install the following NuGet packages
Microsoft.Owin
Microsoft.Owin.Host.SystemWeb
Microsoft.Owin.Security
Microsoft.Owin.Security.Cookies
Inside App_Start folder, add a AuthConfig that look like this:
public static class AuthConfig
{
public const string DefaultAuthType = "DefaultAppCookie"; //example
public const string LoginPath = "System/SignIn"; //example
public static void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthType,
LoginPath = new PathString(LoginPath)
});
}
}
In the root path of the project, add a Startup.cs that look like this
[assembly: OwinStartup(typeof(YourPorject.Startup))]
namespace YourPorject
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
AuthConfig.ConfigureAuth(app);
}
}
}
To authenticate an user (usually inside a Login Action):
//user = the user that is loggin on, retrieved from database
List<Claim> claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.Name),
new Claim(ClaimTypes.Email, user.Email),
//some other claims
};
ClaimsIdentity identity = new ClaimsIdentity(claims, AuthConfig.DefaultAuthType);
IAuthenticationManager authManager = Request.GetOwinContext().Authentication;
authManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
You need to add a ClaimTypes.Role to authorize specific roles.

Related

HttpContext.Current.User.Identity.Name Alternative for Non-Windows User?

I deployed an application to Azure. Internal users with Windows accounts are logged in automatically when they navigate to the application. External users need to enter their username and password to log into the application. Their username is an email address with a domain that is not that same as the domain used by internal users.
I use HttpContext.Current.User.Identity.Name to set the CreatedBy and ModifiedBy values. I also use #User.Identity.Name in a view to display a greeting. Both of these do not display a value for external users with non-Windows accounts.
What are the alternative options for non-Windows accounts to get these values?
Startup.Auth.cs
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
var clientId = ConfigurationManager.AppSettings["ida:ClientId"];
var aADInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
var tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
var postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
var authority = string.Format(CultureInfo.InvariantCulture, aADInstance, tenantId);
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies",
CookieManager = new SystemWebChunkingCookieManager()
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = ClaimTypes.Upn,
RoleClaimType = ClaimTypes.Role
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/");
return Task.FromResult(0);
}
}
}
);
}
}
I tried seeing if HttpContext.Current.User.Identity.Name had other options to get the value needed, such as after Identity and after User. I also checked to see if the active directory user profile had any missing values, such as email address or name.
In ASP.NET Core, the current user's identity information can be accessed through the User property on the HttpContext class.
Whereas the HttpContext.Current property is not available in ASP.NET Core. Instead, you can use dependency injection to get an instance of the IHttpContextAccessor interface and use it to access the current HttpContext.
public class HomeController : Controller
{
private readonly IHttpContextAccessor _contextAccessor;
public HomeController(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public IActionResult Index()
{
var userName = _contextAccessor.HttpContext.User.Identity.Name;
if (string.IsNullOrEmpty(userName) && _contextAccessor.HttpContext.User.Identity.IsAuthenticated)
{
userName = _contextAccessor.HttpContext.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
}
return View(userName);
}
}
In the Startup.cs file, configure the authentication in the ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = new PathString("/Account/Login");
options.AccessDeniedPath = new PathString("/Account/AccessDenied");
});
}
In startup.cs class, in the configure method, use the UseWindowsAuthentication and UseCookieAuthentication for internal and external users.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.Use(async (context, next) =>
{
if (context.User.Identity.IsAuthenticated && context.User.Identity.AuthenticationType == "NTLM")
{
var email = context.User.Identity.Name;
if (!email.EndsWith("#internal-domain.com"))
{
await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
context.Response.Redirect("/Account/Login");
return;
}
}
await next();
});
app.UseWindowsAuthentication();
app.UseCookieAuthentication();
}
Create a Login action method that handles the external user login:
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
if (IsValidUser(model.Email, model.Password))
{
if (!model.Email.EndsWith("#internal-domain.com"))
{
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.Name, model.Email));
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", "Invalid UserName.");
}
}
else
{
ModelState.AddModelError("", "Invalid password.");
}
}
return View(model);
}
private bool IsValidUser(string email, string password)
{
// check with DB to compare the credentials.
return true;
}
I use HttpContext.Current.User.Identity.Name to set the CreatedBy and ModifiedBy values.
I also use #User.Identity.Name in a view to display a greeting.
Both of these do not display a value for external users with non-Windows accounts.
HttpContext.Current.User.Identity.Name and #User.Identity.Name are used to retrieve the user's name in an ASP.NET application.
These properties are based on the authentication mechanism being used in the application.
If the application is using Windows authentication, the User.Identity.Name property will return the user's Windows login name.
If the application is using forms authentication, the User.Identity.Name property will return the username that the user entered when logging in.
In the view, you can use the following code to check if user is authenticated.
#if (User.Identity.IsAuthenticated)
{
<text>Welcome, #User.Identity.Name</text>
}
else
{
<text>Welcome, User</text>
}
Windows Login:
Reference :
Forms and Windows Authentication
Thanks # mvolo for the blog.
For this question, I updated NameClaimType = ClaimTypes.Upn to NameClaimType = ClaimTypes.Name.
I first chose Upn instead of Name because Upn seemed more "unique" per their descriptions.
I confirmed with an external user that the username is now displayed.

How to add additional claims to ClaimsPrincipal after ADFS authentication on MVC app

We have an MVC 5 web app that uses ADFS 4 authentication. I'm trying to find the best place where I can add additional claims into the ClaimsPrincipal, after authentication has been completed.
Are there any events I can access, like OnAuthenticated? How do I access this kind of event?
This is what I intend to use once I can access the event:
IOwinContext context = Request.GetOwinContext();
if (appRoles != null)
{
ClaimsIdentity claimsIdentity = new ClaimsIdentity(System.Web.HttpContext.Current.User.Identity);
foreach (var role in appRoles)
{
claimsIdentity.AddClaim(new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", role));
}
context.Authentication.AuthenticationResponseGrant = new AuthenticationResponseGrant
(new ClaimsPrincipal(claimsIdentity), new AuthenticationProperties { IsPersistent = true });
}
EDIT:
This is what my App_Data\Startup.Auth.cs file looks like:
public partial class Startup
{
private static string realm = ConfigurationManager.AppSettings["ida:Wtrealm"];
private static string adfsMetadata = ConfigurationManager.AppSettings["ida:ADFSMetadata"];
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieManager = new SystemWebCookieManager()
});
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = realm,
MetadataAddress = adfsMetadata
});
}
}
I encountered similar problem and managed to find a way to add additional claims after ADFS login in my MVC 5 app. More info can be found on msdn link.
Here is code from that link.
Firstly create the new ClaimsAuthenticationManager class and inside set additional claims:
class SimpleClaimsAuthenticatonManager : ClaimsAuthenticationManager
{
public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
{
if (incomingPrincipal != null && incomingPrincipal.Identity.IsAuthenticated == true)
{
((ClaimsIdentity)incomingPrincipal.Identity).AddClaim(new Claim(ClaimTypes.Role, "User"));
}
return incomingPrincipal;
}
}
Afterwards specify this class in web.config file, under identityConfiguration element:
<system.identityModel>
<identityConfiguration>
<claimsAuthenticationManager type="ENTER YOUR NAMESPACE HERE.SimpleClaimsAuthenticatonManager, ENTER PROJECT NAME HERE" />
...
</identityConfiguration>
</system.identityModel>

How to validate credentials with user created table and generate Token in web API

I am new to WEB API ,here I need to implement a token based authentication for a login page .
As of now I have completed the token generate functions by providing static values for learning purpose .
Now What I want to do is I want to validate each request with the table which I have created in my SQL SERVER (LOGIN_TABLE) and generate Token instead of validate with Identity tables in web API.
Columns of LOGIN_TABLE
FirstName,LastName,UserName,Email,Password,CreationDate
I don't know is it possible or not ,if possible please help me to do that .
I have created my project as Web API template without MVC and choosed No Authentication then I have added all the necessary packages and classes.
Here I have added the code I have used to generate token based on the static values.
My_Controller.cs
[Authorize]
[HttpGet]
[Route("api/data/authenticate")]
public IHttpActionResult GetForAuthenticate()
{
var identity = (ClaimsIdentity)User.Identity;
return Ok("Hello" + identity.Name);
}
MyAutorizationServerProvider.cs
public class MyAuthorizationServerProvider :OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Resource owner password credentials does not provide a client ID.
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
if (context.UserName == "user" && context.Password == "123")
{
identity.AddClaim(new Claim(ClaimTypes.Role, "USER"));
identity.AddClaim(new Claim("username", "user"));
identity.AddClaim(new Claim(ClaimTypes.Name, "Sakthi"));
context.Validated(identity);
}else
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
}
Startup.cs
public class Startup
{
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
//This enable cors orgin requests
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
var myProvider = new MyAuthorizationServerProvider();
OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(30),
Provider = myProvider
};
app.UseOAuthAuthorizationServer(options);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
}
}
AuthorizeAttribute.cs
public class AuthorizeAttribute :System.Web.Http.AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actioncontext)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
base.HandleUnauthorizedRequest(actioncontext);
}else
{
actioncontext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
}
}
}
I am new to web API if I did any mistakes here please guide me to correct it .
Thanks .

OWIN Cookie Auth Not Returning Cookie

I'm creating an application using OWIN without using ASP Identity. I've struggled to find any documentation on this as 99% of material out there is using Identity with OWIN.
Startup.cs
public class Startup
{
public void Configuration(IAppBuilder app)
{
//
// Wires up WebApi to Owin pipeline
//
var config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieDomain = ".devdomain.com",
CookieName = "AuthCookie",
});
}
}
API Controller
public void Login(string id)
{
var authentication = System.Web.HttpContext.Current.GetOwinContext().Authentication;
var identity = new ClaimsIdentity("Application");
identity.AddClaim(new Claim(ClaimTypes.Name, "<user name>"));
identity.AddClaim(new Claim("CompanyId", id));
authentication.AuthenticationResponseGrant = new AuthenticationResponseGrant(identity, new AuthenticationProperties()
{
IsPersistent = true
});
}
When I invoke the Login method it creates a new identity signs the user in. However a cookie isn't returned in the response.
Am I missing something?

How to expose /Token End point from Startup.Auth.cs with Identity v2

I am working with ASP.NET Identity 2 and trying to enable Token Authentication. From the Identity 2 sample there is nothing about Token Authentication so I setup myself by looking at different post. I am following the last answer of this post app.UseOAuthBearerTokens with ASP.NET Identity 2.0's DbContext middleware?
and able to setup each and everything but now when I try to reach this end point on browser http://localhost:6722/token getting 404 error. Any idea?
Update:
Here is my ApplicationOAuthProvider.cs
public class ApplicationOAuthProvider<TUserManager, TUser, TKey> : OAuthAuthorizationServerProvider
where TUserManager : UserManager<TUser, TKey>
where TUser : class, IUser<TKey>
where TKey : IEquatable<TKey>
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
{
throw new ArgumentNullException("publicClientId");
}
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.Get<TUserManager>(_publicClientId);
TUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await userManager.CreateIdentityAsync(user,
context.Options.AuthenticationType);
ClaimsIdentity cookiesIdentity = await userManager.CreateIdentityAsync(user,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Resource owner password credentials does not provide a client ID.
if (context.ClientId == null)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
Uri expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
}
return Task.FromResult<object>(null);
}
public static AuthenticationProperties CreateProperties(string userName)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
{
"userName", userName
}
};
return new AuthenticationProperties(data);
}
}
Here is my Startup.Oauth.cs
public partial class Startup
{
static Startup()
{
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider<ApplicationUserManager, User, int>(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = false
};
}
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and role manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDb.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, User, int>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
getUserIdCallback: (claim) => int.Parse(claim.GetUserId()))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
//app.UseGoogleAuthentication();
}
}
And I am trying to access /Token end point directly from the browser and it throws 404 error.
I had a similar issue today, what fixed the problem for me was installing the following Nuget package.
I have no idea why it wasn't installed with all of the other packages, but give it a shot and let us know.
The package name is Microsoft.Owin.Host.SystemWeb
Do you have the other part of Startup class? Because you should have a Startup.cs file in your web project that is calling your ConfigureAuth method. If not you will get a 404 exception. The startup.cs file should look like this:
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(YOUR_NAME_SPACE_TO_THE_STARTUP_FILE.Startup))]
namespace YOUR_NAME_SPACE_TO_THE_STARTUP_FILE
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}
If you then trying to access the page through your browser you should get this error:
{
error: "unsupported_grant_type"
}
In my case I wrapped the AllowInsecureHttp in an #if DEBUG processor directive, and ended up publishing it to the server (as Release) and my customer didn't afford SSL.
After removing the DEBUG condition it all works fine now.
I've just had a similar (if not same) issue where I was getting 404s when accessing /token. Eventually worked out that it was due to the order I was calling things in the Startup.Configuration method. I was calling ConfigureAuth(app) at the very bottom of this method, after I had added Ninject config (ie. app.UseNinjectMiddleware(CreateKernel).UseNinjectWebApi(config);. Moving the ConfigureAuth above the Ninject setup fixed this issue and /token now works.

Categories