具有服务器端身份验证和 Cognito 的无服务器框架

Serverless Framework with server-side authentication and Cognito

我已经使用带有 APIG 的无服务器框架、DynamoDB 作为数据存储和用于用户身份验证的 Cognito 以及 Angular2 作为前端实现了各种 REST-APIs。服务器端的 Cognito 授权方可以轻松保护这些功能。缺点是我必须将 AWS SDK 集成到我的前端应用程序中,以便首先使用 Cognito 对用户进行身份验证 (signup/signin, ...)。我也可以使用 AWS_IAM 授权方,但在将请求发送到 API 网关之前,我还必须在客户端使用 AWS specific signature 签署所有请求。

现在我想知道是否有可能在服务器端保留身份验证和授权,这样我就可以为 signup/signin 使用像 JSON Web Tokens 这样的开放标准?这将使我也可以为其他开发人员开放我的 REST-API ,而无需强迫他们使用 Cognito

我知道一种可能性是为我的 lambda 函数实现自定义授权器,但是没有任何稳定的东西可以使用 "out-of-the-box" 吗?我发现的大多数示例都在客户端使用 Cognito 或 IAM auth AWS 签名登录(例如 serverless-stack.com)。

奇怪的是,到目前为止我没有在网上找到任何关于此的有用信息,因为我认为这是 REST APIs 的典型用例。还是我对 API Gateway + Cognito 有概念上的误解?

在理解 AWS Cognito 的工作方式以及可用于实施身份验证和授权的选项方面,我遇到了同样的麻烦。不幸的是,没有 开箱即用的 方法可以满足您的要求。尽管如此,还是希望亚马逊能尽快推出一项功能。

基本上,有 3 个选项可用于实施身份验证。

  1. AWS_IAM
  2. Cognito 授权方
  3. 自定义授权方

AWS_IAM

除了身份验证之外,此方法还可用于使用 IAM 角色或 IAM 用户轻松实现授权。唯一的缺点是您需要发送使用 aws-signature-4 签名的请求,这不是我们在 Auth0 等 IDP 服务中看到的标准方式。

Cognito 授权方

此方法满足发送带有 API 请求的 JWT 令牌的预期。您可以在 Cognito 用户池中创建用户,然后使用它进行身份验证并生成 IdToken。但是,此方法只允许您对用户进行身份验证;授权需要在方法级别处理。

自定义授权方

这个方法可以用来写自己的认证授权方式。它还有助于消除在 API 方法中编写授权逻辑。理想的解决方案是使用 AWS Cognito 用户池对用户进行身份验证,然后为 IAM 角色生成策略文档以访问资源。 这是一个示例 。 另请记住,此解决方案将为您发出的每个请求调用一个额外的 lambda 函数。

您可以使用 Cognito Auth 来 Server-side。以下是步骤。

实施 Sign-up 和 Sign-in

  • 在前端实现 Sign-up 表单,API 网关端点(例如 /register)使用 Lambda 接收 Sign-up 数据,这将创建用户在 Cognito 中使用 AWS SDK。详细参考检查 this link.

    AWSCognito.config.region = 'us-east-1'; //This is required to derive the endpoint
    
    var poolData = { UserPoolId : 'us-east-1_TcoKGbf7n',
        ClientId : '4pe2usejqcdmhi0a25jp4b5sh3'
    };
    var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
    
    var attributeList = [];
    
    var dataEmail = {
        Name : 'email',
        Value : 'email@mydomain.com'
    };
    var dataPhoneNumber = {
        Name : 'phone_number',
        Value : '+15555555555'
    };
    var attributeEmail = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserAttribute(dataEmail);
    var attributePhoneNumber = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserAttribute(dataPhoneNumber);
    
    attributeList.push(attributeEmail);
    attributeList.push(attributePhoneNumber);
    
    userPool.signUp('username', 'password', attributeList, null, function(err, result){
        if (err) {
            alert(err);
            return;
        }
        cognitoUser = result.user;
        console.log('user name is ' + cognitoUser.getUsername());
    });
    
  • 通过创建前端和 API 网关端点(例如 /login)对 Sign-in 执行类似操作

    var authenticationData = {
        Username : 'username',
        Password : 'password'
    };
    var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
    var poolData = { UserPoolId : 'us-east-1_TcoKGbf7n',
        ClientId : '4pe2usejqcdmhi0a25jp4b5sh3'
    };
    var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
    var userData = {
       Username : 'username',
       Pool : userPool
    };
    var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
       cognitoUser.authenticateUser(authenticationDetails, {
          onSuccess: function (result) {
             console.log('access token + ' + result.getAccessToken().getJwtToken());
         /* Use the idToken for Logins Map when Federating User Pools with Cognito Identity or when passing through an Authorization Header to an API Gateway Authorizer */
        console.log('idToken + ' + result.idToken.jwtToken);
       },
       onFailure: function(err) {
          alert(err);
       },
    });
    

从您的浏览器存储和发送 JWT,并在 API 网关进行验证。

  • 从 Sign-in API 端点收到 JWT 后,您可以使用 HTML5 Localstorage、Sessionstorage 或客户端 Cookie 将其存储在用户浏览器本地.或者,如果您需要使用服务器端 Cookie,则需要有一个代理后端,它与 Web 应用程序保持 Session 状态并将其转换为 JWT 以调用 API 网关。
  • 从 Web 浏览器(假设您的客户端直接调用 API 网关)设置一个名为 Authorization 的 HTTP header 并将 JWT 转发到 API 网关调用。
  • 在 API 网关使用 Cognito 授权器作为授权令牌,它还将解析的用户身份转发给您的 Lambda。

注意:这里我有意避免了 IAM 授权,因为它需要 Web App JavaScripts 的一些额外工作才能在浏览器上实现 Signature 4 Signing,并且还需要经常刷新令牌,这是使用 AWS JavaScript SDK 很简单,但如果您需要自己实施它会变得复杂。

请看看这个here

该示例演示了各种配置,包括自定义授权器、cognito、lambda、dynamoDB 等