2

我正在使用带有React Query的 GraphQL 代码生成器,这是我的codegen.yml

overwrite: true

schema: http://localhost:4000/graphql

generates:
  src/lib/__generated__/graphql.ts:
    documents:
      - "**/graphql/**/*.graphql"
      - "!mysqldata/**"
    plugins:
      - add:
          content: &comment "/* DO NOT EDIT! this file was generated by graphql-codegen */\n/* eslint-disable */"
      - add:
          placement: append
          content: "export { fetcher }"
      - typescript
      - typescript-operations
      - typescript-react-query
    config:
      fetcher:
        endpoint: "`${process.env.NEXT_PUBLIC_API_URL}/graphql`"
        fetchParams:
          credentials: include
          headers:
            Content-Type: application/json

这会生成以下提取器:

function fetcher<TData, TVariables>(query: string, variables?: TVariables) {
  return async (): Promise<TData> => {
    const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/graphql` as string, {
      method: "POST",
      credentials: "include",
      headers: {"Content-Type":"application/json"},
      body: JSON.stringify({ query, variables }),
    });
    
    const json = await res.json();

    if (json.errors) {
      const { message } = json.errors[0];

      throw new Error(message);
    }

    return json.data;
  }
}

NEXT_PUBLIC_API_URL指的是外部 GraphQL API。在我的 Next.js 应用程序中,我尝试使用nextjs-auth0auth0-react

nextjs-auth0允许我从 Next.js API 路由访问 Auth0 的 ID 令牌:

export default (req: NextApiRequest, res: NextApiResponse) => {
  const session = getSession(req, res)
  const idToken = session?.idToken

同时auth0-react允许我获取令牌客户端:

const claims = await auth0.getIdTokenClaims();
const idToken = claims.__raw;

问题是,由于这些抽象,我无法找到一种方法将此令牌包含在对我的 GraphQL 端点的请求中,例如:

headers: {
  authorization: `Bearer ${session?.idToken}`,
},
4

1 回答 1

1

在我发布了一个将 ID 令牌包含在 cookie 中的功能请求后,我发现设置的“appSession”cookienextjs-auth0是一个包含 ID 令牌的加密令牌,我使用nextjs-auth0源代码作为参考实现了自定义服务器逻辑:

type DecodedToken = Record<"idToken" | "token_type", string>

const API_BASE_URL = "https://example.com"
const BYTE_LENGTH = 32
const ENCRYPTION_INFO = "JWE CEK"
const HASH = "SHA-256"
const alg = "dir"
const enc = "A256GCM"

/**
 * Derives appropriate sized keys from provided secret random string/passphrase using
 * HKDF (HMAC-based Extract-and-Expand Key Derivation Function) defined in RFC 8569
 * @see https://tools.ietf.org/html/rfc5869
 */
function deriveKey(secret: string) {
  return hkdf(secret, BYTE_LENGTH, { info: ENCRYPTION_INFO, hash: HASH })
}

export const meQueryField = queryField("me", {
  type: "User",
  async resolve(_, __, ctx) {
    const jwe = ctx.request.cookies["appSession"]

    if (!jwe) {
      return null
    }

    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    const octKey = JWK.asKey(deriveKey(process.env["AUTH0_SECRET"]!))

    const { cleartext } = JWE.decrypt(jwe, octKey, {
      complete: true,
      contentEncryptionAlgorithms: [alg],
      keyManagementAlgorithms: [enc],
    })

    const { idToken, token_type: tokenType } = JSON.parse(
      cleartext.toString()
    ) as DecodedToken

    const response = await fetch(`${API_BASE_URL}/users/me`, {
      headers: {
        Authorization: `${tokenType} ${idToken}`,
      },
    })

    const user = (await response.json()) as Response

    return {
      id: user.data.id,
      ...
    }
  },
})

它不漂亮,但它有效。AUTH0_SECRET是用于加密令牌的相同秘密nextjs-auth0

于 2021-09-24T17:16:06.903 回答