kodepik/ums-laravel 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

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

kodepik/ums-laravel

Composer 安装命令:

composer require kodepik/ums-laravel

包简介

Laravel SDK for UMS (User Management System) — SSO authentication & authorization

README 文档

README

Laravel package for SSO authentication & authorization via UMS (User Management System).

Package ini menyediakan:

  • SSO Login/Logout (OAuth2 redirect flow via UMS → Keycloak)
  • JWT token validation via JWKS public key (RS256)
  • Middleware untuk protect routes berdasarkan permission, role, dan module
  • Helper functions untuk cek akses di mana saja (controller, service, blade)
  • API authentication dengan Bearer token

Daftar Isi

  1. Requirements
  2. Installation
  3. Configuration
  4. Prasyarat di UMS Admin
  5. Quick Start — SSO Login
  6. Quick Start — API Bearer Token
  7. Middleware Reference
  8. Helper Functions
  9. UmsClaims Object
  10. Facade
  11. Implementasi Lengkap (Step by Step)
  12. Blade Template
  13. Advanced: Custom Routes
  14. Advanced: Token Refresh
  15. Troubleshooting
  16. Security Notes

Requirements

  • PHP 8.1+
  • Laravel 10.x atau 11.x
  • UMS server yang sudah running

Installation

1. Install package

composer require kodepik/ums-laravel

Package menggunakan Laravel auto-discovery — ServiceProvider dan Facade otomatis terdaftar.

2. Publish config

php artisan vendor:publish --tag=ums-config

File config/ums.php akan dibuat.

Configuration

Tambahkan ke file .env:

# URL server UMS
UMS_BASE_URL=https://ums.yourserver.com

# App ID yang didaftarkan di UMS Admin
UMS_APP_ID=your-app-id

# Callback URL (harus didaftarkan juga di UMS Admin)
UMS_CALLBACK_URL=https://yourapp.com/ums/callback

# SSL verification (set false jika development dengan self-signed cert)
UMS_VERIFY_SSL=true

# Optional: disable auto-registered routes
UMS_ROUTES_ENABLED=true

# Optional: ganti prefix routes (default: 'ums')
UMS_ROUTES_PREFIX=ums

# Optional: JWKS cache duration (default: 3600 seconds / 1 jam)
UMS_JWKS_CACHE_TTL=3600

Prasyarat di UMS Admin

Sebelum integrasi, pastikan di UMS Admin:

  1. Application sudah dibuat — catat app_id (contoh: APP-xxxxx)
  2. Callback URL didaftarkanhttps://yourapp.com/ums/callback
  3. Logout redirect URL didaftarkanhttps://yourapp.com/login
  4. User di-assign ke application — user yang bisa login
  5. Module, Role, Permission — sudah di-assign ke user sesuai kebutuhan

Quick Start — SSO Login

Setelah install dan config, SSO langsung bisa dipakai tanpa coding tambahan.

Auto-registered routes:

Route Method Name Fungsi
/ums/login GET ums.login Redirect ke UMS → Keycloak
/ums/callback GET ums.callback Handle callback setelah login
/ums/logout GET ums.logout Logout + redirect ke UMS logout
/ums/refresh POST ums.refresh Refresh token yang expired

Di halaman login kamu:

<a href="{{ route('ums.login') }}">Login with SSO</a>

Setelah login berhasil:

User di-redirect ke /dashboard (configurable di UmsAuthController). Data tersimpan di session:

session('ums_token');         // JWT access token
session('ums_refresh_token'); // Refresh token  
session('ums_user');          // Decoded claims (array)

Logout:

<a href="{{ route('ums.logout') }}">Logout</a>

Quick Start — API Bearer Token

Untuk API endpoint yang di-consume frontend/mobile, gunakan Bearer token:

// routes/api.php
Route::middleware(['ums.auth'])->group(function () {
    Route::get('/profile', function () {
        return response()->json(ums_user()->toArray());
    });
});

Client mengirim request dengan header:

Authorization: Bearer <jwt_token_dari_ums>

Middleware Reference

ums.auth — Validasi Token

Memastikan request memiliki Bearer token yang valid.

Route::middleware(['ums.auth'])->group(function () {
    // Semua route di sini butuh token valid
});

Response jika gagal:

{"success": false, "message": "Missing authorization token"}     // 401
{"success": false, "message": "Invalid or expired token"}        // 401

ums.permission:module,permission — Cek Permission

Memastikan user punya permission tertentu di module tertentu.

// User harus punya permission 'read' di module 'inventory'
Route::middleware(['ums.auth', 'ums.permission:inventory,read'])->group(function () {
    Route::get('/items', [ItemController::class, 'index']);
});

// User harus punya permission 'write' di module 'inventory'
Route::middleware(['ums.auth', 'ums.permission:inventory,write'])->group(function () {
    Route::post('/items', [ItemController::class, 'store']);
});

Response jika gagal:

{"success": false, "message": "Permission denied: inventory.write"}  // 403

ums.role:module,role — Cek Role

Memastikan user punya role tertentu di module tertentu.

// User harus punya role 'admin' di module 'dashboard'
Route::middleware(['ums.auth', 'ums.role:dashboard,admin'])->group(function () {
    Route::get('/admin', [AdminController::class, 'index']);
});

Response jika gagal:

{"success": false, "message": "Role denied: dashboard.admin"}  // 403

ums.module:module — Cek Akses Module

Memastikan user punya akses ke module tertentu (tanpa cek permission/role spesifik).

// User harus punya akses ke module 'reports'
Route::middleware(['ums.auth', 'ums.module:reports'])->group(function () {
    Route::get('/reports', [ReportController::class, 'index']);
});

Response jika gagal:

{"success": false, "message": "Module access denied: reports"}  // 403

Kombinasi Middleware

Middleware bisa di-chain:

// Harus login + punya module inventory + permission delete
Route::middleware(['ums.auth', 'ums.module:inventory', 'ums.permission:inventory,delete'])
    ->delete('/items/{id}', [ItemController::class, 'destroy']);

Helper Functions

Tersedia global helper yang bisa dipanggil dari mana saja:

// Get current user (dari request attribute atau session)
$user = ums_user();              // UmsClaims|null

// Cek permission
ums_can('inventory', 'read');    // bool
ums_can('inventory', 'delete');  // bool

// Cek role
ums_has_role('dashboard', 'admin');    // bool
ums_has_role('dashboard', 'viewer');   // bool

// Cek module access
ums_has_module('inventory');      // bool
ums_has_module('reports');        // bool

// Get raw JWT token dari session
ums_token();                     // string|null

UmsClaims Object

Object yang dikembalikan oleh ums_user():

$user = ums_user();

// Properties
$user->userId;       // "d4c8546e-3166-4afe-a79f-70c073a7c1f7"
$user->email;        // "user@example.com"
$user->appId;        // "APP-10cd66bc"
$user->modules;      // array of module claims
$user->exp;          // 1782881536 (token expiry unix timestamp)
$user->iat;          // 1782877936 (token issued at)
$user->jti;          // "e2a4b879-ffd9-4a1a-9536-f0edcb1a9ebb"

// Methods
$user->hasPermission('inventory', 'read');     // bool
$user->hasRole('inventory', 'admin');          // bool
$user->hasModule('inventory');                 // bool
$user->getModuleRoles('inventory');            // ['admin', 'editor']
$user->getModulePermissions('inventory');      // ['read', 'write', 'delete']
$user->getModuleNames();                      // ['dashboard', 'inventory']
$user->isExpired();                           // bool
$user->toArray();                             // array

Struktur modules:

$user->modules = [
    [
        'module' => 'dashboard',
        'parent_module' => null,
        'roles' => ['admin'],
        'permissions' => ['create', 'delete', 'update', 'view'],
    ],
    [
        'module' => 'reports',
        'parent_module' => 'dashboard',
        'roles' => ['viewer'],
        'permissions' => ['read'],
    ],
];

Facade

use Kodepik\UMS\Facades\UMS;

// Validate token secara manual
$claims = UMS::validateToken($jwtToken);

// Validate via UMS server (server-side, lebih aman)
$claims = UMS::validateTokenRemote($jwtToken);

// Refresh token
$result = UMS::refreshToken($refreshToken);
// $result = ['token' => '...', 'refresh_token' => '...']

// Get URLs
$loginUrl = UMS::getLoginUrl();
$logoutUrl = UMS::getLogoutUrl('https://myapp.com/login');

// Clear JWKS cache (setelah key rotation di UMS)
UMS::invalidateJwksCache();

Implementasi Lengkap (Step by Step)

Step 1: Install & Config

composer require kodepik/ums-laravel
php artisan vendor:publish --tag=ums-config

Tambahkan ke .env:

UMS_BASE_URL=https://ums.yourserver.com
UMS_APP_ID=APP-xxxxx
UMS_CALLBACK_URL=http://localhost:8000/ums/callback
UMS_VERIFY_SSL=false

Step 2: Buat Login Page

{{-- resources/views/login.blade.php --}}
<a href="{{ route('ums.login') }}">Login with UMS SSO</a>

Step 3: Buat Dashboard (Protected Page)

// routes/web.php
Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
// app/Http/Controllers/DashboardController.php
<?php

namespace App\Http\Controllers;

class DashboardController extends Controller
{
    public function index()
    {
        $user = ums_user();

        if (!$user) {
            return redirect()->route('ums.login');
        }

        return view('dashboard', compact('user'));
    }
}
{{-- resources/views/dashboard.blade.php --}}
<h1>Welcome, {{ ums_user()->email }}</h1>
<p>Your modules: {{ implode(', ', ums_user()->getModuleNames()) }}</p>

<a href="{{ route('ums.logout') }}">Logout</a>

Step 4: Protect API Routes

// routes/api.php
use App\Http\Controllers\Api\ItemController;

Route::middleware(['ums.auth'])->group(function () {

    Route::get('/me', function () {
        return response()->json(['data' => ums_user()->toArray()]);
    });

    // CRUD dengan permission checks
    Route::middleware(['ums.permission:inventory,read'])
        ->get('/items', [ItemController::class, 'index']);

    Route::middleware(['ums.permission:inventory,create'])
        ->post('/items', [ItemController::class, 'store']);

    Route::middleware(['ums.permission:inventory,update'])
        ->put('/items/{id}', [ItemController::class, 'update']);

    Route::middleware(['ums.permission:inventory,delete'])
        ->delete('/items/{id}', [ItemController::class, 'destroy']);

    // Admin-only
    Route::middleware(['ums.role:inventory,admin'])
        ->post('/items/bulk-delete', [ItemController::class, 'bulkDelete']);
});

Step 5: Controller dengan Middleware

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class ItemController extends Controller
{
    public function __construct()
    {
        $this->middleware('ums.auth');
        $this->middleware('ums.permission:inventory,read')->only(['index', 'show']);
        $this->middleware('ums.permission:inventory,create')->only(['store']);
        $this->middleware('ums.permission:inventory,update')->only(['update']);
        $this->middleware('ums.permission:inventory,delete')->only(['destroy']);
    }

    public function index()
    {
        $items = \App\Models\Item::all();
        return response()->json(['success' => true, 'data' => $items]);
    }

    public function store(Request $request)
    {
        $validated = $request->validate(['name' => 'required|string']);

        $item = \App\Models\Item::create([
            ...$validated,
            'created_by' => ums_user()->email,
        ]);

        return response()->json(['success' => true, 'data' => $item], 201);
    }

    public function show(string $id)
    {
        $item = \App\Models\Item::findOrFail($id);
        return response()->json(['success' => true, 'data' => $item]);
    }

    public function update(Request $request, string $id)
    {
        $item = \App\Models\Item::findOrFail($id);
        $item->update($request->validated());
        return response()->json(['success' => true, 'data' => $item]);
    }

    public function destroy(string $id)
    {
        \App\Models\Item::findOrFail($id)->delete();
        return response()->json(['success' => true, 'message' => 'Deleted']);
    }
}

Step 6: Conditional Logic di Service/Controller

// Tanpa middleware — cek manual di logic
public function exportReport()
{
    if (!ums_can('reports', 'export')) {
        abort(403, 'You do not have export permission');
    }

    // ... generate export
}

// Conditional content berdasarkan role
public function dashboard()
{
    $data = ['items' => Item::all()];

    if (ums_has_role('dashboard', 'admin')) {
        $data['admin_stats'] = $this->getAdminStats();
    }

    return view('dashboard', $data);
}

Blade Template

Gunakan helper functions langsung di Blade:

{{-- Tampilkan info user --}}
@if(ums_user())
    <span>Logged in as: {{ ums_user()->email }}</span>
@endif

{{-- Conditional berdasarkan permission --}}
@if(ums_can('inventory', 'create'))
    <button>+ Add Item</button>
@endif

@if(ums_can('inventory', 'delete'))
    <button class="btn-danger">Delete Selected</button>
@endif

{{-- Conditional berdasarkan role --}}
@if(ums_has_role('dashboard', 'admin'))
    <a href="/admin">Admin Panel</a>
@endif

{{-- Conditional berdasarkan module --}}
@if(ums_has_module('reports'))
    <a href="/reports">Reports</a>
@endif

{{-- Show/hide menu items --}}
<nav>
    <a href="/dashboard">Dashboard</a>

    @if(ums_has_module('inventory'))
        <a href="/inventory">Inventory</a>
    @endif

    @if(ums_has_module('reports'))
        <a href="/reports">Reports</a>
    @endif

    @if(ums_has_role('dashboard', 'admin'))
        <a href="/settings">Settings</a>
    @endif
</nav>

Advanced: Custom Routes

Jika ingin handle sendiri tanpa auto-registered routes:

UMS_ROUTES_ENABLED=false

Lalu buat sendiri:

// routes/web.php
use Kodepik\UMS\Services\UmsClient;

Route::get('/auth/login', function (UmsClient $ums) {
    return redirect()->away($ums->getLoginUrl());
});

Route::get('/auth/callback', function (Request $request, UmsClient $ums) {
    $token = $request->query('token');
    $claims = $ums->validateToken($token);

    session([
        'ums_token' => $token,
        'ums_refresh_token' => $request->query('refresh_token'),
        'ums_user' => $claims->toArray(),
    ]);

    return redirect('/dashboard');
});

Route::get('/auth/logout', function (UmsClient $ums) {
    session()->flush();
    return redirect()->away($ums->getLogoutUrl(url('/login')));
});

Advanced: Token Refresh

Otomatis (via endpoint)

SDK menyediakan POST /ums/refresh yang bisa dipanggil dari frontend:

// Panggil sebelum token expired
const response = await fetch('/ums/refresh', {
    method: 'POST',
    headers: {
        'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
    },
});
const data = await response.json();
// data.data.token = new access token

Manual di backend

use Kodepik\UMS\Facades\UMS;

$refreshToken = session('ums_refresh_token');
$result = UMS::refreshToken($refreshToken);

session([
    'ums_token' => $result['token'],
    'ums_refresh_token' => $result['refresh_token'],
]);

Troubleshooting

Problem Penyebab Solusi
"kid" empty, unable to lookup correct key UMS JWKS tidak memiliki kid field SDK sudah handle otomatis (v1.0+)
Invalid or expired token Token JWT sudah expired Login ulang atau panggil /ums/refresh
SSL certificate problem Self-signed cert di staging Set UMS_VERIFY_SSL=false di .env
Missing authorization token Request tanpa header Authorization: Bearer ... Tambahkan header Bearer token
Route /ums/login not found Package belum ter-register Jalankan composer dump-autoload dan clear cache
Redirect ke /login setelah callback Session hilang atau callback URL mismatch Pastikan UMS_CALLBACK_URL match dengan yang terdaftar di UMS
403 padahal user punya permission Nama module/permission case-sensitive Cek exact match dengan yang ada di UMS Admin
JWKS cache stale setelah key rotation Cache belum expired Panggil UMS::invalidateJwksCache() atau php artisan cache:clear

Session Data

Setelah SSO login berhasil, data berikut tersimpan di Laravel session:

Key Type Isi
ums_token string JWT access token
ums_refresh_token string Refresh token
ums_user array Decoded JWT claims

Security Notes

  • ✅ Token divalidasi secara lokal menggunakan RSA public key (RS256) dari JWKS endpoint
  • ✅ JWKS key di-cache selama 1 jam (configurable) untuk minimalisir network calls
  • ✅ Refresh token hanya disimpan di server-side session (tidak di-expose ke client)
  • ⚠️ UMS_VERIFY_SSL=falsehanya untuk development. Di production selalu true
  • ⚠️ Pastikan APP_KEY Laravel sudah di-set (session encryption)
  • ⚠️ Gunakan HTTPS di production untuk protect token di callback URL

Sample Project

Lihat full working example di:

sample/laravel-ums-integration-sample/

Untuk menjalankan:

cd sample/laravel-ums-integration-sample
composer install
cp .env.example .env
php artisan key:generate
# Update .env dengan UMS config
php artisan serve

Buka http://localhost:8000 → Login with SSO → lihat dashboard test.

统计信息

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

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-07-01

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固