Documentation Index
Fetch the complete documentation index at: https://docs.hanko.io/llms.txt
Use this file to discover all available pages before exploring further.
How to use these prompts
You can copy the prompts and paste them into your AI assistant or IDE. Some ideas on how to use them:
- Add them as “project rules” in Cursor -> Guide
- In GitHub Copilot, save the prompt to a file in your project and reference it using #<filename>.
- In Claude Code, include the prompt in your CLAUDE.md file. (recommend for best results)
Prompt
## Add Hanko to Mobile Applications
**Purpose:** Enforce only the **current** and **correct** instructions for integrating [Hanko](https://hanko.io/) passwordless authentication into native mobile applications (iOS/Android).
**Scope:** All AI-generated advice or code related to Hanko mobile integration must follow these guardrails for direct API integration.
---
## **1. Official Hanko Integration Overview**
Use only the **current** approach from Hanko's documentation:
- **Use direct API calls** to Hanko's REST endpoints (no native SDKs available yet)
- **Check user existence** using the `/users` endpoint before creating
- **Create users** via POST to `/users` endpoint when they don't exist
- **Send passcodes** using `/passcode/login/initialize` endpoint
- **Verify passcodes** using `/passcode/login/finalize` endpoint
- **Handle JWT tokens** returned in X-Auth-Token header or cookies
- **Implement passkeys** using native iOS/Android WebAuthn APIs (iOS 16+, Android 9+)
- **Configure APK Key Hash** for Android passkey support
- **Validate tokens** on backend using session validation endpoints
---
## **2. CRITICAL INSTRUCTIONS FOR AI MODELS**
### **2.1 – ALWAYS DO THE FOLLOWING**
1. **Check user existence first** using GET `/users?email={email}` before creating
2. **Handle both 200 and 404 responses** for user existence checks
3. **Store user ID** from responses for subsequent API calls
4. **Use POST method** for user creation and passcode operations
5. **Send JSON payloads** with proper Content-Type headers
6. **Handle JWT tokens** from X-Auth-Token header or cookies
7. **Use native WebAuthn APIs** for passkey implementation (iOS/Android)
8. **Configure APK Key Hash** for Android passkey support
9. **Validate tokens on backend** using session validation
10. **Implement proper error handling** for all HTTP status codes
### **2.2 – NEVER DO THE FOLLOWING**
1. **Do not** skip user existence check before creating users
2. **Do not** use passwords in mobile integration (passwordless only)
3. **Do not** assume native SDK availability (use direct API calls)
4. **Do not** ignore HTTP status codes in API responses
5. **Do not** hardcode API URLs in production code
6. **Do not** skip error handling for network requests
7. **Do not** attempt passkeys on unsupported OS versions
8. **Do not** forget APK Key Hash configuration for Android
---
## **3. CORRECT IMPLEMENTATION PATTERNS**
### **User Existence Check**
```http
GET /users?email=user@example.com
Headers:
Content-Type: application/json
Response 200 (User exists):
{
"id": "user-uuid-here",
"email": "user@example.com"
}
Response 404 (User doesn't exist):
{
"code": 404,
"message": "user not found"
}
User Creation
POST /users
Headers:
Content-Type: application/json
Body:
{
"email": "user@example.com"
}
Response 200 (Created):
{
"id": "user-uuid-here",
"email": "user@example.com"
}
Response 409 (Already exists):
{
"code": 409,
"message": "user already exists"
}
Passcode Flow - Initialize
POST /passcode/login/initialize
Headers:
Content-Type: application/json
Body:
{
"user_id": "user-uuid-here"
}
Response 200:
{
"id": "passcode-id-here",
"ttl": 300
}
Passcode Flow - Finalize
POST /passcode/login/finalize
Headers:
Content-Type: application/json
Body:
{
"id": "passcode-id-here",
"code": "123456"
}
Response 200:
Headers:
X-Auth-Token: jwt-token-here
Set-Cookie: hanko=jwt-token-here
iOS Swift - Passkey Registration
import AuthenticationServices
// Get WebAuthn options from Hanko
func getWebAuthnOptions() async -> PublicKeyCredentialCreationOptions? {
guard let url = URL(string: "\(hankoApiUrl)/webauthn/registration/initialize") else { return nil }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(jwtToken)", forHTTPHeaderField: "Authorization")
do {
let (data, _) = try await URLSession.shared.data(for: request)
return try JSONDecoder().decode(PublicKeyCredentialCreationOptions.self, from: data)
} catch {
print("Error getting WebAuthn options: \(error)")
return nil
}
}
// Create passkey using native API
func createPasskey(options: PublicKeyCredentialCreationOptions) async -> Bool {
let provider = ASAuthorizationPlatformPublicKeyCredentialProvider(relyingPartyIdentifier: options.rp.id)
let request = provider.createCredentialRegistrationRequest(
challenge: Data(base64URLEncoded: options.challenge)!,
name: options.user.name,
userID: Data(base64URLEncoded: options.user.id)!
)
let controller = ASAuthorizationController(authorizationRequests: [request])
// Handle delegate methods and finalize with Hanko API
return true
}
Android Kotlin - Passkey Registration
import androidx.credentials.CredentialManager
import androidx.credentials.CreatePublicKeyCredentialRequest
class HankoAuth {
private val credentialManager = CredentialManager.create(context)
suspend fun getWebAuthnOptions(): PublicKeyCredentialCreationOptions? {
val client = OkHttpClient()
val request = Request.Builder()
.url("$hankoApiUrl/webauthn/registration/initialize")
.post(RequestBody.create("application/json".toMediaType(), "{}"))
.addHeader("Authorization", "Bearer $jwtToken")
.build()
return try {
val response = client.newCall(request).execute()
val json = response.body?.string()
gson.fromJson(json, PublicKeyCredentialCreationOptions::class.java)
} catch (e: Exception) {
null
}
}
suspend fun createPasskey(options: PublicKeyCredentialCreationOptions): Boolean {
val request = CreatePublicKeyCredentialRequest(
requestJson = gson.toJson(options)
)
return try {
val result = credentialManager.createCredential(
context = context,
request = request
)
// Send result to Hanko finalize endpoint
finalizeWebAuthnRegistration(result)
} catch (e: Exception) {
false
}
}
}
APK Key Hash Configuration (Android)
## Generate APK Key Hash for Android passkey support
echo 'android:apk-key-hash:'"$(keytool -exportcert -alias key-name -keystore path-to-keystore | openssl sha256 -binary | openssl base64 | tr -- '+/' '-_' | tr -d '=')"
4. OUTDATED PATTERNS TO AVOID
// ❌ DO NOT skip user existence check
const createUser = await fetch('/users', {
method: 'POST',
body: JSON.stringify({ email: userEmail })
}); // Should check existence first
// ❌ DO NOT ignore HTTP status codes
const response = await fetch('/passcode/login/finalize', requestOptions);
const data = await response.json(); // Should check response.status first
// ❌ DO NOT use passwords in mobile flow
const loginData = {
email: userEmail,
password: userPassword // Hanko is passwordless only
};
// ❌ DO NOT attempt passkeys on unsupported versions
if (iOS < 16 || Android < 9) {
// Still trying to use passkeys - should check version first
createPasskey(options);
}
// ❌ DO NOT forget APK Key Hash for Android
// Missing APK Key Hash configuration will cause Android passkeys to fail
// ❌ DO NOT hardcode API URLs
const response = await fetch('https://hardcoded-url.hanko.io/users');
5. AI MODEL VERIFICATION STEPS
Before returning any Hanko mobile solution, you must verify:
- User Flow: Is user existence checked before creation?
- API Methods: Are correct HTTP methods used (GET for check, POST for create/passcode)?
- Error Handling: Are HTTP status codes checked and handled properly?
- JWT Handling: Are tokens extracted from X-Auth-Token header or cookies?
- Passkey Support: Are OS version requirements checked (iOS 16+, Android 9+)?
- Android Config: Is APK Key Hash mentioned for Android passkey support?
- Native APIs: Are native WebAuthn APIs used instead of web-based solutions?
- Backend Validation: Is token validation on backend mentioned?
If any check fails, stop and revise until compliance is achieved.