AWS Cognito OAuth2 与 PCKE "Invalid Request" 代码挑战

AWS Cognito OAuth2 with PCKE "Invalid Request" Code Challenge

我一直在尝试将 statecode_challenge 添加到我们的流程中,但出于某种原因,我继续收到来自亚马逊的 invalid_request 回复。

我跟着这个 Auth0 tutorial 到了发球台。

  /**
   * Converts buffer to Base64 URL encoded string
   *
   * @param {Buffer} buf The buffer to convert
   * @returns {string}
   */
  private base64URLEncode(str: any): string {
    return str.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
  }

  /**
   * Generates a new code challenge
   *
   * @returns {string} The code challange string
   */
  private generateCodeChallenge(): string {
    if (!this._verifier) this._verifier = this.base64URLEncode(crypto.randomBytes(32));

    return this.base64URLEncode(crypto.createHash('sha256').update(this._verifier).digest());
  }

  /**
   * Generates the Authorization URL
   */
  public generateAuthUrl(): string {
    if (!this.config.clientId) throw new Error('Client ID is missing from configuration.');
    if (!this.config.redirectUrl) throw new Error('Redirect URI is missing from configuration.');

    if (!this._state) this._state = this.base64URLEncode(crypto.randomBytes(28));

    return `${this.config.protocol}://signin.${
      this.config.host
    }/login?response_type=code&client_id=${this.config.clientId}&redirect_uri=${
      this.config.redirectUrl
    }&scope=${this._scope.join('%20')}&state=${
      this._state
    }&code_challenge_method=S256&code_challenge=${this.generateCodeChallenge()}`;
  }

  /**
   * Verifies that the state matches
   *
   * @returns {boolean}
   */
  public verifyState(state: string): boolean {
    return this._state === state;
  }

  /**
   * Retrieves a new OAuth Authorization Grant token
   */
  public async getToken(code: string): Promise<UserAccessToken> {
    if (!this.config.clientId) throw new Error('Client ID is missing from configuration.');
    if (!this.config.redirectUrl) throw new Error('Redirect URI is missing from configuration.');

    try {
      const token = (await this.req.postform(
        `/oauth2/token`,
        {
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Cache-Control': 'no-store',
          },
        },
        {
          grant_type: 'authorization_code',
          code,
          client_id: this.config.clientId,
          code_verifier: this._verifier,
          redirect_uri: this.config.redirectUrl,
        }
      )) as UserAccessToken;

      this._userAccessToken = token;

      return token;
    } catch (err) {
      throw err;
    }
  }

一切正常,直到用代码交换令牌。我们重定向到我们托管的 UI 并成功取回代码。当我们用它交换令牌时,无论我尝试了什么,我们都会得到 invalid_request 响应。如果我删除代码挑战,一切都会按预期工作,所以我非常有信心这就是导致问题的原因。

更新 1

这个函数在我们的 React 应用程序中的 useEffect 挂钩中调用,如下所示:

  useEffect(() => {
    const authUrl = sdk.auth.generateAuthUrl();
    console.log(authUrl);
    if (!code) window.location.assign(authUrl);
    console.log(`New Auth URL: ${authUrl}`);
    sdk.auth
      .getToken(code as string)
      .then((token) => {
        localStorage.setItem('sdk_access_token', JSON.stringify(token));
        sdk.organizations.getUser().then((user) => {
          setUser({ ...user });
          history.push('/');
        });
      })
      .catch((err) => console.log(err));
  }, []);

两个 console.log 语句显示相同的 URL:

但是在网络调试选项卡上,请求URL是不同的:

这怎么可能? window.location.href 是否以某种方式编码 URL?

据我所知,您的代码和消息看起来不错。如果您在 Cognito 中配置了可以解释问题的 App Client Secret(Cognito 错误响应不是最好的)。

也许可以从我的 Cognito 工作样本中尝试这些硬编码值 - 或者 运行 您通过 this tool 计算的值。这将使您知道问题是否与 PKCE 值有关:

  • 挑战:tQV5Ny5h0r5pwqpDr0OGnQWgfU4DTTniuu99HZGOOE0
  • 验证者:4ffc114e504047deb57f3e8dfeeda70d93bbfb6d8f0444c9b9b147f3872dbde24f58cbe3047d49ce9b5194fe80e0905a

虽然我希望 Auth0 代码没问题,但有很多库有一些 working PKCE code...