如何使用MFA构建安全的iOS应用
现代移动应用需要强大的安全解决方案,特别是在处理敏感用户数据或企业级访问时。Okta提供了一个强大的身份平台,通过其Swift SDK中的BrowserSignIn模块,为iOS应用添加安全登录变得可扩展且简单。
在本文中,您将学习如何:
- 设置Okta开发者账户
- 使用最佳实践为iOS应用配置认证
- 使用MFA策略自定义认证体验
- 创建可测试的AuthService协议
- 展示如何在SwiftUI中集成AuthService的示例
使用Okta实现OAuth 2.0和OpenID Connect
第一步是在Okta中将您的应用注册为OpenID Connect客户端,使用带有PKCE的授权码流程,这是最安全且移动友好的OAuth 2.0流程。
配置步骤
- 在管理控制台中创建应用集成
- 选择OIDC - OpenID Connect作为登录方法
- 选择Native Application作为应用类型
- 配置重定向URI:
com.okta.{yourOktaDomain}:/callback
- 设置适当的访问级别
优先使用防钓鱼认证因素
每个新的Integrator Free Plan管理员账户默认必须使用Okta Verify应用来设置MFA。我们推荐使用防钓鱼因素,如带有生物识别功能的Okta Verify和带有WebAuthn的FIDO2。
创建iOS项目并集成Okta移动库
前提条件
- Xcode 15.0或更高版本
- Swift 5+功能
- Swift Package Manager
- 本地安装的Node和npm
添加Okta SDK
使用Swift Package Manager将Okta SDK添加到项目中:
1
2
|
// 在Package Dependencies中添加
https://github.com/okta/okta-mobile-swift
|
配置OIDC
创建Okta.plist文件来管理配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<plist version="1.0">
<dict>
<key>scopes</key>
<string>openid profile offline_access</string>
<key>redirectUri</key>
<string>com.okta.{yourOktaDomain}:/callback</string>
<key>clientId</key>
<string>{yourClientID}</string>
<string>{yourOktaDomain}/oauth2/default</string>
<key>logoutRedirectUri</key>
<string>com.okta.{yourOktaDomain}:/</string>
</dict>
</plist>
|
实现认证服务
AuthService协议
1
2
3
4
5
6
7
8
9
10
|
protocol AuthServiceProtocol {
var isAuthenticated: Bool { get }
var idToken: String? { get }
func tokenInfo() -> TokenInfo?
func userInfo() async throws -> UserInfo?
func signIn() async throws
func signOut() async throws
func refreshTokenIfNeeded() async throws
}
|
登录实现
1
2
3
4
5
6
7
8
|
@MainActor
func signIn() async throws {
BrowserSignin.shared?.ephemeralSession = true
let tokens = try await BrowserSignin.shared?.signIn()
if let tokens {
_ = try? Credential.store(tokens)
}
}
|
令牌管理
1
2
3
4
5
6
7
8
9
10
11
12
|
var isAuthenticated: Bool {
return Credential.default != nil
}
var idToken: String? {
return Credential.default?.token.idToken?.rawValue
}
func refreshTokenIfNeeded() async throws {
guard let credential = Credential.default else { return }
try await credential.refresh()
}
|
在SwiftUI中使用认证服务
AuthViewModel
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
@Observable
final class AuthViewModel {
private let authService: AuthServiceProtocol
var isAuthenticated: Bool = false
var idToken: String?
var isLoading: Bool = false
@MainActor
func handleAuthAction() async {
setLoading(true)
defer { setLoading(false) }
do {
if isAuthenticated {
try await authService.signOut()
} else {
try await authService.signIn()
}
updateUI()
} catch {
errorMessage = error.localizedDescription
}
}
}
|
添加后端授权
设置资源服务器
使用Node.js示例资源服务器:
1
2
3
4
|
const oktaJwtVerifier = new OktaJwtVerifier({
clientId: '{yourClientID}',
});
|
从iOS应用发起授权API请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@MainActor
func fetchMessageFromBackend() async throws -> String {
guard let credential = Credential.default else {
return "Not authenticated."
}
var request = URLRequest(url: URL(string: "http://localhost:8000/api/messages")!)
request.httpMethod = "GET"
await credential.authorize(&request)
let (data, _) = try await URLSession.shared.data(for: request)
let decoder = JSONDecoder()
let response = try decoder.decode(MessageResponse.self, from: data)
if let randomMessage = response.messages.randomElement() {
return "\(randomMessage.text)"
} else {
return "No messages found."
}
}
|
通过遵循这些步骤,您可以构建一个具有完整认证流程的安全iOS应用,包括MFA支持、令牌管理和后端API集成。