Auth — нээлт, token, scope, зөвшөөрөл¶
OAuth 2.0-тэй төстэй, гэхдээ redirect-гүй: host хүнийг аль хэдийн нэвтрүүлсэн тул launch code нь authorization code-ийн үүргийг гүйцэтгэнэ.
1. Урсгал¶
Host (Gerege App) Gerege API Mini app frontend Mini app backend
│ POST /api/v1/miniapp/apps/{slug}/launch (хүний session)
│─────────────────────────────▶│ (төлөв, суваг, scope, consent шалгана)
│◀── {launch_code, url, …} ────│
│ url = entry?launch=<code>&path=<p> ачаална ─────────────────▶│
│ │ POST /your/session {launch}
│ │────────────────────────▶│
│ │◀── POST /api/v1/miniapp/oauth/token (client_id + secret) ─│
│ │── {access_token, refresh_token, user:{sub,…}} ──────────▶│
│ │◀── өөрийн session ──────│
- Host →
POST /api/v1/miniapp/apps/{slug}/launch {"path"?, "channel"?}→{launch_code, url, expires_at, bridge_methods, testing}. Алдаа:428 consent_required({scopes, app}— §5),403 verification_required/403 age_restricted(§1.1),409 maintenance,403 channel_unavailable,400 invalid_path,404. - Host
url-ийг ачаална. Frontendlaunch-ийг URL-аас уншаад history-оос арилгах (history.replaceState) ЗААВАЛ. - Frontend → өөрийн backend. Backend → token endpoint (§2). Backend frontend-д өөрийн session өгнө.
- Session дуусвал frontend bridge-ээр
getLaunchCode()→ шинэ код → 3-р алхам.
1.1 Хэн нээж болох — requires_verified, min_age¶
Manifest-ийн эдгээр хоёр талбар шалгалт амжилттай болох бүрт registry-д хуулагдаж, нээлт бүрт хэрэгжинэ (батлагдсан апп хатууруулж болно, сулруулж болохгүй):
requires_verified: true, эсвэлmin_ageтавьсан апп-ыг eID-аар баталгаажаагүй хүн нээвэл403 verification_required(min_ageапп болmin_ageталбартай) — host хүнийг баталгаажуулах урсгал руу чиглүүлнэ.min_age-тэй апп: нас нь eID-ийн регистрийн дугаараас (YYMMDD, 2000 оноос хойш төрсөнд сар +20) Улаанбаатарын хуанлиар тоологдоно. Хүрэхгүй, эсвэл дугаараас огноо уншигдахгүй бол403 age_restricted(min_ageталбартай). Таны апп хүний насыг, регистрийн дугаарыг хэзээ ч авахгүй — зөвхөн шийдвэр нь хэрэгжинэ.
2. Token endpoint¶
POST https://geregeapp.mn/api/v1/miniapp/oauth/token
- Body:
application/x-www-form-urlencoded(OAuth) эсвэлapplication/json(OMAS) — хоёулаа. - Client нэвтрэлт:
Authorization: Basic base64(client_id:client_secret)(ЗӨВЛӨЖ БАЙНА) эсвэл body-дclient_id,client_secret— хоёуланг нь биш (400 invalid_request). - Client эхэлж шалгагдана: client-ээ баталж чадаагүй дуудагчид код/token-ий талаар юу ч хэлэхгүй.
grant_type |
Параметр |
|---|---|
authorization_code (эсвэл OMAS-ын launch_code) |
code=gml_… |
refresh_token |
refresh_token=gma_rt_… |
Хариу 200 (Cache-Control: no-store):
{
"token_type": "Bearer",
"access_token": "gma_at_…",
"expires_in": 1800,
"refresh_token": "gma_rt_…",
"scope": "profile.basic payments",
"user": { "ref": "u_9f2c4e7a1b3d5f60718293a4b5c6d7e8", "sub": "u_9f2c4e7a1b3d5f60718293a4b5c6d7e8",
"verified": true, "locale": "mn" }
}
ref (OMAS-ын нэр) ба sub (OAuth-ын нэр) нь ижил утга — аль нэгийг нь хэрэглэ.
Алдаа — RFC 6749-ийн хэлбэр {"error", "error_description"}:
| HTTP | error |
Хэзээ |
|---|---|---|
| 400 | invalid_request |
body уншигдсангүй, grant_type алга, Basic + body давхар |
| 401 | invalid_client |
client id/secret буруу, апп suspended/disabled, live credential батлагдаагүй апп-д (WWW-Authenticate: Basic) |
| 400 | invalid_grant |
код/refresh token буруу, хугацаа дууссан, ашиглагдсан, өөр апп/орчных |
| 400 | unsupported_grant_type |
дээрх хоёроос өөр |
| 429 | — | rate limit (минутад 120, burst 30) |
| 500 | server_error |
Хугацаа ба дүрэм¶
- launch code (
gml_…): 60 секунд, нэг удаа, тухайн апп-д холбогдсон. Ашиглагдсан кодыг дахин ашиглавалinvalid_grantба тэр кодоор олгосон гинж бүхэлдээ хүчингүй болно. - access token (
gma_at_…): 30 минут (гинжний хугацаанаас хэтрэхгүй), opaque — бүтцэд нь найдахгүй. Зөвхөн/api/v1/miniapp/*-д хүчинтэй. - refresh token (
gma_rt_…): rotation — хэрэглэх бүрт шинэ хос, хуучин нь үхнэ; хуучныг дахин ашиглавал бүх гинж хүчингүй. Гинж бүхэлдээ нээлтээс хойш 8 цагаас хэтрэхгүй — дараа нь шинэ нээлт. - Хүн апп-аа салгах (consent цуцлах), апп suspend болох үед бүх гинж хүчингүй.
- Test credential (
mapp_test_…): апп батлагдахаас өмнө ч ажиллана, гэхдээ зөвхөн бүртгэсэн гишүүн ба тест хэрэглэгчдийн (console.md §2) нээлтийн кодыг солино. Live credential зөвхөн батлагдсаны дараа. - Token-уудыг зөвхөн backend-д хадгална; лог, аналитик, URL-д ХОРИГЛОНО.
3. Хүний мэдээлэл — GET /api/v1/miniapp/me¶
Authorization: Bearer <access_token> →
{ "ref": "u_9f2c4e…", "sub": "u_9f2c4e…", "verified": true, "locale": "mn",
"scope": "profile.basic profile.email", "environment": "live", "email": "bat@example.mn" }
sub(=ref) ="u_" + hex(HMAC-SHA256(апп-ын salt, хүний ID))[:32]→ апп хооронд холбогдохгүй (pairwise), апп дотор тогтмол. Salt апп-ын мөрөнд хадгалагдана — түлхүүр солигдоход хүмүүсийн нэр өөрчлөгдөхгүй. Payment intent-ийнuser_refнь яг энэ утга.verified— хүн eID-аар (ДАН / eID Mongolia) баталгаажсан эсэх.locale— аппын хэл.- Утгууд нээлтийн мөчийн хуулбар — хүн зөвшөөрсөн тэр мөчид авсан, ар талд нь шинэчлэгдэхгүй.
emailзөвхөнprofile.emailscope олгогдсон үед;phone(E.164, жишээ нь+97699112233) зөвхөнprofile.phoneолгогдсон бөгөөд хүн дугаараа SMS-ээр баталгаажуулсан үед.- Регистрийн дугаар, бүтэн нэр, данс, үлдэгдэл хэзээ ч өгөгдөхгүй. Утас зөвхөн
profile.phone-оор, хүн зөвшөөрсөн бөгөөд дугаараа баталгаажуулсан үед. - Алдаа:
401 {"error":"invalid_token"}(WWW-Authenticate: Bearer error="invalid_token"). Rate limit минутад 600.
4. Scope¶
| Scope | Өгөх зүйл | Зөвшөөрөл |
|---|---|---|
profile.basic |
sub / ref, verified, locale |
Автомат (нээхэд). Заавал |
profile.email |
Имэйл | Consent (host-ийн UI) |
profile.phone |
SMS-ээр баталгаажсан гар утасны дугаар, E.164 (+97699112233); баталгаажуулаагүй бол талбар байхгүй |
Consent (host-ийн UI) |
payments |
Payment intent үүсгэх/унших/capture/цуцлах/буцаах | Автомат — гүйлгээ бүрийг хүн тус бүрт нь баталгаажуулна |
given_name (OMAS-ын profile.basic-д байдаг) — «удахгүй»: платформ модульд нэр нийтэлдэггүй.
Registry-д зарлаагүй, review-ээр батлагдаагүй scope олгогдохгүй; шинэ scope = дахин review.
5. Consent¶
- Consent шаардах scope-ыг хүн зөвшөөрөөгүй бол launch →
428:{"error":"…","code":"consent_required","scopes":["profile.email"],"app":{slug,name,icon_url,developer_name,…}}. - Host өөрийн UI-д (mini app дотор БИШ — тэнд зурсан зөвшөөрлийн дэлгэц худал хэлж чадна) асууна →
POST /api/v1/miniapp/apps/{slug}/consent {"scopes":[…]}→ launch дахин. - Хүн Gerege App-аас апп-аа салгаж болно (
DELETE …/consent) → бүх token хүчингүй, таны backend-дconsent.revokedwebhook ирнэ ({object: "consent", user_ref, revoked_at}— webhooks.md §4): тэрuser_ref-ийн session-ийг хаа. Webhook-ийг хамгийн боломжоор (best effort) илгээдэг тул найдвартай дохио нь хэвээр — тэр хүний token-оор хийх дараагийн дуудлагаinvalid_token/invalid_grantавна.
6. Mini app backend-ийн session (зөвлөмж)¶
- Frontend-д өгөх session нь cookie-д бүү найд (WebView-д third-party cookie хаалттай байж болно).
In-memory token +
Authorizationheader. - Session ≤ 8 цаг (refresh гинжний дээд хязгаар). 401 үед frontend
getLaunchCode()-оор шинэчилнэ.
English¶
Launch → token, OAuth-shaped and redirect-free. The host calls
POST /api/v1/miniapp/apps/{slug}/launch, loads entry?launch=<gml_…>&path=…; your frontend strips
launch from history and hands it to your backend, which calls POST /api/v1/miniapp/oauth/token
(form or JSON; Basic auth recommended, body credentials accepted, never both) with
grant_type=authorization_code (or launch_code) and code, or grant_type=refresh_token. Launch
codes live 60 s and are single use; access tokens 30 min; refresh tokens rotate and the whole chain
ends 8 h after the launch; replaying a used code or refresh token revokes the chain. Test credentials
redeem only the registering member's and the test users' launches; live credentials work after
approval. The token response's user and GET /me carry ref and sub (the same pairwise value),
verified, locale, and on /me also scope, environment, email with profile.email and
phone (E.164, SMS-verified; absent if the person has none) with profile.phone — both by consent. Launch refuses with 403 verification_required / age_restricted for apps whose manifest
sets requires_verified / min_age. Disconnecting sends consent.revoked. given_name is not
available yet.