How to Logout of Owin Providers? - c#

I am following this tutorial yet it does not tell you how to logout. I tried to do
Request.GetOwinContext().Authentication.SignOut(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie);
Request.GetOwinContext().Authentication.SignOut()
Request.GetOwinContext().Authentication.SignOut(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie);
You can get the sample code here: https://github.com/AndersAbel/SocialLoginWithoutIdentity
Just need to add one more action
public ActionResult SignOut()
{
Request.GetOwinContext().Authentication.SignOut(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie);
return RedirectToAction("Index", "Home");
}
This method plus any one of the 3 lines of I posted above
My result right now is, I login, I go to secure page and can see it, I then proceed to my signout and then after signout try to go back to the secure page and I am allowed back to that secure page.
So it actually did not really sign me out.

As mentioned in the tutorial, the middleWare used use the default authentication type but don't override it.
By using only externalCookie as parameter for Owin you are clearing the cookie for Asp, but not the one used to store the Google provider,
to do so, you will have to get the array of all current cookies.
It can be done the easy way like this:
Request.GetOwinContext()
.Authentication
.SignOut(HttpContext.GetOwinContext()
.Authentication.GetAuthenticationTypes()
.Select(o => o.AuthenticationType).ToArray());
This is where it is said on the Tutorial:
The call to UseGoogleAuthentication should be quite obvious why it’s needed.
But the first one toSetDefaultSignInAsAuthenticationType is not as
obvious.
login middleware normally relies on the external cookie middleware
registered before the social login middleware.
external cookie middleware, it sets itself as the default signin type.
That’s how the social login middleware knows that it should use the
external cookie. In this setup there is no external cookie, so we have
to manually set the main cookie middleware as the default signin type.
The cookie middleware will only issue a cookie if the
AuthenticationType matches the one in the identity created by the
social login middleware.Looking at the owin external authentication pipeline a socialIn the setup of the

Try setting the cache control headers.
public ActionResult SignOut() {
var authenticationTypes = new string[] {
DefaultAuthenticationTypes.ApplicationCookie,
DefaultAuthenticationTypes.ExternalCookie
};
AuthenticationManager.SignOut(authenticationTypes);
// HACK: Prevent user from being able to go back to a logged in page once logged out
Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
// now redirect
return RedirectToAction("Index", "Home");
}
private IAuthenticationManager AuthenticationManager {
get {
return Request.GetOwinContext().Authentication;
}
}
There is no stopping the user clicking the back button on the browser, unless you try JavaScript, which can be disabled. The user can go back a page and view what was on the previous page, but if they try to click any protected links or refresh the page, they will be redirected to log in.

Use the [Authorize] attribute on classes which need authorization:
[Authorize]
public class MeController : ApiController
{
// GET api/<controller>
public IEnumerable<object> Get()
{
var identity = User.Identity as ClaimsIdentity;
return identity.Claims.Select(c => new
{
Type = c.Type,
Value = c.Value
});
}
}
source: http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server

Related

Is there a way to get the Auth Cookie data on Anonymous endpoints?

I have this case where I have an anonymous endpoint can work on it's own but if a user have already done authentication with the cookie I need to get from that authenticated cookie session the data about the authenticated user, but as I searched online I haven't found a solution to this and the User.Identity.IsAuthenticated is always false despite the user has the authenticated session cookie but on a Anonymous endpoint it's like being ignored so I can't figure out if the user data in the cookie.
Is there any way to accomplish this?
Example of the needed behaviour:
[HttpGet,AllowAnonymous]
public async Task<IActionResult> GetSomething()
{
if(User.Identity.IsAuthenticated){
//Get data from User.Claims and act upon it
} else {
//Get data without user logic
}
}
You still must use the Authorize attribute. The AllowAnonymous attribute serves to still allow access if the user isn't authorized.
[HttpGet,Authorize,AllowAnonymous]
public async Task<IActionResult> GetSomething()

Authorize Attribute always returning false ASP.net MVC Identity

When I decorate a method with an Authorize roles attribute it returns false everytime. I'm trying to limit access to an admin page for users in the "Admin" role only.
I have verified that the user im currently logged in as is in fact in the "Admin" role.
I have tried to use a custom authorization attribute. Same result. I can add the code if needed.
I have found that the authorization attribute works for Users but not for Roles.
I believe this problem is somehow tied into the fact that the following does not work in my application:
User.IsInRole("Admin").
However, this statement does work:
userManager.IsInRole(user.Id, "Admin")
Here is my code:
public class AdminController : Controller
{
//[AuthLog(Roles = "Admin")] //Custom authorization attribute
[Authorize(Roles = "Admin")]
public ActionResult Users()
{
return View();
}
}
Maybe this can help with debugging:
Microsoft.AspNet.Identity.Core: V.2.1.0
Microsoft.AspNet.Identity.EntityFramework: V.2.1.0
I am open to suggestions on anything else I can post from my project in order to debug easier. I have scoured the stack for 2 weeks now.
Update 1: How user is logged in
// POST: /account/login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(AccountLoginModel viewModel)
{
// Ensure we have a valid viewModel to work with
if (!ModelState.IsValid)
return View(viewModel);
// Verify if a user exists with the provided identity information
var user = await _manager.FindByEmailAsync(viewModel.Email);
// If a user was found
if (user != null)
{
// Then create an identity for it and sign it in
await SignInAsync(user, viewModel.RememberMe);
// If the user came from a specific page, redirect back to it
return RedirectToLocal(viewModel.ReturnUrl);
}
// No existing user was found that matched the given criteria
ModelState.AddModelError("", "Invalid username or password.");
// If we got this far, something failed, redisplay form
return View(viewModel);
}
private async Task SignInAsync(IdentityUser user, bool isPersistent)
{
// Clear any lingering authencation data
FormsAuthentication.SignOut();
// Create a claims based identity for the current user
var identity = await _manager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
// Write the authentication cookie
FormsAuthentication.SetAuthCookie(identity.Name, isPersistent);
}
The
FormsAuthentication.SetAuthCookie(identity.Name, isPersistent);
unfortunately doesn't store any roles with the identity. Thus, when the identity is recreated from the cookie, you have no roles. To verify try
this.User.IsInRole("Admin")
and you'll get false, even though the userManager tells you otherwise.
There are multiple workarounds.
You could for example switch to any other identity persistor, like the SessionAuthenticationModule which could store your username and roles in the cookie. You could follow my tutorial on that.
Another approach would be to have an explicit role manager and use its feature that automatically causes your roles to be stored in another cookie, separate from the forms authentication cookie. This involves configuring the role provider and writing your own role provider that would be an adapter over the user manager.
Finally, you could forget forms authentication and use Identity's native way of issuing cookies, which would involve calling SignInAsync on the authentication manager.

How to make ASP.NET create authenticated session with Owin OpenId Connect library?

After searching through lots of examples online I'm struggling with what seems to be a fairly simple requirement.
I'm trying to extend an existing ASP.NET Application that uses Form Authentication today so that it can use OpenID Connect for authentication as well as some role information coming from the Identity Provider. In particular I'm integrating with an existing hosted Identity Provider that I do not have control over.
I'm using ASP.NET MVC with the Owin components for OpenIdConnect. Namely,
Microsoft.Owin.Security
Microsoft.Owin.Security.Cookies
Microsoft.Owin.Security.OpenIdConnect
I am successfully able to:
In a web browser -- navigate to a controller method that is secured with the [Authorize] attribute
The Owin components properly redirect me to the Identity Provider where I can authenticate and then and I'm redirected back to my app (NOTE: my Identity Provider requires that a redirect_uri be passed in, so I'm currently setting that as part of the OpenIdConnectAuthenticationOptions startup configuration.)
When the redirect back to my app happens, I'm able to see the access_token and the id_token as part of the query string. Additionally, I've been able to use the access_token to call into the user info endpoint and properly derive information about the user using that token.
So far so good! HOWEVER.
What I'm failing to grasp and what most Owin examples I've seen don't seem to explain: what, if any, extra configuration is required to get ASP.NET to actually create an authenticated session in my application based on the redirect from the Identity Provider back to my application.
The general feeling I get from the documentation is that I should NOT have to do extra configuration within the Owin libraries -- that once I've configured the system to use cookie authentication and the OpenId Connect libraries -- that it should just work. However, this doesn't seem to be as easy as it looks. I'm guessing I'm missing something.
Some specific considerations/observations:
Many examples I've found don't require the RedirectUri to be set in the OpenIdConnectAuthenticationOptions, but my Identity Provider requires this parameter to be set each time.
Very few examples that I've found explain whether the controller method that fires as a result of the RedirectUri being hit should be secured with [Authorize] or left anonymous. In my testing, if I mark it as [Authorize] I get into an infinite redirect loop. If I leave it anonymous, I'm able to see the tokens in the request info but the ASP.NET Session is never created. For example, Request.IsAuthenticated is always false.
As a test I've set breakpoints inside several of the OpenIdConnectAuthenticationNotifications() events and currently I'm only seeing my code break into the RedirectToIdentityProvider event, and NONE of the others seem to hit -- which leads me to believe I'm not configuring this right.
Per suggestions I've found, I've set the authentication node this way in the web.config, but it doesn't seem to make a difference if I exclude this node.
<system.web>
<authentication mode="None" />
</system.web>
To summarize:
Do I need to specifically write code to handle the returning redirect from the Identity Provider to manually set up the ASP.NET Session (cookie etc.) for the given user? and
If so, should this code go in the controller method that is called as a result of RedirectUri being hit, or should the code go into one of the "Notifications" events available within OpenIdConnectAuthenticationNotifications()?
Lastly, if I'm NOT supposed to be setting up the Authenticated session manually after redirect from the Identity Provider (if it's supposed to work automatically), any suggestions for common mistakes on this configuration?
For completeness:
My Owin pipeline Startup Configuration method:
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
//no problems on these as far as I can tell
ClientId = "client_id_string",
ClientSecret = "client_secret_string",
Authority = "url_to_identity_provider",
Scope = "email name etc",
//I'm properly redirected to this URL but not sure
//if I should need to create the session manually
RedirectUri = "http://mymachine/mymvcapp/authorize",
//this causes the redirection to come with the access_token,
//which is valid
ResponseType = "token",
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications()
{
RedirectToIdentityProvider = (context) =>
{
//I'm able to break into this method
return Task.FromResult(0);
},
MessageReceived = (context) =>
{
//doesn't seem to run this line
return Task.FromResult(0);
},
SecurityTokenReceived = (context) =>
{
//doesn't seem to run this line
return Task.FromResult(0);
},
SecurityTokenValidated = (context) =>
{
//doesn't seem to run this line
return Task.FromResult(0);
},
AuthorizationCodeReceived = (context) =>
{
//doesn't seem to run this line
return Task.FromResult(0);
},
AuthenticationFailed = (context) =>
{
//doesn't seem to run this line
return Task.FromResult(0);
},
},
});
}
My secured method that properly initiates the login flow:
[Authorize]
public class HomeController : Controller
{
//I'm sent to the login flow the first time this is hit
public ActionResult Index()
{
return View();
}
}
My method at the RedirectUri that does get called but does indicate that the ASP.NET authenticated session was created:
public class AuthorizeController : Controller
{
// [Authorize] -- currently this Authorize attribute is turned off
//so the method is anonymous.
//If I turn that back on, I get infininte redirect loops to
//the Identity Provider
public ActionResult Index()
{
//the incoming request to this controller method from the
//identity provider DOES include valid access_token and id_token
//(which can be used against the user info endpoint) but does not
//create a valid ASP.NET session for my web app
//Request.IsAuthenticated is always false
//should there be a manual creation of the ASP.NET
//session/cookie information in this controller method?
//note: to me it would make most sense if this attribute was not
//anonymous since it's unlikely that the Request would ever appear
//as IsAuthenticated == true, but if you read the entire question
//it will be clear why I'm trying this method with anonymous access
return View();
}
}
As you found out, you can't put an [Authorize] attribute on the method the external server uses to notify you the user was authorized - the session isn't authorized yet, you're just being notified that it should be.
Fortunately, creating that session is not difficult:
How can I manually create a authentication cookie instead of the default method?
(I'm pretty sure you have to do this yourself with the basic Microsoft Owin stuff - and you always can do it yourself if you want.)

Check if user is logged in with Token Based Authentication in ASP.NET Core

I managed to implement this token based authentication system in my application, but I have a little question. How can I check if a user is signed it (eg if the there is a valid token in the request) within the method? So with the [Authorize] ?
So I have controller, and in that controller I want to check if the user is signed in. I thought of using this:
if (_signInManager.IsSignedIn(ClaimsPrincipal.Current))
{
...
}
but it does not work since ClaimsPrincipal.Current is always null
You don't need to use the SigninManager or something similar. The user is injected on the pipeline (on the User property of the base controller) and it's info is filled automatically by the authentication middleware (cookie or token). So, on your controller:
bool isAuthenticated = User.Identity.IsAuthenticated;
yes . put [Authorize] attribute above your class or methods to check if user is authenticate or not. You can get user by this code:
var principal = User as ClaimsPrincipal;
var check = User.Identity.IsAuthenticated;

ASP.NET Identity doesn't update Identity information on same request

I am working on a single page application using AngularJS and ASP.NET Identity 2. I log the user in and the cookie is set; however, when I check the Identity of the user on the same request, it shows it as blank and IsAuthenticated is false. However, these are populated on subsequent requests. I was hoping to send back to the UI whether or not the user was logged in on the same request. Is this possible?
Code as requested (AngularJS makes AJAX post into WebAPI controller Login method)
[HttpPost]
[AllowAnonymous]
[Route("Login")]
public async Task<IHttpActionResult> Login(LoginModel loginModel)
{
var result = await _securityService.Login(loginModel.UserName, loginModel.Password);
if (!result)
{
ModelState.AddModelError("errorMessage", "Invalid username or password.");
return BadRequest(ModelState);
}
return Ok();
}
public async Task<bool> Login(string userName, string password, bool persistCookie = false)
{
var user = await _userManager.FindAsync(userName, password);
if (user != null)
await SignInAsync(user, persistCookie);
else
return false;
return true;
}
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
_authenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
_authenticationManager.SignIn(new AuthenticationProperties() {IsPersistent = isPersistent}, await CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie));
}
public Task<ClaimsIdentity> CreateIdentity(ApplicationUser user, string authenticationType)
{
return _userManager.CreateIdentityAsync(user, authenticationType);
}
You won't get a signed in identity until the next request because the call to SignIn is what's causing a cookie to be set on the response. That cookie will turn into the identity on subsequent requests, but its too late to change your current request's identity.
When using Owin authentication, the AuthenticationManager.SignIn() method barely sends a message to the cookie handler to set a cookie when the cookie handler gets to handle the request after the Web API Controller (see my blog post Understanding the Owin External Authentication Pipeline for details).
But the Login method returns true if the login was successful and false if not, so you can use that information in the Login action to send back information. If you don't only want to know if the login succeeded or not, but also want the actual identity you can change Login() to return the user in case of successful login and null if failed.
I log the user in and the cookie is set; however, when I check the Identity of the user on the same request, it shows it as blank and IsAuthenticated is false.
This is just a lack of knowledge on your part about how the ASP.Net pipeline works.
There is a fairly large pipeline of events that occur. I'm pretty sure MVC runs in the ProcessRequest method. This method is after the AuthenticateRequest event and the PostAuthenticateRequest event. This means that the entire ASP.Net authentication framework can never be updated during the ProcessRequest method. This is why you'll see almost all system do a redirect afterwards, so that the next request has all the authentication (IIdentity, IPrincipal, IsAuthenticated, etc).
I was hoping to send back to the UI whether or not the user was logged in on the same request. Is this possible?
How could the code not be able to? The first request either authenticates them or not, whatever code is doing that knows if they are authenticated.
I was hoping to send back to the UI whether or not the user was logged in on the same request. Is this possible?
Yes. As said in other responses, you can.
I just want to cover the case when you are in the same request but outside the context where the SignIn took place.
Through Owin, you could use something like this extension method:
/// <summary>
/// Check if the user was authenticated in the current request, or in a previous one
/// </summary>
public static bool IsUserAuthenticated(this IOwinContext context)
{
if (context.Request.User.Identity.IsAuthenticated)
return true;
if (null != context.Authentication.AuthenticationResponseGrant && null != context.Authentication.AuthenticationResponseGrant.Identity)
{
return context.Authentication.AuthenticationResponseGrant.Identity.IsAuthenticated;
}
return false;
}

Categories