如何在 graphql 中为基于 jwt 的身份验证实现自动刷新令牌?

Posted

技术标签:

【中文标题】如何在 graphql 中为基于 jwt 的身份验证实现自动刷新令牌?【英文标题】:How to implement auto refresh token in graphql for jwt based authentication? 【发布时间】:2018-12-18 03:51:00 【问题描述】:

我正在尝试在基于 Apollo 的 graphql 服务器 (2.0) 中为我的基于 JWT 的身份验证找出这种情况。

基本上,用户在登录后会从服务器获取 accessToken 和 refreshToken。

AccessToken在一定时间后过期,服务器发送错误消息指示令牌过期(TokenExpiredError),然后客户端需要通过传递refreshToken与服务器通信以获得新的accessToken。

流程如下-

    TokenExpiredError 发生 在客户端获取该错误 使用旧的 accessToken 对所有请求进行排队(这样服务器不会被太多的 refreshToken 调用淹没,并且服务器会生成许多 accessToken) 在graphql服务器上调用refreshToken api获取新的accessToken 使用新的 accessToken 更新所有授权调用的 accessToken 如果 refreshToken 本身已过期,请注销用户 防止任何类型的竞争条件黑白调用

我已经在客户端实现了 refreshToken 突变,但无法弄清楚何时发生错误停止所有请求 -> 请求新令牌 -> 再次发出所有待处理的请求,如果刷新令牌已过期注销用户。

【问题讨论】:

在请求之前请求令牌怎么样? ... :) @dbvt10 这将是一种非常低效的方法 @WitVault 的意思是检查过期日期、在过期前请求新令牌、替换令牌并用新令牌发出新请求……这些进程可以在后台运行,因此您不需要停止/延迟请求。 .. @dbvt10 你的想法确实不错,在令牌实际过期前几分钟刷新令牌并更新本地存储中的令牌,以便新请求使用最新更新的令牌 【参考方案1】:

我最终按照this的方法解决了我的问题

为他人发布我的方法

// @flow
import  ApolloLink, Observable  from 'apollo-link';
import type  ApolloClient  from 'apollo-client';
import type  Operation, NextLink  from 'apollo-link';

import  refreshToken2, getToken  from './token-service';
import  GraphQLError  from 'graphql';

export class AuthLink extends ApolloLink 
     tokenRefreshingPromise: Promise<boolean> | null;

injectClient = (client: ApolloClient): void => 
    this.client = client;
;

refreshToken = (): Promise<boolean> => 
    //if (!this.tokenRefreshingPromise) this.tokenRefreshingPromise = refreshToken(this.client);
    if (!this.tokenRefreshingPromise) this.tokenRefreshingPromise = refreshToken2();
    return this.tokenRefreshingPromise;
;

setTokenHeader = (operation: Operation): void => 
    const token = getToken();
    if (token) operation.setContext( headers:  authorization: `Bearer $token`  );
;

request(operation: Operation, forward: NextLink) 
    // set token in header
    this.setTokenHeader(operation);
    // try refreshing token once if it has expired
    return new Observable(observer => 
        let subscription, innerSubscription, inner2Subscription;
        try 
            subscription = forward(operation).subscribe(
                next: result => 
                    if (result.errors) 
                        console.log("---->", JSON.stringify(result.errors))
                        for (let err of result.errors) 
                            switch (err.extensions.code) 
                              case 'E140':
                                console.log('E140', result)
                                observer.error(result.errors)
                                break;
                              case 'G130':
                                    this.refreshToken().then(response => 
                                        if (response.data && !response.errors) 
                                            this.setTokenHeader(operation);
                                            innerSubscription = forward(operation).subscribe(observer);
                                         else 
                                            console.log("After refresh token", JSON.stringify(response));
                                            observer.next(response)
                                        
                                    ).catch(console.log);
                                break;
                            
                          
                     
                    observer.next(result)

                  ,
                complete: observer.complete.bind(observer),
                error: netowrkError => 
                    observer.error(netowrkError);
                  
                ,
            );
         catch (e) 
            observer.error(e);
        
        return () => 
            if (subscription) subscription.unsubscribe();
            if (innerSubscription) innerSubscription.unsubscribe();
            if (inner2Subscription) inner2Subscription.unsubscribe();
        ;
    );


【讨论】:

以上是关于如何在 graphql 中为基于 jwt 的身份验证实现自动刷新令牌?的主要内容,如果未能解决你的问题,请参考以下文章

如何自定义 django jwt graphql 身份验证

如何在 Spring Security 中为所有请求添加 jwt 身份验证标头?

基于类的视图django中的JWT验证

带有 JWT 身份验证实现的 Django GraphQL API 仍然允许来自 Postman 的未经身份验证的请求获取数据。我该如何解决?

使用 Django GraphQL JWT 和 Graphene Relay 进行身份验证和授权

如何在 python 中生成唯一的身份验证令牌?