Skip to content

OAuth2 Health Check

By Romi Fadli, S.Kom. 11 min read Security & Secrets
On this page
A checklist card on the left, a three-part token in the middle with a check mark, and an identity provider marked with a key on the right

OAuth2 Health Check adalah script Python interaktif yang membuktikan identity provider sudah dikonfigurasi dengan benar untuk satu client sebelum aplikasi mana pun bergantung padanya. Ia menjalankan satu login utuh persis seperti aplikasi, menerima token-nya, lalu memeriksanya sebagaimana aplikasi semestinya memeriksa, sehingga saat saya menulis kode login milik aplikasi itu sendiri, provider sudah tak lagi menjadi tersangka.

OAuth 2.0 adalah standar yang membuat aplikasi memperoleh akses atas nama user tanpa melihat password-nya, dan OpenID Connect (OIDC) menambahkan pernyataan bertanda tangan tentang siapa user itu. PKCE adalah perluasan yang mengikat langkah terakhir login ke client yang memulainya, dan JWKS adalah kumpulan public key yang dipublikasikan provider untuk menandatangani token-nya. Login yang gagal di service setengah jadi bisa berarti konfigurasi provider atau kode service-nya; tool ini menyingkirkan kemungkinan pertama sebelum yang kedua sempat ada.

Sekilas

BentukScript command-line interaktif, oauth2.py
BahasaPython 3, dengan requests, PyJWT, dan InquirerPy
StatusStabil, dipakai
Kode sumberPrivat
Apa tool ini, dibangun dengan apa, dan keadaannya.

Yang dikerjakannya

A. Menggladi satu login utuh

Ia menyusun alamat otorisasi persis seperti aplikasi — Authorization Code flow dengan PKCE, state dan nonce baru, serta alamat redirect yang terdaftar untuk client itu, dengan spasi dikodekan sebagai %20, bukan + — lalu mencetak verifier, challenge, state, dan nonce disertai pengingat untuk menyimpan verifier-nya, jadi setiap langkah bisa diulang dengan tangan. Sesudah itu ia menunggu selagi saya login di browser. Ketika alamat tujuan redirect dari provider saya tempelkan kembali, ia memastikan issuer-nya ada dan memang yang diharapkan, state-nya cocok dengan yang ia kirim, dan code-nya benar-benar kembali, serta menampilkan session state yang disertakan provider. Lalu ia menukar code dengan token; bila pertukaran itu ditolak, status dan body error dari provider dicetak persis seperti yang diterima.

B. Memvalidasi token yang diterimanya

Setiap token berbentuk JWT diverifikasi terhadap public key yang dipublikasikan provider, yang diambil sekali lalu dipakai untuk kedua token. Signature dan masa berlaku selalu diverifikasi, dan token kedaluwarsa mendapat pesannya sendiri. Issuer dan audience diverifikasi bila cocok dengan yang diharapkan konfigurasi; bila tidak, pemeriksaan itu dilewati dengan peringatan kuning, bukan digagalkan, karena provider seperti Google memang berbeda di situ — jadi putusan yang jujur adalah peringatan, bukan lolos. Token yang tak ada di respons dilaporkan hilang. Klaim yang sudah di-decode dicetak di samping putusannya, dengan masa berlaku dalam waktu lokal, jadi kegagalan langsung menunjuk klaim penyebabnya.

C. Memeriksa token opaque seperti resource server memeriksanya

Tak semua provider menerbitkan access token yang bisa di-decode. Untuk yang seperti itu, ia bertanya ke endpoint informasi token milik provider apakah token itu aktif dan untuk siapa, lalu memanggil endpoint informasi user untuk membuktikan token itu sungguh memberi akses — dua pertanyaan yang sama yang harus diajukan resource server. Caranya lewat endpoint informasi token dan informasi user gaya Google, karena Google tak menyediakan introspeksi RFC 7662 publik; provider yang tak punya kedua endpoint itu di konfigurasi membuat token opaque-nya ditandai tak bisa divalidasi, tak pernah diloloskan diam-diam.

D. Logout sekaligus login

Login yang berhasil baru separuh integrasi. Tool ini juga menjalankan RP-Initiated Logout sebagai run terpisah: saya menempelkan ID token hasil login sebagai petunjuk, ia menyusun alamat logout provider dengan petunjuk itu dan state baru, lalu memvalidasi alamat tujuan provider sesudah logout. Pada alamat kembali itu state wajib ada, sedangkan issuer yang hilang diterima dengan catatan, karena Keycloak memang tak menyertakannya di sana.

E. Banyak provider dari satu file

Satu file konfigurasi bisa memuat beberapa provider, masing-masing dengan endpoint, alamat redirect, client, dan scope-nya. Provider, client, flow, scope, dan mode dipilih lewat menu, jadi file yang sama melayani Keycloak yang di-hosting sendiri maupun Google. File yang tak ada, JSON yang rusak, atau provider tanpa client dilaporkan dalam satu kalimat jelas, bukan traceback; setiap scope dimulai dalam keadaan tercentang, dan pilihan kosong menghentikan run.

Cara saya memakainya

Perintahnya cuma satu; sisanya dipilih secara interaktif.

bash
python oauth2.py --config=my-app.config.json
TahapYang terjadi
1. Memilihprovider, client, flow (login atau logout), scope, dan mode
2. Callbacksaya login di browser lalu menempelkan kembali alamat redirect-nya; state dan issuer diperiksa
3. Pertukaran tokencode dan verifier PKCE ditukar dengan token
4. Validasisetiap token diverifikasi dan klaimnya ditampilkan
Empat tahap satu run login.

Dua mode menentukan di mana satu run login berhenti. Mode penuh melewati keempat tahap. Mode body saja berhenti sesudah callback dan mencetak body request yang akan dikirim aplikasi untuk menuntaskan login sendiri, jadi pertukarannya bisa dites terhadap backend saya sendiri. Body itu memuat semua yang dibutuhkan backend saya untuk menuntaskan pertukaran, dan code-nya sengaja dibiarkan belum terpakai, karena code hanya bisa ditukar sekali dan backend saya yang harus memakainya.

Konfigurasi mendeskripsikan tiap provider sekali saja. Client secret hanya tinggal di file lokal, yang tak pernah meninggalkan mesin:

json
{
  "issuers": [
    {
      "iss": "https://id.example.com/realms/main",
      "endpoints": {
        "auth": "…/protocol/openid-connect/auth",
        "token": "…/protocol/openid-connect/token",
        "jwks": "…/protocol/openid-connect/certs",
        "logout": "…/protocol/openid-connect/logout"
      },
      "redirect_uri": {
        "login": "https://app.example.com/callback",
        "logout": "https://app.example.com/signed-out"
      },
      "clients": [{ "client_id": "my-app", "client_secret": "YOUR-CLIENT-SECRET" }],
      "scopes": ["openid", "email", "profile"]
    }
  ]
}

Berikut dua run sungguhan terhadap realm Keycloak, login lalu logout, dipendekkan, dengan setiap alamat, identifier, dan token diganti. Tool ini melapor dalam bahasa Indonesia:

bash
$ python oauth2.py --config=my-app.config.json
? Pilih Issuer yang akan digunakan: [1] https://id.example.com/realms/main
? Pilih Client: [1] my-app
? Pilih flow: [1] Login (Authorization Code + PKCE)
? Pilih mode: [1] Proses PKCE lengkap (exchange code → token)

Paste redirect URL callback: https://app.example.com/callback?state=<state>&iss=…&code=<code>
[✓] Validasi berhasil
[✓] Token Exchange berhasil

── access_token ──
[✓] Signature valid  │  Expire: 2026-10-02 12:11:24 WIB
  "iss": "https://id.example.com/realms/main",
  "aud": ["my-app", "account"],
  "azp": "my-app",
── id_token ──
[✓] Signature valid  │  Expire: 2026-10-02 12:11:24 WIB

$ python oauth2.py --config=my-app.config.json
? Pilih flow: [2] Logout (RP-Initiated Logout)
[~] 'iss' tidak disertakan pada callback logout (wajar untuk Keycloak)
[✓] Logout callback tervalidasi (state cocok)

Keputusan rancangan

Ia menggladi login seutuhnya, jadi provider terbukti sebelum aplikasinya ada. Memeriksa bahwa alamat discovery provider menjawab hampir tak membuktikan apa-apa. Saya membuat tool ini menempuh jalan yang sama dengan aplikasi — PKCE dengan verifier sepanjang yang diminta standar dan metode challenge S256, state baru, alamat redirect yang terdaftar, login sungguhan di browser, dan pertukaran token sungguhan. Bila ia lolos, setiap kegagalan sesudahnya milik aplikasi, dan itu membelah dua area pencarian sebelum pencarian dimulai.

Ia memeriksa apa yang wajib diperiksa aplikasi, dan menyebut alasannya bila ada yang janggal. Signature diperiksa terhadap public key provider, dengan memilih key yang disebut token, melewati key yang ditujukan untuk enkripsi, dan memakai signing key pertama yang bisa dipakai bila token tak menyebut key atau tak ada yang cocok. Hanya algoritma RS dan ES 256, 384, dan 512 bit yang diterima — tak pernah none dan tak pernah algoritma HS yang memakai shared secret — sehingga serangan algorithm confusion, tempat token yang ditandatangani dengan public key sebagai secret HMAC bisa lolos verifikasi, tertutup. Key-key itu diambil dengan User-Agent eksplisit, karena sebagian provider berada di balik firewall yang menolak User-Agent bawaan library Python. Bila client tak ada di audience token tapi tercatat sebagai authorized party-nya, tool menerimanya sambil mengatakannya, bukan gagal diam-diam atau lolos diam-diam — perbedaan yang menentukan apakah pemeriksaan milik aplikasi nanti akan lolos.

Ia memperlakukan provider sebagai data, jadi provider baru hanya berongkos beberapa baris konfigurasi. Provider berbeda-beda dalam hal kecil yang menjengkelkan: satu menerbitkan access token yang bisa di-decode, yang lain opaque; satu menamai key-nya, yang lain tidak; satu mengembalikan issuer di setiap callback, yang lain menghilangkannya saat logout. Tool ini tak bercabang berdasarkan nama provider; ia membaca apa yang dinyatakan tiap provider lalu menyesuaikan diri — menerima alamat kembali logout tanpa issuer, dan memakai endpoint informasi token dan informasi user bila token tak bisa di-decode. Panduan untuk sisi provider, misalnya memasukkan client ke audience token Keycloak atau menambah signing key 2048 bit, disimpan di samping script.

Rahasia tetap di mesin, dan aturannya mencakup setiap nama file yang bisa memuat rahasia. Setiap file konfigurasi yang bisa memuat rahasia diabaikan version control; hanya contoh dengan secret kosong yang di-commit. Token hanya dicetak ke terminal dan tak pernah ditulis ke disk.

Ia berhenti tepat di tempat komponen berikutnya mulai. Run penuh menguji provider; run body saja menyerahkan callback yang sudah selesai ke backend saya; run logout menutup session yang ia buka. Setiap mode berakhir persis di titik bagian lain sistem mengambil alih, jadi kegagalan selalu bisa ditelusuri ke salah satu sisi batas itu.

Spesifikasi

MasukanKonfigurasi JSON berisi provider: issuer, endpoint, alamat redirect, client, dan scope; alamat redirect yang ditempelkan kembali sesudah login
KeluaranAlamat otorisasi dan logout, putusan callback, token beserta klaimnya yang sudah di-decode, putusan per token, atau body request di mode body saja; putusannya ditampilkan di output
FlowAuthorization Code dengan PKCE; RP-Initiated Logout
StandarOAuth 2.0 (RFC 6749), PKCE (RFC 7636), JWT (RFC 7519), JWK dan JWKS (RFC 7517), OpenID Connect Core dan RP-Initiated Logout
SignatureRS256, RS384, RS512, ES256, ES384, ES512
KetergantunganPython 3, requests, PyJWT, InquirerPy
KeamananClient secret tetap di konfigurasi lokal, dan setiap nama konfigurasi yang memuat secret diabaikan version control; token hanya dicetak ke terminal dan tak pernah disimpan
ClientConfidential client yang diautentikasi dengan client secret
Panduan providerCatatan tentang audience mapper Keycloak dan penambahan signing key RSA 2048 bit
Bekerja denganProvider OpenID Connect standar, termasuk Keycloak dan Google
Masukan, keluaran, dan standar tool ini.

Terkait

Cerita di balik tool ini, dan perbedaan antar-provider yang terungkap karenanya.

Bentuknya mengikuti bentuk yang saya berikan pada setiap tool kecil: satu file, satu tugas, dan urutan seksi yang sama setiap kali — hanya saja, karena interaktif, ia melapor lewat pesan-pesannya, bukan lewat exit code.

Stack gateway dan login yang identity provider-nya lebih dulu dibuktikan siap oleh pemeriksaan ini.

Cerita tentang audience yang dikirim sebagai list — pelajaran yang sama di balik aturan audience dan authorized party di sini.

Istilah

Authorization Code flow
Flow login tempat provider mengembalikan code berumur pendek ke client, yang lalu menukarnya dengan token.
PKCE
Proof Key for Code Exchange: verifier rahasia buatan client, yang hash-nya dikirim di awal dan aslinya di akhir, sehingga code yang dicuri tak berguna.
ID token
Token bertanda tangan dari provider OpenID Connect yang menyatakan siapa user-nya dan untuk client mana.
Token opaque
Access token yang tak bisa di-decode client dan harus diperiksa dengan bertanya ke provider.
Audience dan authorized party
Klaim yang menyebut untuk siapa token itu (aud) dan client mana yang memintanya (azp).
RP-Initiated Logout
Mengeluarkan user dengan mengarahkannya ke alamat logout provider beserta petunjuk session mana yang diakhiri.

Technologies covered

  • Python
  • Keycloak
  • JWT
  • OAuth 2.0
  • OpenID Connect
  • PKCE

About the author

Photo of Romi Fadli

Romi Fadli, S.Kom.

Solution Architect · Backend Engineer · Embedded Systems

In my day job I work as a solution architect. I design the system, lead the team that builds it — quality assurance, backend developers, a UI developer, DevOps and DevSecOps — and write the most critical modules myself, such as a certificate authority, a long-term PDF signer, and the core API of a personnel system.

I work across the whole stack that a running system actually needs. On the hardware side that means PCB design, microcontrollers, and embedded firmware — including RISC-V boards running FreeRTOS and Embassy, speaking MQTT over mutually authenticated TLS on a microcontroller with 64 KB of SRAM.

Most of my day-to-day work is software: Rust backends and API modules built on Clean Architecture, an OAuth2/OpenID Connect authorization server written from scratch, PAdES document signing, and certificate authorities — root CAs, timestamping authorities, and OCSP responders. I also build the frontends that sit on top of them.

Running all of that means infrastructure: Docker Compose stacks, networking, Mikrotik and RADIUS, observability with OpenTelemetry, and a steady habit of writing command-line tools and utilities for Linux when the existing ones do not fit.

Share

Enjoyed this?

Get an email when a new article is published.

Referenced by

A purple root circle linked to a blue and an orange intermediate circle, which lead to a signed document with a green seal and to a clock.

Aurograf

DevOps

Platform tanda tangan digital Pemerintah Kota Tangerang Selatan: penanda tangan membuktikan identitasnya, menandatangani PDF hingga PAdES B-LTA dengan sertifikat dari otoritas milik platform sendiri, dan siapa pun bisa memverifikasinya — versi 2, yang saya rancang, bangun, dan operasikan.

10 min read Read more →

Related Articles