Reusing session cookie in HttpClient calls - c#

I have a Web API 2 project using cookie-based sessions for our internal use. I have /api/login and /api/logout actions created and it works fine using a browser to access it.
I'm now trying to create a C# client to communicate with the API by using HttpClient. I can log in fine using:
Uri loginUri = new Uri("http://localhost/api", "login");
HttpResponseMessage response = _http.PostAsJsonAsync(loginUri, loginModel).Result;
And I can get the session id string from the cookie by
var cookies = _httpHandler.CookieContainer.GetCookies(uri);
if (cookies["session_id"] != null)
sessionID = cookies["session_id"].Value;
My question is: for every subsequent request including the logout request, how do I include the session_id cookie in the request header? I would like to reuse the HttpClient object for the duration of the session if possible. I saw this question and answer, but it's for calling login, performing the operation, and logout in the same request.
Thanks!

I ended up going back and used the same FormsAuthentication methods I used in my MVC project. So after calling my custom MembershipProvider, I used FormsAuthentication methods to set and keep track of my session cookies. I called FormsAuthentication.SetAuthCookie() to log in and FormsAuthentication.SignOut() to log out.

Related

Clarify how cookies are working between Angular and c# controller

I would like to clarify how cookies work between angular app and the c# server controller.
This is what I gathered from various discussions and forums:
The angular client sends HTTP Request (e.g. to login) to the c# server
c# server creates the cookie (e.g. refreshToken) using:
Response.Cookies.Append("refreshToken", token, cookieOptions);
c# server returns and the cookie refreshToken is set in store on the client side
Whenever the angular client sends the HTTP request again, the cookie is set in the Request object (probably by the browser, behind the scenes - angular client does not explicitly set the cookie)
c# server receives the request and retrieves the cookie like below:
var refreshToken = Request.Cookies["refreshToken"];
Is my understanding correct?
Yes, your understanding is totally correct.
One note: Angular HttpClient does only include cookies for cross-domain requests (like in dev environment) in the request if HttpOption withCredentials is set to true.

Handling authentication without a cookie

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.

Properly using CAS Authentication with an Angular 11 App and .Net 4.7.2 Web API

In my first exposure to Angular, I have an existing application using an Angular frontend and a .NET Web API for the backend. The pre-existing application was utilizing a login scheme with local accounts stored in the database. I am attempting to modify this app to use our organization's CAS server for authentication, instead.
The work I have done so far was based on the guidance found here: https://www.blinkingcaret.com/2018/10/10/sign-in-with-an-external-login-provider-in-an-angular-application-served-by-asp-net-core/
Thus far, I have made a few key changes. In authentication.service.ts, I have modified the login function to point to my new action on the server
login(){
this.document.location.href = this.casUrl + "login"
}
In my newly added CAS controller, I have the following actions.
public ActionResult Login() {
return new ChallengeResult("CAS");
}
public ActionResult HandleLogin() {
var claimsId = (ClaimsIdentity)User.Identity;
//Do things with claims, check against DB, etc
}
private class ChallengeResult : HttpUnauthorizedResult {
//sets the RedirectUri to HandleLogin, fires the Challenge in the ExecuteResult function
}
Up to this point, things seem to be behaving. The User.Identity populates correctly, so I can retrieve the corresponding user from the database. I am able to construct a LoginResponse object as well, though currently am not doing anything with it. This LoginResponse includes a token generated in another piece of the application, and seems to be a JWT.
At this point, I do not know how to transfer my LoginResponse back to the Angular.
In the original implementation using local accounts, a post was made to an API endpoint, and the response was piped to where it was needed.
return this.http.post(this.apiUrl + 'auth/login',
//parameters
), headers).pipe(
map((user:LoginResponse) => {
//do stuff with LoginResponse
}));
In turn, the component that called this service is subscribed to its return value.
My thought was to have a new function called within the init of a component. The first action it would take would be to go to my CasUrl endpoint and request the LoginResponse constructed using the User.Identity and DB lookup. Unfortunately, the Identity is empty upon all subsequent calls to the server. It does not seem to persist between requests.
One more note that may be relevant: My Web API solution is running through Visual Studio on localhost:46000, whereas the Angular application is running in VSCode on localhost:4200.
What is it I am missing to get external authentication working with this app? Is it possible to have an angular pipe or subscribe wait for results from an external website?
If I understand it correctly you do a full redirect to your CAS-server with location.href which does (after successful authentification) a redirect to your route /login.
So /login should response with the full angular site or redirect to the site with HttpStatus 302. The session cookie generated by the server can then be used for further requests to the backend.
The AccountService.ts can check if the user isAuthenticated by calling the api api/home/isAuthenticated This request should automatically append the newly gathered session cookie.
see example code of the blog you mentioned:
https://github.com/ruidfigueiredo/angular-aspnetcore-external-login/blob/master/AngularWithGoogleLogin/src/app/account.service.ts#:~:text=%20updateuserauthenticationstatus
and its counter piece:
https://github.com/ruidfigueiredo/angular-aspnetcore-external-login/blob/master/GoogleSpaWeb/Controllers/HomeController.cs#:~:text=public%20IActionResult-,IsAuthenticated,-()
Stef's answer is essentially what was required. I found that the session cookie was properly being created and stored in the browser, though I was used to the backend automatically maintaining session state.
I added a call to the API to verify login status, and as stated in the question, the User.Identity value was empty.
What I chose to do was create the LoginResponse object and assign its value to a cookie. Then, on the component's init function, I would check the cookie rather than calling to the API.

Single sign on using custom API

I want to implement a single sign on feature using my own API. Third party application(web application) will call this API and authenticate the users. For the communication between my API and other applications web request will be used. Below is the solution I provided for this,
I have created a API on my application and do the authentication based on request values. After successful authentication, I create the authentication cookie and add it to the response.
On the other app I used a HttpWebRequest and create CookieContainer. Then I get the cookies from response and assign those cookies to Response.
var response = (HttpWebResponse)http.GetResponse();
foreach (Cookie cook in response.Cookies)
{
Response.Cookies.Add(new System.Web.HttpCookie(cook.Name, cook.Value)
{
Domain = cook.Domain,
Expires = cook.Expires
});
}
In my test environment this works fine since both authentication API and other app are in same domain. But in customer testing phase this does not work due to domain mismatch. Because Authentication API is in different domain.
Is there any way to resolve this issue ?
I think it is impossible with cookies because they are domain bound and are not sent along with requests to domains. I guess you need see about other technology.
I hope this link will be useful
Good Luck

HTTP web request doesn't maintain session

I have a program where I want to scrap some useful study material for personal use.
This site maintain a session key and some other key also.
If I tried to go to a nested page then it will end the session.
I'm unable to maintain session key using a web request class.
How can I maintain a session using a web request class?
Please help.
You need to maintain the CookiesCollection across your requests.
var request = (HttpWebRequest)HttpWebRequest.Create("http://www.mysite.com/");
var cookies = new CookieContainer();
//Pass the collection along with each request to maintain session
request.CookieContainer = cookies;
When you get the response back, your container will automatically contain the set of cookies associated with it. You can then reuse that container with subsequent requests.
Essentially you will want to read the session cookie from the response header and pass it back in the header with every request you issue. There may be more to it depending on the application you are targeting.
Got a similar problem when embedding a Page in an iFrame. Solution is, for security purposes sessions get discarded by browsers (mainly IE8). Maybe this is a splution? Google for P3P to get more info.
-sa

Categories

Resources