You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel Socialite如何为Matrix Auth动态配置服务器URL?

解决Laravel Socialite动态传递Matrix Homeserver URL的方案

核心思路

放弃依赖静态配置文件,通过会话(Session)暂存动态认证配置+手动实例化Socialite Provider的方式,既避免多用户请求的竞态问题,又无需存储未认证用户的输入到数据库。


步骤1:前端(Inertia)处理Homeserver输入与Well-Known查询

先让用户输入homeserver地址,前端调用后端接口完成/.well-known/matrix/client查询,避免直接请求第三方服务的CORS问题,同时让后端做合法性校验:

<template>
  <form @submit.prevent="initMatrixLogin">
    <input 
      v-model="form.homeserver" 
      placeholder="输入Matrix服务器地址(如matrix.org)"
      required
    />
    <button type="submit">通过Matrix登录</button>
    <div v-if="form.errors.homeserver" class="text-red-500">{{ form.errors.homeserver }}</div>
  </form>
</template>

<script setup>
import { useForm } from '@inertiajs/vue3'

const form = useForm({
  homeserver: '',
})

const initMatrixLogin = async () => {
  const response = await form.post('/matrix/well-known')
  if (response.ok && response.data.auth_issuer) {
    // 跳转到认证路由,携带合法的issuer和homeserver参数
    window.location.href = `/matrix/auth?issuer=${encodeURIComponent(response.data.auth_issuer)}&homeserver=${encodeURIComponent(form.homeserver)}`
  }
}
</script>

步骤2:后端实现Well-Known校验与会话暂存

路由配置

// routes/web.php
Route::post('/matrix/well-known', [MatrixAuthController::class, 'validateHomeserver']);
Route::get('/matrix/auth', [MatrixAuthController::class, 'redirectToProvider']);
Route::get('/matrix/callback', [MatrixAuthController::class, 'handleCallback']);

控制器核心逻辑

// app/Http/Controllers/MatrixAuthController.php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Laravel\Socialite\Two\AbstractProvider;
use Laravel\Socialite\Two\ProviderInterface;

class MatrixAuthController extends Controller
{
    // 校验homeserver并返回合法的auth_issuer
    public function validateHomeserver(Request $request)
    {
        $request->validate([
            'homeserver' => 'required|url',
        ]);

        $homeserver = rtrim($request->homeserver, '/');
        try {
            // 查询Matrix服务器的well-known配置
            $matrixWellKnown = Http::get("{$homeserver}/.well-known/matrix/client")->throw()->json();
            if (!isset($matrixWellKnown['auth_issuer'])) {
                return response()->json(['error' => '该服务器不支持OIDC认证'], 400);
            }

            // 验证auth_issuer是合法的OIDC颁发者
            $oidcConfig = Http::get(rtrim($matrixWellKnown['auth_issuer'], '/') . '/.well-known/openid-configuration')->throw()->json();
            
            return response()->json([
                'auth_issuer' => $matrixWellKnown['auth_issuer'],
                'homeserver' => $homeserver,
            ]);
        } catch (\Exception $e) {
            return response()->json(['error' => '无效的Matrix服务器地址'], 400);
        }
    }

    // 生成动态Socialite Provider并跳转到认证页面
    public function redirectToProvider(Request $request)
    {
        $request->validate([
            'issuer' => 'required|url',
            'homeserver' => 'required|url',
        ]);

        $issuer = rtrim($request->issuer, '/');
        $oidcConfig = Http::get("{$issuer}/.well-known/openid-configuration")->json();

        // 将动态配置存入用户会话(会话隔离,无竞态问题)
        $request->session()->put('matrix_auth', [
            'issuer' => $issuer,
            'homeserver' => $request->homeserver,
            'auth_url' => $oidcConfig['authorization_endpoint'],
            'token_url' => $oidcConfig['token_endpoint'],
            'userinfo_url' => $oidcConfig['userinfo_endpoint'],
            'client_id' => config('services.matrix.client_id'), // 或动态注册客户端获取
            'client_secret' => config('services.matrix.client_secret'),
            'redirect_uri' => route('matrix.callback'),
        ]);

        // 手动实例化自定义Provider
        $provider = new class($request->session()) extends AbstractProvider implements ProviderInterface {
            protected $config;

            public function __construct($session)
            {
                parent::__construct($session);
                $this->config = $session->get('matrix_auth');
            }

            protected function getAuthUrl($state)
            {
                return $this->buildAuthUrlFromBase($this->config['auth_url'], $state);
            }

            protected function getTokenUrl()
            {
                return $this->config['token_url'];
            }

            protected function getUserByToken($token)
            {
                $response = $this->getHttpClient()->get($this->config['userinfo_url'], [
                    'headers' => ['Authorization' => "Bearer {$token}"],
                ]);
                return json_decode($response->getBody(), true);
            }

            protected function mapUserToObject(array $user)
            {
                return (new \Laravel\Socialite\Two\User())->setRaw($user)->map([
                    'id' => $user['sub'],
                    'nickname' => $user['display_name'] ?? $user['username'],
                    'name' => $user['display_name'] ?? null,
                    'email' => $user['email'] ?? null,
                ]);
            }

            public function getClientId(): string { return $this->config['client_id']; }
            public function getClientSecret(): string { return $this->config['client_secret']; }
            public function getRedirectUri(): string { return $this->config['redirect_uri']; }

            protected function getCodeFields($state = null)
            {
                $fields = parent::getCodeFields($state);
                $fields['scope'] = 'openid profile'; // 按需添加Matrix所需权限
                return $fields;
            }
        };

        return $provider->redirect();
    }

    // 处理认证回调
    public function handleCallback(Request $request)
    {
        $config = $request->session()->get('matrix_auth');
        if (!$config) abort(400, '无效的认证会话');

        // 重新实例化Provider处理token交换
        $provider = new class($request->session()) extends AbstractProvider implements ProviderInterface {
            // 复用上面的Provider实现,建议提取为单独的类(如MatrixOidcProvider)
        };

        $matrixUser = $provider->user();
        $homeserver = $config['homeserver'];

        // 这里处理本地用户的登录/注册逻辑
        // 示例:根据matrixUser->id和homeserver查找或创建用户
        $localUser = User::firstOrCreate(
            ['matrix_user_id' => $matrixUser->id, 'matrix_homeserver' => $homeserver],
            ['name' => $matrixUser->name, 'email' => $matrixUser->email]
        );

        // 清理会话中的临时配置
        $request->session()->forget('matrix_auth');

        auth()->login($localUser);
        return redirect()->intended('/dashboard');
    }
}

关键注意事项

  1. 动态客户端注册:如果需要支持未预先注册的Matrix服务器,需调用OIDC的registration_endpoint动态获取client ID和secret,替换上述代码中的固定配置。
  2. 会话安全:确保应用启用HTTPS,配置安全的会话驱动(如Redis),防止会话劫持。
  3. 输入校验:严格校验homeserver和issuer的格式,拦截恶意请求。
  4. 错误处理:完善各步骤的异常捕获,给用户清晰的错误提示。

内容的提问来源于stack exchange,提问作者MTRNord

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 16:41:16