0

I have an ionic 2 app that uses access tokens to authenticate to an API. If an access token expires, it can be exchanged for a new access token using a refresh token.

There is a function handling an API call that returns a promise. If the API call initially succeeds, the promise resolves. If there is a 401 (Unauthorized) error returned, I want to make another API call that uses a refresh token to get a new access token. Then, the original API call should be tried again with the new access token and either resolve if the data is received or reject if there was an error.

I currently have the function working as I intend it to. However, this function is very hard to read. I was wondering how I can refactor this to make it smaller and more readable.

public readAtmospherePrefs() {
return new Promise((resolve, reject) => {

  this.storage.get('email').then((email) => {

    this.storage.get('token').then((token) => {

      let headers = new Headers();
      headers.append('Authorization', token);
      headers.append('Content-Type', 'application/json');

      let options = new RequestOptions({ headers: headers, search: new URLSearchParams("email=" + email) });

      //Attempt to get data
      this.http.get('api/users/preferences/atmosphere', options).subscribe(res => {
          let data = res.json();

          resolve(data);
          resolve(res.json());
        }, (err) => {
          let error = err;

          //Need to send email and refresh the token if unauthorized 
          if(err.status === 401){
            this.storage.get('refreshToken').then((refreshToken) => {
              let body = { email: email, refreshToken: refreshToken}
              let headers2 = new Headers();

              headers2.append('Content-Type', 'application/json');

              //get new access token using the refresh token
              let options2 = new RequestOptions({ headers: headers2});
              this.http.post('api/users/token', body, options2)
              .subscribe(res => {
                let data = res.json();
                let newToken = data.token;
                //set new access token in storage
                this.storage.set('token', newToken).then((newToken) => {
                      let headers3 = new Headers();
                      headers3.append('Authorization', newToken);
                      headers3.append('Content-Type', 'application/json');

                      //retry call with new access token
                      let options = new RequestOptions({ headers: headers3, search: new URLSearchParams("email=" + email) });
                      this.http.get('api/users/preferences/atmosphere', options).subscribe(res => {
                          let data = res.json();

                          resolve(data);
                          resolve(res.json());
                        },
                      (err) => {
                        reject(err);
                      });
                    });

                  }, (err) => {

                reject(err);
              });

            });

          }
          else{
              console.log(error);
              reject(err);
          }

        });
    });

  });

});

  }
Marco Orlando
  • 70
  • 2
  • 8

1 Answers1

-1

Why not check your token state before you call?

You can use angular2-jwt to check expire state of your token before you retry. Angular2-jwt

Add a function like these:

loggedIn(token) {
  return tokenNotExpired(null,token);
 }

Your get request should be like this

    if(!this.loggedIn(token))
        {
             //reset refresh token
         }
   // your plain & simple get request

No need to retry.Thanks

mnhmilu
  • 2,327
  • 1
  • 30
  • 50
  • This is not a good solution since it effectively doubles the number of API calls, thereby doubling the amount of requests that your server will have to handle – Kal Aug 12 '18 at 22:33
  • effectively doubles the number of API calls? Are you sure what you are saying? – mnhmilu Aug 13 '18 at 05:34
  • yes, unless you're suggesting to only check the exp field of the JWT in order to determine whether a JWT is valid, the true way to check if the JWT is valid is to verify the signature, which can only be done server-side using the private key – Kal Aug 14 '18 at 02:24
  • I am suggesting to only check the expire state of it.Will it call api thorough http? Just need to confirm. – mnhmilu Aug 14 '18 at 04:56
  • No, if you are only suggesting to check the exp field, then you will not hit the server. However, even if the exp timestamp is a future datetime, there is still the possibility that the token is not valid (e.g. if the token has been blacklisted). In this case, a 401 response will still be returned and you will still have to retry the API call with a new token anyway. – Kal Aug 14 '18 at 06:07
  • I think it is not a regular event. Most of the cases it will not call api over http. Do you have any suggestion to improve this solution? – mnhmilu Aug 14 '18 at 06:18
  • You’re missing the point. The OP already has the correct methodology and is simply asking for help to refactor the code a bit to make it more readable. Instead of answering the actual question, you offered an alternative, and that alternative is flawed. That is what I am trying to explain to you. – Kal Aug 14 '18 at 11:03
  • Sorry. I don't agree with your explanation. – mnhmilu Aug 15 '18 at 12:22
  • Not to drag on this exchange, but after some thinking I think I’ve find the source of the misunderstanding. Your solution works only if the JWT is signed using asymmetric encryption (e.g. RS256) where you would have a public key to verify the token on the client side. However, if the token is being signed using symmetric encryption (e.g. HS256), then the token can only be verified on the server side as aforementioned since the single key used to encrypt the token must be kept private. I hope this clears things up. – Kal Aug 22 '18 at 04:36