Angular is not sending the Cookie received in Set-Cookie even if withCredentials is true

匿名 (未验证) 提交于 2019-12-03 02:38:01

问题:

For cookie based authentication, my server sends Set-Cookie to my Angular application. However, the application doesn't send the value back in further requests. Following is my code.

const httpOptions = {   headers: new HttpHeaders({ 'Content-Type': 'application/json' }),   withCredentials: true //this is required so that Angular returns the Cookies received from the server. The server sends cookies in Set-Cookie header. Without this, Angular will ignore the Set-Cookie header };  public getUserProfile(){     console.log('contacting server at '+this.API_URL +this.GET_USER_PROFILE_URL+"with httpOptions "+httpOptions);     return this.http.get(this.GET_USER_PROFILE_URL,httpOptions )       .map(response=>{         console.log('response from backend service',response);         let result= <ServerResponse>response;          console.log("result is "+result.result+' with additional information '+result.additionalInformation)         return result;       })       .catch(this.handleError);   } 

The server sends the cookie as follows in 200OK of my code (not shown here)

Set-Cookie: id=...

The next message however hasn't got the id in the cookie, thus the server returns 401. If I manually add the Cookie using browser's debug tools, then I get 200OK. Thus I am certain it is the absence of the id value in the cookie which is causing the issue.

What am I doing wrong? Do I need to explicitly store the cookie received in Set-Cookie and explicitly add it in further requests?

Update - At the time when the SPA is initially loaded, the server sends Set-Cookie header with some other cookie's information related to CSRF. I notice that that cookie is still sent by the application. Could it be that Angular honors the first Set-Cookie header but ignores the subsequent ones?

I have added couple of pics to explain what I mean

During signing, the client sends a cookie related to CSRF. I dont think it is required as the client also sends CSRF Header but for some reason it does. The server responds with Set-Cookie with id in it

Then when I ask for profile, the client again sends the CSRF cookie but not the id cookie

回答1:

Finally I was able to find the issue. The journey was more satisfying than the result so let me break it down into the steps I took to solve my problem.

In summary, This wansn't an issue with Angular. The cookie I was sending had secureCookie flag on. As I was testing my application without https, it seems that the angular application was not using (or getting access to) the Set-Cookie header received in 200 OK.

My initial code to send the sign in request to the server and handling its response was

  return this.http.post(this.SIGNIN_USER_URL,body,httpOptions)       .map(response=>{         console.log('response from backend service',response);         let result= <ServerResponse>response;          console.log("result is "+result.result+' with additional information '+result.additionalInformation)         return result;       })       .catch(this.handleError); 

I wasn't using observe: 'response' option which meant that the response would only contain body, not headers. I changed the code to following so that I could see which headers are being received.

const httpOptions = {   headers: new HttpHeaders({ 'Content-Type': 'application/json' }),  withCredentials: true,  observe: 'response' as 'response' };    public signinUser(user:UserSigninInfo):any{     console.log('contacting server at '+this.API_URL +this.SIGNIN_USER_URL +" with user data "+user+ " with httpOptions "+httpOptions.withCredentials + ","+httpOptions.headers );       let signinInfo= new UserSignin(user);     let body = JSON.stringify(signinInfo);     return this.http.post(this.SIGNIN_USER_URL,body,httpOptions)       .catch(this.handleError);   } 

The above code was being called as follows. I change that to get the headers in the response

return this.bs.signinUser(user).subscribe((res:HttpResponse<any>)=>{console.log('response from server:',res);   console.log('response headers',res.headers.keys()) } ); 

I also created an intercepter to print the incoming and outgoing messages (copied from SO)

import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from "@angular/common/http"; import {Injectable} from "@angular/core"; import {Observable} from "rxjs/Observable"; import 'rxjs/add/operator/do';  @Injectable() export class CustomInterceptor implements HttpInterceptor {    constructor() {   }    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {      console.log("outgoing request",request);     request = request.clone({       withCredentials: true     });     console.log("new outgoing request",request);      return next       .handle(request)       .do((ev: HttpEvent<any>) => {         console.log("got an event",ev)         if (ev instanceof HttpResponse) {           console.log('event of type response', ev);         }       });   } } 

When I started debugging, I noticed that though the server was sending 10 headers, only 9 were getting printed

headers from server

Print message on console (Set-Cookie was missing! The header my application needed to get authentication cookie)

This gave me a direction that the application is not seeing Set-Cookie header. I thought I'll be able to resolve it by adding CORS policy in play framework exposedHeaders = ["Set-Cookie"] but that didnt work. Later I looked closer at the cookie and noticed secureCookie setting

Set-Cookie: id=...Secure; HTTPOnly 

This made me think that maybe my cookie settings are wrong for my environment (localhost, no HTTPS). I changed the cookie settings in Silhoutte

val config =  CookieAuthenticatorSettings(secureCookie=false) 

And it worked!

Though I'll make the above code work for secureCookie and this wasn't an issue with Angular, I hope that some folks might find the approach helpful



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!