BCJTI / ng2-cookies

Simple library to deal with cookies in Angular2
64 stars 31 forks source link

Trying to use Cookie as token storage : problem with the refresh. #49

Closed ghost closed 7 years ago

ghost commented 7 years ago

I'm wondering if you have a solution for my problem.

I'm using the library defining 2 cookies : one called access token and the other called refresh token.

I store inside the cookies tokens retrieved in a json after an http.post on an auth server.

I'm trying to implement the refreshing of the token but the async behave of the http calls is giving me some problem.

Here's the code: getRefresh Function

  getRefresh(token){

    let params = new URLSearchParams();
    params.append('grant_type','refresh_token');
    params.append('refresh_token',token);
    params.append('client_id','web_app');

    let headers = new Headers({'Content-type': 'application/x-www-form-urlencoded; charset=utf-8','Authorization': 'Basic '+btoa("web_app:")});
    let options = new RequestOptions({headers: headers});

    return this._http.post('http://localhost:9997/oauth/token',params.toString(),options)
      .map(res => res.json())
      .subscribe(
      data => {
        this.saveToken(data)
      });

  }

saveToken Function

  saveToken(token){

    var expireDate = 0.00034722222;
    //var expireDate = token.expires_in;

    Cookie.set("access_token", token.access_token, expireDate);
    Cookie.set("refresh_token", token.refresh_token, 1);

    this._router.navigate(['/']);
  }

getResource Function

getResource(resourceUrl): Observable<any> {
    var cookie;

    cookie = Cookie.get("access_token");
    var headers = new Headers({
      'Content-type': 'application/x-www-form-urlencoded; charset=utf-8',
      'Authorization': 'Bearer ' + cookie                            
    });

    var options = new RequestOptions({headers: headers});

    return this._http.get(resourceUrl, options)
      .map((res: Response) => res.text())
      .catch((error: any) => Observable.throw(error().json().error || 'Server error'));

  }

Everything starts from the main component that call the service via event bound to a button.

How can i resolve?

Bigous commented 7 years ago

Hi,

I couldn't figure out what's your issue.

I understood that:

  1. You are retrieving an access_token and storing it in the cookie with 30 seconds to expire.
  2. You are retrieving the refresh_token and storing it in the cookie with expiration set to tomorrow.
  3. When you getResource, you make a HTTP header with the access_token.

But where is your code to actually refresh the code if Cookie.get('access_token') retrieves nothing (because it has already expired for example)?

[]'s

ghost commented 7 years ago

I'm sorry , this is the complete code of getResource

getResource(resourceUrl): Observable<any> {
  debugger;
  var cookie;

  var bool = (!(Cookie.get("access_token")));
  console.log("step in GET RESOURCE");
  console.log("access token e' assente? : "+bool);

  if (!Cookie.get("access_token")) { //controllo sei il token e' sempre vivo
    this.getRefresh(Cookie.get("refresh_token"))

  } 
    cookie = Cookie.get("access_token");

    var headers = new Headers({
      'Content-type': 'application/x-www-form-urlencoded; charset=utf-8',
      'Authorization': 'Bearer ' +  cookie                                 //Cookie.get('access_token')
    });
    //a questo punto gli headers sono pronti e il token e' stato assegnato
    var options = new RequestOptions({headers: headers});

    return this._http.get(resourceUrl, options)
      .map((res: Response) => res.text())
      .catch((error: any) => Observable.throw(error().json().error || 'Server error'));

}

BTW the issue is that i don't catch the Cookie in time to assign it to the cookie var, because the http request resolves after the code

Bigous commented 7 years ago

Ok, got it.

You should return an observable or a promise or callback from the getRefresh or use async/await pattern to set the Header after you get the refreshed token. The way it is as you posted, you are not waiting for the refresh's post to update the token.

Correct me if I'm wrong, if the cookie must stay alive only for 30 seconds, wouldn't it be better to use a state in a regular service class instead of a cookie (unless you are not doing a Single Page Application and then why to use angular?)

[]'s

ghost commented 7 years ago

I'm just working as the boss asked me! i'm trying to use async/await but i have to deal with promises!

I've tried returning observables as getRefresh return but i'm not very skilled in angular2

Bigous commented 7 years ago

Sorry about the delay.

So, I think your boss wanna you to learn a little bit more about javascript and/or angular ;).

Try to look at some tutorials like this.

But I think that the best should use a service instead of the cookie. Services are simple class with the @Injectable() decorator. Nothing special. You can use the observable tutorial to retrieve the tokens and store it in a local variable with the expiration date. So, when you call getToken you check if it exists and is not expired, you return it in an observable way, if it's expired or don't exist, you call http to get the token and return it in an observable way.

[]'s