ikechukwukalu/sanctumauthstarter

Laravel sanctum auth starter

v2.0.2 2023-08-30 11:45 UTC

README

Latest Version on Packagist Quality Score Code Quality Known Vulnerabilities Github Workflow Status Total Downloads Licence

这是一个非常灵活且可定制的 Laravel 包(模板),利用 laravel/uilaravel-sanctum 来创建基本认证类和其他有助于快速开始构建 REST API 的功能。以下功能可供使用:

  • 用户注册
  • 用户登录
  • 注册后自动登录
  • 登录节流
  • 登录双因素认证
  • 社交媒体登录
  • 忘记密码
  • 电子邮件验证
  • 重新发送电子邮件验证
  • 重置密码
  • 更改密码
  • 编辑用户资料
  • 通知
    • 欢迎通知
    • 电子邮件验证
    • 登录通知
    • 密码更改通知
  • 生成文档
  • GitHub 的辅助 CI/CD 文件

需求

  • PHP 8+
  • Laravel 9+

安装步骤

composer require ikechukwukalu/sanctumauthstarter
  • php artisan ui bootstrap
  • npm install --save-dev laravel-echo pusher-js
  • User 模型类中取消注释 use Illuminate\Contracts\Auth\MustVerifyEmail;
  • User 模型类中将 two_factor 列添加到 fillablehidden 数组中。最终 User 应该看起来像这样
use Laravel\Sanctum\HasApiTokens;
use Laragear\TwoFactor\TwoFactorAuthentication;
use Laragear\TwoFactor\Contracts\TwoFactorAuthenticatable;

class User extends Authenticatable implements TwoFactorAuthenticatable, MustVerifyEmail
{
    use HasApiTokens, HasFactory, Notifiable, TwoFactorAuthentication;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
        'two_factor',
        'socialite_signup',
        'form_signup'
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
        'two_factor',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

生成认证控制器、请求、服务和路由

您可以通过运行 php artisan sas:setup 一次性生成它们。您也可以单独生成它们

  • php artisan sas:controllers
  • php artisan sas:routes
  • php artisan sas:tests

发布迁移和配置

  • php artisan vendor:publish --tag=sas-migrations
  • php artisan vendor:publish --tag=sas-config

WEBSOCKETS 和 QUEUE

此包利用 Laravel beyondcode/laravel-websockets 在认证后向客户端传递 access_token。首先,您必须设置您的 Laravel 应用以进行广播。为此,请运行以下命令:

  • php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="migrations"
  • php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="config"
  • 在您的 .env 文件中设置 REDIS_CLIENT=predisBROADCAST_DRIVER=pusher
  • 您的 laravel-echo 配置应类似于以下内容
window.Echo = new Echo({
    broadcaster: 'pusher',
    key: import.meta.env.VITE_PUSHER_APP_KEY,
    wsHost: window.location.hostname,
    wsPort: 6001,
    forceTLS: false,
    encrypted: false,
    enabledTransports: ['ws', 'wss'],
    disableStats: true,
    cluster:import.meta.env.VITE_PUSHER_APP_CLUSTER,
    authorizer: (channel, options) => {
        return {
            authorize: (socketId, callback) => {
                axios.post('/broadcasting/auth', {
                    socket_id: socketId,
                    channel_name: channel.name
                })
                .then(response => {
                    callback(false, response.data);
                })
                .catch(error => {
                    callback(true, error);
                });
            }
        };
    },
});

您需要一个 队列 工作员来处理通知和其他事件。

  • 在您的 .env 文件中设置 QUEUE_CONNECTION=redis
  • config/app.php 中取消注释 App\Providers\BroadcastServiceProvider::class
  • 您的 .env 应该类似于以下内容
PUSHER_APP_KEY=app-key
PUSHER_APP_ID=app-id
PUSHER_APP_SECRET=app-secret
PUSHER_HOST=127.0.0.1
PUSHER_PORT=6001
PUSHER_SCHEME=http
PUSHER_APP_CLUSTER=mt1
  • 对于使用 Apache 的 SSL,以下片段应放置在 virtualhost SSL 配置中。
    ProxyPass "/app/" "ws://127.0.0.1:6001/app/"
    ProxyPass "/app/" "http://127.0.0.1:6001/app/"
  • 运行 php artisan config:clearphp artisan migratephp artisan websockets:servephp artisan queue:work
  • php artisan serve
  • npm install && npm run dev

WEBVIEW 登录

  • 社交媒体登录
  • 双因素登录

社交媒体登录

将以下内容添加到您的 config/services.php 文件中。

'google' => [
    'client_id' => env('GOOGLE_CLIENT_ID'),
    'client_secret' => env('GOOGLE_CLIENT_SECRET'),
    'redirect' => env('GOOGLE_CLIENT_REDIRECT'),
],
  • 导航到 auth/socialite 以查看生成的 access_token 后的示例 Google 注册/登录页面。取消注释 web.php 中的路由。以下是在视图 resources/views/vendor/sanctumauthstarter/socialite/auth.blade.php 中调用的脚本。
window.addEventListener('DOMContentLoaded',  () => {
    const getUserUUID = () => {
        let userUUID = localStorage.getItem('user_uuid');

        if (!userUUID) {
            userUUID = crypto.randomUUID();
            localStorage.setItem('user_uuid', userUUID);
        }

        console.log('user_uuid created', userUUID);
        return userUUID;
    }

    const removeUserUUID = () => {
        if (localStorage.getItem('user_uuid')) {
            localStorage.removeItem('user_uuid');
        }

        console.log('user_uuid removed');
    }

    const USER_UUID = getUserUUID();
    const TIMEOUT = parseInt("{{ $minutes }}") * 60 * 1000;

    window.Echo.channel(`access.token.socialite.${USER_UUID}`)
    .listen('.Ikechukwukalu\\Sanctumauthstarter\\Events\\SocialiteLogin', (e) => {
        console.log(`payload:`, e);
    });

    document.getElementById('googleSignUp').onclick = () => {
        window.open(
            "{{ url('auth/redirect') }}/" + USER_UUID,
            '_blank'
        )
    }

    setTimeout(() => {
        removeUserUUID();
    }, TIMEOUT);
});
  • 认证成功后,此视图显示在 resources/views/vendor/sanctumauthstarter/socialite/callback.blade.php,其中包含以下脚本
window.addEventListener('DOMContentLoaded',  () => {
    if (localStorage.getItem('user_uuid')) {
        localStorage.removeItem('user_uuid');
    }
});

双因素登录

该软件包利用 Laragear/TwoFactor 来启用双因素认证登录,并使用 beyondcode/laravel-websockets 在认证后向客户端传递 access_token

已为密码登录和社交媒体登录实现了双因素认证。

  • php artisan vendor:publish --provider="Laragear\TwoFactor\TwoFactorServiceProvider"
  • php artisan migrate
  • resources/views/vendor/two-factor/login.blade.php 中的表单替换为以下代码
<form method="get">
    @php
        foreach ($_GET as $key => $value) {
            $key = htmlspecialchars($key);
            $value = htmlspecialchars($value);
            echo "<input type='hidden' name='$key' value='$value'/>";
        }
    @endphp
    @csrf
    <p class="text-center">
        {{ trans('two-factor::messages.continue') }}
    </p>
    <div class="form-row justify-content-center py-3">
        @if($errors->isNotEmpty() || isset($message))
            <div class="col-12 alert alert-danger pb-0">
                <ul>
                    @if (isset($message))
                        <li>{{ $message }}</li>
                    @endif
                    @foreach ($errors->all() as $error)
                        <li>{{ $error }}</li>
                    @endforeach
                </ul>
            </div>
        @endif
        <div class="col-sm-8 col-8 mb-3">
            <input type="text" name="{{ $input }}" id="{{ $input }}"
                    class="@error($input) is-invalid @enderror form-control form-control-lg"
                    minlength="6" placeholder="123456" required>
        </div>
        <div class="w-100"></div>
        <div class="col-auto mb-3">
            <button type="submit" class="btn btn-primary btn-lg">
                {{ trans('two-factor::messages.confirm') }}
            </button>
        </div>
    </div>
</form>
  • 调用 api/create-two-factor 来创建双因素认证。
{
    "status": "success",
    "status_code": 200,
    "data": {
        "qr_code": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg...",
        "uri": "otpauth://totp/%3Ajohndoe@xyz.com?label=johndoe%40xyz.com&secret=EQGRSNGAE3TREOT7XOLB5VVRS42LLXYS&algorithm=SHA1&digits=6",
        "string": "EQGRSNGAE3TREOT7XOLB5VVRS42LLXYS"
    }
}
  • 调用 api/confirm-two-factor 来确认双因素认证并获得恢复码。
{
    "status": "success",
    "status_code": 200,
    "data": {
        "message": "Download your recover codes and keep them safe!",
        "codes": [
            {
                "code": "SV6OH71D",
                "used_at": null
            },
            {
                "code": "62KEBSNE",
                "used_at": null
            },
            {
                "code": "YSVHOG9X",
                "used_at": null
            },
            ...
        ]
    }
}
  • 调用 api/disable-two-factor 来禁用双因素认证,调用 api/current-recovery-codes 来检索当前恢复码,调用 api/new-recovery-codes 来生成新的恢复码,这将替换之前的批次。

为密码登录启用双因素认证

当双因素认证已启用且用户尝试登录时,将返回一个包含 user_uuidtwofactor_url 的负载。

{
    "status": "success",
    "status_code": 200,
    "data": {
        "twofactor_url": "http://127.0.0.1:8000/twofactor/required/johndoe@xyz.com/0220dbe7-08dc-470e-b1e2-4411ba155bc1",
        "user_uuid": "0220dbe7-08dc-470e-b1e2-4411ba155bc1",
        "access_token": null,
        "message": "Two-Factor Authentication is required!"
    }
}

使用 user_uuid 创建一个将监听 Laravel 广播的 laravel-echo 通道。导航到 auth/twofactor/{email}/{uuid} 来查看在 web.php 中取消注释路由后的生成的 access_token。此视图 resources/views/vendor/sanctumauthstarter/twofactor/auth.blade.php 包含一个示例 javascript 代码,用于实现这一功能。

window.addEventListener('DOMContentLoaded',  () => {
    const USER_UUID = "{{ Route::input('uuid') }}";

    console.log(USER_UUID);

    window.Echo.channel(`access.token.twofactor.${USER_UUID}`)
    .listen('.Ikechukwukalu\\Sanctumauthstarter\\Events\\TwoFactorLogin', (e) => {
        console.log(`payload:`, e);
    });
});

为社交媒体登录启用双因素认证

当双因素认证已启用时,将在您的浏览器中弹出双因素认证页面。

文档

要生成文档

  • composer require --dev knuckleswtf/scribe
  • php artisan vendor:publish --tag=scribe-config
  • 如果您想看到 web.php 的 API 文档,请将 'prefixes' => ['api/*'] 更改为 'prefixes' => ['*']
  • php artisan scribe:generate

访问您新生成的文档

  • 如果您使用的是 static 类型,请找到您 public/ 文件夹中的 docs/index.html 文件并在浏览器中打开它。
  • 如果您使用的是 laravel 类型,请启动您的应用程序(php artisan serve),然后访问 /docs

example_languages:对于每个端点,都会在数组中指定的每种语言中显示一个示例请求。目前,只包括 bash(curl)、javascript(Fetch)、php(Guzzle)和 python(requests)。您可以添加更多语言,但您还必须创建相应的 Blade 视图(查看添加更多示例语言)。

默认:["bash", "javascript"]

请访问 scribe 了解更多详情。

测试

建议您在开始添加自定义模型和控制器之前运行测试。确保将 database/factories/UserFactory.php 类更新为与您的 users 表匹配,以便测试可以继续成功运行。

密码

database/factories/UserFactory.php 类中创建的密码必须与以下验证匹配

'password' => ['required', 'string', 'max:16',
                Password::min(8)
                    ->letters()->mixedCase()
                    ->numbers()->symbols()
                    ->uncompromised(),

运行测试

  • php artisan test

推荐软件包

composer require ikechukwukalu/makeservice
composer require ikechukwukalu/databasebackup
composer require ikechukwukalu/requirepin
composer require ikechukwukalu/clamavfileupload

发布视图

  • php artisan vendor:publish --tag=sas-views

发布语言

  • php artisan vendor:publish --tag=sas-lang

发布 Laravel 邮件通知 Blade

  • php artisan vendor:publish --tag=laravel-notifications

发布 GitHub 工作流程

  • php artisan vendor:publish --tag=github

许可证

SAS软件包是一个开源软件,根据MIT许可证授权。