# JWT API Authentication for Odoo 19 Modul `grt_jwt_token` menyediakan autentikasi JWT untuk aplikasi eksternal yang ingin mengakses endpoint API Odoo dengan pola: 1. Aplikasi eksternal mengirim `client_id` dan `client_secret`. 2. Odoo memvalidasi API Client. 3. Odoo mengembalikan JWT access token. 4. Aplikasi eksternal memakai token tersebut sebagai `Authorization: Bearer ` untuk mengakses endpoint API yang dilindungi. Modul ini cocok untuk integrasi API dan SSO berbasis token antar aplikasi. Untuk login otomatis ke UI web Odoo, masih diperlukan flow tambahan yang membuat Odoo session secara eksplisit. ## Fitur - Menu aplikasi utama: **JWT Auth**. - Model konfigurasi API Client. - Generate `client_id` otomatis. - Generate dan rotasi `client_secret`. - Penyimpanan secret sebagai hash PBKDF2, bukan plaintext. - Token JWT dengan claim `iss`, `aud`, `exp`, `iat`, `jti`, `sub`, dan `client_id`. - Decorator `@check_jwt_auth` untuk melindungi controller custom. - Contoh endpoint protected: `/api/ifc/projects`. ## Struktur Modul ```text grt_jwt_token/ +-- __manifest__.py +-- __init__.py +-- README.md +-- controllers/ | +-- __init__.py | +-- main.py +-- models/ | +-- __init__.py | +-- api_client.py +-- security/ | +-- ir.model.access.csv +-- static/ | +-- description/ | | +-- icon.png | | +-- icon_128.png | +-- src/img/ | +-- jwt_auth_icon.png +-- views/ +-- api_client_views.xml ``` ## Konfigurasi Odoo Tambahkan `jwt_secret` di file `odoo.conf`. ```ini [options] jwt_secret = ganti_dengan_secret_panjang_minimal_32_karakter ``` Ketentuan: - Minimal 32 karakter. - Jangan pakai secret contoh di production. - Simpan secret hanya di server. - Setelah mengubah `odoo.conf`, restart Odoo. Contoh konfigurasi lokal: ```ini db_name = odoo19 http_port = 8071 jwt_secret = W2V8bVN38YJZJQhcew8Fcgw6vG5Ab79hbRzD3SpjJvGQxNPvKcNFQ8syb6h9PqRr ``` ## Instalasi 1. Pastikan folder `grt_jwt_token` berada di `addons_path`. 2. Restart Odoo. 3. Buka **Apps**. 4. Update Apps List jika modul belum muncul. 5. Install **JWT API Authentication**. Atau lewat CLI: ```powershell $env:PYTHONPATH='C:\odoo19\server' .\.venv\Scripts\python.exe C:\odoo19\server\odoo-bin -c .\odoo.conf -d odoo19 -i grt_jwt_token --stop-after-init ``` Untuk update modul: ```powershell $env:PYTHONPATH='C:\odoo19\server' .\.venv\Scripts\python.exe C:\odoo19\server\odoo-bin -c .\odoo.conf -d odoo19 -u grt_jwt_token --stop-after-init ``` ## Membuat API Client 1. Login sebagai Administrator. 2. Buka app **JWT Auth**. 3. Buat record baru. 4. Isi: - **Application Name**: nama aplikasi eksternal. - **Odoo User**: user Odoo yang akan dipakai ketika token digunakan. - **Token Lifetime (seconds)**: masa berlaku token, default `7200`. 5. Simpan. 6. Salin `Client ID` dan `New Client Secret`. Penting: `client_secret` hanya ditampilkan saat dibuat atau setelah tombol **Regenerate Secret** ditekan. Setelah halaman ditutup, secret tidak bisa dilihat lagi karena database hanya menyimpan hash. ## Endpoint Token URL: ```text POST /api/auth/token ``` Content-Type: ```text application/json ``` Body: ```json { "client_id": "CLIENT_ID_ANDA", "client_secret": "CLIENT_SECRET_ANDA" } ``` `secret_key` juga diterima sebagai alias dari `client_secret` untuk kompatibilitas integrasi. Response sukses: ```json { "access_token": "JWT_TOKEN", "expires_in": 7200, "token_type": "Bearer" } ``` Response gagal: ```json { "error": "Invalid credentials." } ``` ## Contoh Request Token PowerShell: ```powershell $body = @{ client_id = "CLIENT_ID_ANDA" client_secret = "CLIENT_SECRET_ANDA" } | ConvertTo-Json Invoke-RestMethod ` -Uri "http://localhost:8071/api/auth/token" ` -Method Post ` -Body $body ` -ContentType "application/json" ``` cURL: ```bash curl -X POST http://localhost:8071/api/auth/token \ -H "Content-Type: application/json" \ -d '{"client_id":"CLIENT_ID_ANDA","client_secret":"CLIENT_SECRET_ANDA"}' ``` ## Mengakses Endpoint Protected Contoh endpoint: ```text GET /api/ifc/projects ``` Header: ```text Authorization: Bearer JWT_TOKEN ``` PowerShell: ```powershell $headers = @{ Authorization = "Bearer JWT_TOKEN" } Invoke-RestMethod ` -Uri "http://localhost:8071/api/ifc/projects" ` -Method Get ` -Headers $headers ``` cURL: ```bash curl http://localhost:8071/api/ifc/projects \ -H "Authorization: Bearer JWT_TOKEN" ``` Response contoh: ```json { "message": "JWT token is valid.", "user_id": 2, "user_name": "Administrator", "client_id": "CLIENT_ID_ANDA", "data": [ "Proyek_IFC_Gedung_A", "Proyek_IFC_Infrastruktur_B" ] } ``` ## Melindungi Controller Custom Import decorator dari controller modul: ```python from odoo import http from odoo.http import request from odoo.addons.grt_jwt_token.controllers.main import check_jwt_auth, json_response class MyApiController(http.Controller): @http.route('/api/my/resource', type='http', auth='public', methods=['GET'], csrf=False) @check_jwt_auth def my_resource(self, **kwargs): return json_response({ 'user_id': request.env.user.id, 'user_name': request.env.user.name, }) ``` Saat token valid, decorator akan: - Decode dan validasi JWT. - Memastikan client masih aktif. - Memastikan user Odoo masih aktif. - Mengisi `request.jwt_payload`. - Mengisi `request.jwt_client`. - Mengisi `request.jwt_user_id`. - Menjalankan `request.update_env(user=user_id)`. ## Claim JWT Token berisi claim berikut: | Claim | Isi | | --- | --- | | `iss` | Issuer, default `odoo` | | `aud` | Audience, default `odoo-api` | | `exp` | Waktu kedaluwarsa token | | `iat` | Waktu token diterbitkan | | `jti` | ID unik token | | `sub` | ID user Odoo dalam format string | | `client_id` | Client ID API Client | ## Keamanan Rekomendasi production: - Gunakan HTTPS. - Gunakan `jwt_secret` yang panjang dan random. - Jangan commit `jwt_secret` production ke repository. - Buat satu API Client per aplikasi eksternal. - Mapping API Client ke user Odoo dengan hak akses minimum. - Rotasi secret secara berkala dengan tombol **Regenerate Secret**. - Nonaktifkan API Client yang tidak digunakan. - Batasi masa berlaku token sesuai kebutuhan. - Tambahkan rate limiting di reverse proxy jika endpoint dibuka ke internet. ## Troubleshooting ### Error: JWT secret is not configured Penyebab: - `jwt_secret` belum ada di `odoo.conf`. - Panjang `jwt_secret` kurang dari 32 karakter. - Odoo belum direstart setelah config diubah. Solusi: 1. Tambahkan `jwt_secret` yang valid. 2. Restart Odoo. ### Error: Invalid credentials Penyebab: - `client_id` salah. - `client_secret` salah. - API Client tidak aktif. - User Odoo yang dipetakan tidak aktif. ### Error: Token has expired Token melewati `Token Lifetime (seconds)`. Minta token baru melalui `/api/auth/token`. ### Menu JWT Auth belum muncul Jika modul sudah ter-install tapi menu belum terlihat: 1. Logout. 2. Hapus local storage browser untuk `webclient_menus` dan `webclient_menus_version`. 3. Login ulang atau pakai private/incognito window. ## Catatan Batasan Modul ini belum membuat session login web Odoo otomatis. Token JWT dipakai untuk akses endpoint API yang memakai `@check_jwt_auth`. Jika membutuhkan SSO penuh ke UI Odoo, perlu tambahan flow session seperti OAuth/OIDC callback atau endpoint login session yang divalidasi dengan signature.