承接 directorytree/bartender 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

directorytree/bartender

Composer 安装命令:

composer require directorytree/bartender

包简介

An opinionated way to authenticate users using Laravel Socialite.

关键字:

README 文档

README

An opinionated way to authenticate users using Laravel Socialite.

Bartender serves you a controller, routes, and a default implementation for easily handling authentication with Laravel Socialite providers. Almost everything in Bartender can be customized.

Index

Requirements

  • PHP >= 8.0
  • Laravel >= 9.0
  • Laravel Socialite >= 5.0

Installation

You can install the package via composer:

composer require directorytree/bartender

Then, publish the migrations. They will create the required columns on the users table:

  • provider_id
  • provider_name
  • provider_access_token
  • provider_refresh_token

If your application does not need to store/access provider tokens, you may delete the 2024_10_27_131354_add_provider_token_columns_to_users_table.php migration.

php artisan vendor:publish --provider="DirectoryTree\Bartender\BartenderServiceProvider"

Finally, run the migrations:

php artisan migrate

Setup

Register the authentication routes using Bartender::routes().

This will register the /auth/{driver}/redirect and /auth/{driver}/callback routes.

// routes/web.php

use DirectoryTree\Bartender\Facades\Bartender;

Bartender::routes();

Set up any Socialite Providers you need, and update your services.php configuration file with the redirect URL for each provider:

Important

Remember to fully complete the installation steps for each Socialite Provider you wish to use.

If you receive a Driver [X] not supported exception, you have not completed the installation steps for the provider.

// config/services.php

return [
    // ...

    'google' => [
        // ...
        'redirect' => '/auth/google/callback',
    ],
    
    'microsoft' => [
        // ...
        'redirect' => '/auth/microsoft/callback',
    ],
];

Finally, register the Socialite Provider in your AppServiceProvider using Bartender::serve():

// app/Providers/AppServiceProvider.php

use DirectoryTree\Bartender\Facades\Bartender;

class AppServiceProvider extends ServiceProvider
{
    // ...

    public function boot(): void
    {
        Bartender::serve('google');
        Bartender::serve('microsoft');
    }
}

If your application uses a User model outside the default App\Models namespace, you can set it using the Bartender facade.

If your application uses the default Laravel User model in the App\Models namespace, skip this step.

// app/Providers/AuthServiceProvider.php

use App\User;
use DirectoryTree\Bartender\Facades\Bartender;

class AuthServiceProvider extends ServiceProvider
{
    // ...

    public function boot(): void
    {
        Bartender::setUserModel(User::class);
    }
}

Usage

Direct your user to the /auth/{driver}/redirect route to authenticate with the given driver:

<a href="{{ route('auth.driver.redirect', 'google') }}">
    Login with Google
</a>

<a href="{{ route('auth.driver.redirect', 'microsoft') }}">
    Login with Microsoft
</a>

Once the user successfully authenticates, they will be redirected to the /auth/{driver}/callback route, which will automatically create or update their application user account.

Important

If you receive a Routing requirement for "driver" cannot be empty exception upon clicking one of the login links, you have forgotten to register your the Socialite provider with Bartender using Bartender::serve() in your AppServiceProvider.

Soft Deletes

With the default UserProviderRepository, soft-deleted users will be restored if they log in with their provider.

To change this behaviour, swap out the repository.

Email Verification

With the default UserProviderRepository, users with emails will be automatically verified (by setting the email_verified_at timestamp column) if it is not already set.

To change this behaviour, swap out the repository.

Access/Refresh Tokens

To enable storing the authentication provider access and refresh tokens on your user so that you can access them later, you may apply the StoresProviderTokens interface on your model:

// app/Models/User.php

namespace App\Models;

use DirectoryTree\Bartender\StoresProviderTokens;

class User extends Authenticatable implements StoresProviderTokens
{
    // ...    
}

You may also want to add these columns to your model's $hidden attributes, along with encrypted casts for additional security:

// app/Models/User.php

class User extends Authenticatable implements StoresProviderTokens
{
    /**
     * The attributes that should be hidden for serialization.
     */
    protected $hidden = [
        'provider_access_token',
        'provider_refresh_token'
    ];

    /**
     * Get the attributes that should be cast.
     */
    protected function casts(): array
    {
        return [
            'provider_access_token' => 'encrypted',
            'provider_refresh_token' => 'encrypted',
        ];
    }
}

Otherwise, if you do not need to store these tokens, you are free to delete the published 2024_10_27_131354_add_provider_token_columns_to_users_table.php migration file and omit applying the StoresProviderTokens interface. Bartender will skip storing these tokens as it does not require them to successfully authenticate users.

Extending & Customizing

Almost everything can be swapped out in Bartender.

If you would like to handle everything yourself for OAuth redirects and callbacks, you may create your own ProviderHandler:

// app/Socialite/UserProviderHandler.php

namespace App\Socialite;

use Illuminate\Http\Request;
use Laravel\Socialite\Contracts\Provider;
use DirectoryTree\Bartender\ProviderHandler;

class UserProviderHandler implements ProviderHandler
{
    /**
     * Constructor.
     */
    public function __construct(
        protected Request $request
    ) {
    }

    /**
     * Handle redirecting the user to the OAuth provider.
     */
    public function redirect(Provider $provider, string $driver): RedirectResponse
    {
        // Perform additional logic here...
    
        return $provider->redirect();
    }

    /**
     * Handle an OAuth response from the provider.
     */
    public function callback(Provider $provider, string $driver): RedirectResponse
    {
        // Authenticate the user your own way...
    
        return redirect()->route('dashboard');
    }
}

Then, provide it into the second argument in the Bartender::serve method:

// app/Providers/AuthServiceProvider.php

namespace App\Providers;

use App\Socialite\UserProviderHandler;
use DirectoryTree\Bartender\Facades\Bartender;

class AuthServiceProvider extends ServiceProvider
{
    // ...

    public function boot(): void
    {
        Bartender::serve('google', UserProviderHandler::class);
        Bartender::serve('microsoft', UserProviderHandler::class);
    }
}

You may also extend the built-in UserProviderHandler implementation if you prefer.

For example, if you need to adjust the scopes for a single provider:

// app/Socialite/MicrosoftUserHandler.php

namespace App\Socialite;

use Illuminate\Http\RedirectResponse;
use Laravel\Socialite\Contracts\Provider;
use DirectoryTree\Bartender\UserProviderHandler;

class MicrosoftUserHandler extends UserProviderHandler
{
    /**
     * Handle redirecting the user to Microsoft.
     */
    public function redirect(Provider $provider, string $driver): RedirectResponse
    {
        $provider->scopes([
            'Mail.ReadWrite',
            // ...
        ]);
    
        return parent::redirect($provider, $driver);
    }
}

Then register it as the handler:

Bartender::serve('microsoft', MicrosoftUserHandler::class);

User Creation & Updating

If you would like to customize the creation of the user in the default handler, you may create your own ProviderRepository implementation:

// app/Socialite/UserProviderRepository.php

namespace App\Socialite;

use App\Models\User;
use Illuminate\Contracts\Auth\Authenticatable;
use DirectoryTree\Bartender\ProviderRepository;
use Laravel\Socialite\Contracts\User as SocialiteUser;

class UserProviderRepository implements ProviderRepository
{
    /**
     * Determine if the user already exists under a different provider.
     */
    public function exists(string $driver, SocialiteUser $user): bool
    {
        return User::withTrashed()->where('...')->exists();
    }

    /**
     * Update or create the socialite user.
     */
    public function updateOrCreate(string $driver, SocialiteUser $user): Authenticatable
    {
        $user = User::withTrashed()->firstOrNew([
            // ...
        ]);
        
        return $user;
    }
}

Then, bind your implementation in the service container in your AppServiceProvider:

// app/Providers/AppServiceProvider.php

namespace App\Providers;

use App\Socialite\UserProviderRepository;
use DirectoryTree\Bartender\ProviderRepository;

class AppServiceProvider extends ServiceProvider
{
    // ...

    public function register(): void
    {
        $this->app->bind(ProviderRepository::class, UserProviderRepository::class);
    }
}

User Redirects & Flash Messaging

If you would like to customize the behavior of the redirects of the default redirector and flash messages depending on the outcome of a OAuth callback, you can create your own ProviderRedirector implementation:

It's recommended to regenerate the session after authentication to prevent users from exploiting a session fixation attack.

// app/Socialite/UserProviderRedirector.php

namespace App\Socialite;

use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;

class UserProviderRedirector implements ProviderRedirector
{
    /**
     * Redirect when unable to authenticate the user.
     */
    public function unableToAuthenticateUser(Exception $e, string $driver): RedirectResponse
    {
        report($e);

        return redirect()->route('login')->with('error', 'Unable to authenticate user.');
    }

    /**
     * Redirect when the user already exists.
     */
    public function userAlreadyExists(SocialiteUser $user, string $driver): RedirectResponse
    {
        return redirect()->route('login')->with('error', 'User already exists.');
    }

    /**
     * Redirect when unable to create the user.
     */
    public function unableToCreateUser(Exception $e, SocialiteUser $user, string $driver): RedirectResponse
    {
        report($e);

        return redirect()->route('login')->with('error', 'Unable to create user.');
    }

    /**
     * Handle when the user has been successfully authenticated.
     */
    public function userAuthenticated(Authenticatable $user, SocialiteUser $socialite, string $driver): RedirectResponse
    {
        Auth::login($user);
        
        Session::regenerate();
    
        return redirect()->route('dashboard');
    }
}

Then, bind your implementation in the service container in your AppServiceProvider:

// app/Providers/AppServiceProvider.php

namespace App\Providers;

use App\Socialite\UserProviderRedirector;

class AppServiceProvider extends ServiceProvider
{
    // ...

    public function register(): void
    {
        $this->app->bind(ProviderRedirector::class, UserProviderRedirector::class);
    }
}

directorytree/bartender 适用场景与选型建议

directorytree/bartender 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 17.75k 次下载、GitHub Stars 达 285, 最近一次更新时间为 2024 年 04 月 03 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「oauth」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 directorytree/bartender 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 directorytree/bartender 我们能提供哪些服务?
定制开发 / 二次开发

基于 directorytree/bartender 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 17.75k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 285
  • 点击次数: 18
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 285
  • Watchers: 2
  • Forks: 13
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2024-04-03