I am extremely confused about how the actual authentication works so that [Authorize] does not redirect me to the login page.
Here's my Configuration:
public class IdentityConfig
{
public void Configuration(IAppBuilder app)
{
app.CreatePerOwinContext(() => new MyANTon.DataContext.AntContext());
app.CreatePerOwinContext<UserManager>(UserManager.Create);
app.CreatePerOwinContext<RoleManager<AppRole>>((options, context) =>
new RoleManager<AppRole>(
new RoleStore<AppRole>(context.Get<MyANTon.DataContext.AntContext>())));
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Home/Login"),
});
}
}
In the controller, I want to call an Authenticate(string Email, String Password) method, authenticate the user and return a bool. However, I have no idea how the actual authentication works.
In FormsAuthentication I would create a ticket, what do I do for Identity?
Here's what I have:
public static async System.Threading.Tasks.Task<bool> AuthUserAsync(string Email, string Password)
{
using (var db = new AntContext())
{
string hashedPW = GetHash(Password);
bool userValid = db.Users.Any(user => user.Email == Email && user.Password == hashedPW);
if (userValid)
{
var actUser = db.Users.FirstOrDefault(u => u.Email == Email && u.Password == hashedPW);
if (actUser != null && !actUser.IsLocked)
{
/** What do I do here? **/
}
else if (actUser.IsLocked)
{
LoggingServices.AuthLog(actUser.Email, "Hat versucht auf ein gesperrtes Konto zuzugreifen.");
}
}
return false;
}
}
You are heading in the right direction, what you are doing is sort of using OAuth to faciliate the mapping of your tokens and letting OWin handle the browser information. So, by using the [Authorize] attribute, like you are doing, how are you handling the signing of the identity? Like mentioned above with Forms authentication, you still have to create the Identity/Claim token. In my projects, I do something like this,
protected void IdentitySignin(IUserModel userModel, string providerKey = null, bool isPersistent = true)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, userModel.Id.ToString()),
new Claim(ClaimTypes.Name, userModel.UserName),
new Claim("UserContext", userModel.ToString())
};
var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties
{
IsPersistent = isPersistent,
ExpiresUtc = DateTime.UtcNow.AddDays(7)
}, identity);
}
This forces OWIN/OAuth to login the user, and in my web.config I have the following:
<system.webserver>
<authentication mode="None" />
</system.webserver>
And to sign my user out, and force the browser to get a new token:
protected void IdentitySignout()
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie,
DefaultAuthenticationTypes.ExternalCookie);
}
My AuthenticationManager is defined as such:
private IAuthenticationManager AuthenticationManager
{
get { return HttpContext.GetOwinContext().Authentication; }
}
Which is part of Microsoft.OWin.IOwinContext, you will have to add the reference if it does not exist.
You can handle an unauthorized user via the web.config file, or a base-controller, I opted for the base-controller option like so:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (UserContext == null || UserContext.Id.Equals(Guid.Empty))
{
if (filterContext.Controller.GetType() == typeof(AccountController) || filterContext.Controller.GetType() == typeof(HomeController)) return;
filterContext.Result = new RedirectResult("/Home/Index");
return;
}
}
But you can also do it via the AuthorizeAttribute if you wanted. This link will go into great detail on handling Oauth and Asp.Net MVC, may seem daunting at first, but it gives a great layout on using other providers if you decided to incorporate them into a release version.
https://www.codeproject.com/Articles/577384/Introduction-to-OAuth-in-ASP-NET-MVC
When you want Login in to your web-site you send your token to client and you can work with that to request and response in many, in other word, you have to login with your server side.
But when you want to logout from web-site, your client have to know that you want to logout this is not work just for server, the client should do this issue for log out.
I want to suggest you Token-JWT
if you want know about JWT click here
If you want I'll create an Example for you.
Related
I use an external provider in order to authenticate user in my ASP.NET MVC app without any problem. However, I also need to authorize users in order to prevent them from direct access or expired access (session for 2 min). I had used ASP.NET Identity before, but this time I do not need to keep neither users nor roles on the table and for this reason I need a quick and good workaround for this problem. So, how can I prevent a user accessing the In dex page of my app without authenticating by the provider that I use. Similarly I also need to check if there is more than 2 minutes after user's last action and in such sitıuation I need to redirect user to Login page. I tried to use OWIN Cookie, but unfortunately I cannot logout user by using at least 10 different approach :(
Startup:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
//other options
ExpireTimeSpan = TimeSpan.FromMinutes(1)
//Provider = new CookieAuthenticationProvider(),
//CookieName = "MyCookieName",
//CookieHttpOnly = true
});
}
}
Controller:
[HttpGet]
public ActionResult Login(string code)
{
//At this stage I want to force user to sign out, but none of the following methods work
//method 1
HttpContext.GetOwinContext().Authentication.SignOut("ApplicationCookie");
//method 2
var ctx = Request.GetOwinContext();
var authManager = ctx.Authentication;
authManager.SignOut("ApplicationCookie");
//or
//authManager.SignOut();
//method 3
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
//method 4 (using only one of them at a time)
Request.GetOwinContext().Authentication.SignOut();
Request.GetOwinContext().Authentication.SignOut(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie);
HttpContext.GetOwinContext().Authentication.SignOut(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie);
//check session
var isAuthenticated = HttpContext.GetOwinContext().Authentication.User.Identity.IsAuthenticated; // >>> always returns true
string tc = HttpContext.GetOwinContext().Authentication.User.Identity.Name; // >>> always returns name value
//if user is authenticated via OAuth2.0
if (user.isAuthenticated)
{
var claims = new[] {
new Claim(ClaimTypes.Name, user.Name)
};
var identity = new ClaimsIdentity(claims, "ApplicationCookie");
//// Add roles into claims
//var roles = _roleService.GetByUserId(user.Id);
//if (roles.Any())
//{
// var roleClaims = roles.Select(r => new Claim(ClaimTypes.Role, r.Name));
// identity.AddClaims(roleClaims);
//}
var context = Request.GetOwinContext();
var authManager = context.Authentication;
authManager.SignIn(new AuthenticationProperties
{ IsPersistent = false }, identity); // ??? I am not sure if IsPersistent should be true ?
return View();
}
// login failed
return RedirectToAction("Account", "Login");
}
Finally I have fixed the problem by using OWIN cookie authentication. Here is the code for those who might need to use OWIN cookie authentication on ASP.NET MVC.
On the other hand, I would really like to integrate JWT to my ASP.NET MVC project, but unfortunately was not able to do. However, many thanks and voted up the answers that are also helpful for me.
Startup:
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
LogoutPath = new PathString("/Account/LogOff"),
ExpireTimeSpan = TimeSpan.FromMinutes(5),
SlidingExpiration = true,
Provider = new CookieAuthenticationProvider(),
CookieName = "YOUR_COOKIE_NAME",
CookieHttpOnly = true,
// !!! Using this setting "Always" causing "302 Redirect..." error while ddebugging >>>
CookieSecure = CookieSecureOption.SameAsRequest
});
}
AccountController:
public ActionResult Login()
{
//authenticate user
var user = db.GetUser("John");
if (user != null)
{
var claims = new[] {
new Claim(ClaimTypes.Name, user.Name),
new Claim(ClaimTypes.Email, user.Email)
//you can add more claims
};
var identity = new ClaimsIdentity(claims, "ApplicationCookie");
// Add roles into claims
var roles = _roleService.GetByUserId(user.Id);
if (roles.Any())
{
var roleClaims = roles.Select(r => new Claim(ClaimTypes.Role, r.Name));
identity.AddClaims(roleClaims);
}
var context = Request.GetOwinContext();
var authManager = context.Authentication;
authManager.SignIn(new AuthenticationProperties
{ IsPersistent = true }, identity);
return RedirectToAction("Index", "Home");
}
// login failed.
return RedirectToAction("Login", "Account");
}
You need to set the [Authorize] attribute on the action and/or controller.
And about the session only last for 2 minutes. You can put the timestamp in a session cookie when the user logins and then build a middleware to check the session value each time an action is made. If the session value is older than 2 minutes log out the user.
How to use session:
In Startup file add:
services.AddSession(options =>
{
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
and
app.UseSession();
//middleware for checking the 2 minute limit
app.UseMiddleware<SignoutMiddleware>();
Add the session wherever your user gets logged in:
HttpContext.Session.SetString(subjectId, DateTime.Now);
Middleware:
public class SignoutMiddleware
{
private readonly RequestDelegate _next;
public SignoutMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
var sessionExpire = context.Session.GetString(context.User.GetSubjectId());
//Do some logic here
await _next.Invoke(context);
}
}
As for what your code currently does for login you probably needs to change some. But there should be many tutorials if you just google some :)
You could use a Entity Framwork, JSON Web Token (JWT) and Claims. It is really easy to limit the amount of time (days, hours, minutes) you want a user to have access to a section of your Controllers with JWTs.
You can limit the amount of time a JWT has access for by using the Expires object in SecurityTokenDescriptor. So in your case I would do the following:
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddMinutes(2),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
There is several great FULL examples done by Jason Watmore for .NET Core with Role Based Auth and Secure Auth For Hashed Password in Database. Not sure what library you're using though so if this does not help you it would help me to help you if you specify.
I'm AmirReza that you talking before.
Edit
In order for MVC to understand anything about your JWT you basically have to tell it :-) . First, install the Jwt package from nuget:
Install-Package Microsoft.Owin.Security.Jwt
Then open up your Startup.cs file and add a new funtion that will tell MVC how to consume JWT. At basics your Startup will look something like:
using System.Configuration;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler.Encoder;
using Microsoft.Owin.Security.Jwt;
using Owin;
[assembly: OwinStartupAttribute(typeof(TOMS.Frontend.Startup))]
namespace TOMS.Frontend {
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
ConfigureOAuthTokenConsumption(app);
}
private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
var issuer = ConfigurationManager.AppSettings["Issuer"];
var audienceId = ConfigurationManager.AppSettings["AudienceId"];
var audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["AudienceSecret"]);
You will notice that I am placing the issuer, audienceId and audienceSecret in my Web.config file. (Those values should match the ones on your Resource Server). Also, you might want to ensure you have an updated System.IdentityModel.Tokens.Jwt running:
Update-package System.IdentityModel.Tokens.Jwt
With those set, you may decorate your controller Action with the [Authorize] attribute and play ball.
UPDATE By The way, if you wish to add the values in your web.config file in order to retrieve them as I did above; simply add them under the AppSettings:
<configuration> <appSettings> <add key="Issuer" value="YOUR_ISSUER" /> <add key="AudienceId" value="YOUR_AUDIENCEID" /> <add key="AudienceSecret" value="YOUR_AUDIENCESECRET" /> </appSettings> </configuration>
I'm trying to find a proper way where I can inject a service to validate if user exists or registered in my application after being successfully authenticated from an external identity provider like Azure Active Directory. What I want to do is to redirect user to a custom error page or display an Unauthorized message if his account is not yet registered in my application.
I tried utilizing the IProfileService interface but it seems not the right way to go.
Here is my Startup.cs setup:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services
.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddTestUsers(Config.GetUsers())
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients()) // Client was configured with RequireConsent = false, EnableLocalLogin = false,
.AddProfileService<ProfileService>()
.Services.AddTransient<IUserRepository,UserRepository>();
services.AddAuthentication()
.AddOpenIdConnect("AAD", "Azure Active Directory", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.SignoutScheme;
options.Authority = "https://login.microsoftonline.com/MyTenant";
options.ClientId = "MyClientId";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false
};
options.GetClaimsFromUserInfoEndpoint = true;
});
}
public class ProfileService : IProfileService
{
private readonly IUserRepository _userRepository;
public ProfileService(IUserRepository userRepository)
{
_userRepository = userRepository
}
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var user = _userRepository.FindByUser(context.Subject.Identity.Name);
// This will display HTTP 500 instead of 401
if(user == null) throw new UnauthorizedAccessException("You're not registered");
// I add custom claims here
return Task.FromResult(0);
}
public Task IsActiveAsync(IsActiveContext context) => Task.FromResult(0);
}
Is there any available service or interface I can use where I can inject my user validation as well as allowing me to inject my user repository in that service? Is it possible to inject this kind of process inside IdentityServer4? Can someone point me in the right direction to accomplish my goal using IdentityServer4?
Note: Lets assume I have SPA web app and I have my own separate registration mechanism. I don't want to redirect back to my SPA if user doesn't exist and handle it inside IdentityServer4 instead. Btw, some of the code above are not included for brevity.
The IdentityServer4 QuickStart UI is configured to auto-provision local user accounts when signing-in through an external provider. That's all handled in ExternalController.Callback:
// lookup our user and external provider info
var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result);
if (user == null)
{
// this might be where you might initiate a custom workflow for user registration
// in this sample we don't show how that would be done, as our sample implementation
// simply auto-provisions new external user
user = AutoProvisionUser(provider, providerUserId, claims);
}
In your situation, you can perform whatever logic you need to perform instead of calling AutoProvisionUser. As this is a regular MVC action that's being executed, you have the ability to inject your own classes into ExternalController's constructor or into Callback itself (using [FromServices]). Here's a rough idea of the changes you might want to make:
public async Task<IActionResult> Callback([FromServices] IUserRepository userRepository)
{
...
// lookup our user and external provider info
var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result);
if (user == null)
{
// We don't have a local user.
return RedirectToAction("SomeAction", "SomeController");
}
...
}
You can write your custom logic in ExternalLoginCallback function in in AccountController if you are using ASP.NET Identity . After getting JWT token issued from Azure AD , you can decode the token ,get the user claims such as email/name :
if (remoteError != null)
{
ErrorMessage = $"Error from external provider: {remoteError}";
return RedirectToAction(nameof(Login));
}
// read external identity from the temporary cookie
var aadResult1 = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
if (aadResult1?.Succeeded != true)
{
throw new Exception("External authentication error");
}
// retrieve claims of the external user
var externalUser = aadResult1.Principal;
if (externalUser == null)
{
throw new Exception("External authentication error");
}
// retrieve claims of the external user
var claims = externalUser.Claims.ToList();
// try to determine the unique id of the external user - the most common claim type for that are the sub claim and the NameIdentifier
// depending on the external provider, some other claim type might be used
var userIdClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject);
if (userIdClaim == null)
{
userIdClaim = claims.FirstOrDefault(x => x.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier");
}
if (userIdClaim == null)
{
throw new Exception("Unknown userid");
}
Then you can write your service implement/logic in database to confirm whether user is already in database . If yes , login in user;if no , redirect user to confirmation/register view . Something like:
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync("YourProvider", userIdClaim.Value, isPersistent: false, bypassTwoFactor: true);
if (result.Succeeded)
{
_logger.LogInformation("User logged in with {Name} provider.", "YourProvider");
// delete temporary cookie used during external authentication
await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
return RedirectToLocal(returnUrl);
}
if (result.IsLockedOut)
{
return RedirectToAction(nameof(Lockout));
}
else
{
// If the user does not have an account, then ask the user to create an account.
ViewData["ReturnUrl"] = returnUrl;
ViewData["LoginProvider"] = "YourProvider";
var email = claims.FirstOrDefault(x => x.Type == ClaimTypes.Upn).Value;
return View("ExternalLogin", new ExternalLoginViewModel { Email = email });
}
It depends on you for how to link AD user to local database user .use Azure AD's object ID or UPN .
For beta testing, I would like to use Basic Authentication (or Digest) but I am unable to combine the Cookie Authentication provided by default in MVC with Basic Authentication.
Following Dead simple ASP.NET MVC 5 password protection? (and older posts such as link1 and link2), I set up an action filter in FilterConfig.cs:
GlobalFilters.Filters.Add(new BasicAuthenticationAttribute("myUsername", "myPassword"));
However, the result is an infinite login loop:
http://localhost:52200/account/login?ReturnUrl=%2Faccount%2Flogin%3FReturnUrl%3D%252Faccount%252Flogin%253FReturnUrl%253D%25252Faccount%25252Flogin%25253FReturnUrl%25253D%2525252Faccount%2525252Flogin%2525253FReturnUrl%2525253D%252525252Faccount%252525252Flogin%252525253FReturnUrl%252525253D%25252525252Faccount%25252525252Flogin%25252525253FReturnUrl%25252525253D%2525252525252Faccount%2525252525252Flogin%2525252525253FReturnUrl%2525252525253D%252525252525252Faccount%252525252525252Flogin%252525252525253FReturnUrl%252525252525253D%25252525252525252Faccount%25252525252525252Flogin%25252525252525253FReturnUrl%25252525252525253D%2525252525252525252F
The project has Anonymous Authentication Enabled and Windows Authentication Disabled. The BasicAuthentication filter is as follows:
using System;
using System.Web;
using System.Web.Mvc;
public class BasicAuthenticationAttribute : ActionFilterAttribute
{
public string BasicRealm { get; set; }
protected string Username { get; set; }
protected string Password { get; set; }
public BasicAuthenticationAttribute(string username, string password)
{
Username = username;
Password = password;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var req = filterContext.HttpContext.Request;
var auth = req.Headers["Authorization"];
if (!string.IsNullOrEmpty(auth))
{
var cred = System.Text.Encoding.ASCII.GetString(Convert.FromBase64String(auth.Substring(6))).Split(':');
var user = new { Name = cred[0], Pass = cred[1] };
if (user.Name == Username && user.Pass == Password)
{
return;
}
else
{
throw new HttpException(403, "Forbidden"); // For Elmah
}
}
var res = filterContext.HttpContext.Response; // The 4 lines below cause the login redirect
res.StatusCode = 401;
res.AddHeader("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", BasicRealm ?? "MyProject"));
res.End();
}
}
My understanding is that upon triggering the http 401, UseCookieAuthentication in Startup.Auth.cs redirects to login, which in turns calls BasicAuthentication, thus starting the loop until the browser throws an error. This is the default UseCookieAuthentication:
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, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
Many posts indicate that web.config needs to modified (Owin, formsAuthentication, etc.) I won't cite them here because there seems to be no commonly accepted answer.
Would a temporary password-protected index be wiser / preferable for beta testing?
Per OWIN Authentication with IIS Basic Authentication, the following line in Startup.Auth.cs must be deleted to avoid starting an infinite login loop:
LoginPath = new PathString("/Account/Login"),
It is the only way for Cookie Authentication and Basic Authentication to work simultaneously in MVC - no further setup required in IIS. Once a user has been authenticated via Basic Authentication, users who have logged in remain successfully logged in across the site.
How to use claims? For example, I want to set access to each page (resource) for each user. I understand, I can do it using roles, but as I understand, claim-based is more effectively. But when I try to create a claim, I see the following method:
userIdentity.AddClaim(new Claim(ClaimTypes.Role, "test role"));
first parameter of constructor of Claim class get ClaimTypes enum, which has many "strange" members like Email, Phone etc. I want to set that this claim and then check this claim to have access to certain resource. I'm on wrong way? How to do it?
From the code above, I am assuming you have already added the claim in startup class on authenticated of your provider as below.
context.Identity.AddClaim(new Claim("urn:google:name", context.Identity.FindFirstValue(ClaimTypes.Name))); // added claim for reading google name
context.Identity.AddClaim(new Claim("urn:google:email", context.Identity.FindFirstValue(ClaimTypes.Email))); // and email too
Once you have added the claims in startup, when the request is actually processed check if its a callback and if yes, read the claims as below(in IHttpHandler).
public void ProcessRequest(HttpContext context)
{
IAuthenticationManager authManager = context.GetOwinContext().Authentication;
if (string.IsNullOrEmpty(context.Request.QueryString[CallBackKey]))
{
string providerName = context.Request.QueryString["provider"] ?? "Google";//I have multiple providers so checking if its google
RedirectToProvider(context, authManager, providerName);
}
else
{
ExternalLoginCallback(context, authManager);
}
}
If its 1st call redirect to provider
private static void RedirectToProvider(HttpContext context, IAuthenticationManager authManager, string providerName)
{
var loginProviders = authManager.GetExternalAuthenticationTypes();
var LoginProvider = loginProviders.Single(x => x.Caption == providerName);
var properties = new AuthenticationProperties()
{
RedirectUri = String.Format("{0}&{1}=true", context.Request.Url, CallBackKey)
};
//string[] authTypes = { LoginProvider.AuthenticationType, DefaultAuthenticationTypes.ExternalCookie };
authManager.Challenge(properties, LoginProvider.AuthenticationType);
//without this it redirect to forms login page
context.Response.SuppressFormsAuthenticationRedirect = true;
}
And finally read the claims you get back
public void ExternalLoginCallback(HttpContext context, IAuthenticationManager authManager)
{
var loginInfo = authManager.GetExternalLoginInfo();
if (loginInfo == null)
{
throw new System.Security.SecurityException("Failed to login");
}
var LoginProvider = loginInfo.Login.LoginProvider;
var ExternalLoginConfirmation = loginInfo.DefaultUserName;
var externalIdentity = authManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
var emailClaim = externalIdentity.Result.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email);
var email = emailClaim.Value;
var pictureClaim = externalIdentity.Result.Claims.FirstOrDefault(c => c.Type.Equals("picture"));
var pictureUrl = pictureClaim.Value;
LogInByEmail(context, email, LoginProvider); //redirects to my method of adding claimed user as logged in, you will use yours.
}
Claim doesn't set permission. It's used to verify you that "you are who you claim to be you are". These claims are identified by issuer, usually a 3rd party. See for example this article for description.
So, you should define which claims are necessary (who user should be) in order to access a certain page. Otherwise, using claim-based authorization will be same as using identity based or role based.
I've spent a week now securing my Web API, creating custom filters and uses of authentication tokens. My problem now was when I'm requesting in my Web API using POSTMAN and the user was already sign out I can still get values from my API.
How can i manage to force expire my access token? Or is there other way to manage this kind of situation?
NOTE: When I'm requesting using POSTMAN, I copied my access token from the local storage.
Update:
This is what i followed in creating access token. http://www.asp.net/web-api/overview/security/individual-accounts-in-web-api
I tried the same situation as mine to the downloaded solution, still my access token is authenticated
You need to remove the cookies and the session if sign out is not doing that.
FormsAuthentication.SignOut();
Session.Abandon();
// clear authentication cookie
HttpCookie cookie1 = new HttpCookie(FormsAuthentication.FormsCookieName, "");
cookie1.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie1);
// clear session cookie (not necessary for your current problem but i would recommend you do it anyway)
HttpCookie cookie2 = new HttpCookie("ASP.NET_SessionId", "");
cookie2.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie2);
FormsAuthentication.RedirectToLoginPage();
Refrence Taken from here
As per http://www.asp.net/web-api/overview/security/individual-accounts-in-web-api
In this article all the details regarding authorization token is stored on session along with cookies. So you have two ways to sort out this.
Clear all the session and cookies on the logout.
You can also make a custom autorization filter and generate custom access token and store them to local file or database with the timeout limit. On logout you can clear tokens as per the user.
here is an example, how to set custom filters in web api 2.
public class CustomAuthenticateAttribute : Attribute, IAuthenticationFilter
{
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
HttpRequestMessage request = context.Request;
AuthenticationHeaderValue authorization = request.Headers.Authorization;
if (authorization == null)
return;
if (authorization.Scheme != "Bearer")
return;
if (String.IsNullOrEmpty(authorization.Parameter))
{
context.ErrorResult = new AuthenticationFailureResult("Missing token", request);
return;
}
TokenL1 tokenL1;
var validateToken = TokenHelper.DecryptToken(authorization.Parameter, out tokenL1);
if (!validateToken)
{
context.ErrorResult = new AuthenticationFailureResult("Token invalid", request);
return;
}
if (!(tokenL1.tokenexpiry > DateTime.Now))
{
context.ErrorResult = new AuthenticationFailureResult("Token expire", request);
return;
}
IPrincipal principal = new GenericPrincipal(new GenericIdentity(tokenL1.email), new string[] { "user" });
if (principal == null)
{
context.ErrorResult = new AuthenticationFailureResult("Invalid token", request);
return;
}
else
{
context.Principal = principal;
}
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
var challenge = new AuthenticationHeaderValue("Bearer");
context.Result = new AddChallengeOnUnauthorizedResult(challenge, context.Result);
return Task.FromResult(0);
}
public bool AllowMultiple
{
get { return false; }
}
}
use this custom filer on the actionresult of a controller like this
[CustomAuthenticate]
public ActionResult Index()
{
return View();
}