Angular2 - How to best handle expired authentication token?

前端 未结 5 1261
孤独总比滥情好
孤独总比滥情好 2021-02-05 11:06

I am using Angular 2.1.2.

I have an authentication token (using angular2-jwt) and if it expires my webApi call fails with a 401 error. I am looking for a solution where

相关标签:
5条回答
  • 2021-02-05 11:26

    Usually I would provide a HttpService myself instead of using Http directly. So with your requirement, I can provide my own get() method to chain the authentication before sending any real HTTP requests.

    Here is the service:

    @Injectable()
    class HttpService {
      constructor(private http: Http, private auth: Authentication) {}
    
      public get(url: string): Observable<Response> {
        return this.auth.authenticate().flatMap(authenticated => {
          if (authenticated) {
            return this.http.get(url);
          }
          else {
            return Observable.throw('Unable to re-authenticate');
          }
        });
      }
    }
    

    Here is the component to call the service:

    @Component({
      selector: 'my-app',
      template: `<h1>Hello {{name}}</h1>
      <button (click)="doSomething()">Do Something</button>
    
      <div [hidden]="!auth.showModal">
      <p>Do you confirm to log in?</p>
      <button (click)="yes()">Yes</button><button (click)="no()">No</button>
      </div>
      `,
    })
    export class AppComponent {
       name = 'Angular';
    
       constructor(private httpSvc: HttpService, public auth: Authentication) {}
    
       ngOnInit() {
       }
    
       doSomething() {
         let a = this.httpSvc.get('hello.json').subscribe(() => {
           alert('Data retrieved!');
         }, err => {
           alert(err);
         });
       }
    
       yes() {
        this.auth.confirm.emit(true);
       }
    
       no() {
         this.auth.confirm.emit(false);
       }
    }
    

    By chaining observables, the Authentication service determines whether to interrupt the normal flow to show the modal (though currently only lives with the App component, it can certainly be implemented separately). And once a positive answer is received from the dialog, the service can resume the flow.

    class Authentication {
      public needsAuthentication = true;
      public showModal = false;
      public confirm = new EventEmitter<boolean>();
    
      public authenticate(): Observable<boolean> {
        // do something to make sure authentication token works correctly
        if (this.needsAuthentication) {
          this.showModal = true;
          return Observable.create(observer => {
            this.confirm.subscribe(r => {
              this.showModal = false;
              this.needsAuthentication = !r; 
              observer.next(r);
              observer.complete();
            });
          });
        }
        else {
          return Observable.of(true);
        }
      }
    }
    

    I have a full live example here.

    http://plnkr.co/edit/C129guNJvri5hbGZGsHp?open=app%2Fapp.component.ts&p=preview

    0 讨论(0)
  • 2021-02-05 11:28

    Well, reloading is simple: (<any>window).location.reload(true);

    It is good idea to show login/password popup and allow user to continue working if he can provide password, if user hits cancel just do redirect to login page.

    There are also connection problems, timeouts, server errors. The problem is that it is difficult to reliably implement proper handling using Angular 2 Http module and rxjs. Personally, i gave up using Http module, because its error handling has bad design and implemented my own http module, which allows configurable error handling and transparent retries.

    0 讨论(0)
  • 2021-02-05 11:30

    Use a browser session:

    https://developer.mozilla.org/de/docs/Web/API/Window/sessionStorage

    Store the data as json string and then recreate form data if request fails.

    0 讨论(0)
  • 2021-02-05 11:31

    Ideally, the request would just 'pause' until the user logs in from the modal. I have not found a way to implement this.

    Did you try to switch your button by using tokenNotExpiredattribute like in this example : https://github.com/auth0/angular2-jwt#checking-authentication-to-hideshow-elements-and-handle-routing

    It allows you to prevent the 401...

    0 讨论(0)
  • 2021-02-05 11:36

    After I do a login and receive a token (that in my case expires in 60 minutes), I set a interval that checks every minute to see if 59 minutes have passed. If yes, then I open up a login dialog.

    The idea, however, is the login dialog is not a route, but just an overlay that opens on top of whatever screen the user happens to be in on (thus I put the login component within the html of the app root component, and use an observable to call up the login dialog from anywhere in the app).

    When the user correctly re-logins in, I close the login dialog and the user goes merrily on their way with whatever screen they were using before they had to re-login.

    I don't know if this is a "best practice" but it works in the situation you are describing. Let me know and I can put up code.

    0 讨论(0)
提交回复
热议问题