Okta Open ID Connect 库
此库是围绕 AppAuth-iOS Objective-C 代码的 Swift 包装器,用于作为 OAuth 2.0 + OpenID Connect 提供者与 Okta 通信,并遵循原生应用当前的最佳实践,使用 授权代码流 + PKCE。
您可以在我们文档中的 Okta + iOS 页面上了解更多信息。您还可以下载我们的 示例应用程序。
目录
入门
将 OktaOidc SDK 安装到项目中的方法是简单的。将此库集成到项目中最简单的方法是通过 CocoaPods。
您还需要
- 一个 Okta 账户,称为 组织(如果您需要一个,请免费注册 开发者组织)。
- 一个 Okta 应用程序,配置为原生应用程序。这需要从 Okta 开发者控制台完成,您可以在 此处 找到说明。在跟随向导时,请使用默认属性。它们是为了与我们的示例应用程序一起使用而设计的。
注意:如果您想使用自己的应用内用户界面而不是网络浏览器,您可以使用我们的Swift认证SDK。
支持的平台
iOS
Okta OIDC支持iOS 11及以上版本。
macOS
Okta OIDC支持macOS(OS X)10.14及以上版本。库同时支持自定义方案;通过一个小型嵌入服务器进行回环HTTP重定向。
安装
Swift包管理器
将以下内容添加到您的Package.swift
文件中定义的dependencies
属性。您可以使用majorVersion
和minor
参数选择版本。例如
dependencies: [
.Package(url: "https://github.com/okta/okta-oidc-ios.git", majorVersion: <majorVersion>, minor: <minor>)
]
CocoaPods
只需将以下行添加到您的Podfile
pod 'OktaOidc'
然后将其安装到您的项目中
pod install --repo-update
Carthage
使用Carthage将此SDK集成到您的Xcode项目中,在Cartfile中指定它。
github "okta/okta-oidc-ios"
然后将其安装到您的项目中
carthage update --use-xcframeworks
注意:确保Carthage版本为0.37.0或更高,否则Carthage可能会失败。
使用指南
要了解此库的功能和身份验证流程概述,请查阅我们的开发者文档。
您还可以浏览完整的API参考文档。
配置参考
创建OIDC对象
在使用此SDK之前,您必须创建一个新的OktaOidc
对象。您可以无参实例化OktaOidc
,这意味着SDK将使用Okta.plist
中的配置值。或者,您可以使用自定义配置创建OktaOidc
。
import OktaOidc
// Use the default Okta.plist configuration
let oktaOidc = OktaOidc()
// Use configuration from another resource
let config = OktaOidcConfig(/* plist */)
let config = OktaOidcConfig(/* dictionary */)
// Instantiate OktaOidc with custom configuration object
let oktaOidc = OktaOidc(configuration: config)
需要刷新令牌吗?刷新令牌是一种特殊令牌,用于生成额外的访问和ID令牌。确保在配置中包含offline_access
作用域,以在应用中静默更新用户的会话!
属性列表
最简单的方法是在应用程序的捆绑包中创建一个属性列表。默认情况下,此库检查文件Okta.plist
的存在。然而,任何属性列表文件都可以用来创建配置对象。确保创建一个包含以下字段的文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>issuer</key>
<string>https://{yourOktaDomain}.com/oauth2/default</string>
<key>clientId</key>
<string>{clientId}</string>
<key>redirectUri</key>
<string>{redirectUri}</string>
<key>logoutRedirectUri</key>
<string>{logoutRedirectUri}</string>
<key>scopes</key>
<string>openid profile offline_access</string>
</dict>
</plist>
配置对象
或者,您可以通过具有所需值的字典创建配置对象(OktaOidcConfig
)
let configuration = OktaOidcConfig(with: [
"issuer": "https://{yourOktaDomain}/oauth2/default",
"clientId": "{clientID}",
"redirectUri": "{redirectUri}",
"logoutRedirectUri": "{logoutRedirectUri}",
"scopes": "openid profile offline_access",
// Custom parameters
"login_hint": "[email protected]"
])
禁用认证会话的单一登录
您可以将OktaOidcConfig
实例的noSSO
标志设置为true
来禁用SSO功能。
let configuration = OktaOidcConfig(with: {YourOidcConfiguration})
if #available(iOS 13.0, *) {
configuration?.noSSO = true
}
注意该标志适用于iOS 13及更高版本。
令牌时间验证
通过实现OKTTokenValidator
协议并设置tokenValidator
变量,可以实现自定义令牌时间验证。
configuration?.tokenValidator = self
默认设置为OKTDefaultTokenValidator
对象。
在Objective-C项目中使用
要在Objective-C项目中使用此SDK,您应该执行以下操作
- 在您的Pod文件中添加
use_frameworks!
。 - 添加项目设置
SWIFT_VERSION = 5.0
。为此,请在Xcode中打开构建设置,选择编辑 -> 添加构建设置 -> 添加用户定义设置。指定SWIFT_VERSION
和5.0
作为设置名称和相应的值。 - 将自动生成的头文件
OktaOidc/OktaOidc-Swift.h
包含到源代码中。
API 参考
signInWithBrowser
通过简单地调用 signInWithBrowser
来启动授权流程。如果授权成功,该操作将在其回调中返回有效的 OktaOidcStateManager
。客户端需要负责进一步存储和管理该管理器。
注意:可以通过指定带有 idp 参数的参数来传递 IDP。
iOS
oktaOidc.signInWithBrowser(from: viewController, additionalParameters: ["idp": "your_idp_here"]) { stateManager, error in
if let error = error {
// Error
return
}
// stateManager.accessToken
// stateManager.idToken
// stateManager.refreshToken
}
示例应用 示例。
macOS
// Create redirect server configuration and start local HTTP server if you don't want to use custom schemes
let serverConfig = OktaRedirectServerConfiguration.default
serverConfig.port = 63875
oktaOidc.signInWithBrowser(redirectServerConfiguration: serverConfig, additionalParameters: ["idp": "your_idp_here"]) { stateManager, error in
if let error = error {
// Error
return
}
// stateManager.accessToken
// stateManager.idToken
// stateManager.refreshToken
}
signOutOfOkta
此方法在浏览器中结束用户的 Okta 会话。该方法将删除 Okta 的持久性 Cookie 并禁用 SSO 功能。
重要:此方法 不会 清除或吊销 Okta 颁发的令牌。使用 OktaOidcStateManager
的 revoke
和 clear
方法来终止您应用程序中用户的本地会话。
iOS
// Redirects to the configured 'logoutRedirectUri' specified in Okta.plist.
oktaOidc.signOutOfOkta(authStateManager, from: viewController) { error in
if let error = error {
// Error
return
}
}
示例应用 示例。
macOS
// Create redirect server configuration and start local HTTP server if you don't want to use custom schemes
let serverConfig = OktaRedirectServerConfiguration.default
serverConfig.port = 63875
// Redirects to the configured 'logoutRedirectUri' specified in Okta.plist.
oktaOidc.signOutOfOkta(authStateManager: authStateManager, redirectServerConfiguration: serverConfig) { error in
if let error = error {
// Error
return
}
}
退出登录
此方法有助于执行多步骤退出流程。此方法提供您想要执行的操作选项,并且 SDK 将这些选项作为一个批次运行。可用的选项有
revokeAccessToken
- SDK 撤销访问令牌revokeRefreshToken
- SDK 撤销刷新令牌removeTokensFromStorage
- SDK 从安全存储中删除令牌signOutFromOkta
- SDK 调用signOutOfOkta
revokeTokensOptions
- 撤销访问和刷新令牌allOptions
- 撤销令牌、从 Okta 退出登录以及从安全存储中删除令牌
SDK 执行的操作顺序
- 如果设置了选项,则撤销访问令牌。如果该步骤失败,则跳过第 3 步。
- 如果设置了选项,则撤销刷新令牌。如果该步骤失败,则跳过第 3 步。
- 如果设置了选项,则从安全存储中删除令牌。
- 如果设置了选项,则进行浏览器退出登录。
iOS
let options: OktaSignOutOptions = .revokeTokensOptions
options.insert(.signOutFromOkta)
oktaOidc?.signOut(authStateManager: authStateManager, from: viewController, progressHandler: { currentOption in
if currentOption.contains(.revokeAccessToken) {
// update progress
} else if currentOption.contains(.revokeRefreshToken) {
// update progress
} else if currentOption.contains(.signOutFromOkta) {
// update progress
}
}, completionHandler: { success, failedOptions in
if !success {
// handle error
}
})
macOS
// Create redirect server configuration and start local HTTP server if you don't want to use custom schemes
let serverConfig = OktaRedirectServerConfiguration.default
serverConfig.port = 63875
let options: OktaSignOutOptions = .revokeTokensOptions
options.insert(.signOutFromOkta)
oktaOidc?.signOut(authStateManager: authStateManager,
redirectServerConfiguration: serverConfig,
progressHandler: { currentOption in
if currentOption.contains(.revokeAccessToken) {
// update progress
} else if currentOption.contains(.revokeRefreshToken) {
// update progress
} else if currentOption.contains(.signOutFromOkta) {
// update progress
}
}, completionHandler: { success, failedOptions in
if !success {
// handle error
}
})
authenticate
如果您已经登录到 Okta 并拥有有效的会话令牌,则可以通过调用 authenticate(withSessionToken:)
来完成授权。授权成功后,此操作在回调中返回一个有效的 OktaOidcStateManager
。客户端负责进一步存储和维护管理器。
oktaOidc.authenticate(withSessionToken: token) { stateManager, error in
self.hideProgress()
if let error = error {
// Error
return
}
// stateManager.accessToken
// stateManager.idToken
// stateManager.refreshToken
}
示例应用程序 示例。
stateManager
令牌安全存储在钥匙串中,可以通过访问OktaOidcStateManager检索。
stateManager?.accessToken
stateManager?.idToken
stateManager?.refreshToken
用户负责存储由signInWithBrowser
或authenticate
操作返回的OktaAuthStateManager。要存储管理器,请调用writeToSecureStorage
方法
oktaOidc.signInWithBrowser(from: self) { stateManager, error in
stateManager.writeToSecureStorage()
}
示例应用示例。
要检索存储的管理器,请调用readFromSecureStorage(for:)
并传入对应您感兴趣管理器的Okta配置。
guard let stateManager = OktaOidcStateManager.readFromSecureStorage(for: oktaConfig) else {
// unauthenticated
}
//authenticated
// stateManager.accessToken
// stateManager.idToken
// stateManager.refreshToken
示例应用示例。
注意:在OktaOidc SDK 3.0中,我们添加了对多个OAuth 2.0账户的支持。因此,开发人员可以在一个应用程序中使用Okta端点、社交端点和其他端点。因此,OktaOidcStateManager
使用基于配置的复合键存储在钥匙串中。为了保持向后兼容性,有一个方法readFromSecureStorage()
尝试读取以传统方式存储的OktaOidcStateManager
,以便用户可以在切换到较新版本的SDK后检索先前存储的OktaOidcStateManager
。
introspect
调用内省端点检查指定令牌的有效性。
stateManager?.introspect(token: accessToken, callback: { payload, error in
guard let isValid = payload["active"] as? Bool else {
// Error
return
}
print("Is token valid? \(isValid)")
})
示例应用示例。
renew
由于访问令牌传统上生命周期很短,您可以通过用刷新令牌交换新令牌来续期过期的令牌。请查阅配置参考以确保您的应用程序已正确配置。
stateManager?.renew { newAccessToken, error in
if let error = error else {
// Error
return
}
// renewed TokenManager
}
示例应用示例。
revoke
调用撤销端点以撤销指定的令牌。
stateManager?.revoke(accessToken) { response, error in
if let error = error else {
// Error
return
}
// Token was revoked
}
示例应用示例。
getUser
通过存储的访问令牌调用OpenID Connect UserInfo端点以返回用户声明信息。
stateManager?.getUser { response, error in
if let error = error {
// Error
return
}
// JSON response
}
示例应用示例。
清除
通过从密钥链中删除缓存的令牌来删除本地认证状态。
警告: SDK 删除了应用程序可访问的所有密钥链项。
stateManager.clear()
示例应用 示例。
开发
运行测试
要进行端到端测试,请更新 Okta.plist
文件以匹配在 先决条件 中指定的配置。接下来,导出以下环境变量
export USERNAME={username}
export PASSWORD={password}
export CLIENT_ID={clientId}
export ISSUER=https://{yourOktaDomain}/oauth2/default
export REDIRECT_URI={redirectUri}
export LOGOUT_REDIRECT_URI={logoutRedirectUri}
# Run E2E end Unit tests
bash ./scripts/build-and-test.sh
注意: 您可能需要更新模拟器设备以匹配您的 Xcode 版本。
修改网络请求
您可以跟踪和修改 OktaOidc
发出的网络请求。为此,创建一个遵循 OktaNetworkRequestCustomizationDelegate
协议的对象,并将其设置到一个 OktaOidcConfig
实例的 requestCustomizationDelegate
属性上。
let configuration = OktaOidcConfig(with: {YourOidcConfiguration})
configuration.requestCustomizationDelegate = {YourDelegateInstance}
例如,代理可以按如下方式实现
extension SomeNSObject: OktaNetworkRequestCustomizationDelegate {
func customizableURLRequest(_ request: URLRequest?) -> URLRequest? {
guard var modifiedRequest = request else {
return nil
}
modifiedRequest.setValue("Some value", forHTTPHeaderField: "custom-header-field")
print("Okta OIDC network request: \(modifiedRequest)")
return modifiedRequest
}
func didReceive(_ response: URLResponse?) {
guard let response = response else {
return
}
print("Okta OIDC network response: \(response)")
}
}
注意: 极力建议将原始 URLRequest 对象的现有所有参数完全复制到修改后的请求中,不要做任何更改。改变这些数据可能导致网络请求失败。如果 customizableURLRequest(_:)
方法返回 nil
,则将使用默认请求。
迁移
从3.10.x迁移到3.11.x
SDK okta-oidc-ios
在错误处理方面有重大变更。请遵循以下指南来更新您的代码。
APIError
已重命名为api
。api
错误有一个额外的参数underlyingError
,它是可选的并指示错误的来源。- 引入了一个新的错误
authorization(error:description:)
。 authorization
错误出现在授权服务器因授权过程中出现错误而失败时。unexpectedAuthCodeResponse(statusCode:)
有一个错误代码参数。OktaOidcError
符合CustomNSError
协议。这意味着您可以转换错误为NSError
并获取code
、userInfo
、domain
、underlyingErrors
。OktaOidcError
符合Equatable
协议。可以使用运算符==
对错误进行比较,以判断它们是否相等,或者使用运算符!=
比较它们是否不相等。
已知问题
{App} Wants to Use {Auth Domain} to Sign In
)用于 Okta 登出流程
iOS 显示权限对话框(这是一已知 iOS 的问题,iOS 提供没有任何好的方法来终止活动的身份验证会话并删除 SSO 饼干。目前唯一正确的方法是使用 ASWebAuthenticationSession
类来终止会话。ASWebAuthenticationSession
删除了所有 SSO 饼干,但显示了 Sign In
权限对话框。
您还可以考虑以下解决方法:
- 如果不需要 SSO 功能,请使用 OIDC 配置对象中的
noSSO
选项。请注意,此选项仅适用于 iOS 13 及以上版本。 - 分叉存储库并更改用户代理实现(
OIDExternalUserAgentIOS.m
),仅使用SFSafariViewController
。此方法的一些潜在问题在此处进行描述这里。
贡献
我们欢迎对所有开源项目的贡献。请参阅贡献指南了解如何构建贡献。