Featured image of post Using gRPC Client for System Login in PHP Backend dcat-admin

Using gRPC Client for System Login in PHP Backend dcat-admin

gRPC Client

Introduction

  • The company has implemented a unified account center service using go-zero
  • Some project backends built with dcat-admin need to authenticate users through this account center, documenting the implementation process

Environment Setup

Implementation Code

Overridden Auth Controller

<?php

namespace App\Admin\Controllers;

use App\Services\AccountRpc;
use Dcat\Admin\Admin;
use Dcat\Admin\Http\Controllers\AuthController as BaseAuthController;
use Dcat\Admin\Models\Administrator;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;

class AuthController extends BaseAuthController
{
    public function postLogin(Request $request)
    {
        $username = $request->input('username');
        $password = $request->input('password');
        $remember = (bool)$request->input('remember', false);

        $admin = Administrator::query()->firstOrNew(['username' => $username]);
        
        if ($admin->exists) {
            if (!Hash::check($password, $admin->password)) {
                return $this->validationErrorsResponse([$this->username() => 'Invalid password']);
            }

            try {
                AccountRpc::me($admin->remember_token);
                $this->guard()->login($admin, $remember);
                return $this->sendLoginResponse($request);
            } catch (\Exception $e) {
                Log::error($e);
            }
        }

        try {
            $res = AccountRpc::login($username, $password);
        } catch (\Exception $e) {
            return $this->validationErrorsResponse([$this->username() => 'RPC Error: ' . $e->getMessage()]);
        }

        $admin->name = $res->getUser()->getNickname();
        $admin->password = Hash::make($password);
        $admin->remember_token = $res->getSession()->getToken();
        $admin->save();

        $this->guard()->login($admin, $remember);
        return $this->sendLoginResponse($request);
    }

    public function getLogout(Request $request)
    {
        $admin = $this->guard()->user();

        try {
            AccountRpc::logout($admin->remember_token);
        } catch (\Exception $e) {
            Log::error($e->getMessage());
        }

        $this->guard()->logout();
        $request->session()->invalidate();
        
        $path = admin_url('auth/login');
        return $request->pjax() 
            ? "<script>location.href = '$path';</script>" 
            : redirect($path);
    }
}

Account Service Client

<?php

namespace App\Services;

use Account\AccountClient;
use Grpc\ChannelCredentials;

class AccountRpc
{
    protected static $client;

    public static function login($username, $password)
    {
        $meta = ['appId' => ['test'], 'groupId' => ['test']];
        list($res, $status) = self::client()->Auth(new AuthRequest(), $meta, ['timeout' => 3000000])->wait();

        if ($status->code !== 0) {
            throw new \Exception($status->details);
        }
        return $res;
    }

    public static function client()
    {
        return self::$client ??= new AccountClient(
            config('rpc.account'),
            ['credentials' => ChannelCredentials::createInsecure()]
        );
    }

    // Other methods (me(), logout()) omitted for brevity
}

Troubleshooting

gRPC Blocking Issue in Production

Add these configurations to Dockerfile when using Laravel-S:

RUN install-php-extensions pcntl redis pdo_mysql zip bcmath gd grpc

RUN echo "grpc.enable_fork_support = 1" >> /usr/local/etc/php/conf.d/docker-php-ext-grpc.ini &&\
    echo "grpc.poll_strategy = epoll1" >> /usr/local/etc/php/conf.d/docker-php-ext-grpc.ini

Key points:

  1. Proper timeout configuration for gRPC calls
  2. Handle both local user store and remote gRPC authentication
  3. gRPC client initialization with insecure credentials for testing
  4. Critical error logging for RPC failures

This implementation enables hybrid authentication that first checks local credentials, then verifies/updates via gRPC, creating local user records when necessary.