OAuth on Mobile
Web apps receive OAuth tokens in a URL fragment on their callback page. A mobile app has no callback page: the system browser must hand control back to the app, and tokens must never travel through the operating system’s URL handler. Ghayma supports two ways to come back and one way to skip the browser entirely.
| Custom scheme | Verified link | Native sign-in | |
|---|---|---|---|
| Setup | none | host association files on your domain + app entitlements | provider SDK + client IDs |
| What comes back | one-time code | one-time code | nothing to redirect (provider ID token) |
| Providers | Google, GitHub | Google, GitHub |
A project that only serves a mobile app needs no site: answer No site — only databases, storage and auth at ghayma init and it holds your auth app, databases and buckets without deploying anything.
1. Register where the browser comes back
In the console, open Project → Auth → your app → Allowed Origins and add your app’s deep-link origin exactly:
- Custom scheme:
com.example.app://callback(scheme + host, no path).*never covers a custom scheme, and the link must have a host segment:myapp://callback, notmyapp:///callback. - Verified link (iOS universal link / Android App Link): the https origin, for example
https://app.example.com. Your domain must host the Apple and Google association files.
file, javascript, data, blob, about, vbscript and ftp are refused.
2. Browser flow with a one-time code (PKCE)
- Generate a random
code_verifier(43–128 characters fromA-Z a-z 0-9 - . _ ~) and its challengeBASE64URL(SHA-256(code_verifier))without padding. - Open this URL in the system browser (
ASWebAuthenticationSessionon iOS, Custom Tabs on Android,flutter_web_auth_2in Flutter):
https://auth.ghayma.tech/v1/{appSlug}/auth/google
?redirect_uri=com.example.app://callback
&code_challenge=<challenge>
&code_challenge_method=S256Use /auth/github for GitHub.
3. After the provider, the browser is sent to com.example.app://callback?code=<code>. On failure it is sent to com.example.app://callback?error=<message>.
4. Exchange the code within 120 seconds; it works once:
curl -X POST https://auth.ghayma.tech/v1/{appSlug}/oauth/exchange \
-H "Content-Type: application/json" \
-d '{"code":"<code>","code_verifier":"<code_verifier>"}'Response: the same session body as login.
{ "access_token": "…", "refresh_token": "…", "expires_in": 900, "token_type": "Bearer", "user": { "…": "…" } }Errors: 400 {"error":"invalid or expired code","code":"invalid_grant"} (unknown, expired, reused, wrong verifier or wrong app), 403 {"error":"Account is disabled"}, 429 with Retry-After.
Web apps keep the fragment flow: without code_challenge the callback still returns #access_token=…. Nothing changes for existing sites.
3. Native sign-in with Google (no browser)
- In Google Cloud, create iOS and Android OAuth client IDs in the same project as your web client ID.
- Paste them in the console under Google OAuth → Native client IDs (iOS / Android).
- Sign in with the Google Sign-In SDK on the device and post the ID token:
curl -X POST https://auth.ghayma.tech/v1/{appSlug}/oauth/id-token \
-H "Content-Type: application/json" \
-d '{"provider":"google","id_token":"<id_token>","nonce":"<nonce if you set one>"}'Ghayma verifies the token against Google’s public keys, checks that its audience is one of your registered client IDs, creates or links the user by email, and returns the session body. Errors: 401 {"error":"invalid id token","code":"invalid_token"}, 400 when the provider is disabled or the token carries no email, 403 when the app’s user limit is reached or the account is disabled, 429.
Android’s Credential Manager returns tokens whose audience is your web client ID; iOS returns the iOS client ID. Both are accepted once listed.
Rate limits
| Endpoint | Per client | Per app |
|---|---|---|
POST /oauth/exchange | 20 / 15 min | 600 / 15 min |
POST /oauth/id-token | 10 / 15 min | 600 / 15 min |
What is coming
- Sign in with Apple (required by the App Store for iOS apps that offer Google sign-in) is planned.
- SDKs: the Dart / Flutter SDK (
ghayma_auth) is out — it runs the PKCE flow, the code exchange and native Google sign-in for you — and@ghayma/sdk1.2 ships the PKCE helpers and native sign-in for JavaScript. Swift, Kotlin and PHP are planned.
See the full contract in the Auth Service API.