あなたの iOS (Swift) アプリケーションに認証 (Authentication) を追加する
このガイドは、Admin Console で「Native app」タイプのアプリケーションを作成したことを前提としています。
インストール
Logto Swift SDK のサポートされている最小 iOS バージョンは iOS 13 です。
Logto Swift SDK には 2 つの主要バージョンがあります:
- v2:サインイン体験を
ASWebAuthenticationSession(システムブラウザ)で開きます。これによりパスキーサインインが可能になり、ブラウザセッションが共有されます。v2 ではネイティブソーシャルプラグインターゲットが削除されていますが、ソーシャルコネクターは引き続きブラウザ経由で動作します。ネイティブの WeChat や Alipay SDK 連携が必要な場合は v1 をご利用ください。 - v1:サインイン体験を埋め込み WebView で開きます。これはネイティブソーシャルプラグインターゲットに必要ですが、パスキーサインイン には対応していません(WebView はパスキーの基盤となる WebAuthn をサポートしていません)。
このガイドは両バージョンに対応しています。下記のタブでバージョンを選択してください。選択はガイド全体で同期されます。
Swift Package Manager で Logto SDK を依存関係として追加するには、以下の URL を使用してください。
https://github.com/logto-io/swift.git
Xcode 11 以降、Swift パッケージを直接インポート できます。追加ツールは不要です。
Xcode でパッケージバージョンを尋ねられたら、統合したいバージョンを選択してください:
- v2
- v1
バージョンには最新の v2 リリースを使用してください。最新の v2 バージョンは 2.0.0 です。
Package.swift を直接使用する場合:
// Logto Swift SDK を依存関係として追加
.package(url: "https://github.com/logto-io/swift.git", from: "2.0.0")
ネイティブソーシャルプラグインターゲットが必要な場合は、最新の v1 リリースを使用してください。最新の v1 バージョンは 1.2.0 です。
Package.swift を直接使用する場合:
// Logto Swift SDK を依存関係として追加
.package(url: "https://github.com/logto-io/swift.git", from: "1.2.0")
Carthage および CocoaPods には、技術的な理由により現時点では対応していません。
Carthage
Carthage は ビルドに xcodeproj ファイルが必要 です。今後回避策を検討します。
統合
LogtoClient を初期化する
LogtoConfig オブジェクトを使用して LogtoClient インスタンスを作成することで、クライアントを初期化します。
import Logto
import LogtoClient
let config = try? LogtoConfig(
endpoint: "<your-logto-endpoint>", // 例: http://localhost:3001
appId: "<your-app-id>"
)
let client = LogtoClient(useConfig: config)
デフォルトでは、ID トークンやリフレッシュ トークンのような資格情報を Keychain に保存します。したがって、ユーザーは戻ってきたときに再度サインインする必要はありません。
この動作をオフにするには、usingPersistStorage を false に設定します:
let config = try? LogtoConfig(
// ...
usingPersistStorage: false
)
サインインとサインアウトを実装する
詳細に入る前に、エンドユーザー体験の概要を簡単にご紹介します。サインインプロセスは次のようにシンプルにまとめられます:
- アプリがサインインメソッドを呼び出します。
- ユーザーは Logto のサインインページにリダイレクトされます。ネイティブアプリの場合は、システムブラウザが開かれます。
- ユーザーがサインインし、アプリ(リダイレクト URI として設定)に戻されます。
リダイレクトベースのサインインについて
- この認証 (Authentication) プロセスは OpenID Connect (OIDC) プロトコルに従い、Logto はユーザーのサインインを保護するために厳格なセキュリティ対策を講じています。
- 複数のアプリがある場合、同じアイデンティティプロバイダー (Logto) を使用できます。ユーザーがあるアプリにサインインすると、Logto は別のアプリにアクセスした際に自動的にサインインプロセスを完了します。
リダイレクトベースのサインインの理論と利点について詳しく知るには、Logto サインイン体験の説明を参照してください。
リダイレクト URI の設定
- v2
- v1
Logto コンソールのアプリケーション詳細ページに切り替えましょう。リダイレクト URI io.logto.app://callback を追加し、「変更を保存」をクリックします。

v2 では、サインイン体験は ASWebAuthenticationSession(システムブラウザ)で開かれ、リダイレクトは OS レベルのコールバックマッチングを通じてアプリに戻されます。io.logto.app://callback のようなカスタムスキームのリダイレクト URI を使う場合、アプリの Info.plist にはスキーム部分(io.logto.app)のみを登録し、Logto アプリケーションの Redirect URIs には完全なリダイレクト URI を追加してください。
Xcode でアプリのターゲットを開き、Info を選択し、URL Types を展開して、URL Schemes に io.logto.app を追加します。Info.plist を直接編集する場合は、以下を追加します:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>io.logto.app</string>
<key>CFBundleURLSchemes</key>
<array>
<string>io.logto.app</string>
</array>
</dict>
</array>
v2 のブラウザフローでは、LogtoClient.handle(url:) を呼び出す必要はありません。このプラグインハンドオフ API は埋め込み WebView フローとともに削除されました。
カスタムスキームの代わりに Universal Links を使う?
https://example.com/callback のような HTTPS リダイレクト URI も利用できます:
- アプリに Associated Domains 機能を追加します。
webcredentials:example.comを設定し、iOS 17.4 以降でASWebAuthenticationSessionが HTTPS コールバックをマッチできるようにします。- 同じ URL で認証セッション外でも Universal Link としてアプリを開きたい場合は、
applinks:example.comを設定し、ドメインとパスに有効なapple-app-site-associationファイルをホストします。 - Logto アプリケーションの Redirect URIs に HTTPS URI を追加します。
signInWithBrowserに同じ URI を渡します。
iOS 17.4 以降では、SDK は ASWebAuthenticationSession の HTTPS コールバックマッチング API を利用するため、HTTPS リダイレクトで自動的にセッションを完了・終了できます。古い iOS バージョンでは、認可リクエストで HTTPS リダイレクト URI を使えますが、Universal Link コールバックをアプリ側で処理しない限りセッションが自動で閉じない場合があります。古い iOS バージョンで自動完了が必要な場合は、互換性のためにカスタムスキームリダイレクトも残しておいてください。
Logto コンソールのアプリケーション詳細ページに切り替えましょう。リダイレクト URI io.logto://callback を追加し、「変更を保存」をクリックします。

iOS SDK の Redirect URI は内部利用のみです。コネクターから要求されるまで Custom URL Scheme を追加する 必要はありません。
サインインとサインアウト
.signInWithBrowser(redirectUri:) を呼び出す前に、Admin Console でリダイレクト URI
が正しく設定されていることを確認してください。 :::
- v2
- v1
v2 では、client.signOut(postLogoutRedirectUri:) を使うと完全なサインアウトが実行されます:ローカル認証情報のクリア、リフレッシュ トークン (Refresh token) の取り消し、システムブラウザでエンドセッションエンドポイントを開いて Logto セッションを終了します。その後、ブラウザはサインアウト後リダイレクト URI を通じてアプリに戻ります。利用前に Logto Console のアプリケーション詳細ページでサインアウト後リダイレクト URI io.logto.app://signed-out を追加し、「Save changes」をクリックしてください。サインアウト後リダイレクト URI にはサインイン時に登録したカスタムスキームを使えます。
SwiftUI アプリの例:
// コメントは翻訳済み
struct ContentView: View {
@State var isAuthenticated: Bool
private let redirectUri = "io.logto.app://callback"
private let postLogoutRedirectUri = "io.logto.app://signed-out"
init() {
isAuthenticated = client.isAuthenticated
}
var body: some View {
VStack {
if isAuthenticated {
Button("Sign Out") {
Task { [self] in
let error = await client.signOut(postLogoutRedirectUri: postLogoutRedirectUri)
if let error = error {
print(error)
return
}
isAuthenticated = false
}
}
} else {
Button("Sign In") {
Task { [self] in
do {
try await client.signInWithBrowser(redirectUri: redirectUri)
isAuthenticated = true
} catch let error as LogtoClientErrors.SignIn {
// サインイン中にエラーが発生
} catch {
// その他のエラー
}
}
}
}
}
}
}
- サインアウト後リダイレクト URI なしで
client.signOut()を呼ぶこともできます。この場合 Console 側の設定は不要です:ブラウザに Logto サインアウトページが表示され、ユーザーは手動で閉じてアプリに戻ります。 - UI コンテキストが利用できない場合は、
client.clearCredentials()でローカル認証情報のクリアとリフレッシュ トークン (Refresh token) の取り消しが可能です。ただし、この場合ブラウザ上の Logto セッションは残るため、次回のsignInWithBrowserでそのセッションを使ってサイレントサインインされる場合があります。
client.signInWithBrowser(redirectUri:) でユーザーのサインイン、client.signOut() でサインアウトができます。
SwiftUI アプリの例:
// コメントは翻訳済み
struct ContentView: View {
@State var isAuthenticated: Bool
init() {
isAuthenticated = client.isAuthenticated
}
var body: some View {
VStack {
if isAuthenticated {
Button("Sign Out") {
Task { [self] in
await client.signOut()
isAuthenticated = false
}
}
} else {
Button("Sign In") {
Task { [self] in
do {
try await client.signInWithBrowser(redirectUri: "io.logto://callback")
isAuthenticated = true
} catch let error as LogtoClientErrors.SignIn {
// サインイン中にエラーが発生
} catch {
// その他のエラー
}
}
}
}
}
}
}
チェックポイント: アプリケーションをテストする
これで、アプリケーションをテストできます:
- アプリケーションを実行すると、サインインボタンが表示されます。
- サインインボタンをクリックすると、SDK がサインインプロセスを初期化し、Logto のサインインページにリダイレクトされます。
- サインインすると、アプリケーションに戻り、サインアウトボタンが表示されます。
- サインアウトボタンをクリックして、トークンストレージをクリアし、サインアウトします。
ユーザー情報を取得する
ユーザー情報を表示する
ユーザーの情報を表示するには、client.getIdTokenClaims() メソッドを使用できます。例えば、SwiftUI アプリでは:
struct ContentView: View {
@State var isAuthenticated: Bool
@State var name: String?
init() {
isAuthenticated = client.isAuthenticated
name = try? client.getIdTokenClaims().name
}
var body: some View {
VStack {
if isAuthenticated {
Text("Welcome, \(name)")
} else {
Text("Please sign in")
}
}
}
}
追加のクレームをリクエストする
client.getIdTokenClaims() から返されるオブジェクトに一部のユーザー情報が欠けていることがあります。これは、OAuth
2.0 と OpenID Connect (OIDC) が最小特権の原則 (PoLP) に従うように設計されており、Logto
はこれらの標準に基づいて構築されているためです。
デフォルトでは、限られたクレーム (Claims) が返されます。より多くの情報が必要な場合は、追加のスコープ (Scopes) をリクエストして、より多くのクレーム (Claims) にアクセスできます。
「クレーム (Claim)」はサブジェクトについての主張であり、「スコープ (Scope)」はクレーム (Claims) のグループです。現在のケースでは、クレーム (Claim) はユーザーに関する情報の一部です。
スコープ (Scope) とクレーム (Claim) の関係の非規範的な例を示します:
「sub」クレーム (Claim) は「サブジェクト (Subject)」を意味し、ユーザーの一意の識別子(つまり、ユーザー ID)です。
Logto SDK は常に 3 つのスコープ (Scopes) をリクエストします:openid、profile、および offline_access。
追加のスコープをリクエストするには、スコープを LogtoConfig オブジェクトに渡すことができます。例えば:
let config = try? LogtoConfig(
endpoint: "<your-logto-endpoint>", // 例: http://localhost:3001
appId: "<your-app-id>",
scopes: [
UserScope.Email.rawValue,
UserScope.Phone.rawValue,
]
)
その後、client.getIdTokenClaims() の戻り値で追加のクレームにアクセスできます:
let claims = try? client.getIdTokenClaims()
// これで追加のクレーム `claims.email`、`claims.phone` などにアクセスできます。
ネットワークリクエストが必要なクレーム (Claims)
ID トークンの肥大化を防ぐために、一部のクレーム (Claims) は取得するためにネットワークリクエストが必要です。例えば、custom_data クレームはスコープで要求されてもユーザーオブジェクトに含まれません。これらのクレームにアクセスするには、 client.fetchUserInfo() メソッドを使用できます:
let userInfo = try? client.fetchUserInfo()
// これでクレーム `userInfo.custom_data` にアクセスできます。
スコープとクレーム
Logto は OIDC の スコープ (Scope) とクレーム (Claim) の規約 を使用して、ID トークンおよび OIDC userinfo エンドポイント からユーザー情報を取得するためのスコープ (Scope) とクレーム (Claim) を定義しています。「スコープ (Scope)」と「クレーム (Claim)」は、OAuth 2.0 および OpenID Connect (OIDC) 仕様の用語です。
標準の OIDC クレーム (Claim) については、ID トークンへの含有はリクエストされたスコープ (Scope) によって厳密に決定されます。拡張クレーム (Claim)(例:custom_data や organizations)は、カスタム ID トークン 設定を通じて ID トークンに追加で表示するように構成できます。
こちらはサポートされているスコープと対応するクレーム (Claims) の一覧です:
標準 OIDC スコープ
openid(デフォルト)
| Claim name | Type | 説明 |
|---|---|---|
| sub | string | ユーザーの一意の識別子 |
profile(デフォルト)
| Claim name | Type | 説明 |
|---|---|---|
| name | string | ユーザーのフルネーム |
| username | string | ユーザー名 |
| picture | string | エンドユーザーのプロフィール画像の URL。この URL は画像ファイル(例:PNG、JPEG、GIF 画像ファイル)を指す必要があり、画像を含む Web ページではありません。この URL は、エンドユーザーを説明する際に表示するのに適したプロフィール写真を特に参照するべきであり、エンドユーザーが撮影した任意の写真ではありません。 |
| created_at | number | エンドユーザーが作成された時刻。Unix エポック(1970-01-01T00:00:00Z)からのミリ秒数で表されます。 |
| updated_at | number | エンドユーザー情報が最後に更新された時刻。Unix エポック(1970-01-01T00:00:00Z)からのミリ秒数で表されます。 |
その他の 標準クレーム (Standard Claims) には、family_name、given_name、middle_name、nickname、preferred_username、profile、website、gender、birthdate、zoneinfo、locale などがあり、これらも profile スコープに含まれます(userinfo エンドポイントをリクエストする必要はありません)。上記のクレームとの違いは、これらのクレームは値が空でない場合のみ返される点です。一方、上記のクレームは値が空の場合 null が返されます。
標準クレーム (Standard Claims) とは異なり、created_at および updated_at クレームは秒ではなくミリ秒を使用しています。
email
| Claim name | Type | 説明 |
|---|---|---|
string | ユーザーのメールアドレス | |
| email_verified | boolean | メールアドレスが認証済みかどうか |
phone
| Claim name | Type | 説明 |
|---|---|---|
| phone_number | string | ユーザーの電話番号 |
| phone_number_verified | boolean | 電話番号が認証済みかどうか |
address
アドレスクレームの詳細については OpenID Connect Core 1.0 を参照してください。
(デフォルト) と記載されたスコープは常に Logto SDK によってリクエストされます。標準 OIDC スコープ下のクレーム (Claims) は、対応するスコープがリクエストされた場合、常に ID トークン (ID token) に含まれます — 無効化できません。
拡張スコープ
以下のスコープは Logto によって拡張されており、userinfo エンドポイント を通じてクレーム (Claims) を返します。これらのクレームは Console > Custom JWT を通じて ID トークン (ID token) に直接含めるよう設定することもできます。詳細は カスタム ID トークン を参照してください。
custom_data
| Claim name | Type | 説明 | デフォルトで ID トークンに含まれるか |
|---|---|---|---|
| custom_data | object | ユーザーのカスタムデータ |
identities
| Claim name | Type | 説明 | デフォルトで ID トークンに含まれるか |
|---|---|---|---|
| identities | object | ユーザーのリンク済みアイデンティティ | |
| sso_identities | array | ユーザーのリンク済み SSO アイデンティティ |
roles
| Claim name | Type | 説明 | デフォルトで ID トークンに含まれるか |
|---|---|---|---|
| roles | string[] | ユーザーのロール | ✅ |
urn:logto:scope:organizations
| Claim name | Type | 説明 | デフォルトで ID トークンに含まれるか |
|---|---|---|---|
| organizations | string[] | ユーザーが所属する組織 ID | ✅ |
| organization_data | object[] | ユーザーが所属する組織データ |
これらの組織クレーム (Organization Claims) は、不透明トークン (Opaque token) を使用している場合でも userinfo エンドポイント経由で取得できます。ただし、不透明トークン (Opaque token) は組織トークン (Organization token) として組織固有リソースへのアクセスには使用できません。詳細は 不透明トークン (Opaque token) と組織 (Organizations) を参照してください。
urn:logto:scope:organization_roles
| Claim name | Type | 説明 | デフォルトで ID トークンに含まれるか |
|---|---|---|---|
| organization_roles | string[] | ユーザーが所属する組織ロール(<organization_id>:<role_name> 形式) | ✅ |
API リソース
まず 🔐 ロールベースのアクセス制御 (RBAC) を読むことをお勧めします。これにより、Logto の RBAC の基本概念と API リソースを適切に設定する方法を理解できます。
Logto クライアントを設定する
API リソースを設定したら、アプリで Logto を設定する際にそれらを追加できます:
let config = try? LogtoConfig(
endpoint: "<your-logto-endpoint>", // 例: http://localhost:3001
appId: "<your-app-id>",
resources: ["https://shopping.your-app.com/api", "https://store.your-app.com/api"], // API リソースを追加
)
let client = LogtoClient(useConfig: config)
各 API リソースには独自の権限 (スコープ) があります。
例えば、https://shopping.your-app.com/api リソースには shopping:read と shopping:write の権限があり、https://store.your-app.com/api リソースには store:read と store:write の権限があります。
これらの権限を要求するには、アプリで Logto を設定する際にそれらを追加できます:
let config = try? LogtoConfig(
endpoint: "<your-logto-endpoint>",
appId: "<your-app-id>",
scopes: ["shopping:read", "shopping:write", "store:read", "store:write"],
resources: ["https://shopping.your-app.com/api", "https://store.your-app.com/api"],
)
let client = LogtoClient(useConfig: config)
スコープが API リソースとは別に定義されていることに気付くかもしれません。これは、OAuth 2.0 のリソースインジケーター が、リクエストの最終的なスコープはすべてのターゲットサービスでのすべてのスコープの直積になると指定しているためです。
したがって、上記のケースでは、Logto での定義からスコープを簡略化できます。両方の API リソースは、プレフィックスなしで read と write スコープを持つことができます。その後、Logto の設定では:
let config = try? LogtoConfig(
endpoint: "<your-logto-endpoint>",
appId: "<your-app-id>",
scopes: ["read", "write"],
resources: ["https://shopping.your-app.com/api", "https://store.your-app.com/api"],
)
let client = LogtoClient(useConfig: config)
各 API リソースは、read と write の両方のスコープを要求します。
API リソースで定義されていないスコープを要求しても問題ありません。例えば、API リソースに email スコープが利用できなくても、email スコープを要求できます。利用できないスコープは安全に無視されます。
サインインが成功すると、Logto はユーザーのロールに応じて適切なスコープを API リソースに発行します。
API リソースのアクセス トークンを取得する
特定の API リソースのアクセス トークンを取得するには、getAccessToken メソッドを使用できます:
let accessToken = try await client.getAccessToken(for: "https://shopping.your-app.com/api")
このメソッドは、ユーザーが関連する権限を持っている場合に API リソースにアクセスするために使用できる JWT アクセス トークンを返します。現在キャッシュされているアクセス トークンが期限切れの場合、このメソッドは自動的にリフレッシュ トークンを使用して新しいアクセス トークンを取得しようとします。
アクセストークンをリクエストヘッダーに添付する
トークンを HTTP ヘッダーの Authorization フィールドに Bearer 形式 (Bearer YOUR_TOKEN) で配置し、準備完了です。
Bearer トークンの統合フローは、使用しているフレームワークやリクエスターによって異なる場合があります。リクエスト Authorization ヘッダーを適用する独自の方法を選択してください。
await LogtoRequest.get(
useSession: session,
endpoint: userInfoEndpoint,
headers: ["Authorization": "Bearer \(accessToken)"]
)