如果next.handle返回401(未经授权),则在httpinterceptor中异步更新令牌

时间:2019-04-12 11:59:09

标签: angular typescript oauth rxjs refresh-token

我正在使用HttpInterceptor将访问令牌应用于传出的httprequests。拦截器调用返回Observable的getUser(),将accesstoken添加到请求中。这一切都很好,花花公子。但是我现在想要的是,如果请求返回401,我想刷新令牌,然后再次处理请求。因此,我发现了所有错误,并调用了renewToken,它也返回了Observable。但是问题在于它似乎无法访问renewToken的回调函数。因此,刷新的令牌永远不会应用,并且api调用也永远不会重新执行,但是,renewToken出现在我的网络标签中,并且成功。

我通过用renewToken()替换第一个getUser()调用来确保renewToken()确实有效,并且可以正常工作。所以我的猜测是,我对rxjs的困惑以及如何链接长管道等是这出了问题的地方。

intercept(
        request: HttpRequest<any>,
        next: HttpHandler
    ): Observable<HttpEvent<any>> {
        if (request.url.includes(environment.apiBaseUrl)) {
            return this.authService.getUser().pipe(
                mergeMap((user: User) => {
                    console.log('in getUser call ' + user.expires_in);
                    if (user) {
                        request = request.clone({
                            setHeaders: {
                                Authorization: `Bearer ${user.access_token}`
                            }
                        });
                    }

                    return next.handle(request).pipe(
                        tap(
                            () => {},
                            (err: any) => {
                                debugger
                                if (err instanceof HttpErrorResponse) {
                                    if (err.status === 401) {
                                        return this.authService.renewToken().pipe(
                                            mergeMap((renewedUser: User) => {
                                                console.log('in renewToken call');
                                                if (renewedUser) {
                                                    console.log(renewedUser.expires_in);
                                                    request = request.clone({
                                                        setHeaders: {
                                                            Authorization: `Bearer ${
                                                                renewedUser.access_token
                                                            }`
                                                        }
                                                    });
                                                }
                                                return next.handle(request).pipe(
                                                    tap(
                                                        () => {},
                                                        newErr => {
                                                            console.log(newErr);
                                                            this.authService.login();
                                                        }
                                                    )
                                                );
                                            }),
                                            catchError(renewError => {
                                                return Observable.throw(renewError);
                                            })
                                        );
                                    }
                                }
                            }
                        )
                    );
                })
            );
        } else {
            return next.handle(request);
        }
    }

因此,如果我在第一个请求上未经授权,我想刷新令牌并重做请求,但是我从未输入renewToken()的回调函数来添加新的accesstoken并重做请求。

此外,理想的情况是对用户使用过期属性,如果过期,则刷新令牌,然后仅处理请求一次,但是我不知道如何异步实现。

编辑:我很确定这与以下事实有关:我无法以我认为可以通过点击功能返回的方式返回某些内容。因此,当尝试返回renewToken或next.handle时,这是不可能的。尽管如此,如果第一个请求返回错误,我不知道如何实际更新令牌并使用新令牌重做请求

1 个答案:

答案 0 :(得分:0)

我设法通过用catchError替换tap来解决我的问题,为什么我不确定。如果有人知道,请解释。如果有人在发送第一个请求之前有一个想法,可以通过检查用户的expired属性来使其刷新,然后使其变得不安全,请告诉我。

return next.handle(request).pipe(
                        catchError((err: any) => {
                            if (err instanceof HttpErrorResponse) {
                                if (err.status === 401) {
                                    return this.authService.renewToken().pipe(
                                        mergeMap((renewedUser: User) => {
                                            if (renewedUser) {
                                                request = this.addBearerToken(
                                                    request,
                                                    renewedUser.access_token
                                                );
                                            }
                                            return next.handle(request).pipe(
                                                tap(
                                                    () => {},
                                                    newErr => {
                                                        console.log(newErr);
                                                        this.authService.login();
                                                    }
                                                )
                                            );
                                        }),
                                        catchError(renewError => {
                                            return Observable.throw(renewError);
                                        })
                                    );
                                }
                            }
                        })
                    );