2

I have an Angular 2 service that has a logout function. When the function is called from the app component it cause a full page refresh. When using angular 1 projects I haven't experienced this behavior. If I call my logout endpoint with postman the session cookie is deleted. It is not deleted if I use my angular 2 authentication service.

Service

import {Injectable} from 'angular2/core';
import {User} from './user';
import {Headers, RequestOptions, Http, Response} from 'angular2/http';
import {Observable} from 'rxjs/Observable';
import {Cookie} from '../extensions/cookies';

@Injectable()
export class AuthenticationService {

    constructor (private http: Http) {}

    private _prepTestHost = 'http://localhost:8000/';
    private _prepTestLoginUrl = this._prepTestHost + 'login/';
    private _prepTestLogoutUrl = this._prepTestHost + 'logout/';

    private _authenticated: boolean;

    getUser() {}

    isAuthenticated() {
        return this._authenticated;
    }

    setAuthenticated() {
        this._authenticated = true;
    }

    loginUser(username, password) : Observable<User> {
        let body = JSON.stringify({username, password});
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });

        return this.http.post(this._prepTestLoginUrl, body, options)
                                .map(res => <User> res.json(), this.setAuthenticated())
                                .catch(this.handleError)
    }

    logoutUser() : Observable<void> {
        let body = JSON.stringify({});
        let csrfcookie = Cookie.getCookie('csrftoken');
        let headers = new Headers({
            'X-CSRFToken': csrfcookie,
            'Content-Type': 'application/json'
        });
        let options = new RequestOptions({ headers: headers});
        return this.http.post(this._prepTestLogoutUrl, body, options)
                        .map(res => <void> res.json())
                        .catch(this.handleError);

    }

    private handleError (error: Response) {
        // in a real world app, we may send the server to some remote      logging infrastructure
        // instead of just logging it to the console
        console.error(error);
        return Observable.throw(error.json().error || 'Server error');
    }
}

App Component

import {Component} from 'angular2/core';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';

import {WelcomeCenterComponent} from './welcome-center/welcome-center.component';
import {AuthenticationService} from './authentication/authentication.service';
import {LoginModalComponent} from './authentication/login-modal.component';
import {BrowserXhr, HTTP_PROVIDERS} from "angular2/http";
import {CORSBrowserXHR} from './extensions/corsbrowserxhr';
import {provide} from "angular2/core";

@Component({
    selector: 'my-app',
    template: `
    <nav class="navbar navbar-default">
      <div class="container-fluid">
        <div class="navbar-header">
          <a class="navbar-brand" href="#" [routerLink]="['WelcomeCenter']">Brand</a>
        </div>
        <ul class="nav navbar-nav navbar-right">
          <li *ngIf="!authenticated()">
            <a href="#" data-toggle="modal" data-target="#myModal">Login</a>
          </li>
          <li *ngIf="authenticated()">
            <a href="#" data-dismiss="modal" (click)="logout()">Logout</a>
          </li>
        </ul>
      </div>
    </nav>
    <router-outlet></router-outlet>
    <login-modal></login-modal>
    `,
    directives: [ROUTER_DIRECTIVES, LoginModalComponent],
    providers: [HTTP_PROVIDERS,
        provide(BrowserXhr, {useClass: CORSBrowserXHR}),
        AuthenticationService]
})
@RouteConfig([
    {
        path: '/welcome-center/...',
        name: 'WelcomeCenter',
        component: WelcomeCenterComponent,
        useAsDefault: true
    }
])
export class AppComponent {

    constructor(private _authenticationService: AuthenticationService) {}

    authenticated() {
        return this._authenticationService.isAuthenticated();
    }

    logout() {
        console.log("Logout button pressed");
        this._authenticationService.logoutUser().subscribe();
    }
}

Setting withCredentials attribute:

import {BrowserXhr, HTTP_PROVIDERS} from "angular2/http";
import {Injectable, provide} from "angular2/core";

@Injectable()
export class CORSBrowserXHR extends BrowserXhr{
    build(): any{
        var xhr:any = super.build();
        xhr.withCredentials = true;
        return xhr;
    }
}
MichaelB
  • 373
  • 4
  • 13
  • is angular throw any error on page refresh ? if yes please post seems same problem i had faced may be ill help you. – Pardeep Jain Feb 13 '16 at 06:50
  • @PardeepJain There doesn't seem to be an error. My server responds with status 200 and the session is destroyed on the server. – MichaelB Feb 13 '16 at 15:16
  • ohh okay...i think one more error is there in your `logoutUser` you `.map` the observable that return `null or void` and you subscribe to null. may be this one too produce affect upto some extent chk it again. – Pardeep Jain Feb 13 '16 at 15:40

2 Answers2

2

I think that the page reload is due to the fact that you don't prevent event propagation when blocking on the layout button (you an 'a' HTML element with an 'href' attribute). You could use 'return false' at the end of your logout function or '$event.stopPropagation()'.

See the question for more details:

Regarding the cookie problem, I these that you use cross domain requests (CORS). I think that you should try to set to true the 'withCredentials' attribute on the underlying XHR object. See this question for more details:

Community
  • 1
  • 1
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • it was the `href="#"` that was causing the page reload. That was masking an error being thrown that I am looking into now. – MichaelB Feb 13 '16 at 18:49
  • I was already setting the withCredentials attribute, I've added the code above. ` – MichaelB Feb 13 '16 at 18:52
0

You could do something kind of hacky but I can't think of another way.

Cookie.setCookie(nameOfCookie, "", -1);

This would effectively delete the cookie on logout. I'd love to know if there was a better way though!

I also am not sure why you are getting any kind of page reload at all, I have yet to experience that on anything I've done yet, hopefully someone else will know.

Morgan G
  • 3,089
  • 4
  • 18
  • 26
  • may i know the syntax you posted is javascript's syntax to remove cookie ? – Pardeep Jain Feb 13 '16 at 06:53
  • @PardeepJain it comes from an extension in angular2 https://www.npmjs.com/package/ng2-cookies, I assumed he was using it as well as he has the same syntax. – Morgan G Feb 13 '16 at 07:00
  • @MorganG Removing the `href="#"` also appears to have let the code that handles clearing the sessionid cookie execute, and correctly remove the cookie. I've never had to manually remove sessionid cookies with angular 1 applications, and now it appears to be the same in angular 2. Thank you for you help. Thierry Templier's answer was correct. – MichaelB Feb 13 '16 at 19:06
  • @MichaelB no problem! Thierry usually is correct and has better solutions. Glad he was able to answer your question! – Morgan G Feb 13 '16 at 19:41