Ana Sayfa / Blog / Sign in with Apple entegrasyonu: zorunlu, yanlışı zor

Sign in with Apple entegrasyonu: zorunlu, yanlışı zor

App Store Guideline 4.8 ile üçüncü taraf login sunan her iOS app'te Sign in with Apple zorunlu. ASAuthorization framework'ü, anonymous email relay, Sign in revocation webhook üzerine pratik notlar.

Apple 2019’da Sign in with Apple’ı tanıttı ve App Store Guideline 4.8 ile şu kuralı koydu: üçüncü taraf login (Google, Facebook, vb) sunan app Sign in with Apple’ı da sunmalı. Bu zorunluluğu atlayan app review’da reddedilir.

12 uygulamamda farklı auth setup’ları yaptım. Sign in with Apple başta basit görünen, detaylarda karmaşık bir entegrasyon. Bu yazıda pratik notlar.

Neden Apple bu kadar dayatıyor

Sign in with Apple’ın Apple için iki stratejik kazancı:
– Privacy: email relay, ad tracking yok, üçüncü parti veri minimize
– Ekosistem lock-in: iCloud ile derinleşen kullanıcı bağı

Developer için kazancı: onboarding friction düşük (tek dokunuş), kullanıcı email address’i verebilir veya relay kullanabilir, biometric auth zaten cihazda.

Kullanıcı için: privacy-first login seçeneği.

Zorunluluğa rağmen iyi bir tool olduğunu söyleyebilirim. 12 app’te ekledim, pişman değilim.

ASAuthorization framework

iOS 13+ için AuthenticationServices framework’ü kullanılıyor.

Minimal implementation:

import AuthenticationServices

class SignInViewController: UIViewController, ASAuthorizationControllerDelegate {
    func signInWithApple() {
        let request = ASAuthorizationAppleIDProvider().createRequest()
        request.requestedScopes = [.fullName, .email]
        
        let controller = ASAuthorizationController(authorizationRequests: [request])
        controller.delegate = self
        controller.presentationContextProvider = self
        controller.performRequests()
    }
    
    func authorizationController(
        controller: ASAuthorizationController,
        didCompleteWithAuthorization authorization: ASAuthorization
    ) {
        if let credential = authorization.credential as? ASAuthorizationAppleIDCredential {
            let userId = credential.user
            let identityToken = credential.identityToken
            let authCode = credential.authorizationCode
            // backend'e gönder
        }
    }
}

Apple Sign In UI butonu ASAuthorizationAppleIDButton class’ı ile native geliyor, custom tasarlama gerekli değil (ve Apple özelleştirmeye izin vermiyor – uyarı geliyor review’da).

Backend doğrulama: identity token

Client’tan gelen identity token JWT. Apple’ın public key’leri ile signature verify edilmeli. Client’ın gönderdiği userId’ye güvenmeyin, identity token’dan çıkarın.

Backend’de adımlar:

  1. Apple public keys endpoint’inden fetch: https://appleid.apple.com/auth/keys
  2. JWT header’daki kid‘e göre doğru public key seç
  3. Signature verify et
  4. Claims kontrol et: iss == "https://appleid.apple.com", aud == your_bundle_id, exp > now
  5. sub claim’i user’ın unique ID’si, email varsa email relay veya real

PHP örneği:

use Firebase\JWT\JWT;
use Firebase\JWT\JWK;

$jwks = json_decode(file_get_contents('https://appleid.apple.com/auth/keys'), true);
$keys = JWK::parseKeySet($jwks);
$decoded = JWT::decode($identityToken, $keys);

Library kullanın, manuel JWT parse etmeyin.

Email relay: gizli email

Kullanıcı “email’imi paylaşma” seçeneğini seçebiliyor. Bu durumda Apple xxxx@privaterelay.appleid.com formatında unique bir relay email veriyor.

Email bu adrese gidiyor, Apple kullanıcının gerçek email’ine iletiyor. Kullanıcı istediği zaman relay’i kesebiliyor, o noktadan sonra email’leriniz gidmiyor.

Dikkat:
– Relay email’e pazarlama email göndermek Apple Terms ihlali (developer portal’dan kaldırılabilirsiniz)
– Email iletilebilir olmalı (SPF, DKIM doğru ayarlı, güvenilir gönderen)
– DB’de email’i aynen kaydedin, “real email” ayrı bir field olarak ayırın

User identifier persistence

Apple sub claim’i sabittir, app reinstall olsa bile aynı kullanıcı için aynı ID. Ama:
– Farklı bundle ID’lere farklı sub verir (app’inizin iki varyantı varsa kullanıcı iki hesap)
– Team ID değişirse sub’lar sıfırlanır

Bu nedenle user matching sadece sub ile yapmayın. Email, phone, ya da diğer unique key’lerle fallback bulundurun.

Revocation webhook: zorunluluk

2022’den sonra Apple developer’ların kullanıcı Sign in with Apple’ı iptal ettiğinde bildirim alması için webhook endpoint kurmasını zorunlu kıldı. Uymayan app’ler yeni submission’da reddediliyor.

Setup:
1. Apple Developer Portal’da Sign in with Apple webhook URL’i kaydet
2. Endpoint POST request alıyor, events field’ı JWT
3. JWT’yi parse et, event type kontrol et
4. Event’e göre aksiyon: kullanıcıyı sil, disable et, gracefully kapat

Event types:
consent-revoked: kullanıcı Sign in with Apple’ı kaldırdı
account-delete: kullanıcı Apple ID’yi sildi
email-disabled: email relay kapatıldı
email-enabled: email relay tekrar açıldı

Bu event’leri handle etmek GDPR compliance için de önemli. “Right to be forgotten” Apple tarafından iletildiği anda honor edilmeli.

Nonce kullanımı

Replay attack’a karşı nonce zorunlu. Client sign-in request’ini yaparken random nonce üretir, SHA256 hash’ini request’e koyar, orijinal nonce’u Firebase veya backend validation’a gönderir.

let nonce = randomNonceString()
let request = ASAuthorizationAppleIDProvider().createRequest()
request.nonce = sha256(nonce)

Backend JWT’deki nonce claim’i ile client’ın gönderdiği orijinal nonce’u match ediyor. Farklıysa replay attack.

SwiftUI implementation

SwiftUI’da daha şık:

import AuthenticationServices

SignInWithAppleButton { request in
    request.requestedScopes = [.fullName, .email]
    request.nonce = sha256(currentNonce)
} onCompletion: { result in
    switch result {
    case .success(let authorization):
        handleAuthorization(authorization)
    case .failure(let error):
        handleError(error)
    }
}
.signInWithAppleButtonStyle(.black)
.frame(height: 50)

iOS 14+.

Error handling

Kullanıcı sign-in akışını iptal edebilir, network hatası olabilir. Yaygın errors:

  • ASAuthorizationError.canceled: kullanıcı iptal etti, sessizce geç
  • ASAuthorizationError.failed: teknik hata, generic mesaj
  • ASAuthorizationError.invalidResponse: token bozuk, retry önerilebilir
  • ASAuthorizationError.notHandled: system error
  • ASAuthorizationError.unknown: beklenmeyen

Her durumda kullanıcıya ne yapacağını söyleyin. “Apple login başarısız” yerine “İnternet bağlantınızı kontrol edin ve tekrar deneyin” daha iyi.

Test disiplini

Simulator’da Sign in with Apple çalışır ama sınırlı. Real device test şart.

Test Apple ID’lerinin ayrı tutulması önemli: test user’ların real email address’i de kullanabilirler, ama test account hesaplarını Apple Sandbox’ta yönetin, production user pool’una karışmasın.

Performans etkisi

Sign in with Apple’ı kullanan app’lerde onboarding completion rate’im:
– Email/password: %35
– Google Sign-In: %62
– Sign in with Apple: %74

Friction gerçekten düşük. Face ID tap ile sign-in, hiç form doldurma yok. Kullanıcıların %40+’ı Sign in with Apple’ı tercih ediyor, email/password hiç tercih etmiyor.

Tavsiye

Sign in with Apple’ı “zorunlu olduğu için” değil, “iyi çalıştığı için” eklemek fark yaratıyor. Default login seçeneği olarak promote edin, revocation webhook’u ilk implementation’da ekleyin, email relay’e saygılı olun.

Bu 3 madde hem Apple ile iyi durmanızı hem de kullanıcı memnuniyetini sağlıyor.

Bu konuda bir projeniz mi var?

Kısa bir özet bırakın, 24 saat içinde size dönüş yapayım.

İletişime Geç