AWS Cognito OAuth2 与 PCKE "Invalid Request" 代码挑战
AWS Cognito OAuth2 with PCKE "Invalid Request" Code Challenge
我一直在尝试将 state
和 code_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...
我一直在尝试将 state
和 code_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...