我想使用 Aws Rest Api Gateway 构建一个 Rest API。这将是现有生产 api(托管在私有服务器上)的新版本。
在当前版本的 api 中,我们使用带有授予类型密码的 oauth2 进行身份验证。这意味着客户端发送其用户名并传递到“.../access_token”端点,从那里获取令牌。有了这个令牌,他们就可以调用其他端点。
在新的 api 版本中,我将 AWS Api Gateway 与 Authorizer 结合使用。我想根据用户名和密码字段提供对资源的访问权限。
我创建了一个用户池并在其中添加了我的用户。如何仅使用 api 端点进行身份验证?
我无法使用 Oauth“客户端凭据”流程,因为它是机器到机器的,客户端机密将被暴露。
在授权代码或隐式授权中,我必须要求用户登录 AWS/自定义 ui 并获得重定向。所以我不能在 Api 中使用这些。
我错过了什么?
我了解您需要在不使用浏览器的情况下对用户进行身份验证。一个想法是创建一个登录端点,用户将在其中提供他们的用户名和密码并取回令牌。您应该自己实现此端点。来自这个问题:
aws cognito-idp admin-initiate-auth --region {your-aws-region} --cli-input-json file://auth.json
auth.json 所在位置:
{
"UserPoolId": "{your-user-pool-id}",
"ClientId": "{your-client-id}",
"AuthFlow": "ADMIN_NO_SRP_AUTH",
"AuthParameters": {
"USERNAME": "[email protected]",
"PASSWORD": "password123"
}
}
这将为您的用户提供访问权限、ID 和刷新令牌(与授权授予类型相同的方式)。他们应该能够使用访问令牌来访问资源,并针对令牌端点使用刷新令牌来更新访问令牌。
这不是验证 API 的常用方法,并且可能会产生一些安全隐患。
我通过在 NodeJS 16x 中使用公开的 URL 创建自定义 Lambda 解决了这个问题,它使用存储的应用程序客户端 ID、用户池 ID、秘密在 Cognito 端进行基本身份验证。我在此处附加了代码,但您仍然需要使用 Cognito SDK 创建 lambda 层,并自行配置 IAM。
const AWS = require('aws-sdk');
const {
CognitoIdentityProviderClient,
AdminInitiateAuthCommand,
} = require("/opt/nodejs/node16/node_modules/@aws-sdk/client-cognito-identity-provider");
const client = new CognitoIdentityProviderClient({ region: "eu-central-1" });
exports.handler = async (event, context, callback) => {
let username = event.queryStringParameters.username;
let password = event.queryStringParameters.password;
let app_client_id = process.env.app_client_id;
let app_client_secret = process.env.app_client_secret;
let user_pool_id = process.env.user_pool_id;
let hash = await getHash(username, app_client_id, app_client_secret);
let auth = {
"UserPoolId": user_pool_id,
"ClientId": app_client_id,
"AuthFlow": "ADMIN_NO_SRP_AUTH",
"AuthParameters": {
"USERNAME": username,
"PASSWORD": password,
"SECRET_HASH": hash
}
};
let cognito_response = await requestToken(auth);
var lambda_response;
if (cognito_response.startsWith("Error:")){
lambda_response = {
statusCode: 401,
body: JSON.stringify(cognito_response) + "\n input: username = " + username + " password = " + password,
};
}
else {
lambda_response = {
statusCode: 200,
body: JSON.stringify("AccessToken = " + cognito_response),
};
}
return lambda_response;
};
async function getHash(username, app_client_id, app_client_secret){
const { createHmac } = await import('node:crypto');
let msg = new TextEncoder().encode(username+app_client_id);
let key = new TextEncoder().encode(app_client_secret);
const hash = createHmac('sha256', key) // TODO should be separate function
.update(msg)
.digest('base64');
return hash;
}
async function requestToken(auth) {
const command = new AdminInitiateAuthCommand(auth);
var authResponse;
try {
authResponse = await client.send(command);
} catch (error) {
return "Error: " + error;
}
return authResponse.AuthenticationResult.AccessToken;
}