authentication について
AuthenticationServices などを使った iOS のアカウント認証実装を支援するスキルです。Apple でサインイン、パスキー、OAuth、パスワードの自動入力、生体認証による再認証を扱い、認証情報の管理とサーバー側検証も説明します。
主な機能
- 01Apple でサインインと認証状態確認
- 02パスキーの登録と認証フロー
- 03ASWebAuthenticationSession による OAuth
- 04パスワード自動入力の認証情報処理
- 05LocalAuthentication による生体認証
- 06トークン検証と Keychain 保存の指針
活用シーン
- 01iOS のログインに Apple でサインインを追加
- 02サーバー発行チャレンジを使うパスキー対応
- 03サードパーティー OAuth プロバイダーでの認証
- 04アカウントの重要操作前の本人確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dpearson2699/ swift-ios-skills --skill authenticationインストール後、Claude Code で/authenticationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
evals/evals.jsonreferences/keychain-biometric.mdpasskeys.md詳細
Authentication
AuthenticationServices フレームワークを使用して iOS に認証フローを実装します。Sign in with Apple、パスキー、OAuth/サードパーティーの Web 認証、Password AutoFill、生体認証による再認証に対応します。
Contents
- Sign in with Apple
- Credential Handling
- Credential State Checking
- Token Validation
- Existing Account Setup Flows
- Passkeys
- ASWebAuthenticationSession (OAuth)
- Password AutoFill Credentials
- Biometric Authentication
- Security Boundaries
- SwiftUI SignInWithAppleButton
- Common Mistakes
- Review Checklist
- References
Sign in with Apple
これらの API を使用する前に、Xcode で「Sign in with Apple」ケイパビリティを追加してください。
UIKit: ASAuthorizationController Setup
import AuthenticationServices
final class LoginViewController: UIViewController {
func startSignInWithApple() {
let provider = ASAuthorizationAppleIDProvider()
let request = provider.createRequest()
request.requestedScopes = [.fullName, .email]
let controller = ASAuthorizationController(authorizationRequests: [request])
controller.delegate = self
controller.presentationContextProvider = self
controller.performRequests()
}
}
extension LoginViewController: ASAuthorizationControllerPresentationContextProviding {
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
view.window!
}
}
Delegate: Handling Success and Failure
extension LoginViewController: ASAuthorizationControllerDelegate {
func authorizationController(
controller: ASAuthorizationController,
didCompleteWithAuthorization authorization: ASAuthorization
) {
guard let credential = authorization.credential
as? ASAuthorizationAppleIDCredential else { return }
let userID = credential.user // Stable, unique, per-team identifier
let email = credential.email // nil after first authorization
let fullName = credential.fullName // nil after first authorization
let identityToken = credential.identityToken // JWT for server validation
let authCode = credential.authorizationCode // Short-lived code for server exchange
// Save userID to Keychain for credential state checks
// See references/keychain-biometric.md for Keychain patterns
saveUserID(userID)
// Send identityToken and authCode to your server
authenticateWithServer(identityToken: identityToken, authCode: authCode)
}
func authorizationController(
controller: ASAuthorizationController,
didCompleteWithError error: any Error
) {
switch (error as? ASAuthorizationError)?.code {
case .canceled, .notInteractive:
break
case .failed:
showError("Authorization failed")
default:
showError("Authorization failed: \(error.localizedDescription)")
}
}
}
Credential Handling
| Credential data | 必須の処理 |
|---|---|
user | この安定したチームごとの識別子を、資格情報の状態確認に備えて保存してください。 |
email, fullName | これらの省略可能な値は初回の認証時にのみ届くため、すぐにキャッシュしてください。 |
identityToken, authorizationCode | 検証または交換のためにサーバーへ送信してください。クライアント側での証明として信頼しないでください。 |
realUserStatus は不正防止のシグナルとしてのみ扱い、認証の証明として扱わないでください。
Credential State Checking
アプリを起動するたびに資格情報の状態を確認してください。ユーザーは「設定」>「Apple Account」>「サインインとセキュリティ」から、いつでもアクセスを取り消すことができます。
func checkCredentialState() {
let provider = ASAuthorizationAppleIDProvider()
guard let userID = loadSavedUserID() else {
showLoginScreen()
return
}
provider.getCredentialState(forUserID: userID) { state, _ in
DispatchQueue.main.async {
switch state {
case .authorized:
proceedToMainApp()
case .revoked:
// User revoked -- sign out and clear local data
signOut()
showLoginScreen()
case .notFound:
showLoginScreen()
case .transferred:
// App transferred to new team -- migrate user identifier
migrateUser()
@unknown default:
showLoginScreen()
}
}
}
}
Credential Revocation Notification
NotificationCenter.default.addObserver(
forName: ASAuthorizationAppleIDProvider.credentialRevokedNotification,
object: nil,
queue: .main
) { _ in
// Sign out immediately
AuthManager.shared.signOut()
}
Token Validation
identityToken は JWT です。検証のためサーバーへ送信してください。クライアント側だけで信頼しないでください。
サーバー側では、https://appleid.apple.com/auth/keys(JWKS)にある Apple の公開鍵を使って JWT を検証してください。iss が https://appleid.apple.com であること、aud がバンドル ID と一致すること、exp が過ぎていないことを確認します。有効期間の短い認証コードをサーバー側で交換し、取得したアプリのセッショントークンを Keychain に保存してください。
Existing Account Setup Flows
ログイン画面を表示する前に、起動時に既存の Sign in with Apple 資格情報とパスワード資格情報をサイレントに確認してください。
func performExistingAccountSetupFlows() {
let appleIDRequest = ASAuthorizationAppleIDProvider().createRequest()
let passwordRequest = ASAuthorizationPasswordProvider().createRequest()
let controller = ASAuthorizationController(
authorizationRequests: [appleIDRequest, passwordRequest]
)
controller.delegate = self
controller.presentationContextProvider = self
controller.performRequests(
options: .preferImmediatelyAvailableCredentials
)
}
この処理は viewDidAppear またはアプリの起動時に呼び出してください。既存の資格情報が見つからない場合、デリゲートには .notInteractive エラーが届きます。このエラーはサイレントに処理し、通常のログイン UI を表示してください。
Passkeys
webcredentials: Associated Domain と AASA エントリーが設定されたリライングパーティードメインでのみ、パスキーを使用してください。登録とアサーションのどちらにも、新しいサーバーチャレンジ、ASAuthorizationPlatformPublicKeyCredentialProvider、アクティブなプレゼンテーションアンカーを持つ認証コントローラー、セッションを発行する前のサーバー側検証が必要です。
標準的な登録、アサーション、結果処理、AutoFill を利用したフロー、物理セキュリティキーのフローについては、references/passkeys.md を参照してください。
ASWebAuthenticationSession (OAuth)
OAuth やサードパーティー認証(Google、GitHub など)には、ASWebAuthenticationSession を使用してください。認証フローに WKWebView を使用しないでください。
import AuthenticationServices
final class OAuthController: NSObject, ASWebAuthenticationPresentationContextProviding {
private weak var presentationAnchor: ASPresentationAnchor?
init(presentationAnchor: ASPresentationAnchor) {
self.presentationAnchor = presentationAnchor
}
func startOAuthFlow() {
let authURL = URL(string:
"https://provider.com/oauth/authorize?client_id=YOUR_ID&redirect_uri=myapp://callback&response_type=code"
)!
let session = ASWebAuthenticationSession(
url: authURL, callback: .customScheme("myapp")
) { callbackURL, error in
guard let callbackURL, error == nil,
let code = URLComponents(url: callbackURL, resolvingAgainstBaseURL: false)?
.queryItems?.first(where: { $0.name == "code" })?.value else { return }
Task { await self.exchangeCodeForTokens(code) }
}
session.presentationContextProvider = self
session.prefersEphemeralWebBrowserSession = true // No shared cookies
session.start()
}
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
guard let presentationAnchor else {
fatalError("ASWebAuthenticationSession needs the active window")
}
return presentationAnchor
}
}
SwiftUI では、@Environment(\.webAuthenticationSession) を使用し、.customScheme("myapp") または .https(host:path:) を指定して authenticate(using:callback:preferredBrowserSession:additionalHeaderFields:) を呼び出してください。プロバイダーのフローで共有ブラウザー Cookie を避ける必要がある場合に限り、.ephemeral を優先してください。
Password AutoFill Credentials
Existing Account Setup Flows にある単一のコントローラーで、Sign in with Apple と併せて ASAuthorizationPasswordProvider を提供してください。そのコントローラーのデリゲートで ASPasswordCredential を処理します。
AutoFill が機能するよう、テキストフィールドに textContentType を設定してください。
usernameField.textContentType = .username
passwordField.textContentType = .password
生体認証
アカウント設定を表示する前や機密性の高い操作を開始する前に、ローカルで再認証するには LocalAuthentication の LAContext を使用してください。返された Bool を、保存された秘密情報のロックを解除する証明として扱わないでください。秘密情報は Keychain のアクセス制御で保護してください。標準的な LAContext、フォールバック、SecAccessControl、.biometryCurrentSet のパターンについては、references/keychain-biometric.md を参照してください。
必須: Info.plist に NSFaceIDUsageDescription を追加してください。このキーがないと、Face ID 対応デバイスでクラッシュします。
セキュリティ境界
このスキルが担当するのは、ユーザー向けのアカウント認証、すなわち Sign in with Apple、パスキー、Password AutoFill、ASAuthorizationController、OAuth セッションの表示、認証情報の状態、ローカルでの生体認証による再認証です。セキュリティの詳細な作業は swift-security に振り分けてください。対象は、Keychain のアーキテクチャと移行、CryptoKit、Secure Enclave、証明書のピン留めと信頼性、Keychain の共有、ストレージの強化、OWASP MASVS/MASTG です。ここでは必要最小限のストレージに関する方針のみを扱います。トークンと秘密情報は Keychain に保存し、LAContext.evaluatePolicy だけで保護された秘密情報を解放しないでください。
SwiftUI の SignInWithAppleButton
ログイン画面が SwiftUI の場合は、SwiftUI ビューで SignInWithAppleButton を使用してください。.fullName と .email をリクエストし、成功した結果を ASAuthorizationAppleIDCredential にダウンキャストして、共通のトークン検証フローに渡してください。.signInWithAppleButtonStyle(...) でスタイルを設定してください。
よくある間違い
- ローカルにセッションを保存していることを、Apple ID 認証情報が引き続き有効である証拠とみなすこと。起動時に認証情報の状態を確認し、失効通知に対応してください。
- 既存アカウントの設定フローを試す前に、ログイン画面全体を表示すること。
.notInteractiveを「ローカルに認証情報がない」場合の通常の処理として扱ってください。 emailやfullNameを強制アンラップすること。初回の認証時にキャッシュし、以降はnilを処理してください。- プレゼンテーションコンテキストプロバイダーなしで
ASAuthorizationControllerを作成すること。認証 UI には、アクティブなプレゼンテーションアンカーが必要です。 - ID トークン、認証コード、アクセストークン、パスワード、パスキーのサーバー状態を
UserDefaults、ファイル、Core Data に保存すること。秘密情報は Keychain に保存し、パスキーのリライングパーティー検証はサーバー側で行ってください。 - リライングパーティーのドメインに
webcredentials:Associated Domains を設定せずにパスキーのリクエストを追加すること、または無関係な Web サイトでアプリネイティブのパスキーを使おうとすること。 - 認証作業の範囲を CryptoKit、Secure Enclave、証明書のピン留め、OWASP MASVS に広げること。これらは
swift-securityに振り分けてください。
レビューチェックリスト
- Xcode プロジェクトに「Sign in with Apple」機能を追加済み
-
ASAuthorizationControllerPresentationContextProvidingを実装済み - アプリの起動ごとに認証情報の状態を確認済み(
getCredentialState(forUserID:completion:)) -
credentialRevokedNotificationのオブザーバーを登録済み。サインアウトを処理済み - 初回の認証時に
emailとfullNameをキャッシュ済み(以降も取得できるとは想定しない) -
identityTokenをサーバーに送信して検証し、クライアント側だけで信頼していない - トークンを UserDefaults やファイルではなく Keychain に保存済み
- ログイン UI を表示する前に
performExistingAccountSetupFlowsを呼び出し済み - エラーケースを処理済み:
.canceled、.failed、.notInteractive - 生体認証のために Info.plist に
NSFaceIDUsageDescriptionを設定済み - OAuth に
WKWebViewではなくASWebAuthenticationSessionを使用済み - 適切な場合に OAuth の
prefersEphemeralWebBrowserSessionを設定済み - AutoFill のためにユーザー名/パスワードのフィールドに
textContentTypeを設定済み - パスキーのリライングパーティーに
webcredentials:Associated Domains を設定済み - パスキー登録/アサーションのチャレンジをサーバーから取得し、サーバー側で検証済み
- Keychain、CryptoKit、Secure Enclave、証明書のピン留め、MASVS に関する詳細な作業を
swift-securityに振り分け済み
参考資料
- キーチェーンと生体認証のパターン:references/keychain-biometric.md
- パスキーのパターン:references/passkeys.md
- AuthenticationServices
- ASAuthorizationAppleIDProvider
- ASAuthorizationAppleIDCredential
- ASAuthorizationController
- ASWebAuthenticationSession
- パスキーのサポート
- ASAuthorizationPlatformPublicKeyCredentialProvider
- ASAuthorizationPasswordProvider
- SignInWithAppleButton
- 「Appleでサインイン」を使用したユーザー認証の実装