OAuth
Biarkan orang masuk ke klienmu dengan akun Atli mereka, dan bertindak atas nama mereka, dengan OAuth 2.1 standar: discovery, PKCE, refresh token yang berotasi, dan pencabutan. Klien apa pun bisa memakainya: aplikasi, bot, situs web, server, atau asisten AI.
Kapan menggunakannya
Asisten AI adalah salah satu klien seperti itu. Saat terhubung ke server MCP Atli, mereka memasukkan orang dengan cara ini dengan sendirinya, jadi tidak ada yang perlu dibuat untuk mereka. Alur yang sama melayani setiap klien.
Klien apa pun yang bertindak untuk seseorang: aplikasi ponsel, situs web, bot, atau server yang menampilkan eSIM seseorang atau membelikannya, dengan akun Atli milik orang itu sendiri. Daftarkan klien milikmu sendiri, dan token-nya bisa dipakai di API Atli.
Orang yang masuk melalui klien milikmu, atau di alamat partnermu pada server MCP, bisa dikaitkan denganmu sebagai referralmu. Fitur ini nonaktif secara default, dan Atli bisa mengaktifkannya untuk akunmu. Apa pun pengaturannya, klienmu bisa sendiri mencatat orang yang dibawanya sebagai referralmu: lihat "Dapatkan kredit atas orang yang kamu bawa" di bawah.
Hanya akun yang bisa dimasuki lagi di tempat lain yang bisa terhubung: akun dengan email, termasuk lewat Google, Apple, atau Facebook, atau dengan Telegram atau WhatsApp.
Cara kerjanya
API Atli adalah server otorisasi. Proses masuk dan layar persetujuan ada di heyatli.com, di /connect, jadi aplikasimu tidak pernah melihat kata sandi. Baca setiap endpoint dari metadata, jangan ditulis langsung di kode. Alamat di sini mengikuti pengalih Sandbox dan Produksi.
Daftarkan klien sekali. Lalu, untuk setiap orang: arahkan mereka ke endpoint otorisasi dengan challenge PKCE, ambil kode yang mereka bawa kembali, tukarkan dengan token, dan panggil API dengan access token. Perbarui saat habis masa berlakunya, dan cabut saat mereka keluar.
curl https://api.heyatli.com/.well-known/oauth-authorization-server
{
"issuer": "https://api.heyatli.com",
"authorization_endpoint": "https://api.heyatli.com/oauth2/authorize",
"token_endpoint": "https://api.heyatli.com/oauth2/token",
"revocation_endpoint": "https://api.heyatli.com/oauth2/revoke",
"registration_endpoint": "https://api.heyatli.com/oauth2/register",
"jwks_uri": "https://api.heyatli.com/oauth2/jwks",
"scopes_supported": ["api", "offline_access"],
"code_challenge_methods_supported": ["S256"],
"client_id_metadata_document_supported": true,
…
}Untuk apa sebuah token
Setiap permintaan menyebutkan alamat tujuan token-nya, sebagai resource (RFC 8707), dan token hanya berfungsi di sana. Alamat yang bukan salah satu dari ini, atau kode referral yang tidak ada, ditolak dengan invalid_target, sehingga keduanya tidak bisa ditebak.
| Alamat | Siapa yang bisa memintanya | Apa yang dilakukan token |
|---|---|---|
API Atli https://api.heyatli.com | Hanya klien milikmu | Bearer token untuk API, yang bertindak sebagai orang tersebut, seperti token dari |
Server MCP Atli https://mcp.heyatli.com | Klien mana pun | Memungkinkan asisten bertindak atas nama orang tersebut melalui alat MCP. |
Alamat partnermu https://mcp.heyatli.com/r/YOUR_CODE | Klien mana pun | Sama, dan orang tersebut bisa dikaitkan denganmu sebagai referralmu, setelah fitur itu diaktifkan untuk akunmu. |
Mendaftarkan klien
Ada tiga cara. Untuk aplikasimu sendiri, daftarkan klien milikmu sendiri: hanya jenis ini yang bisa meminta API Atli.
Klien milikmu sendiri
Setelah masuk dengan akunmu sendiri, panggil
POST /user/oauth-clients. Kamu bisa punya 10. Pilihpublicuntuk apa pun yang berjalan di perangkat orang lain, seperti aplikasi ponsel atau halaman di browser mereka, danconfidentialuntuk server yang bisa menyimpan rahasia.client_secretmilik klien rahasia ada di respons ini dan tidak di tempat lain. Jika hilang, ganti denganPOST /user/oauth-clients/{id}/rotate-secret. Akunmu harus punya email, karena klien memerlukan kode referral; kode akan dibuatkan jika kamu belum punya.curl https://api.heyatli.com/user/oauth-clients \ -H 'Authorization: Bearer YOUR_TOKEN' \ -H 'Content-Type: application/json' \ -d '{ "name": "Travel Bot", "redirect_uris": ["https://bot.example/oauth/callback"], "client_type": "confidential" }' { "client_id": "atli_client_7f3a9c2e…", "client_secret": "…", "client_type": "confidential", … }Pendaftaran dinamis
POST /oauth2/register(RFC 7591), tanpa masuk. Begitulah kebanyakan asisten mendaftar. Ini mengizinkan 10 pendaftaran per jam dari satu alamat, dan klien yang tidak pernah dihubungkan siapa pun dihapus setelah 30 hari.curl https://api.heyatli.com/oauth2/register \ -H 'Content-Type: application/json' \ -d '{ "client_name": "Travel Bot", "redirect_uris": ["https://bot.example/oauth/callback"] }' { "client_id": "atli_client_…", "token_endpoint_auth_method": "none", … }Dokumen metadata client ID
Gunakan alamat
httpsyang kamu hosting sebagaiclient_id, yang menyajikan dokumen JSON berisi deskripsi klien. Atli membacanya saat seseorang masuk, jadi tidak ada yang perlu didaftarkan. Spesifikasi MCP lebih menyarankan cara ini daripada pendaftaran dinamis.// Served at https://bot.example/oauth/client.json { "client_id": "https://bot.example/oauth/client.json", "client_name": "Travel Bot", "redirect_uris": ["https://bot.example/oauth/callback"], "token_endpoint_auth_method": "none" }
Redirect URI dicocokkan secara persis. Masing-masing berupa
https, atauhttpdilocalhost,127.0.0.1, atau[::1], yang hanya boleh berbeda port-nya, tanpa fragmen. Satu klien bisa punya hingga 10.
Memasukkan seseorang
Alur authorization code dengan PKCE (S256), yang dipakai setiap klien, publik maupun rahasia.
Buat pasangan PKCE
Simpan verifier untuk penukaran kode, dan kirim challenge SHA-256-nya bersama permintaan. Buat pasangan baru, dan
statebaru, untuk setiap proses masuk.const base64url = (bytes) => btoa(String.fromCharCode(...bytes)) .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); const verifier = base64url(crypto.getRandomValues(new Uint8Array(32))); const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier)); const challenge = base64url(new Uint8Array(digest)); const state = base64url(crypto.getRandomValues(new Uint8Array(16)));Arahkan mereka untuk masuk
Buka endpoint otorisasi di browser mereka.
scopeadalahapi, danoffline_accessmenambahkan refresh token. Mereka masuk dan menyetujui di heyatli.com/connect, yang menampilkan nama klienmu, ke mana klien itu mengembalikan mereka, dan apakah mereka akan menjadi referral seseorang. Permintaan berlaku 10 menit.const url = new URL('https://api.heyatli.com/oauth2/authorize'); url.search = new URLSearchParams({ response_type: 'code', client_id: CLIENT_ID, redirect_uri: 'https://bot.example/oauth/callback', scope: 'api offline_access', state, code_challenge: challenge, code_challenge_method: 'S256', // Atli's API, for a client you own; or the MCP server, https://mcp.heyatli.com resource: 'https://api.heyatli.com', }).toString(); window.location.assign(url);Ambil kode yang mereka bawa kembali
Mereka kembali ke redirect URI-mu dengan
code,statemilikmu, daniss. Pastikanstatemilikmu danissadalah penerbitnya. Jika mereka menolak, yang dibawa adalaherror=access_denied.https://bot.example/oauth/callback?code=4bd81f…&state=…&iss=https%3A%2F%2Fapi.heyatli.com https://bot.example/oauth/callback?error=access_denied&state=…&iss=https%3A%2F%2Fapi.heyatli.comTukarkan kode dengan token
Kirim kode bersama verifier,
redirect_uridanresourceyang sama, serta kredensial klienmu jika ada. Kode berlaku 60 detik dan hanya bisa dipakai sekali: mengirimnya dua kali, atau dengan verifier yang salah, mengakhiri akses orang tersebut, karena mungkin orang lain memilikinya.Access token berlaku
expires_indetik, yaitu 15 menit. Refresh token selalu ikut bersamanya.curl https://api.heyatli.com/oauth2/token \ -u 'CLIENT_ID:CLIENT_SECRET' \ -d grant_type=authorization_code \ -d code=THE_CODE \ -d code_verifier=THE_VERIFIER \ -d redirect_uri=https://bot.example/oauth/callback \ -d client_id=CLIENT_ID \ -d resource=https://api.heyatli.com { "access_token": "…", "token_type": "Bearer", "expires_in": 900, "refresh_token": "…", "scope": "api offline_access" }
Menjaga mereka tetap masuk
Saat access token habis masa berlakunya, minta yang baru dengan refresh token. Kamu bisa meminta scope yang lebih sempit, tetapi tidak pernah yang lebih luas.
Refresh token berubah setiap kali dipakai, jadi simpan yang baru setiap kali. Jika sebuah respons tidak pernah sampai kepadamu, mengirim token lama lagi dalam waktu singkat akan mendapat respons yang sama. Setelah itu, token lama berarti token tersebut telah disalin, dan akses orang tersebut berakhir.
curl https://api.heyatli.com/oauth2/token \
-u 'CLIENT_ID:CLIENT_SECRET' \
-d grant_type=refresh_token \
-d refresh_token=THE_REFRESH_TOKEN \
-d client_id=CLIENT_ID \
-d resource=https://api.heyatli.comMenggunakan token
Kirim sebagai Authorization: Bearer ACCESS_TOKEN, ke alamat tempat token itu diterbitkan dan tidak ke tempat lain.
Token untuk API Atli bertindak sebagai orang tersebut di setiap rute API, seperti token dari /login. Perlakukan sebagai opak: isinya bisa berubah.
Token untuk server MCP adalah JWT yang ditandatangani dengan EdDSA. Server memeriksanya dengan kunci di /oauth2/jwks, berdasarkan kid-nya, dan memastikan aud-nya adalah alamat server itu sendiri. Token ini tidak berisi email atau detail lain: hanya sub (id orang tersebut), scope, grant_id, dan waktu-waktunya.
curl https://api.heyatli.com/esims \
-H 'Authorization: Bearer ACCESS_TOKEN'Dapatkan kredit atas orang yang kamu bawa
Mengaitkan orang yang masuk melalui klienmu, atau di alamat partnermu, denganmu nonaktif secara default, dan Atli bisa mengaktifkannya untuk akunmu. Aktif atau tidak, klienmu bisa sendiri mengaitkan orang yang sudah masuk dengan kode referralmu, menggunakan access token mereka: POST /user/referral/redeem dengan code milikmu.
result menjelaskan apa yang terjadi: ok berarti mereka sudah dikaitkan, already berarti mereka sudah punya perujuk, notnew berarti mereka pernah membeli, own berarti itu kodemu sendiri, dan notfound berarti kode itu tidak ada. Hanya orang tanpa perujuk yang belum pernah membeli yang bisa dikaitkan, dan hanya sekali.
curl https://api.heyatli.com/user/referral/redeem \
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
-d '{ "code": "YOUR_CODE" }'
{
"success": true,
"result": "ok",
"message": "…",
"discount_percent": 10
}Keluar dan mencabut akses
Saat seseorang keluar dari aplikasimu, cabut refresh token mereka. Itu mengakhiri akses mereka di klienmu, beserta semua token di dalamnya. Endpoint ini menjawab 200 bahkan untuk token yang tidak dikenalnya, sehingga tidak bisa dipakai untuk mencari tahu token mana yang ada.
Orang juga bisa melihat aplikasi yang mereka hubungkan dan mencabut aksesnya sendiri (GET /user/oauth-grants, DELETE /user/oauth-grants/{id}). Sejak itu klienmu tidak bisa mendapatkan token baru, jadi anggap pembaruan yang ditolak sebagai keluar.
curl https://api.heyatli.com/oauth2/revoke \
-u 'CLIENT_ID:CLIENT_SECRET' \
-d token=THE_REFRESH_TOKEN \
-d client_id=CLIENT_IDError
Endpoint token, pencabutan, dan pendaftaran menjawab dengan format error milik OAuth sendiri, {"error": "invalid_grant", "error_description": "…"}, bukan format API yang biasa. Buat percabangan berdasarkan error, jangan pernah berdasarkan deskripsi. Setiap respons dari endpoint token membawa Cache-Control: no-store.
invalid_grant: kode sudah dipakai atau habis masa berlakunya, verifier salah, atau refresh token dipakai lagi setelah masa tenggangnya. Minta orang tersebut masuk lagi.invalid_target:resourcebukan alamat yang bisa menjadi tujuan token, atau menyebut kode referral yang tidak ada.invalid_client: kredensial klien salah atau tidak ada. Semua jenis gagal dengan cara yang sama.invalid_scope: pembaruan meminta lebih dari yang diizinkan orang tersebut.