ztcognito/aws-cognito

AWS Cognito 包,允许使用 AWS SDK for PHP 进行身份验证和其他相关功能

dev-main 2022-01-06 08:33 UTC

This package is auto-updated.

Last update: 2024-09-06 14:37:25 UTC


README

使用 AWS SDK for PHP 的 AWS Cognito 包

此包提供了一种简单的方法,在 Laravel 7.x 中使用 AWS Cognito 进行 Web 和 API 身份验证驱动程序的认证。此包的思路和一些代码基于 Pod-Point 的包,您可以在以下位置找到它:Pod-Point/laravel-cognito-authblack-bits/laravel-cognito-authtymondesigns/jwt-auth

我们决定将其用于并将它作为一个社区包贡献出来,鼓励使用标准化的方式,并使用 AWS Cognito 进行身份验证的 RAD 工具。

特性

  • 注册和确认电子邮件
  • 首次登录强制更改密码(可配置)
  • 登录
  • 记住我Cookie
  • 单点登录
  • 忘记密码
  • 用户删除
  • 编辑用户属性
  • 重置用户密码
  • 确认注册
  • 简单的 API 令牌处理(使用缓存驱动程序)
  • 支持 DynamoDB 进行 Web 会话和 API 令牌(对于服务器冗余或多容器很有用)
  • 简单的令牌过期配置(使用 cognito 控制台管理,无需代码或配置)

安装

您可以通过 composer 安装此包。

composer require ztcognito/aws-cognito

Laravel 5.4 及之前版本

使用 Laravel 5.5 之前的版本,您需要手动注册服务提供者。

    // config/app.php
    'providers' => [
        ...
        Zt\Cognito\Providers\AwsCognitoServiceProvider::class,
        
    ];

接下来,您可以发布配置和视图。

    php artisan vendor:publish --provider="Zt\Cognito\Providers\AwsCognitoServiceProvider"

最后,您需要更改身份验证驱动程序。要这样做,请打开您的 config\auth.php 文件,并将其更改为以下内容

    'guards' => [
        'web' => [
            'driver' => 'cognito-session', // This line is important for using AWS Cognito as Web Driver
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'cognito-token', // This line is important for using AWS Cognito as API Driver
            'provider' => 'users',
        ],
    ],

Cognito 用户池

要使用 AWS Cognito 作为身份验证提供者,您需要一个 Cognito 用户池。

如果您还没有创建一个,请转到您的 Amazon 管理控制台 并创建一个新的用户池。

接下来,生成一个应用程序客户端。这将为您提供用于 .env 文件的应用程序客户端 ID 和应用程序客户端密钥。

重要提示:不要忘记勾选启用基于服务器的身份验证的登录 API 的复选框。身份验证流程称为:ADMIN_USER_PASSWORD_AUTH(以前称为 ADMIN_NO_SRP_AUTH)

您还需要一个新的 IAM 角色,具有以下访问权限

  • AmazonCognitoDeveloperAuthenticatedIdentities
  • AmazonCognitoPowerUser
  • AmazonESCognitoAccess

从这个用户中,您可以获取 AWS_ACCESS_KEY_ID 和 AWS_SECRET_ACCESS_KEY。

Cognito API 配置

将以下字段添加到您的 .env 文件中,并根据您的 AWS 设置设置值

    # AWS configurations for cloud storage
    AWS_ACCESS_KEY_ID="Axxxxxxxxxxxxxxxxxxxxxxxx6"
    AWS_SECRET_ACCESS_KEY="mxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx+"

    # AWS Cognito configurations
    AWS_COGNITO_CLIENT_ID="6xxxxxxxxxxxxxxxxxxxxxxxxr"
    AWS_COGNITO_CLIENT_SECRET="1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1"
    AWS_COGNITO_USER_POOL_ID="xxxxxxxxxxxxxxxxx"
    AWS_COGNITO_REGION="xxxxxxxxxxx" //optional - default value is 'us-east-1'
    AWS_COGNITO_VERSION="latest" //optional - default value is 'latest'

有关如何为您的应用程序找到 AWS_COGNITO_CLIENT_ID、AWS_COGNITO_CLIENT_SECRET 和 AWS_COGNITO_USER_POOL_ID 的更多详细信息,请参阅COGNITOCONFIG 文件

将现有用户导入到 Cognito 池中

如果您正在处理一个现有的项目,并希望集成 Cognito,您必须 将用户 csv 文件导入到您的 Cognito 池中

用法

我们的包为您提供了 6 个特质,您只需将其添加到您的 Auth 控制器中即可使我们的包运行。

  • Zt\Cognito\Auth\AuthenticatesUsers
  • Zt\Cognito\Auth\RegistersUsers
  • Zt\Cognito\Auth\ResetsPasswords
  • Zt\Cognito\Auth\RespondsMFAChallenge
  • Zt\Cognito\Auth\SendsPasswordResetEmails
  • Zt\Cognito\Auth\VerifiesEmails

最简单的方法就是遍历您的Auth控制器,并将当前从Laravel中实现的特性命名空间进行更改。

您可以根据需要更改结构。请注意blade文件中的@extend语句,以适应您项目的结构。在当前状态下,您需要在这里定义这4个表单字段:token、email、password和password_confirmation。

单点登录

使用我们的包和AWS Cognito,我们为您提供了一个简单的方式来使用单点登录。有关配置选项,请参阅config cognito.php

当您希望启用SSO并且用户尝试登录到您的应用程序时,该包会检查用户是否存在于您的AWS Cognito池中。如果用户存在,并且如果add_missing_local_user_sso设置为true,则用户将自动在您的数据库中创建,并且可以同时登录。

这就是我们使用sso_user_model和cognito_user_fields字段的原因。在sso_user_model中,您定义用户模型类。在大多数情况下,这将是简单地App\User

使用cognito_user_fields,您可以定义应存储在Cognito中的字段。请注意这里。如果您定义了一个不与注册请求一起发送的字段,这将抛出InvalidUserFieldException,您将无法注册。

现在,您已经将用户及其属性注册到AWS Cognito池和数据库中,并且您想附加一个应该使用相同池的第二个应用程序。实际上,这非常简单。您可以使用允许多个项目消费相同AWS Cognito池的API provision。

*重要提示:如果您的用户表有密码字段,您将不再需要它。您需要做的是将此字段设置为可空的,以便可以在没有密码的情况下创建用户。从现在开始,密码将存储在Cognito中。

您有任何其他注册数据,例如firstname、lastname,需要添加到cognito.php cognito_user_fields配置中,以便推送到Cognito。否则,它们仅在本地上存储,并且如果您想使用单点登录,则不可用。

API路由的中间件配置

如果您将此库用作API驱动程序,可以将中间件注册到kernal.php中的$routeMiddleware。

    protected $routeMiddleware = [
        ...
        'aws-cognito' => \Zt\Cognito\Http\Middleware\AwsCognitoAuthenticate::class
    ]

要使用中间件在Web路由中,您可以像下面那样使用标准的auth中间件

    Route::middleware('auth')->get('user', 'NameOfTheController@functionName');

要使用中间件在API路由中,如下所示

    Route::middleware('aws-cognito')->get('user', 'NameOfTheController@functionName');

注册用户

默认情况下,如果您使用Cognito注册新用户,Cognito将在signUp过程中发送一封电子邮件,其中包含用户名和临时密码,以便用户进行身份验证。

结合使用此库和AWS Lambda,可以自定义电子邮件模板和内容。电子邮件模板可以是文本或HTML。Lambda代码不包括在此代码库中。您可以创建自己的。任何传递给注册方法的对象(数组)都将原样传递给Lambda函数,我们不对属性名称进行规定。

我们使任何人都能轻松使用默认行为。

  1. 您不需要创建额外的字段来存储验证令牌。
  2. 您不必担心会话或API令牌,它们由您管理。会话或令牌是通过Laravel的标准机制管理的。您可以根据自己的意愿将其保留在任何地方,没有安全漏洞。
  3. 如果您使用我们提供的特性 'Zt\Cognito\Auth\RegistersUsers',代码将仅限于几行。
  4. 如果您使用 Laravel 框架的 scafolding,请将数据库中的密码设置为可空或从模式中删除。密码将由 AWS Cognito 管理。
    use Zt\Cognito\Auth\RegistersUsers;

    class UserController extends BaseController
    {
        use RegistersUsers;

        public function register(Request $request)
        {
            $validator = $request->validate([
                'name' => 'required|max:255',
                'email' => 'required|email|max:64|unique:users',
                'password' => 'sometimes|confirmed|min:6|max:64',
            ]);

            //Create credentials object
            $collection = collect($request->all());
            $data = $collection->only('name', 'email', 'password'); //passing 'password' is optional.

            //Register User in cognito
            if ($cognitoRegistered=$this->createCognitoUser($data)) {

                //If successful, create the user in local db
                User::create($collection->only('name', 'email'));
            } //End if

            //Redirect to view
            return view('login');
        }
    }
  1. 您不需要关闭 Cognito 来发送电子邮件。我们更建议使用 AWS Cognito 或 AWS SMS 邮件发送者,这样使用凭证总是安全的。

用户身份验证

我们提供了一种有用的特性,使身份验证变得非常简单(适用于 Web 或 API 路由)。您不需要担心任何额外的代码来管理会话和令牌(对于 API)。

该特性接受一些额外的参数,请参考以下特性的函数签名。注意,该函数接受 Illuminate\Support\Collection 对象而不是 Illuminate\Http\Request 对象。这将允许您在任何代码层中使用此函数。

此外,'guard' 名称引用是通过传递的,因此您可以在项目中重用该函数以针对多个守护程序驱动程序。该函数具有处理多个驱动程序和提供程序的能力,这些驱动程序和提供程序在 /config/auth.php 中定义。

    namespace Zt\Cognito\Auth;

    protected function attemptLogin (
        Collection $request, string $guard='web', 
        string $paramUsername='email', string $paramPassword='password', 
        bool $isJsonResponse=false
    ) {
        ...
        ...


        ...
    }

如果您想使用此特性进行 Web 登录,您可以在 AuthController.php 中编写以下代码。

    namespace App\Http\Controllers;

    ...

    use Zt\Cognito\AwsCognitoClaim;
    use Zt\Cognito\Auth\AuthenticatesUsers as CognitoAuthenticatesUsers;

    class AuthController extends Controller
    {
        use CognitoAuthenticatesUsers;

        /**
         * Authenticate User
         * 
         * @throws \HttpException
         * 
         * @return mixed
         */
        public function login(\Illuminate\Http\Request $request)
        {
            ...

            //Convert request to collection
            $collection = collect($request->all());

            //Authenticate with Cognito Package Trait (with 'web' as the auth guard)
            if ($response = $this->attemptLogin($collection, 'web')) {
                if ($response===true) {
                    return redirect(route('home'))->with('success', true);
                } else if ($response===false) {
                    // If the login attempt was unsuccessful you may increment the number of attempts
                    // to login and redirect the user back to the login form. Of course, when this
                    // user surpasses their maximum number of attempts they will get locked out.
                    //
                    //$this->incrementLoginAttempts($request);
                    //
                    //$this->sendFailedLoginResponse($collection, null);
                } else {
                    return $response;
                } //End if
            } //End if

        } //Function ends

        ...
    } //Class ends

如果您想使用此特性进行基于 API 的登录,您可以在 AuthApiController.php 中编写以下代码。

    namespace App\Api\Controller;

    ...

    use Zt\Cognito\AwsCognitoClaim;
    use Zt\Cognito\Auth\AuthenticatesUsers as CognitoAuthenticatesUsers;

    class AuthApiController extends Controller
    {
        use CognitoAuthenticatesUsers;

        /**
         * Authenticate User
         * 
         * @throws \HttpException
         * 
         * @return mixed
         */
        public function login(\Illuminate\Http\Request $request)
        {
            ...

            //Convert request to collection
            $collection = collect($request->all());

            //Authenticate with Cognito Package Trait (with 'api' as the auth guard)
            if ($claim = $this->attemptLogin($collection, 'api', 'username', 'password', true)) {
                if ($claim instanceof AwsCognitoClaim) {
                    return $claim->getData();
                } else {
                    return response()->json(['status' => 'error', 'message' => $claim], 400);
                } //End if
            } //End if

        } //Function ends


        ...
    } //Class ends

删除用户

如果您想允许用户从您的应用程序中删除自己,您可以使用我们来自 CognitoClient 的 deleteUser 函数。

要删除用户,您应该调用 deleteUser 并将用户的电子邮件作为参数传递给它。用户在您的 Cognito 池中被删除后,也从您的数据库中删除用户。

        $cognitoClient->deleteUser($user->email);
        $user->delete();

我们实现了一个新的配置选项 delete_user,您可以通过 AWS_COGNITO_DELETE_USER 环境变量来访问它。如果您将此配置设置为 true,则用户将在 Cognito 池中被删除。如果设置为 false,它将保持注册状态。默认情况下,此选项设置为 false。如果您想使用此行为,您应将 USE_SSO 设置为 true,以便用户在成功登录后恢复自己。

要访问我们的 CognitoClient,您可以简单地将其作为参数传递到您要在其中执行删除操作的控制器操作中。

    public function deleteUser(Request $request, AwsCognitoClient $client)

Laravel 将自动处理依赖注入。

    IMPORTANT: You want to secure this action by maybe security questions, a second delete password or by confirming 
    the email address.

将 Web 会话或 API 令牌存储在 DynamoDB 中(适用于多服务器/容器实现)

如果您有一个涉及多个服务器的部署架构,并且您想跨服务器维护 Web 会话或 API 令牌,您可以使用 AWS DynamoDB。该库能够轻松处理 DynamoDB。您需要做的就是创建 AWS DynamoDB 中的表并更改一些配置。

在 AWS DynamoDB 中创建新表

  1. 前往 AWS 控制台并创建新表。
  2. 输入您偏好的唯一表名。
  3. 主键(或分区键)应该是类型为 stringkey
  4. 使用默认设置并点击 创建 按钮。

更新 .env 文件以进行 DynamoDB 配置

将以下字段添加到您的 .env 文件中,并根据您的 AWS 设置设置值

    # Cache Configuration
    CACHE_DRIVER="dynamodb"
    DYNAMODB_CACHE_TABLE="table-name-of-your-choice" //This should match the table name provided above

    # Session Configuration
    SESSION_DRIVER="dynamodb"
    SESSION_LIFETIME=120
    SESSION_DOMAIN="set-your-domain-name" //The domain name can be as per your preference
    SESSION_SECURE_COOKIE=true

    # DynamoDB Configuration
    DYNAMODB_ENDPOINT="https://dynamodb.us-west-2.amazonaws.com" // You can change the endpoint based of different regions

参考 AWS DynamoDB 文档 并参考 服务端点 部分中提供的端点。

更新 DynamoDB 表以 TTL 列为 expires_at

为 API 使用自动更新用户密码(适用于新 Cognito 用户)

对于新 Cognito 用户,AWS SDK 将发送一个会话密钥,并期望用户以强制方式更改密码。请确保您强制新 Cognito 用户在首次登录时更改密码。

但是,如果您有一个基于API的实现,并且想要在不强制更改密码的情况下自动验证用户,您可以通过将以下设置字段添加到您的 .env 文件来实现。

    AWS_COGNITO_FORCE_PASSWORD_CHANGE_API=false     //Make true for forcing password change
    AWS_COGNITO_FORCE_PASSWORD_AUTO_UPDATE_API=true //Make false for stopping auto password change

免责声明

该软件包目前处于生产就绪模式,已经完成了一些实现。我们很高兴听到您的反馈,包括缺陷或新功能增强。然而,鉴于这是免费支持,我们无法承诺支持服务等级协议(SLA)或时间表。