How to overwrite post logout redirect url - asp.net-core

I am using IdentityServer3 and i have ASP.NET Core as Client application.
Here is my LoggOff action method
[HttpPost]
public async Task LogOff()
{
await Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.SignOutAsync(HttpContext, CookieAuthenticationDefaults.AuthenticationScheme);
await Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.SignOutAsync(HttpContext, OpenIdConnectDefaults.AuthenticationScheme);
}
When user logs out i see the following redirects in fiddler
GET /identity/connect/endsession?post_logout_redirect_uri=https%3A%2F%2Flocalhost%3A44352%2Fsignout-callback-oidc&state=XXXXXX&x-client-SKU=XXXXXX&x-client-ver=5.3.0.0 HTTP/1.1
GET /identity/logout?id=XXXXXXXXXX
GET /identity/connect/endsessioncallback?sid=XXXXXXX
and eventually in browser url is set to /identity/logout?id=XXXXXXXXXX. These are identity server's URL not Client Application URL.
This is working as expected when logoff button invokes LogOff action method.
Now i have a requirement. When user goes to AccessDenied page i want to logoff user first,and then redirect to AccessDenied view. The AccessDenied page is in ClientAppliction. So i have another action method that invokes SingnOut and set RedirectUri
[HttpGet]
public async Task AccessDenied()
{
await Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.SignOutAsync(HttpContext, CookieAuthenticationDefaults.AuthenticationScheme);
await Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.SignOutAsync(HttpContext,
OpenIdConnectDefaults.AuthenticationScheme,
new Microsoft.AspNetCore.Authentication.AuthenticationProperties()
{
RedirectUri = "Account/AccessDenied"
});
}
This is not working. User still goes to identity/logout instead of AccessDenied. Looks like it is not setting post logout redirect uri.

Its not a typo that you forgot to prefix the url with / ?
like
RedirectUri = "/Account/AccessDenied"
instead of
RedirectUri = "Account/AccessDenied"

Related

Net Core - Cookie SameSite

I found the problem in my project. The problem is that the cookie is not saved when the user is logged in via the iframe. Cookies are saved when you log in to the site normally, that is, directly through the domain. There are 2 different problems for me right now:
When logging into the site directly (www.example.com), the relevant cookie is saved. But this cookie is saved as Lax. I want it to be saved as none.
If there is no direct login to the site before, if you want to log in from within the iframe, the cookie is not saved. I want to create cookies on logins made via iframe.
I did not perform anything on the cookie. I guess SignAsync() creates the relevant cookies automatically. I am also attaching my codes inside the Account Controller.
public IActionResult Login(string returnurl)
{
return View(new LoginViewModel {ReturnUrl=returnurl, IsPersistent=true });
}
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model)
{
var result = await signInManager.PasswordSignInAsync(model.Username, model.Password, model.IsPersistent, true);
if (result.Succeeded)
{
return Redirect(model.ReturnUrl ?? "/");
}
else
{
ModelState.AddModelError("","Invalid User Login");
return View(model);
}
}
I also tried the solutions in the links below but it didn't work for me. Maybe I have applied these solutions incorrectly.
https://learn.microsoft.com/tr-tr/aspnet/core/security/samesite/rp31?view=aspnetcore-3.1&viewFallbackFrom=aspnetcore-6.0
https://learn.microsoft.com/tr-tr/aspnet/core/security/samesite?view=aspnetcore-6.0
And here is a photo:
Cookie View with Cookie Editor

How to provide own login page if windows authentication get failed?

Currently i am working on one POC with Identity server4 where i have to show my own login page if windows authentication get failed(in this case i just want to show my own login page and avoid browser login popup .
My question is where to inject my own login page in code? and how application will know windows authentication get failed?If you check below code, first request to AuthenticateAsync always return null and then it call Challenge from else block which ask browser to send Kerberos token
and we achieve SSO but now i want to show my own login page if SSO fail.
My scenario is exactly similar like this
Anyone know how to achieve this?
private async Task<IActionResult> ProcessWindowsLoginAsync(string returnUrl)
{
// see if windows auth has already been requested and succeeded.
var result = await HttpContext.AuthenticateAsync(_windowsAuthConfig.WindowsAuthenticationProviderName);
if (result?.Principal is WindowsPrincipal wp)
{
var props = new AuthenticationProperties
{
RedirectUri = Url.Action("Callback"),
Items =
{
{ "returnUrl", returnUrl},
{ "scheme", _windowsAuthConfig.WindowsAuthenticationProviderName}
}
};
var id = new ClaimsIdentity(_windowsAuthConfig.WindowsAuthenticationProviderName);
var claims = await _userStore.GetClaimsForWindowsLoginAsync(wp);
id.AddClaims(claims);
_logger.LogDebug("Signing in user with windows authentication.");
await HttpContext.SignInAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme,new ClaimsPrincipal(id),props);
return Redirect(props.RedirectUri);
}
else
{
_logger.LogDebug("Re-triggered windows authentication using ChallengeResult.");
// Trigger windows auth
// since windows auth don't support the redirect uri,
// this URL is re-triggered when we call challenge
return Challenge(_windowsAuthConfig.WindowsAuthenticationSchemes);
}
}

Not able to SignOut using Saml2 from Sustainsys

This should be redirecting my app to my AdFs signOut Page, and then redirect me back to my app.
However, it simply redirects me to my route "/logout".
Watching the log on my ADFS server nothing happens.
[AllowAnonymous]
[HttpGet]
[Route("api/logout")]
public async Task<IActionResult> Logout()
{
return SignOut(new AuthenticationProperties()
{
RedirectUri = "/logout"
},
Saml2Defaults.Scheme);
}
SignIn works fine. I even tried this same approach, but does not work. Here, the ReturnUrl method gets the location from HttpContext.Response.Header. When I try this for the logout, the location is always null.
[AllowAnonymous]
[HttpGet]
[Route("api/login")]
public async Task<string> LoginAdfs()
{
string redirectUri = _appSettings.Saml.SpEntityId;
await HttpContext.ChallengeAsync(new AuthenticationProperties
{
RedirectUri = string.Concat(redirectUri, "/autenticado")
});
return ReturnUrl();
}
Any idea what could be happening?
UPDATE 21/11/2019
Turns out the Saml2Handler is simply not trying to send the request to the server. I'm getting these messages on my output window:
Sustainsys.Saml2.AspNetCore2.Saml2Handler: Debug: Initiating logout, checking requirements for federated logout
Issuer of LogoutNameIdentifier claim (should be Idp entity id):
Issuer is a known Idp: False
Session index claim (should have a value):
Idp has SingleLogoutServiceUrl:
There is a signingCertificate in SPOptions: True
Idp configured to DisableOutboundLogoutRequests (should be false):
Sustainsys.Saml2.AspNetCore2.Saml2Handler: Information: Federated logout not possible, redirecting to post-logout
Here is my StartUp Configuration, I don't get what is wrong here:
ServiceCertificate se = new ServiceCertificate()
{
Certificate = new X509Certificate2(SpCert, "",X509KeyStorageFlags.MachineKeySet),
Use = CertificateUse.Signing
};
SPOptions sp = new SPOptions
{
AuthenticateRequestSigningBehavior = SigningBehavior.Never,
EntityId = new EntityId(SpEntityId),
ReturnUrl = new Uri("/login"),
NameIdPolicy = new Sustainsys.Saml2.Saml2P.Saml2NameIdPolicy(null, Sustainsys.Saml2.Saml2P.NameIdFormat.Unspecified),
};
sp.ServiceCertificates.Add(se);
IdentityProvider idp = new IdentityProvider(new EntityId(appSettings.Saml.EntityId), sp);
idp.Binding = Saml2BindingType.HttpPost;
idp.AllowUnsolicitedAuthnResponse = true;
//idp.WantAuthnRequestsSigned = true;
idp.SingleSignOnServiceUrl = new Uri("/login");
//idp.LoadMetadata = true;
idp.SigningKeys.AddConfiguredKey(new X509Certificate2(IdpCert));
idp.MetadataLocation = theMetadata;
idp.DisableOutboundLogoutRequests = true;
For the logout to work, two special claims "LogoutNameIdentifier" and "SessionIndex" (full names are http://Sustainsys.se/Saml2/LogoutNameIdentifier and http://Sustainsys.se/Saml2/SessionIndex need to be present on the user. Those carries information about the current session that the Saml2 library needs to be able to do a logout.
Now I don't see your entire Startup, so I cannot understand your application's flow. But those claims should be present in the identity returned by the library - possibly stored in an External cookie (if you are using asp.net identity). When your application then sets the application cookie those two claims must be carried over to the session identity.
Also you have actually disabled outbound logout with DisableOutboundLogoutRequests. But that's not the main problem here as your logs indicates that the required claims are not present.
From my own experience, the two claims, as mentioned by Anders Abel, should be present on the user. I had not seen these claims until I passed all of the claims along with the sign-in request. ASP.NET Core recreates the principal on SignInAsync and needs claims to be passed in with the request.
With the following, I am able to fulfill a SingleLogout with my service:
await HttpContext.SignInAsync(user.SubjectId, user.Username, props, user.Claims.ToArray());
what you are using as a service provider.

Yahoo OAuth returning error using WebAuthenticationBroker

I was trying to login using yahoo from my UWP app.
StartUri is https://api.login.yahoo.com/oauth2/request_auth?response_type=code&scope=openid&client_id=dj0yJmk9TDNtd2MxeGNMT1pUJmQ9WVdrOVQwVlNVbFpQTkdjbWNHbzlNQS0tJnM9Y29uc3VtZXJzZWNyZXQmeD05Mw&redirect_uri=http://localhost:8080
EndUri is http://localhost:8080/
WebAuthenticationResult WebAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
WebAuthenticationOptions.None,
StartUri,
EndUri);
It is properly showing the sign in
but after sign in it shows error page
if we press close it will direct me to yahoo home page instead of asking user consent. anyone having idea why this happen?
There are two problems in your authorization URL.
Firstly, the client_id in your URL is not right. Usually, client_id is end up with --, using the client_id in Authorization Code Flow for Server-side App for example, it is
dj0yJmk9ak5IZ2x5WmNsaHp6JmQ9WVdrOVNqQkJUMnRYTjJrbWNHbzlNQS0tJnM9Y29uc3VtZXJzZWNyZXQmeD1hYQ--
So I think your client_id in wrong.
The second problem is your redirect_uri, the redirect_uri should match the Callback Domain you've set in your app.
Please specify the domain to which your application will be returning after successfully authenticating. Yahoo OAuth flow will redirect users to a URL only on this domain (or its sub-domain) after they authorize access to their private data.
So redirect_uri need to be a domain and http://localhost:8080 don't meet this requirement. In my test I just used localhost.com for example:
public async Task<string> AuthorizeWithYahoo()
{
var clientId = "<My client id>";
var StartUri = new Uri($"https://api.login.yahoo.com/oauth2/request_auth?client_id={clientId}&response_type=code&redirect_uri=http://localhost.com");
var EndUri = new Uri("http://localhost.com");
WebAuthenticationResult WebAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None,
StartUri, EndUri);
if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
{
var responseData = WebAuthenticationResult.ResponseData;
return responseData;
}
else if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
{
return $"HTTP Error returned by AuthenticateAsync() : {WebAuthenticationResult.ResponseErrorDetail.ToString()}";
}
else
{
return $"Error returned by AuthenticateAsync() : {WebAuthenticationResult.ResponseStatus.ToString()}";
}
}
And after sign in, you will see something like:
The second problem is your redirect_uri, the redirect_uri should match the Callback Domain you've set in your app.
The Redirect URL, can set in my localhost to developers on Visual Studio??

Ember + web api single page redirect

I have a asp.net mvc web api app with ember and simplemembershipprovider. I am using the ember template and with it, ember app is created upon user successfully logged in in the home controller.
public ActionResult Index(string returnUrl)
{
if (User.Identity.IsAuthenticated)
{
return View("App");
}
ViewBag.ReturnUrl = returnUrl;
return View();
}
Sometimes user would click a link in an email with an id when visiting the site, if the url includes an id, upon successful login, I want to redirect user to a detail page base on the provided id in the url. An example would be http://siteURL.com/#/product/1412 . I am having a hard time figuring out how to do this. Since this is a client side ember route, MVC does not differentiate between this route and http://siteURL.com so it just ignores the redirect request. Here is what I have tried.
assign the url in the login controller - nothing happens after json data is returned, stays in the login page and never hit the HomeController even though user is not authenticated.
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
returnUrl = "http://siteURL.com/#/product/1412";
return Json(new { success = true, redirect = returnUrl });
use response redirect. Same as #1
Response.Redirect(returnUrl);
Assigned url in home controller, same as above.
if (User.Identity.IsAuthenticated)
{
returnUrl = "http://siteURL.com/#/product/1412";
return View("App");
}
ViewBag.ReturnUrl = returnUrl;
return View();
Most browsers don't even send the # up to the server, so you won't have it to redirect. Here's a few options
Don't use the hash, not every browser supports it, http://emberjs.com/guides/routing/specifying-the-location-api/
Give them a fake address that redirects, http://siteURL.com/Redirect/product/1412
inject that url into some js on the page that redirects on load