简体   繁体   English

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

[英]How to implement auto refresh token in graphql for jwt based authentication?

I am trying to figure out this scenario for my JWT based authentication in Apollo based graphql server (2.0) . 我试图在基于Apollo的graphql server (2.0)中为基于JWT的身份验证找出这种情况。

Basically after login a user gets accessToken and refreshToken from server. 基本上在登录后,用户从服务器获取accessToken和refreshToken。

AccessToken gets expired after certain period of time and server sends an error message indicating that token expired ( TokenExpiredError ) and then client need to communicate with server for new accessToken via passing refreshToken. AccessToken在一段时间后过期,服务器发送一条错误消息,表明令牌已过期( TokenExpiredError ),然后客户端需要通过传递refreshToken与服务器进行新的accessToken通信。

Flow is as following - 流量如下 -

  1. TokenExpiredError occurs 发生TokenExpiredError
  2. Get that error on client side 在客户端获取该错误
  3. Queue all requests with old accessToken(so that server is not flooded with too many refreshToken calls and many accessTokens are generated by server) 使用旧的accessToken对所有请求进行排队(这样服务器不会被太多的refreshToken调用淹没,并且服务器会生成许多accessTokens)
  4. Call refreshToken api on graphql server to get new accessToken 在graphql server上调用refreshToken api以获取新的accessToken
  5. update accessToken for all authorised calls with new accessToken 使用新的accessToken更新所有授权呼叫的accessToken
  6. Logout user incase refreshToken itself is expired 注销用户incase refreshToken本身已过期
  7. Prevent any kind of race condition b/w calls 防止任何种类的竞争条件黑白电话

I have already implemented refreshToken mutation on client side but can't figure out about when error occurs stop all requests -> request new token -> make all pending request again and if refresh token is expired logout user. 我已经在客户端实现了refreshToken变异但是无法弄清楚何时发生错误停止所有请求 - >请求新令牌 - >再次进行所有待处理请求以及刷新令牌是否已过期注销用户。

I followed this approach to solve my problem finally 我按照这种方法最终解决了我的问题

Posting my approach for others 向别人发布我的方法

// @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();
        };
    });
}
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM