sevaske / laravel-discourse
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.10
- illuminate/contracts: ^10.0||^11.0||^12.0
- sevaske/discourse: ^1.5
Requires (Dev)
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.1.1||^7.10.0
- orchestra/testbench: ^9.0.0||^8.22.0
- pestphp/pest: ^2.36
- pestphp/pest-plugin-laravel: ^2.4
- phpstan/phpstan: ^2.1
README
Laravel wrapper for sevaske/discourse.
This package provides simple integration of Discourse API and SSO (Single Sign-On) into your Laravel application.
For a full list of available API endpoints and features, see the core sevaske/discourse package.
✨ Features
-
🔑 Discourse Connect (SSO)
- Full support for Discourse SSO: signing and validating payloads.
- Built-in
SsoController
, fully configurable via.env
:DISCOURSE_SSO_ENABLED
— enable/disable the SSO route.DISCOURSE_SSO_URI
— the route path (default:/discourse/sso
).DISCOURSE_SSO_CONTROLLER
— controller class handling the request.DISCOURSE_SSO_MIDDLEWARE
— comma-separated list of middleware (default:web, auth, discourse.sso.signature
).
- Middleware:
discourse.sso.signature
— validates the incoming SSO signature.
-
📡 Webhooks
- Enable via
.env
or config file:DISCOURSE_WEBHOOKS_ENABLED
— enable/disable webhook route.DISCOURSE_WEBHOOKS_SECRET
— secret for signature validation.DISCOURSE_WEBHOOKS_URI
— the route path (default:/discourse/webhook
).DISCOURSE_WEBHOOKS_CONTROLLER
— controller class handling the request.DISCOURSE_WEBHOOKS_MIDDLEWARE
— comma-separated list of middleware (default:discourse.webhook.signature
).
- Dispatches
DiscourseWebhookReceived
event with:eventName
,eventType
,eventId
, andpayload
.
- Enable via
-
📡 API client integration
- Access the full Discourse API through
$discourse->api()
. - Covers categories, users, posts, groups, private messages, webhooks, and more.
- Automatically signs requests with your API key and username.
- Access the full Discourse API through
-
⚡ Laravel-ready
- Service Provider auto-registration.
- Facade
Discourse
for clean syntax. - Middleware aliases registered automatically.
-
🧩 Built on top of
sevaske/discourse
- All low-level functionality extracted into a framework-agnostic core package.
- This wrapper adds Laravel integration, convenience, and conventions.
📦 Installation
Install via Composer:
composer require sevaske/laravel-discourse
⚙️ Configuration
Publish the config file:
php artisan vendor:publish --tag="discourse-config"
It creates the config file config/discourse.php
:
<?php return [ /* |-------------------------------------------------------------------------- | Discourse Base Settings |-------------------------------------------------------------------------- */ 'base_url' => env('DISCOURSE_BASE_URL'), 'api_key' => env('DISCOURSE_API_KEY'), 'api_username' => env('DISCOURSE_API_USERNAME'), /* |-------------------------------------------------------------------------- | Discourse Connect (SSO) |-------------------------------------------------------------------------- | | Define the route where Discourse will redirect the user for SSO. | */ 'sso' => [ 'enabled' => env('DISCOURSE_SSO_ENABLED', false), 'secret' => env('DISCOURSE_SSO_SECRET'), 'uri' => env('DISCOURSE_SSO_URI', '/discourse/sso'), 'controller' => env( 'DISCOURSE_SSO_CONTROLLER', \Sevaske\LaravelDiscourse\Http\Controllers\SsoController::class ), 'middleware' => env_array('DISCOURSE_SSO_MIDDLEWARE', 'web,auth,discourse.sso.signature'), // user attributes to provide discourse 'user' => [ // required: 'id' => env('DISCOURSE_SSO_USER_ID', 'id'), // external ID 'email' => env('DISCOURSE_SSO_USER_EMAIL', 'email'), // verified email // optional: 'name' => env('DISCOURSE_SSO_USER_NAME'), 'username' => env('DISCOURSE_SSO_USER_USERNAME'), 'avatar_url' => env('DISCOURSE_SSO_USER_AVATAR_URL'), 'bio' => env('DISCOURSE_SSO_USER_BIO'), 'admin' => env('DISCOURSE_SSO_USER_ADMIN'), 'moderator' => env('DISCOURSE_SSO_USER_MODERATOR'), ], ], /* |-------------------------------------------------------------------------- | Discourse Webhook |-------------------------------------------------------------------------- | | Configure the route and options for handling incoming Discourse webhooks. | */ 'webhook' => [ 'enabled' => env('DISCOURSE_WEBHOOK_ENABLED', false), 'secret' => env('DISCOURSE_WEBHOOK_SECRET'), 'uri' => env('DISCOURSE_WEBHOOK_URI', '/discourse/webhook'), 'controller' => env( 'DISCOURSE_WEBHOOK_CONTROLLER', \Sevaske\LaravelDiscourse\Http\Controllers\WebhookController::class ), 'middleware' => env_array('DISCOURSE_WEBHOOK_MIDDLEWARE', 'discourse.webhook.signature'), ], ];
Update your .env:
DISCOURSE_BASE_URI=https://your-discourse-url.com DISCOURSE_API_KEY=your-api-key DISCOURSE_API_USERNAME=system # sso DISCOURSE_SSO_ENABLED=false DISCOURSE_SSO_SECRET=super-secret # webhook DISCOURSE_SSO_ENABLED=false DISCOURSE_SSO_SECRET=super-secret
🚀 Usage
You can use the Discourse
facade or resolve it from the container.
API
This Laravel wrapper exposes the full power of sevaske/discourse.
Refer to its documentation for a complete list of API endpoints and usage examples.
Example:
use Sevaske\LaravelDiscourse\Facades\Discourse; // Get categories $response = Discourse::api()->categories()->list(); // Create a user $response = Discourse::api()->users()->create( name: 'John Doe', email: 'john@example.com', password: 'secret123', username: 'john' );
Discourse SSO
This package ships a ready-to-use route for Discourse Connect (SSO).
The route is registered from routes/discourse.php
only when config('discourse.sso.enabled')
is true
.
⚙️ How it works
- Discourse calls your app at
{DISCOURSE_SSO_URI}
withsso
andsig
. - Middleware stack (default:
web, auth, discourse.sso.signature
) is applied. - The controller (
DISCOURSE_SSO_CONTROLLER
) builds the signed response. - User is redirected back to Discourse.
🎯 Customization
- Route URI →
DISCOURSE_SSO_URI
- Controller →
DISCOURSE_SSO_CONTROLLER
- Middleware →
DISCOURSE_SSO_MIDDLEWARE
For example, you can create your own controller and return JSON instead of redirect.
<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\Request; use Sevaske\LaravelDiscourse\Facades\Discourse; class CustomSsoController { public function __invoke(Request $request) { $redirectTo = Discourse::connect($request->query('sso'), $request->user()); return response()->json(['to_connect' => $redirectTo]); } }
Webhook
Short: enable webhooks, protect them with signature verification + TLS, and listen for the dispatched event.
Enable webhooks via your .env
or config file:
DISCOURSE_WEBHOOKS_ENABLED=true DISCOURSE_WEBHOOKS_SECRET=your-webhook-secret
When a webhook is received, the package will dispatch the Sevaske\LaravelDiscourse\Events\DiscourseWebhookReceived event. It contains
- eventName
- eventType
- eventId
- payload
Example listener:
use Sevaske\LaravelDiscourse\Events\DiscourseWebhookReceived; Event::listen(DiscourseWebhookReceived::class, function ($event) { \Log::info("Webhook received: {$event->eventName}", $event->payload); });
🛠 Testing
composer test