Found it because of #914.
Are you seeing this in 1.0.0 and not an older build like RC2?
@HaoK, correct I'm using 1.0.0.
I can give you access to the repository if you like.
Easy to reproduce both of the issues.
Let me know if you want.
@HaoK Any temporary workarounds?
You can always clear the external cookie explicitly via SignOut with a try catch around ExternalCallbackConfirmation
I have some trouble in my application on 1.0.0 and in fresh WebApplication template with oAuth.
I see in logs:
2016-07-27 21:44:41 [Information] AuthenticationScheme: "Identity.External" was successfully authenticated.
2016-07-27 21:44:41 [Information] AuthenticationScheme: "Identity.External" was forbidden.
2016-07-27 21:44:41 [Information] AuthenticationScheme: "Google" was forbidden.
and redirect to /Account/AccessDenied
I tried add await SignInManager.SignOutAsync(); to ExternalLogin methods but it not works.
@kroniak I have the same issue, one thing you might consider using is adding an AccessDenied Action on AccountController.
Inside of it you can use await SignInManager.SignOutAsync() and show the user a view with _Sorry pal, something went wrong, please try to login again. This time it will work, we almost promise._ :sweat_smile:
It's a nasty workaround though, I'm waiting for a decent fix too.
@gdoron In my situation await SignInManager.SignOutAsync() not works.
2016-07-27 22:07:06 [Information] AuthenticationScheme: "Identity.Application" signed out.
2016-07-27 22:07:06 [Information] AuthenticationScheme: "Identity.External" signed out.
2016-07-27 22:07:06 [Information] AuthenticationScheme: "Identity.TwoFactorUserId" signed out.
2016-07-27 22:07:23 [Information] Executing ChallengeResult with authentication schemes (["Ecwid"]).
2016-07-27 22:07:23 [Information] AuthenticationScheme: "Identity.External" was successfully authenticated.
2016-07-27 22:07:23 [Information] AuthenticationScheme: "Identity.External" was forbidden.
2016-07-27 22:07:23 [Information] AuthenticationScheme: "Ecwid" was forbidden.
2016-07-27 22:13:47 [Warning] Authorization failed for the request at filter '"Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter"'.
2016-07-27 22:13:47 [Information] Executing ChallengeResult with authentication schemes ([]).
2016-07-27 22:13:47 [Information] AuthenticationScheme: "Identity.Application" was challenged.
2016-07-27 22:13:48 [Information] Executing ChallengeResult with authentication schemes (["Google"]).
2016-07-27 22:13:48 [Information] AuthenticationScheme: "Identity.External" was successfully authenticated.
2016-07-27 22:13:48 [Information] AuthenticationScheme: "Identity.External" was forbidden.
After first time was happen there are no successful login with oauth.
I can delete Identity.External Cookie but is a bug.
Just to clarify the purpose of the external cookie, its there to store the identity/claims from another auth provider (i.e. google/facebook/OAuth). Identity will pick up and use that cookie mostly just to link an external login.
If your app ends up in a state where you got a 'bad' external cookie, there is no other recourse other than clearing it explicitly (or waiting for it to time out on its own).
That said, why do you think the external cookie is 'corrupted'?
@kroniak SignOutAsync should be deleting the external cookie
That said, why do you think the external cookie is 'corrupted'?
Because unless I clear it, ExternalLoginCallback isn't even being invoked and I'm redirected to AccessDenied.
That doesn't seem like an expected behavior, right?
cc @Tratcher this new behavior where the presence of the "external" cookie causing an immediate forbidden is a side effect of the new RemoteAuthHandler.Authenticate => SignInScheme delegation behavior change we made.
In the new world with this behavior, the presence of any external cookie, will prevent challenges from any other social auth provider, since they will result in Forbiddens as long as the cookie is there since its the 'SignInScheme"
@HaoK Cookie is not deleting in some sceneries. It is in my situation on base template:
if (User.Identity.IsAuthenticated)
{
await _signInManager.SignOutAsync();
}
Stop, how can we logout if we are not logged?? OK, add simple await _signInManager.SignOutAsync(); to ExternalLogin
Log:
Now listening on: http://localhost:5000
Authorization failed for user: .
Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'. Executing ChallengeResult with authentication schemes ().
AuthenticationScheme: Identity.Application was challenged.
Request starting HTTP/1.1 GET http://localhost:5000/Account/Login?ReturnUrl=%2F
Request starting HTTP/1.1 POST http://localhost:5000/Account/ExternalLogin?returnurl=%2F
AuthenticationScheme: Identity.Application signed out.
AuthenticationScheme: Identity.External signed out.
AuthenticationScheme: Identity.TwoFactorUserId signed out.
Executing ChallengeResult with authentication schemes (Google).
AuthenticationScheme: Google was challenged.
Request starting HTTP/1.1 GET http://localhost:5000/signin-google?state=CLnFuABFF-JfefQWd
AuthenticationScheme: Identity.External signed in.
Request starting HTTP/1.1 GET http://localhost:5000/Account/ExternalLoginCallback?ReturnUrl=%2F
AuthenticationScheme: Identity.External was successfully authenticated.
Request starting HTTP/1.1 POST http://localhost:5000/Account/ExternalLogin?returnurl=%2F application/x-www-form-urlencoded 198
AuthenticationScheme: Identity.Application signed out.
AuthenticationScheme: Identity.External signed out.
AuthenticationScheme: Identity.TwoFactorUserId signed out.
Executing ChallengeResult with authentication schemes (Google).
AuthenticationScheme: Identity.External was successfully authenticated.
AuthenticationScheme: Identity.External was forbidden.
AuthenticationScheme: Google was forbidden.
The Identity+OAuth login flow is a multi-step process that always assumes it's starting from scratch. This assumption causes issues when it gets to the external login step if the is already an external identity. The external identity is probably valid, the flow is just not set up to account for its presence.
There are at least three places in the login flow where an external identity could be correctly accounted for.
Option 1 is the cleanest, but 2 would also work for most scenarios. 3 has some complexities we'd rather not deal with if we can avoid them.
@Tratcher Yes, first option is clear.
How to delete Extrenal.Identity cookie before Challenge? await _signInManager.SignOutAsync(); does not do it.
To options 2: what happens if user decides to use other provider at the middle of a process? Option 1 wins.
await httpContext.Authentication.SignOutAsync("External");
@Tratcher I see in _signInManager.SignOutAsync()
await this.Context.Authentication.SignOutAsync(this.Options.Cookies.ExternalCookieAuthenticationScheme);
Is it not equv with await httpContext.Authentication.SignOutAsync("External"); ???
In logs I see
AuthenticationScheme: Identity.Application signed out.
AuthenticationScheme: Identity.External signed out.
AuthenticationScheme: Identity.TwoFactorUserId signed out.
The cookie was not deleted.
If I am using suggested method: await httpContext.Authentication.SignOutAsync("External"); throws exception Message "No authentication handler is configured to handle the scheme: External"
Those sign out calls are equivalent assuming you pass in the right AuthenticationScheme for the external cookie, is more stuff happening in the request after your call to SignOut?
@HaoK look at https://github.com/aspnet/Identity/issues/915#issuecomment-235760019 I wrote. Nothing changed.
You can reproduce this issue in my repo.
Why is there a call to
Executing ChallengeResult with authentication schemes (Google).
AuthenticationScheme: Google was challenged.
in your log after the signouts?
@HaoK because await _signInManager.SignOutAsync(); is at start of ExternalLogin method.
When user clicks on provider link I try to signout it and then challenge it.
@HaoK I can send a whole log.
You have to sign out, do a redirect to clear the cookie, and then start the login. You need to send a response that clears out the cookie, so you can't sign out/sign in in the same request
@HaoK Where should I make a redirect?
Redirect to an action that signs out and bounces back to external login
Why not implement option 1 above where you call signout when loading the Login page?
@Tratcher It is not right. If user get /login page is not eq user wants to logout.
It works by:
``` c#
[HttpGet]
[AllowAnonymous]
public async Task
{
await _signInManager.SignOutAsync();
_logger.LogInformation(4, "User logged out.");
return RedirectToAction("ExternalLogin", "Account", new { provider, returnUrl });
}
[HttpGet]
[AllowAnonymous]
public IActionResult ExternalLogin(string provider, string returnUrl = null)
{
// Request a redirect to the external login provider.
var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return Challenge(properties, provider);
}
```
@Tratcher
await httpContext.Authentication.SignOutAsync("External");
Do I replace External with Google etc?
@mikes-gh no, it needs to be the auth scheme from your CookieAuthenticationOptions that stores your short term login-cookie. If you're using Identity then it will be "External".
Hmm that doesn't work for me.
InvalidOperationException: No authentication handler is configured to handle the scheme: External
@Tratcher
If I use
await _signInManager.SignOutAsync();
In the Login [HttpGet] action it works but it doesn't cover the situation where the user clicks back.
ie in order for it to work the user would have to do ctrl F5 to get the action to execute.
Could we have more details on how to implement option 2 and 3.
I think this stuff should be baked into the standard templates.
I came up with this workaround (comments welcome)
It shouldn't be this hard though.
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ExternalLogin(string provider, string returnUrl = null)
{
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info?.LoginProvider != provider)
{
if (info != null)
{
await _signInManager.SignOutAsync();
// Redirect needed to logout
return RedirectToAction(nameof(AccountController.ExternalLoginRedirect), "Account", new { provider = provider, returnUrl = returnUrl });
}
else
{
var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return new ChallengeResult(provider, properties);
}
}
else
{
return RedirectToAction(nameof(AccountController.ExternalLoginCallback), "Account");
}
}
[HttpGet]
[AllowAnonymous]
public IActionResult ExternalLoginRedirect(string provider, string returnUrl = null)
{
var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return new ChallengeResult(provider, properties);
}
@Tratcher maybe we should add an option to disable the new behavior we added, something like RemoteAuthenticationOptions.AuthenticateUsingSignInScheme = false (true by default)
@mikes-gh
I think this stuff should be baked into the standard templates.
I think this stuff should not even be in a template, it simply needs to work, developer shouldn't care how identity and/or OAuth works, it should do all the heavy lifting by its own.
@HaoK Lets go over it offline. Who owns this template these days?
Not sure @phenning might know who the new PM owner is?
@HaoK
Don't understand why we need another option to make it work. Can't identity be a bit more intelligent here. Asking the developer to set an obscure option doesn't seem the right solution.
And regardless, people already scaffolded their application, changing the template won't help all the existing applications.
It's a huge deal, people can lose a lot of visitors because of this, I hope you are aware of this and plan to fix it in 1.0.1 (to be honest I thought you will publish a patch with a fix even before 1.0.1 because of the severity)
Just to add weight to @gdoron comment above.
I put my app out for internal testing.
The first thing that happened was this issue because the user pressed back( I was standing over the user).
The user then kept getting access denied and gave up.
Login flows get interrupted all the time.
This will lose visitors.
@mikes-gh I had exactly the same thing with a friend over the phone.
Thing is, it doesn't reproduce every time with the back button, but it happens quite frequently.
@mikes-gh I would be a bit simplified your code:
``` c#
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task
{
var info = await SignInManager.GetExternalLoginInfoAsync();
if (info?.LoginProvider == provider)
return RedirectToAction(nameof(ExternalLoginCallback), "Account");
if (info != null)
await SignInManager.SignOutAsync();
// Redirect needed to logout
return RedirectToAction(nameof(ExternalLoginRedirect), "Account", new { provider, returnUrl });
}
[HttpGet]
[AllowAnonymous]
public IActionResult ExternalLoginRedirect(string provider, string returnUrl = null)
{
var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
var properties = SignInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return new ChallengeResult(provider, properties);
}
```
Here's my changes to the template for the different options:
Option 1: Reset the state at the start of the login flow.
// GET: /Account/Login
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Login(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info != null)
{
await _signInManager.SignOutAsync();
}
return View();
}
The above works for the Login link, but didn't work in some of my [Authorize] tests because LoginProviderKey is missing from the auth properties collection so GetExternalLoginInfoAsync returns null. Needs investigation.
The following works in both scenarios:
// GET: /Account/Login
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Login(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
var externaluser = await HttpContext.Authentication.AuthenticateAsync("Identity.External");
if (externaluser != null)
{
await HttpContext.Authentication.SignOutAsync("Identity.External");
}
return View();
}
Option 2: Detect if the external identity is already present and resume the login flow from that point.
// GET: /Account/Login
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Login(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info != null)
{
return RedirectToAction("ExternalLoginCallback");
}
return View();
}
Option 3: Check for the external identity when the users selects an external provider from the login page:
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ExternalLogin(string provider, string returnUrl = null)
{
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info != null)
{
return RedirectToAction("ExternalLoginCallback");
}
// Request a redirect to the external login provider.
var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return Challenge(properties, provider);
}
@Tratcher
Option1. What if the user happened to be on the login page? Log out? I think not.
Option 2. What if the user is in the middle of the process, he changed his mind and decided to login through a login password?
I think @mikes-gh workaround is more logicaly.
@kroniak I don't follow your question about option 1. If the user goes to the login page then they want to log in, no? The link won't be present if they are already logged in.
@Tratcher I think that user wants to log in when it press login button. User can follow /login page by bookmarks or by mistake and then It will change mind and clicks back and see login page again.
Either way, if they end up on the login page it seems reasonable to do the login flow.
That said, some version of option 3 is the most graceful.
@HaoK, @sayedihashimi is the new PM owner for templates.
@Tratcher
option 1 and 2 are not valid as the user may arrive at a cached login page since it is an HttpGet action e.g. back button.
As I said earlier in this thread I tried that and it does not work unless the user refreshes the page which is not going to happen in the real world.
option 3 you don't account for the Identity.External cookie being from a different provider.
The ExternalLogin action is a post and will always run.
None of this stuff should be at the user level.
Please mark this as a bug.
The framework needs to deal with this transparently.
@phenning
Please don't try to fix this in the templates.
The bug should be fixed in the framework.
@kroniak
I had the code that way before.
But it adds an unnecessary redirect to the most common scenario when there is no Identity.External cookie present
Hi folks, we had a meeting about this and we think the best fix is the template fix because that's where the underlying bug is. The problem is in the template's authentication flow with OAuth providers and about what happens when the flow stops (or has a problem) part way through the process.
Making a change in the framework itself would almost certainly regress other behaviors such as the protection against infinite redirects in case of certain failed/partial authentication scenarios.
As such, I am closing this bug here and we'll make the template change as part of https://github.com/aspnet/Templates/issues/660.
Anyone encountering this issue can use option (3) described at https://github.com/aspnet/Identity/issues/915#issuecomment-238939350 to implement the fix themselves within their app.
@Eilon, If it requires manual work from the developers, I think you should publish an announcement, that's a bug all developers should be aware of.
@gdoron good point, I'll get an announcement posted!
FYI Here's the proposed fix: https://github.com/aspnet/Templates/pull/662
@Tratcher I'm not sure how it fixes the most common scenario where there is no Login page and people don't go through the Login action.
https://github.com/aspnet/Templates/pull/662#issuecomment-240206032
BTW, it would have been nice if the proposed fix was discussed with the community before it was a done deal and merged, not after...
To be honest I'm a bit disappointed with the fix and the process.
Well... c'est la vie
I do appreciate everyone's work on this but I'm feeling a bit short changed.
I put a fair bit of effort in on this issue and it feels like community input has been ignored.
My scenario is not edge case. It is real world findings from real users.
In order to properly address this issue the fix need to happen in the ExternalLogin POST as discussed at length with reason why.
@mikes-gh Since they decided the fix should be in the templates, you can simply ignore the fix, and change your code as you wish.
It's bad for the community that the templates might have severe issues, but it doesn't affect you if you know the flaws.
@gdoron
Thanks. Yes I am sticking with my fix. (Hopefully a better one will make it into 1.1.0).
As you say its more about saving other developers hours of head scratching in the future.
@mikes-gh when we tested this we found it took deliberate effort to get to a cached version of the login page. The most obvious way was to click back, but you'd have to do it multiple times with the right timing to get past the external provider redirects.
When reviewing option 3 we found it added complexity and opened additional security concerns around anti-forgery.
In the end we opted for a simple solution that addressed the most reproducible scenarios. We also plan to address https://github.com/aspnet/Security/issues/859 which should prevent this issue when switching providers.
If this continues to be a common problem for users we'll see what else can be done about it.
@Tratcher well I still don't know what should be the fix in my scenario where the external login isn't issues from the Login action.
Do I need to send an ajax request what have this inside: await HttpContext.Authentication.SignOutAsync(_externalCookieScheme);
?
If this continues to be a common problem for users we'll see what else can be done about it.
Users not being able to login and practically leaving the website and never return is severe enough IMHO, that fix should fix common and uncommon scenarios alike.
@gdoron it would help if you shared a sample app showing your usage scenario. It's unclear how significantly it diverges from the template.
@Tratcher It doesn't diverge too much.
Basically we took the login View template, added some CSS and stuff and backed it in a partial view, and it's loaded in the layout.cshtml
@model LoginViewModel
<section class="popupModal" id="login">
<div class="container-full login">
<form asp-controller="Account" asp-action="ExternalLogin" asp-route-returnurl="@(ViewData["ReturnUrl"] ?? Context.Request.Path)" method="post">
<a href="" class="close-popup">
<img src="/images/close.png" alt="">
</a>
<h2 class="form-signin-heading">login to companyName</h2>
<ul>
<li>
<button type="submit" value="Facebook" name="provider">
<img src="/images/Facebook.png" alt="Facebook">
</button>
</li>
<li>
<button type="submit" value="Google" name="provider">
<img src="/images/Google.png" alt="Google">
</button>
</li>
<li>
<button type="submit" value="LinkedIn" name="provider">
<img src="/images/Linkedin.png" alt="LinkedIn">
</button>
</li>
</ul>
<fieldset>
<legend>or</legend>
</fieldset>
</form>
<form class="form-signin text-center" asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]">
<input type="email" asp-for="Email" id="inputEmail" class="form-control" placeholder="Email" required>
<input asp-for="Password" id="inputPassword" class="form-control" placeholder="Password" required>
<button class="btn btn-lg btn-success btn-block" type="submit">login</button>
<div class="checkbox pull-left">
<label>
<input type="checkbox" asp-for="RememberMe"> Remember me
</label>
</div>
<div class="checkbox pull-right">
<a asp-controller="Account" asp-action="ForgotPassword">Forgot Password</a>
</div>
</form>
<div class="login-footer">
<div>
Not a member yet? <a href="#" class="register-now">Register now</a> - it's fun and easy!
</div>
</div>
</div>
</section>
The server template code wasn't changed too much (we don't use the ExternalLoginConfirmation to allow users pick their email/username after the oauth but create the user inside the ExternalLoginCallback)
I'm adding you as a collaborator to the real full private repository in case you want to see it exactly.
Just please be careful with it, as currently since we're still in beta, our DB and server passwords are saved inside the repository, we'll fix it someday soon...
Almost forgot, I really appreciate the effort @Tratcher!
Thanks,
Doron
BTW when I said people can't login is a huge deal, it's a huge deal for every website, losing users in the most important phase of the registration.
But it's even more important for us, we are building a revolutional website for people with disabilities, some of our users have ALS and can only move their eyeballs... We really don't want to have any technical issues.
If these users will have technical issues, they won't call us, they simply can't :crying_cat_face:
That's a curious pattern you have there. It looks like you render your login section and register section into every page. This is half-way to a SPA app. I don't see a fool-proof option for you. You're already using Option 3 as described above, and that should be adequate unless the user wants to switch providers part way through login. You could facilitate provider switching by adding a "Start Over / Switch Provider" button to your ExternalLoginCallback view that pointed to the LogOff endpoint.
Thanks for taking a look Chris.
The "Start Over / Switch Provider" button seems like a nice workaround in theory, but in practice, no one will understand what it means.
The whole benefit of social logins is that they are commonly used, and I have never seen a website with such a button, so no one is going to click on it.
If I'll stop render the login/register on every page and just load it on demand as a partial view, and in the action that renders these views I'll signout the user.
Will that be a fool proof solution?
This is what Mike and I said about the problem with the suggested fix, it doesn't fix existing applications which might have a bit different flow (except the cache page issue that Mike was concerned about).
Thanks Chris!
Yes, that would be in line with what we now do in the templates.
@Tratcher
I only need to click back once. If I use Facebook button and at the registration page decide I want to register with Google. I click back and get this issue.
This is if I am already logged in to the provider (common scenario).
@mikes-gh Try adding [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)] to the action that renders the view.
Do you still get a cached copy?
(I still think the templating option isn't ideal even if it works since it's very easy to change your application in a way that the fix won't be used.)
@mikes-gh Did it help?
@gdoron
Sorry haven't tried it yet because I have a working solution. ( I do appreciate the suggestion). Will update here when I have findings
I am pretty sure this issue has been biting me for months and no one could figure it out. Thanks for putting up the announcement. The template fix is a good start.
Does anyone know how to fix this template from Auth0?
https://github.com/auth0-samples/auth0-aspnetcore-sample/tree/master/01-Login
I've had a ticket open with auth0 for over 20 days (not realizing at the time that the underlying problem was likely caused by this) and even pointed them to the announcement and also posted an issue on their repo, but they still haven't been able to provide a fix.
I tried a couple of things on my own to try to get the cookie cleared, but I just couldn't hit the right combination.
I would like to make a motion to strike my comment from the record. After more research, I believe my issue with Auth0 (still unresolved after more than a month) is specific to Auth0 and has nothing to do with any of Microsoft's code
Is the final suggested solution to this problem "removing the cookie"?
Please checkout my project react-aspnet-boilerplate. Without going into great detail, I pretty much have to have the ability for me to leave the cookie as is. Is there no other solution?
I might have to create my own encrypted cookie to store the external login info so that I can retrieve across different requests.
I absolutely agree with @gdoron here. I have built my entire boilerplate (here)(https://github.com/pauldotknopf/react-aspnet-boilerplate) around this feature behaving as it did in RC2, and now it is completely broken. Sure, you fixed it in the templates, but my workflow is completely different for the external logins and your fix in the templates doesn't translate to my boilerplate.
This is incredibly frustrating. Where is the source of the problem? I will fork and fix it.
@Tratcher @Eilon
Though it was discussed in length already, I just want to add one more thing.
We now changed our homepage to have two big icons of Google and FB for non registered users.
The template fix won't help here, and obviously I can't delete the auth cookie every time some goes to the homepage...
I used @mikes-gh 's code, so I'm sort of safe, but other people which surely 99.99% of them didn't track this thread are not safe.
Our homepage in case someone wants to better understand what I'm talking about:
https://yooocan.com
The thing is, the template fix is good as long as you use the exact same flow, pages and code for login/register as in the template.
If you diverge even the slightest, it's completely useless.
@gdoron I still believe this is an issue, but I have a workaround that will fix your problem. Instead if redirecting (to flush cookie) before they get to the external login redirect, do the flushing right at the external login redirect.
For example:
``` c#
[Route("externalloginredirect")]
public async Task
{
// when we first visit this redirect page, we need delete any previous authentication tokens.
// See https://github.com/aspnet/Security/issues/299
if (!didRefresh)
{
// redirec the user to this same exact page, but clear out any external login cookie that may be there.
// initial requests to this page should not have the "didRefresh" set.
await HttpContext.Authentication.SignOutAsync(_externalCookieScheme);
var queryString = new QueryString(Request.QueryString.ToString());
return Redirect(Request.Path + queryString.Add("didRefresh", "true"));
}
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, "/externallogincallback?autoLogin=" + autoLogin);
return new ChallengeResult(provider, properties);
}
```
@pauldotknopf Looks good to me.
BTW, what is autoLogin?
The autoLogin is something specific to my workflow. External authentications don't always trigger logins. Checkout the boilerplate I mentioned for more details.
I just installed Visual Studio 2017 and ran the authentication Template. PROBLEM NOT SOLVED - If you do not follow the assumed sequence of operations when registering you very quickly ends up with Account/AccessDenied.
This is totally unacceptable. Being able to always log in should be the most basic requirement of a web site!...
PLEASE HELP! DO ANYONE HAVE A SOLUTION TO THE Account/AccessDenied PROBLEM?
I am having a hard time understanding if any of the previous comments above gives a good solution.
Thanks @pauldotknopf
I'm using parts of your code now. Btw I remove cookie in ExternalLoginCallback - after user logins via external service I read all auth data into an ExternalLoginInfo and delete cookie. And it works pretty fine for me.
if (HttpContext.Request.Cookies["Identity.External"] != null)
{
HttpContext.Authentication.SignOutAsync("Identity.External").Wait();
}
UPD1: Not working - if I approach, that I described - I can't post externalLoginConfirmation
Most helpful comment
@Tratcher
option 1 and 2 are not valid as the user may arrive at a cached login page since it is an HttpGet action e.g. back button.
As I said earlier in this thread I tried that and it does not work unless the user refreshes the page which is not going to happen in the real world.
option 3 you don't account for the Identity.External cookie being from a different provider.
The ExternalLogin action is a post and will always run.
None of this stuff should be at the user level.
Please mark this as a bug.
The framework needs to deal with this transparently.