SSO Laravel
Petunjuk penggunaan pustaka SSO (Single Sign-On) Laravel Universitas Udayana.
Prasyarat
- PHP versi minimal 7.4.
- Laravel versi 8 sampai 10.
Catatan saat pengujian tutorial dilakukan pada Laravel versi 11. Bila Anda mengalami kendala silakan buat isu di ristekusdi/sso-laravel.
Tutorial Web Guard - Tingkat Dasar
Tutorial ini akan menjelaskan cara memproteksi halaman-halaman dengan otentikasi SSO IMISSU.
- Instal
ristekusdi/sso-laravel
dengan perintah di bawah ini.
composer require ristekusdi/sso-laravel
Setelah diinstal, silakan ambil nilai environment SSO di website IMISSU2 dan taruh di file .env
.
KEYCLOAK_ADMIN_URL=
KEYCLOAK_BASE_URL=
KEYCLOAK_REALM=
KEYCLOAK_REALM_PUBLIC_KEY=
KEYCLOAK_CLIENT_ID=
KEYCLOAK_CLIENT_SECRET=
- Jalankan perintah di bawah ini.
php artisan vendor:publish --tag=sso-laravel-web-demo-basic
Perintah di atas akan menghasilkan file-file di bawah ini.
- app/Http/Controllers/SSO/Web/AuthController.php
- app/Http/Controllers/SSO/Web/DemoController.php
- app/Models/SSO/Web/User.php
- config/sso.php
- resources/views/sso-web/basic.blade.php
- resources/views/sso-web/demo.blade.php
- routes/sso-web-demo.php
- Tambahkan guard dan provider
imissu-web
diconfig/auth.php
.
'guards' => [
// ...
'imissu-web' => [
'driver' => 'imissu-web',
'provider' => 'imissu-web',
],
],
'providers' => [
// ...
'imissu-web' => [
'driver' => 'imissu-web',
'model' => App\Models\SSO\Web\User::class,
],
],
- Daftarkan route
sso-web
dengan perintah di bawah ini.
php artisan vendor:publish --tag=sso-laravel-web-route
Perintah di atas akan menghasilkan file sso-web.php
di folder routes
. Isi dari file berupa route dengan method login
, callback
, logout
.
- Tambahkan route
sso-web.php
ke dalam fileroutes/web.php
.
<?php
// ...
require __DIR__.'/sso-web.php';
- Ubah nilai
redirect_url
menjadi'/sso-web-basic'
diconfig/sso.php
.
'redirect_url' => '/',
'redirect_url' => '/sso-web-demo',
Hal ini bertujuan ketika login dengan SSO maka langsung diarahkan ke halaman /sso-web-demo
.
- Tambahkan route
sso-web-demo.php
ke dalam fileroutes/web.php
untuk melihat demo SSO web.
<?php
// ...
require __DIR__.'/sso-web.php';
require __DIR__.'/sso-web-demo.php';
Buka halaman
/sso-web-demo
dengan URLhttp://localhost:<port>/sso-web-demo
atauhttp://yourapp.test/sso-web-demo
dengan bantuan Laragon.Klik tautan "Basic" dan tampilannya seperti gambar di bawah.
RANGKUMAN
Pada tutorial Web Guard tingkat dasar ini kita telah belajar cara:
- Instalasi package ristekusdi/sso-laravel.
- Mendaftarkan guard dan provider imissu-web di config/auth.php.
- Mendaftarkan route SSO web untuk login, callback, dan logout.
- Mengubah nilai redirect_url di config/sso.php.
- Melihat halaman SSO web demo basic sebagai contoh implementasi Web Guard tingkat dasar.
Tutorial Web Guard - Tingkat Lanjut
PERINGATAN
Anda diwajibkan mengikuti Tutorial Web Guard Tingkat Dasar terlebih dahulu!
Kita akan belajar menyisipkan atribut tambahan seperti roles
, role
di objek pengguna IMISSU. Lalu, kita akan mengubah nilai dari atribut role
dengan perintah session
.
- Jalankan perintah di bawah ini.
php artisan vendor:publish --tag=sso-laravel-web-demo-advance
Perintah di atas akan mengimpor file resources/views/sso-web/advance.blade.php
.
- Akses halaman demo dengan
http://localhost:<port>/sso-web-demo
atauhttp://yourapp.test/sso-web-demo
di aplikasi. Klik tautan "Advance" dan Anda akan mendapatkan tampilan halaman berikut.
Pada gambar di atas, kita belum mendapatkan nilai dari Current role dan Permissions yang melekat pada Current role (lihat pada tanda ???
di gambar). Tugas kita adalah memberikan nilai pada Current role dan Permissions.
- Kita akan menerapkan konsep Accessor dan Mutators Laravel untuk menangani kondisi ini. Buka file
app/Models/SSO/Web/User.php
dan ikuti isian seperti kode di bawah.
<?php
namespace App\Models\SSO\Web;
use RistekUSDI\SSO\Laravel\Models\Web\User as Model;
class User extends Model
{
protected $appends = ['roles', 'role'];
public function getRolesAttribute()
{
if (session()->has('roles')) {
return $this->attributes['roles'] = session()->get('roles');
} else {
$client_roles = $this->getAttribute('client_roles');
$permissions = [
'Admin' => [
'user:view',
'user:edit',
'user:delete'
],
'Super Admin' => [
'user:view',
'user:create',
'user:edit',
'user:delete'
],
'Pegawai' => [
'profile:view',
'profile:edit'
],
];
$roles = [];
foreach ($client_roles as $client_role) {
array_push($roles, ['name' => $client_role]);
}
foreach ($roles as $key => $role) {
foreach ($permissions as $_key => $perm) {
if ($role['name'] === $_key) {
$roles[$key]['permissions'] = $permissions[$_key];
}
}
}
$roles = json_decode(json_encode($roles));
session()->put('roles', $roles);
return $this->attributes['roles'] = session()->get('roles');
}
}
public function setRoleAttribute($value)
{
session()->forget('role');
session()->put('role', $value);
$this->attributes['role'] = session()->get('role');
}
public function getRoleAttribute()
{
if (session()->has('role')) {
return $this->attributes['role'] = session()->get('role');
} else {
return $this->attributes['role'] = $this->getAttribute('roles')['0'];
}
}
}
- Refresh halaman "Advance" maka nilai dari field Current role sudah berubah seperti gambar di bawah.
- Kita akan mengubah session
role
. Tambahkan kode berikut di dalam group middlewareimissu-web
di fileroutes/sso-web-demo.php
.
Route::middleware(['imissu-web'])->group(function () {
//...
Route::post('/sso-web-demo/change-role', [App\Http\Controllers\SSO\Web\DemoController::class, 'changeRole']);
});
- Tambahkan kode untuk mengubah peran di
resources/views/sso-web/advance.blade.php
sebelum tag</body>
.
<input type="hidden" name="url_change_role" value="{{ url('/sso-web-demo/change-role') }}">
<script>
document.getElementById('roles').onchange = function (e) {
const value = e.target.value;
const url = document.querySelector('input[name="url_change_role"]').value;
const home_url = document.querySelector('input[name="home_url"]').value;
const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
if (value != '0') {
fetch(url, {
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': token
},
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify({ role: value })
})
.then(response => {
if (response.ok) {
window.location.href = home_url;
} else {
switch (response.status) {
case 401:
throw new Error("Sesi login Anda sudah habis! Silakan login kembali.");
break;
case 403:
throw new Error("Tidak dapat mengubah peran aktif! Silakan login kembali.");
default:
throw new Error("Terjadi kesalahan sistem! Silakan login kembali.");
break;
}
}
})
.then(err => {
alert(err.message);
window.location.reload();
});
}
}
</script>
- Tambahkan method
changeRole()
diapp/Http/Controllers/Web/DemoController.php
.
<?php
// ..
public function changeRole(Request $request)
{
auth('imissu-web')->user()->role = json_decode($request->role);
return response()->json([
'message' => 'Berhasil mengubah peran aktif',
], 204);
}
- Di halaman "Advance" ubah peran Admin menjadi Super Admin dan hasilnya seperti gambar di bawah.
Kita telah menambahkan atribut tambahan dari session aplikasi. Bagaimana cara menghapusnya saat pengguna keluar aplikasi? Jika Anda sudah mengikuti langkah 5 sampai 8 mungkin Anda sudah menemukan caranya. Jika belum, kita lanjutkan tutorial ini.
Tutorial selesai. 🎉
RANGKUMAN
Pada tutorial Web Guard tingkat lanjut ini kita telah belajar cara:
- Menerapkan Accessor dan Mutator untuk atribut tambahan seperti
roles
danrole
di User model. - Mengubah nilai
role
menggunakansession()
Laravel.
Tutorial Web Guard - Refactoring
Pada tutorial Web Guard tingkat lanjut kita sudah menyisipkan atribut tambahan seperti roles
, roles
dari session aplikasi, mengubah session role
, dan menghapus session aplikasi. Setiap sistem pasti memiliki kebutuhan atribut-atribut tambahan yang beragam dan membuat baris kode dalam suatu file akan panjang sehingga sulit dibaca oleh programmer. Maka dari itu dibutuhkan proses refactoring. Refactoring adalah proses mengubah struktur kode program tanpa mengubah perilaku dari kode program. Salah satu manfaat dari proses refactoring adalah memudahkan kode program dibaca dan dipahami oleh programmer lain saat berkolaborasi membuat program perangkat lunak.
- Jalankan perintah di bawah ini.
php artisan vendor:publish --tag=sso-laravel-web-session
Perintah di atas akan menghasilkan file-file di bawah ini.
- app/Http/Controllers/SSO/Web/SessionController.php
- app/Facades/WebSession.php
- app/Providers/WebSessionProvider.php
- app/Services/WebSession.php
- routes/web-session.php
- Daftarkan route
web-session.php
ke dalam fileroutes/web.php
.
// ...
require __DIR__.'/web-session.php';
- Hapus baris kode di file
routes/sso-web-demo.php
dan tambahkan baris kode di fileroutes/web-session.php
.
Route::post('/sso-web-demo/change-role', [App\Http\Controllers\SSO\Web\DemoController::class, 'changeRole']);
Route::post('/web-session/change-role', [App\Http\Controllers\SSO\Web\SessionController::class, 'changeRole']);
Pindahkan method
changeRole()
dari fileapp/Http/Controllers/SSO/Web/DemoController.php
ke fileapp/Http/Controllers/SSO/Web/SessionController.php
.Daftarkan provider
WebSession
dan facadesWebSession
diconfig/app.php
pada Laravel versi 8 - 10 ataubootstrap/providers
pada Laravel versi 11.
'providers' => [
//...
// WebSession
App\Providers\WebSessionProvider::class,
],
'aliases' => [
//...
// WebSession
'WebSession' => App\Facades\WebSession::class,
]
<?php
return [
// ...
App\Providers\WebSessionProvider::class,
];
- Refactor kode program di
app/Models/SSO/Web/User.php
.
<?php
namespace App\Models\SSO\Web;
use App\Facades\WebSession;
use RistekUSDI\SSO\Laravel\Models\Web\User as Model;
class User extends Model
{
protected $appends = ['roles', 'role'];
public function getRolesAttribute()
{
return $this->attributes['roles'] = WebSession::getRoles($this->getAttribute('client_roles'));
}
public function setRoleAttribute($value)
{
WebSession::setRole($value);
$this->attributes['role'] = session()->get('role');
}
public function getRoleAttribute()
{
return $this->attributes['role'] = WebSession::getRole($this->getAttribute('roles')['0']);
}
}
- Tambahkan kode program berikut di
app/Services/WebSession.php
.
<?php
namespace App\Services;
class WebSession
{
public function getRoles($client_roles)
{
if (session()->has('roles')) {
return session()->get('roles');
} else {
$permissions = [
'Super Admin' => [
'user:view',
'user:create',
'user:edit',
'user:delete'
],
'Admin' => [
'user:view',
'user:edit',
],
'Pegawai' => [
'profile:view',
'profile:edit'
],
];
$roles = [];
foreach ($client_roles as $client_role) {
array_push($roles, ['name' => $client_role]);
}
foreach ($roles as $key => $role) {
foreach ($permissions as $_key => $perm) {
if ($role['name'] === $_key) {
$roles[$key]['permissions'] = $permissions[$_key];
}
}
}
$roles = json_decode(json_encode($roles));
session()->put('roles', $roles);
return session()->get('roles');
}
}
public function setRole($role)
{
session()->forget('role');
session()->put('role', $role);
}
public function getRole($role)
{
if (session()->has('role')) {
return session()->get('role');
} else {
return $role;
}
}
}
- Ganti value dari
url_change_role
di fileresources/views/sso-web/advance.blade.php
.
<input type="hidden" name="url_change_role" value="{{ url('/sso-web-demo/change-role') }}">
+ <input type="hidden" name="url_change_role" value="{{ url('/web-session/change-role') }}">
- Proses refactoring telah selesai. Silakan cek ulang apakah proses mengubah session
role
di halaman "Advance" masih berjalan atau tidak. Bila tidak berjalan silakan cek kembali langkah-langkah di atas.
Web Guard - Auth
Berikut perintah-perintah yang digunakan untuk mengakses data pengguna SSO.
# Attributes
// sub adalah id user di Keycloak.
// Atribut TIDAK DIREKOMENDASIKAN untuk menyimpan id unik pengguna.
auth('imissu-web')->user()->sub;
auth('imissu-web')->user()->preferred_username;
auth('imissu-web')->user()->name;
auth('imissu-web')->user()->email;
// Daftar peran pengguna dalam suatu aplikasi.
auth('imissu-web')->user()->client_roles;
// id user di Unud.
// Atribut ini DIREKOMENDASIKAN untuk menyimpan id unik pengguna.
auth('imissu-web')->user()->unud_identifier_id;
// id sso Unud.
// Atribut ini DIREKOMENDASIKAN untuk menyimpan id unik pengguna.
auth('imissu-web')->user()->unud_sso_id;
// id tipe pengguna di Unud.
auth('imissu-web')->user()->unud_user_type_id;
# Virtual attributes
auth('imissu-web')->user()->username;
// Identifier = NIP/NIM
auth('imissu-web')->user()->identifier;
// Identitas lengkap dalam bentuk NIP/NIM Nama pengguna
auth('imissu-web')->user()->full_identity;
# Methods
// Mengecek apakah pengguna memiliki peran tertentu dalam daftar peran aplikasi.
// Nilai `$roles` bertipe string atau array.
auth('imissu-web')->user()->hasRole($roles);
// Mengecek apakah pengguna memiliki peran tertentu.
// Nilai input bertipe string atau array
auth('imissu-web')->user()->hasRole($role);
// Mengecek apakah pengguna memiliki permission tertentu.
// Nilai input bertipe string atau array.
auth('imissu-web')->user()->hasPermission($permissions);
# Utility
// Mengecek apakah pengguna sudah login?
auth('imissu-web')->check();
// Mengecek apakah pengguna belum login?
auth('imissu-web')->guest();
Web Guard Middleware
Berikut daftar web middleware yang disediakan oleh SSO Laravel.
// Middleware untuk mengecek apakah pengguna sudah terotentikasi.
// Jika belum terotentikasi maka diarahkan ke halaman login.
middleware('imissu-web');
// Middleware untuk mengecek apakah pengguna memiliki peran Admin
// Jika peran lebih dari satu maka gunakan tanda "|"
Route::middleware('imissu-web.role:Admin');
Route::middleware('imissu-web.role:Admin|Super Admin');
// Middleware untuk mengecek apakah pengguna memiliki permission user.create
// Jika permission lebih dari satu maka gunakan tanda "|"
Route::middleware('imissu-web.permission:user.create');
Route::middleware('imissu-web.permission:user.create|user.view');
Soal Sering Ditanya
Bagaimana cara mendapatkan access token dan refresh token?
Terdapat dua cara untuk mendapatkan access token dan refresh token:
- Menggunakan facade
IMISSUWeb
dari packageristekusdi/sso-laravel
.
<?php
use RistekUSDI\SSO\Laravel\Facades\IMISSUWeb;
$token = IMISSUWeb::retrieveToken();
$access_token = $token['access_token'];
$refresh_token = $token['refresh_token'];
- Menggunakan fitur Session dari Laravel.
<?php
$access_token = session()->get('_sso_token.access_token');
$refresh_token = session()->get('_sso_token.refresh_token');