I have a MVC app that has a couple of WebAPI endpoints. The reason for this is we have an app that should be able to communicate with the application. For authentication, I am using Identity.
After I logged in and want to log out again, it works in the UI. So I implemented the same logic in the Logout actions in one of my API endpoints:
public async Task<JsonResult> LogOut()
{
await _signInManager.SignOutAsync();
_logger.LogInformation("User logged out.");
return new JsonResult(new { Anything = "Logout successful." });
}
I also tried most of the approaches I found here when I search for the same question. However, no matter what I do, the cookie ".AspNetCore.Identity.Application" is always in the next request and I am still authenticated.
I am using Postman to test the API endpoints, if that makes any difference.
Right now you only have cookie authentication and when you call SignOutAsync() a response is being generated which indicates to delete the authentication cookie and in response to that browser deletes the authentication cookie so in the next call there is no cookie and you are not logged in anymore but if you store the authentication cookie before sign out and then add it to the browser after you sign out you are still logged in because your credential is in the cookie.
So this is how browser behaves, and you don't have this behaviour in postman or HttpClient.
Your options are to either use Reference Token for your apis
or
You can config a Session Store with your Cookie Authentication
Related
Our team is using Itfoxtec as the saml2 handler in our SP as follows:
A client clicks on the link of the login API.
The API redirects the user to the IdP login page.
On successful login, The API gets a SAML2 response to the ASC route.
We fetch the claims from the response.
If the user is good to go, we generate a JWT token to be used in next requests to other services using the Authorization header, otherwise we send unauthorized response.
Here is the configuration of the handler:
builder.Services
.AddAuthentication("saml2")
.AddCookie("saml2", cookieAuthenticationOptions =>
{
cookieAuthenticationOptions.SlidingExpiration = true;
cookieAuthenticationOptions.LoginPath = new PathString("/saml/request");
cookieAuthenticationOptions.Cookie.SameSite = SameSiteMode.None;
cookieAuthenticationOptions.Cookie.SecurePolicy = CookieSecurePolicy.Always;
Task UnAuthorizedResponse(RedirectContext<CookieAuthenticationOptions> context) =>
Task.FromResult(context.Response.StatusCode = (int)HttpStatusCode.Unauthorized);
cookieAuthenticationOptions.Events.OnRedirectToAccessDenied = UnAuthorizedResponse;
cookieAuthenticationOptions.Events.OnRedirectToLogin = UnAuthorizedResponse;
});
As we can see that the use of Itfoxtec is done in the initial login process only, where the user either gets unauthorized response, or gets JWT token and no additional calls to the SP is done.
Note that when I remove the Cookie related configurations, I get an exception thrown while fetching the claims and creating a session on the following line:
customClaims = await saml2AuthnResponse.CreateSession(HttpContext, claimsTransform: GetCustomClaimsPrincipal);
Our question is, is there any way else to use the Itfoxtec as a SAML2 handler but without using Cookies?
You do not need to use session cookies. If you do not use session cookies you should not call the CreateSession method which create the cookie based .NET session.
The SAML 2.0 Authn request is validated in the Unbind method and you can thereafter safely read the users claims in saml2AuthnResponse.ClaimsIdentity.Claims.
FoxIDs use the ITfoxtec Identity SAML 2.0 component without using the cookie based .NET session, you can see how the SAML 2.0 Authn request is validated in the AuthnResponseAsync method.
Edit:
Here is my question reformulated:
I have a web server with secured api endpoints - one must have been authenticated with Google prior to using them. I implemented Challenge and Callback endpoints for that.
This works well from a browser with my SPA web front-end. The user gets redirected to the Google website to sign-in and then gets redirected back to my webapp; the browser then has the authenticated cookies and the webapp can use the endpoints to update its state.
I also have a WPF application that will communicate with the web server.
I want the WPF application to do the same as the web front-end: Use the web api endpoints after being authenticated with Google. The connection between the WPF application and my web server is done through an HttpClient.
My problem is I don't know how to authenticate that HttpClient connection between the WPF app and the web server.
I tried using the same Challenge endpoint but the response I get is of course the HTML from the Google Sign-In page, so I guess I can't use that with an HttpClient...
I also tried authenticating with GoogleApis from the WPF app and use the authenticated token to set cookies in the HttpClient but apparently this is not compatible.
How to authenticate an HttpClient connection to a web api with an external provider such as Google?
Original question:
From a WPF application, the user authenticates with Google with this code:
using Google.Apis.Auth.OAuth2;
...
public void Authenticate()
{
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "myClientId",
ClientSecret = "myClientSecret"
},
new[] { "email", "openid" },
"user",
CancellationToken.None).Result;
}
This works and the UserCredential object contains the authenticated token:
How to embed this token information in a web request made with an HttpClient in order to call my webapi endpoint?
I think the request must include some cookies to inform the server that it has been authenticated, but I don't know which ones exactly.
The endpoint on the server-side validates that the user is authenticated with the help of IdentityServer:
var result = await HttpContext.AuthenticateAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme);
if (result?.Succeeded != true)
{
throw new Exception("External authentication error");
}
If I got your question right, you just have to set the Authorization header
var credentials = await GoogleWebAuthorizationBroker.AuthorizeAsync(
clientSecrets,
new[] { "email", "openid" },
"user",
CancellationToken.None);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
credentials.Token.TokenType,
credentials.Token.IdToken);
Maybe you'll find below a helpful hint to better understand OpenID :)
The confusion stems from mixing GoogleApis and IdentityServer frameworks.
Authentication/authorization can be achieved using either of them.
Objects from Google.Apis.Auth.OAuth2 and IdentityServer4 namespaces are not designed to interact.
No manual cookie handling is necessary, for sure.
Ask yourself to whom does Google provide trust for the user. If it calls back to WPF, then webapi trusting WPF is a separate issue.
You answer your own question in the question:
the browser then has the authenticated cookies and the webapp can use
the endpoints to update its state
HttpClient needs to send those same cookies.
How do I set a cookie on HttpClient's HttpRequestMessage
If I understood your question right, then I faced the same problem not too long ago.
The way I implemented it is that in the backend, no matter who tries to access the endpoint, they had to send a Bearer X authorization token.
The token contained the identity of the client that wanted to access the resource, and I checked if he was permitted.
No matter what kind of client wants to access the endpoint, it just has to have that authroziation header in the request that he sends and the backend will treat it the same.
In my scenario, I used an authentication service that returns a cookie to the client with a certain JWT that contains the identity information.
Then from the client I send in every request the JWT received from the authentication service as an authorization header to the backend.
The reason I had to put the JWT that I receive from the service in a header, is that the authentication service and the backend service are not in the same domain, so cookies cant be shared.
This results in such design that no matter how you authenticate the client, the end result must be some sort of token that the backend can receive and read.
Hope this helps.
I'm updating our MVC application to use IdentityServer v3 to implement OpenID Connect authentication using implicit flow.
I've overridden the IdentityServer AuthenticateLocalAsync method of the UserServiceBase class to call into our existing authentication service to validate the supplied credentials. This service also sets various cookies required by our application.
The issue I'm encountering is that upon entering the credentials in the login page, it doesn't redirect to the ReturnUri specified in the OpenIdConnectAuthenticationOptions parameter of UseOpenIdConnectAuthentication method. Instead it remains on the login page and appears to go into an infinite loop, alternating between calls to the notification handlers SecurityTokenValidated and SecurityTokenReceived. There is no indication of any error in the IdentityServer logs.
I think the cause of the issue is related to setting the application-specific cookies in our authentication service. If I comment out the following, it then successfully redirects upon logging in:
var cookie = new HttpCookie("AppCookie") { HttpOnly = false, Secure = true };
cookie ["CustomField1"] = HttpUtility.UrlEncode(customValue1);
cookie ["CustomField2"] = HttpUtility.UrlEncode(customValue2);
this._httpContext.Response.Cookies.Set(cookie);
However these cookies are required by our application and so need to be set in the response. Why would setting these cookies prevent it from redirecting? Are there any workarounds? Any assistance would be much appreciated.
I'm playing around with the authentication using Owin according to a blog and when I execute the following, I'm supposed to get an (application) cookie. I'm unsure how to verify that I actually got one. So it very well might be there, somewhere in the memory or on the disk but I'm too ignorant of security issues to determine that.
List<Claim> claims = new List<Claim> { new Claim("Donkey", "Hazaa") };
ClaimsIdentity identity = new ClaimsIdentity(
claims, DefaultAuthenticationTypes.ApplicationCookie);
HttpContext.GetOwinContext().Authentication.SignIn(new AuthenticationProperties
{
AllowRefresh = false,
IsPersistent = false,
ExpiresUtc = DateTime.Now.AddMinutes(1)
});
I can't verify by being able/unable to log in, since this is just a dummy without any backbone yet. The only thing I need to do at this stage is to confirm that something gets somewhere and that it vanishes when a minute has passed. It'd be awesomely awesome if there was an actual cookie file popping up somewhere and then poofing away after a while. Is there such?
Of course it is, but the Cookie is sent to the browser once the authentication was requested and validated. The cookie has the same behavior as in previous version of cookie auth with forms authentication, so is a task for the browser to receive and manage the cookie; on each HTTP request, the browser sends its cookies, which are processed by OWIN to check authentication.
Take the code and implement a simple authorization on memory (like user: abc, pass: 123) and check how the browser receives the cookie. By the way, the only difference between cookie and token validation is that the cookie is automatically sent on every request, whilst on token auth you need to explicitly send the token information on header.
Be aware that cookie authentication is intended for browsers, as mobile applications or other applications that need to connect to your API will not have access to the cookie response, like in a desktop app.
Regards
I am creating a web application that is hosted under a web site with forms authentication enabled. I have a role in my authentication database "Admins". Here is my controller code:
[RequireHttps]
[Authorize(Roles = "Admins")]
public ActionResult Index()
{
return this.View();
}
When I go to the Index page, if I'm not authenticated, it redirects me to the login page where I enter my credentials. The login page then redirects back to Index page of the new app, but the controller doesn't recognize that the user is authenticated.
I have taken the Authorize attribute off and looked at the request as it went out in the Chrome developer console and confirmed that the cookie is indeed being sent. But if I leave the Authorize attribute as is, and go to the Index page, the cookie collection on the request in my controller is empty. The headers collection contains a header entitled "Cookie", and the value of the header contains the .ASPXAUTH cookie.
The login page calls logs in with this code:
FormsAuthentication.SetAuthCookie(userName, remember, "/");
This behavior is reproducible in all major browsers.
What can I do to cause the Cookies collection of the request to be populated?
What do I need to do to make the application realize that the user really is authenticated?
Edit:
I still don't have it working, but I'm pretty sure it's something to do with the ASPXAUTH cookie being filtered.
I'm sure there are multiple causes of this problem. In my case, the problem was that the version of MVC I was using to write the cookie was different from the version that was decrypting it. I changed my sites to all be running MVC 4, and the cookie that was created by one site was consumable by the other site.
Is the .ASPXAUTH cookie generated a secure cookie, i.e. SSL? If so and your Index.aspx is only over HTTP not HTTPS, you will not see the cookie in the collection.