trusfin/aws-cognito

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

v2.0 2021-10-03 09:22 UTC

This package is auto-updated.

Last update: 2024-09-12 03:11:03 UTC


README

EllaiSys AWS Cloud Capability

Laravel 包,用于使用 AWS Cognito 管理Web和API身份验证

使用 AWS SDK for PHP 的 AWS Cognito 包

Latest Version on Packagist Release Date Total Downloads APM

此包提供了一种简单的方法,在 Laravel 中使用 AWS Cognito 身份验证进行 Web 和 API 身份验证驱动程序。这个包的想法和一些代码基于 Pod-Point 的包,您可以在以下位置找到:[Pod-Point/laravel-cognito-auth](https://github.com/Pod-Point/laravel-cognito-auth),[black-bits/laravel-cognito-auth](https://github.com/black-bits/laravel-cognito-auth),[tymondesigns/jwt-auth](https://github.com/tymondesigns/jwt-auth)。

演示应用程序。您可以尝试注册和登录。首次登录时,将强制用户更改密码。演示应用程序的 源代码 也可在 GitHub 上找到。

我们决定使用它并将其作为一个包贡献给社区,这个包鼓励标准化使用和 AWS Cognito 身份验证的 RAD 工具。

特性

兼容性

安装

您可以通过 composer 安装此包。

composer require ellaisys/aws-cognito

Laravel 5.4 及以前版本

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

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

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

    php artisan vendor:publish --provider="Ellaisys\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 管理控制台](https://console.aws.amazon.com/cognito/home) 并创建一个新的用户池。

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

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

AWS IAM 配置

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

  • AmazonCognitoDeveloperAuthenticatedIdentities
  • AmazonCognitoPowerUser
  • AmazonESCognitoAccess

从这个 IAM 用户,您必须在 Laravel 环境文件中使用 AWS_ACCESS_KEY_IDAWS_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 控制器中即可运行我们的包。

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

以最简单的方式,您只需遍历您的 Auth 控制器,并将当前从 Laravel 实现的特性中的命名空间更改即可。

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

单点登录

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

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

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

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

现在,您已经在 AWS Cognito 池和数据库中注册了用户及其属性,并且您想要连接第二个应用程序,该应用程序应使用相同的池。实际上,这很简单。您可以使用允许多个项目消费相同 AWS Cognito 池的 API 规定。

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

您拥有的任何其他注册数据,例如 firstnamelastname,需要添加到 cognito.php 中的 cognito_user_fields 配置中,以便推送到 Cognito。否则,它们只会在本地存储,并且当您想要使用单点登录时不可用。

忘记密码并重新发送选项

如果用户尚未激活账户,AWS Cognito 作为默认功能不允许用户使用忘记密码功能。我们引入了 AWS 文档中记录的功能,允许重新发送密码。

我们已将其配置为可供开发人员使用,以便他们可以根据业务需求使用它。配置采用布尔值。默认值为 true(允许重新发送忘记密码)

    AWS_COGNITO_ALLOW_FORGOT_PASSWORD_RESEND=true

API 路由的中间件配置

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

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

要使用中间件进行Web路由,可以像下面这样使用标准认证中间件

    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. 如果您使用我们提供的特质'Ellaisys\Cognito\Auth\RegistersUsers',代码将仅限于几行
  4. 如果您使用Laravel支架,则在数据库中将密码设置为可空或在模式中删除它。密码将仅由AWS Cognito管理。
    use Ellaisys\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邮件,这样使用的凭证总是安全的。

  2. 如果您想抑制发送给新用户的邮件,可以配置以下参数以跳过新用户注册的欢迎邮件。默认配置将发送欢迎邮件。

    AWS_COGNITO_NEW_USER_MESSAGE_ACTION="SUPPRESS"
  1. 以下配置允许自动将新用户的电子邮件地址标记为已验证。默认配置
    AWS_COGNITO_FORCE_NEW_USER_EMAIL_VERIFIED=false
  1. 在注册时为新用户分配默认组,请根据通过AWS Cognito管理控制台完成的配置设置用户组名称。默认值设置为null。
    AWS_COGNITO_DEFAULT_USER_GROUP="Customers"

用户认证

我们提供了一种有用的特质,使认证非常简单(通过Web或API路由)。您不必担心任何额外的代码来管理会话和令牌(API)。

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

此外,'guard'名称引用是传递的,因此您可以在项目中为多个guard驱动程序重用该函数。该函数具有处理多个驱动程序和提供者(在/config/auth.php中定义)的会话和令牌保护的能力。

    namespace Ellaisys\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 Ellaisys\Cognito\AwsCognitoClaim;
    use Ellaisys\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 Ellaisys\Cognito\AwsCognitoClaim;
    use Ellaisys\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

注销(删除访问令牌)

注销方法现在是guard实现的一部分,注销方法从AWS中删除访问令牌,并从由本库管理的应用程序存储中删除。只需调用auth guard注销方法即可。您可以根据开发偏好将其实现到路由或控制器中。

注销方法现在接受一个可选的布尔参数(true),用于吊销刷新令牌。默认值为(false),这将使刷新令牌在AWS Cognito中持续存在。

   ...

   Auth::guard('api')->logout();


   ...

   Auth::guard('api')->logout(true); //Revoke the Refresh Token.

刷新令牌

您可以使用此特质为API生成新令牌

    namespace App\Api\Controller;

    ...

    use Ellaisys\Cognito\AwsCognitoClaim;
    use Ellaisys\Cognito\Auth\RefreshToken;

    class AuthApiController extends Controller
    {
        use RefreshToken;

        /**
         * Generate a new token using refresh token.
         * 
         * @throws \HttpException
         * 
         * @return mixed
         */
        public function refreshToken(\Illuminate\Http\Request $request)
        {
            ...

            $validator = $request->validate([
                'email' => 'required|email',
                'refresh_token' => 'required'
            ]);
            
            try {
                if ($claim = $this->refresh($request, 'email', 'refresh_token')) {
                    if ($claim instanceof AwsCognitoClaim) {
                        return $claim->getData();
                    } else {
                        if ($claim->getData()->error == 'cognito.validation.invalid_username') {
                            return response()->json(['status' => 'error', 'message' => $claim->getData()], 400);
                        } //End if
                    } //End if
                } //End if
            } catch (CognitoIdentityProviderException $exception) {
                return response()->json(['status' => 'error', 'message' => 'Invalid refresh token.'], 400);
            }

        } //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.

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

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

在AWS DynamoDB中创建新表

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

更新DynamoDB配置的.env文件

将以下字段添加到您的.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

支持未启用Secret的App客户端

现在,库支持AWS配置中的App客户端,其中Client Secret设置为禁用。将以下配置添加到环境文件中以启用/禁用此功能。默认设置为启用(即我们期望App Client Secret在AWS Cognito配置中启用)

   AWS_COGNITO_CLIENT_SECRET_ALLOW=false

变更日志

请参阅变更日志以获取有关最近更改的更多信息。

安全性

如果您发现任何安全相关的问题,请通过support@ellaisys.com发送电子邮件,并将其添加到问题跟踪器。

路线图

https://github.com/ellaisys/aws-cognito/wiki/RoadMap

如何贡献

  • 在GitHub上星标此项目。
  • 通过创建新问题或向问题添加评论来报告错误或建议功能
  • 提交拉取请求
  • 通过博客关于SimplCommerce或在社会网络上分享来传播信息
  • 向我们捐赠

致谢 & 贡献者

此项目得益于所有贡献者。

支持我们

EllaiSys是一家专注于云计算(AWS和Azure)、DevOps和产品工程的网络和咨询公司。我们已于2021年10月关闭了专业服务产品,然而团队仍将继续支持开源项目,这是我们对社区的承诺。任何有兴趣支持开发的人士都欢迎加入。

许可证

MIT许可证(MIT)。请参阅许可证文件以获取更多信息。

免责声明

该软件包目前处于生产就绪模式,已经完成了一些实施。我们很高兴听取您关于缺陷或新功能改进的意见。然而,由于这是免费支持,我们无法承诺支持SLA或时间表。