この記事を書いた動機
Auth0 を利用した iOS アプリ(SwiftUI)の開発に関する記事はあるが、画面の表示を担当する ContentView
に認証に関する処理を実装するものが多かった。
メンテナンスしやすいように認証に関する処理を別のクラスに実装したので、その備忘録として書いた。
この記事で書くこと・書かないこと
書くこと
- Auth0 を利用した iOS アプリ(SwiftUI)開発
書かないこと
- Auth0 のアカウント作成方法
- Auth0 で新しい Application(Native App) の作成方法
- Auth0 でテストで使用するユーザーの具体的な作成方法
- Xcode を利用して新しいプロジェクト(SwiftUI)を作成する方法
- Xcode の
Add Package
を利用してライブラリを導入する方法 - プロジェクトに
.plist
ファイルを追加する方法
上記の内容は記載しないため、具体的な方法については他の記事を確認すること
Auth0 とは
- クラウド型の認証プラットフォーム
- ユーザのログイン情報やトークンの管理などを自社開発のアプリに導入できる
画面イメージ
ログイン画面
ユーザーが Log in ボタン を押すと、アプリは認証画面を表示する
認証画面
ユーザーがこの画面で、メールアドレス・パスワードを入力し Continue ボタン を押すと、アプリは Auth0 を利用して認証処理を実行する
- 認証処理が成功したら、ログイン状態になりホーム画面に遷移する

ホーム画面
アプリは、ユーザー名とユーザーの画像を表示する
- ユーザーが Log out ボタン を押すと、Auth0 を利用してログオフ状態になりログイン画面に遷移する

前提条件
- 事前に Auth0 のアカウントを作成していること
- Xcode を利用してプロジェクト(SwiftUI)を作成していること
開発環境
項目 | 内容 | 備考 |
---|---|---|
PC | MacBook Air | M1 2020 |
IDE | Xcode | ver 14.3.1 |
バージョン管理 | GitHub |
GitHub
実際のコードを下記リポジトリにプッシュしている
上記 GitHub のコードには Auth0
ファイル(.plist
形式) をプッシュしていないため、各自 Auth0
のファイルを作成、設定すること
追加するライブラリ
- Auth0
- JWTDecode
- SimpleKeyChain
Xcode の Add Package で Auth0
を指定しインポートを実行すると、自動的に JWTDecode
SimpleKeyChain
も自動的に反映される。
ディレクトリ・ファイル構成
Auth0 で準備すること
Application(Native App) を作成する
作成した Application の項目に値を設定
設定対象の項目は次の通り
- Allowed Callback URLs
- Allowed Logout URLs
設定する値は次の通り
{BundleID}://{Auth0 Domain}/ios/{BundleID}/callback
例
項目 | 値 |
---|---|
BundleID | sample.Sample-App-with-Auth0 |
Auth0 Domain | dev-XXXXXXXXXX.us.auth0.com |
設定する値
sample.Sample-App-with-Auth0://dev-XXXXXXXXXX.us.auth0.com/ios/sample.Sample-App-with-Auth0/callback
テストで使用するユーザーを作成する
この作成したユーザーのメールアドレスとパスワードを利用して、ログインが可能。
クラス一覧
名称 | 役割 | 備考 |
---|---|---|
Sample_App_with_Auth0App | エントリーポイント | |
Auth0Service |
Auth0 を利用した認証情報を各クラスに提供 |
ObservableObject を準拠する |
ContentView | 認証の状況に応じて表示する画面を切り替える | |
HomeView | ログイン後の画面 | |
LoginView | ログイン前の画面 |
このクラス構成にした理由
-
Auth0
を利用した認証に関する処理は Auth0Service クラス に、画面に関する処理は 各 View クラス に分けて記載したかったため
コード
Auth0Service クラス
import Foundation
import Auth0
class Auth0Service:ObservableObject {
@Published var isAuthenticated = false
@Published var userProfile = Profile.empty
internal func login() {
Auth0
.webAuth()
.start { result in
switch result {
case .success(let credentials):
print("Obtained credentials: \(credentials)")
self.isAuthenticated = true
self.userProfile = Profile.from(credentials.idToken)
case .failure(let error):
print("Failure: \(error.localizedDescription)")
}
}
}
internal func logout(){
Auth0
.webAuth()
.clearSession { result in
switch result {
case .success:
print("Session cookie cleared")
self.isAuthenticated = false
self.userProfile = Profile.empty
case .failure(let error):
print("Failed with: \(error.localizedDescription)")
}
}
}
}
ポイント
- ログイン状態を
isAuthenticated
に保持している -
ObservableObject
を準拠しているためisAuthenticated
の値が変化すると、他のクラスにもそれが伝播する
Sample_App_with_Auth0App クラス
import SwiftUI
@main
struct Sample_App_with_Auth0App: App {
@StateObject var service: Auth0Service = Auth0Service()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(service)
}
}
}
ポイント
-
Auth0Service
クラスのインスタンスを.environmentObject()
を利用して他のクラスがisAuthenticated
の値を取得できるようにしている。
ContentView クラス
import SwiftUI
struct ContentView: View {
@EnvironmentObject var service: Auth0Service
var body: some View {
if service.isAuthenticated {
HomeView()
} else {
LoginView()
}
}
}
ポイント
-
Auth0Service
クラスのisAuthenticated
の値に応じて表示する画面を切り替えている。
HomeView クラス
struct HomeView: View {
@EnvironmentObject var service: Auth0Service
var body: some View {
VStack {
userImage
Text(service.userProfile.name)
.padding()
logoutButton
.padding()
}
}
private var userImage: some View {
AsyncImage(url: URL(string: service.userProfile.picture)){ response in
response.image?
.resizable()
.scaledToFit()
.frame(width: 100,height: 100)
.cornerRadius(50)
}
}
private var logoutButton: some View {
Button {
logout()
} label: {
Text("Log out")
}
.fontWeight(.black)
.frame(width: 160, height: 48)
.foregroundColor(.white)
.background(Color.primary)
.cornerRadius(24)
}
}
extension HomeView {
private func logout() {
service.logout()
}
}
ポイント
- ボタンを押すと
logout()
を実行し、具体的な処理をAuth0Service
クラスに委任する
LoginView クラス
import SwiftUI
struct LoginView: View {
@EnvironmentObject var service: Auth0Service
var body: some View {
VStack {
title
loginButton
}
}
private var title: some View {
Text("Welcome")
.font(.largeTitle)
.fontWeight(.black)
}
private var loginButton: some View {
Button {
login()
} label: {
Text("Log in")
}
.fontWeight(.black)
.frame(width: 160, height: 48)
.foregroundColor(.white)
.background(Color.primary)
.cornerRadius(24)
}
}
extension LoginView {
private func login() {
service.login()
}
}
ポイント
- ボタンを押すと
login()
を実行し、具体的な処理をAuth0Service
クラスに委任する
ポイント
ファイル Auth0 について
- ファイル形式は
.plist
Auth0 ファイルに関する設定
項目名 | データ型 | 値 | 備考 |
---|---|---|---|
Domain | String | Auth0 の Domain の値 |
|
ClientId | String | Auth0 の ClientId の値 |
参考資料
Auth0
YouTube
下記動画に手順がわかりやすく紹介されている。
英語ですが聞き取りやすいのでオススメです。