Cerita ini bermula dari tulisan integrasi custom OAuth2 dengan Moodle menggunakan Laravel Passport. Terdapat bug saat menggunakan mekanisme Authorization Code Flow di Laravel Passport.

Mari kita ibaratkan Moodle sebagai penyedia layanan (Service Provider) dan OAuth2 ini sebagai penyedia identitas (Indentity Provider). Moodle dengan situs moodle.tld dan OAuth2 dengan situs oauth2.tld.

Apa? OAuth2 sebagai penyedia identitas? Ya, kamu ngga salah baca. Sebenarnya yang benar adalah menggunakan OpenID Connect (lapisan kecil untuk di atas OAuth2 untuk otentikasi pengguna). Tetapi, untuk tulisan ini saya anggap OAuth2 sebagai penyedia identitas.

Jika Anda penasaran apa bedanya OAuth2 dengan OpenID Connect silakan tonton video berjudul “An Illustrated Guide to OAuth and OpenID Connect” dari Okta.

Kembali ke topik, kondisi sebelumnya yang terjadi ketika menghubungkan melakukan autentikasi di Moodle dengan custom OAuth2 sebagai berikut:

  1. Pengguna menuju ke halaman login Moodle.
  2. Pengguna mengklik tombol login Testing OAuth.
  3. Pengguna diarahkan ke website OAuth2 untuk menuju ke halaman persetujuan. Namun, karena pengguna belum login ke website OAuth2 maka di arahkan ke halaman login OAuth2.
  4. Setelah berhasil login ke website OAuth2, mestinya pengguna diarahkan ke halaman persetujuan seperti gambar di bawah.
Halaman permintaan otorisasi di Laravel Passport

Namun, yang terjadi adalah pengguna tidak diarahkan ke halaman persetujuan tersebut dan malah diarahkan ke halaman utama OAuth setelah login sehingga proses menuju ke halaman persetujuan terputus di tengah jalan. 😭

  1. Akhirnya, pengguna mengulang ke halaman login Moodle.
  2. Pengguna mengulang klik tombol login Testing OAuth.
  3. Karena pengguna sudah login di langkah sebelumnya maka langsung diarahkan ke halaman persetujuan apakah Moodle diijinkan untuk mendapatkan akses identitas milik kita dari OAuth2 server.
  4. Pengguna mengklik tombol Autorize untuk memberikan ijin Moodle mendapatkan data identitas kita.
Halaman permintaan otorisasi di Laravel Passport
  1. Pengguna di arahkan ke halaman pengguna Moodle.

Kondisi di atas membutuhkan 9 langkah untuk login ke Moodle menggunakan OAuth2. Hal yang mengganjal terjadi setelah langkah ke-empat. Jika kita berhasil memperbaiki bug di masalah otorisasi di Laravel Passport maka hanya dibutuhkan 6 langkah saja.

Pertama, kita analisa tautan mengarah ke halaman persetujan.

Tautannya seperti di bawah ini.

1
oauth/authorize?client_id=4&response_type=code&redirect_uri=abc&state=1234&scope=identity

Kita cek di Laravel Passport, apakah ada middleware yang melekat di tautan oauth/authorize?

Kita jalankan perintah php artisan route:list dan hasilnya tautan oauth/authorize dipasang oleh middleware web dan auth. Kita fokus ke middleware auth karena dia bertugas sebagai pengecek jika user belum login maka akan diarahkan ke tautan login.

Hasil dari PHP artisan route list untuk oauth authorize

Middleware auth berasal dari file app/Http/Middleware/Authenticate.php. Kita perhatikan isi method redirectTo().

1
2
3
4
5
6
protected function redirectTo($request)
{
    if (! $request->expectsJson()) {
        return route('login');
    }
}

Kalau request di atas tidak meminta respon berupa JSON maka arahkan ke tautan login. Dari sini kita bisa mengubah isi dari method redirectTo() menjadi seperti di bawah ini.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
protected function redirectTo($request)
{
    if (! $request->expectsJson()) {
      // Start modified line
        if ($request->path() === 'oauth/authorize') {
            if (isset($request->query()['client_id'])) {
                $params = array(
                    'client_id' => $request->query()['client_id'],
                    'return_to' => \Request::getRequestUri(),
                );
                return route('login', $params);
            } else {
                return route('login');
            }
        }
        // End modified line
        return route('login');
    }
}
  1. Cek apakah request yang dituju bukan meminta respon JSON?
  2. Jika iya, cek apakah path url nya adalah oauth/authorize?
  3. Jika benar, cek apakah ada query param bernama client_id?
  4. Jika ada, buat sebuah array bernama $params yang isinya adalah client_id dan return_to.
  5. Kemudian, tempelkan sebagai query string param di route login route('login', $params).

Hasil seperti di bawah ini.

1
login?client_id=4&return_to=%2Foauth%2Fauthorize?client_id=4&response_type=code&redirect_uri=abc&state=1234&scope=identity

Jika di langkah ketiga tidak ada query param bernama client_id maka arahkan dia ke route login tanpa query string param.

Berikutnya, kita override method showLoginForm() dari file app/Http/Controllers/Auth/LoginController.php.

1
2
3
4
public function showLoginForm(Request $request)
{
    return view('auth.login', array('request_uri' => \Request::getRequestUri()));
}

Di method showLoginForm() akan menampilkan view bernama login.blade.php yang terletak di direktori resources/views/auth dan sisipkan data bernama request_uri.

Nilai dari request_uri adalah

1
login?client_id=4&return_to=%2Foauth%2Fauthorize%2Fclient_id=4&response_type=code&redirect_uri=abc&state=1234&scope=identity

Lalu, kita modifikasi ganti nilai dari action pada form elemen di file login.blade.php.

1
2
3
4
<!--- Sebelum --->
<form method="POST" action="{{ route('login') }}">
<!--- Sesudah --->
<form method="POST" action="{{ $request_uri }}">

Selanjutnya, kita perlu memodifikasi method login() di file LoginController agar ketika user berhasil login maka di arahkan ke tautan otorisasi.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public function login(Request $request)
{
    // Kita berasumsi bahwa pengguna sudah terautentikasi 
    // dan berikutnya akan diarahkan ke halaman mana berdasarkan request
    if (isset($request->query()['return_to'])) {
        return redirect($request->query()['return_to']);
    } else {
        return redirect('/home');
    }
}

Selesai. Setelah diperbaiki, hanya butuh 6 langkah untuk login ke Moodle dengan Laravel Passport.

  1. Pengguna menuju ke halaman login Moodle.
  2. Pengguna mengklik tombol login Testing OAuth.
  3. Pengguna diarahkan ke website OAuth2 untuk menuju ke halaman persetujuan. Namun, karena pengguna belum login ke website OAuth2 maka di arahkan ke halaman login OAuth2.
  4. Setelah berhasil login ke website OAuth2, pengguna diarahkan halaman otorisasi untuk memberi konfirmasi ya atau tidak Moodle diijinkan untuk mendapatkan akses identitas milik kita dari OAuth2 server tadi.
  5. Pengguna mengklik tombol Authorize untuk memberikan ijin Moodle mendapatkan data identitas kita.
Halaman permintaan otorisasi di Laravel Passport
  1. Pengguna di arahkan ke halaman pengguna Moodle.

Saya sudah mencari jawaban di issue Laravel Passport terkait Passport doesn’t redirect back to authorization form after login di issue nomor #248 dan #703. Namun, tidak ada jawaban yang sesuai.

Tiba-tiba, saya teringat pernah mengimplementasikan login ke web PHPBali dengan Github OAuth2 menggunakan Socialite. Dengan bermodalkan inspect network, ketelitian dalam membaca url address bar, mengecek middleware yang menempel di path oauth/authorize dan bermain fungsi dd() yang disediakan oleh Laravel akhirnya permasalahan berhasil diselesaikan.