AppAuth for iOS 和 macOS 是与 OAuth 2.0 和 OpenID Connect 提供商通信的客户端 SDK。它力图直接映射那些规范中的请求和响应,同时遵循实现语言的惯用风格。除了映射原始协议流程外,还提供便捷方法以帮助执行带新鲜令牌的操作等常见任务。
它遵循 RFC 8252 - OAuth 2.0 for Native Apps 中的最佳实践,包括在 iOS 上使用 SFAuthenticationSession
和 SFSafariViewController
进行授权请求。由于安全性和可用性原因(见 RFC 8252 第 8.12 节),显式不支持 UIWebView
和 WKWebView
。
它还支持 OAuth 的 PKCE 扩展,该扩展是为了在使用自定义 URI 方案重定向时保护公开客户端中的授权代码而创建的。该库易于处理其他扩展(标准或非标准),能处理所有协议请求和响应中的额外参数。
规范
iOS
支持版本
AppAuth 支持 iOS 7 及以上版本。
从iOS 9开始,使用应用内浏览器标签模式(通过SFSafariViewController
),并在旧版本上回退到系统浏览器(移动Safari)。
授权服务器要求
该库可以与自定义URI方案(所有iOS支持的版本)和通用链接(iOS 9以上)一起使用。
一般情况下,AppAuth可以与支持原生应用的授权服务器(AS)一起工作,如RFC 8252中所述,无论是通过自定义URI方案重定向还是通用链接。假设所有客户端都是基于Web的或要求客户端维护客户端密钥机密的AS可能无法很好地工作。
macOS
支持版本
AppAuth支持macOS(OS X)10.9及以上版本。
授权服务器要求
macOS的AppAuth支持自定义方案和一个小的嵌入式服务器通过回环HTTP重定向。
一般情况下,AppAuth可以与支持原生应用的授权服务器(AS)一起工作,如RFC 8252中所述,无论是通过自定义URI方案还是回环HTTP重定向。假设所有客户端都是基于Web的或要求客户端维护客户端密钥机密的AS可能无法很好地工作。
尝试
想要试试AppAuth吗?只需运行
pod try AppAuth-logout
请按照Examples/README.md中的说明配置您自己的OAuth客户端(您需要使用客户端信息更新3个配置点以尝试示例)。
配置
AppAuth支持三种依赖管理选项。
CocoaPods
使用CocoaPods,请在您的Podfile
中添加以下行
pod 'AppAuth-logout'
然后运行pod install
。
Carthage
使用Carthage,请在您的Cartfile
中添加以下行
github "yurikoles/AppAuth-logout" "master"
然后运行carthage bootstrap
。
静态库
您还可以将AppAuth作为静态库使用。这需要链接库和您的项目,并包含头文件。建议的配置
- 创建一个Xcode Workspace。
- 将
AppAuth.xcodeproj
添加到您的Workspace中。 - 将libAppAuth作为链接库包含到您的目标中(在您的目标的“General -> Linked Framework and Libraries”部分)。
- 将
AppAuth-iOS/Source
添加到您目标的支持的搜索路径中(在“Build Settings -> Header Search Paths”)。
认证流程
AppAuth支持您手动与授权服务器交互,进行您的令牌交换,以及为您执行一些此逻辑的便利方法。此示例使用便利方法,可返回一个OIDAuthState
对象或错误。
OIDAuthState
是一个类,用于跟踪授权和令牌的请求和响应,并提供了方便的方法来使用新鲜令牌调用 API。这是您需要序列化的唯一对象,以保留会话的授权状态。
配置
您可以通过指定端点直接配置 AppAuth
NSURL *authorizationEndpoint =
[NSURL URLWithString:@"https://#/o/oauth2/v2/auth"];
NSURL *tokenEndpoint =
[NSURL URLWithString:@"https://www.googleapis.com/oauth2/v4/token"];
OIDServiceConfiguration *configuration =
[[OIDServiceConfiguration alloc]
initWithAuthorizationEndpoint:authorizationEndpoint
tokenEndpoint:tokenEndpoint];
// perform the auth request...
或者通过发现
NSURL *issuer = [NSURL URLWithString:@"https://#"];
[OIDAuthorizationService discoverServiceConfigurationForIssuer:issuer
completion:^(OIDServiceConfiguration *_Nullable configuration,
NSError *_Nullable error) {
if (!configuration) {
NSLog(@"Error retrieving discovery document: %@",
[error localizedDescription]);
return;
}
// perform the auth request...
}];
授权 – iOS
首先,您需要在 AppDelegate 中有一个属性来保存会话,以便从重定向继续授权流程。
// property of the app's AppDelegate
@property(nonatomic, strong, nullable)
id<OIDExternalUserAgentSession> currentAuthorizationFlow;
并在主类中,一个属性来存储授权状态
// property of the containing class
@property(nonatomic, strong, nullable) OIDAuthState *authState;
然后,发起授权请求。通过使用 authStateByPresentingAuthorizationRequest
便捷方法,将自动执行令牌交换,并且所有内容都将使用 PKCE(如果服务器支持)进行保护。AppAuth 还允许您手动执行这些请求。请参见包含的示例应用中的 authNoCodeExchange
方法以进行演示。
// builds authentication request
OIDAuthorizationRequest *request =
[[OIDAuthorizationRequest alloc] initWithConfiguration:configuration
clientId:kClientID
scopes:@[OIDScopeOpenID,
OIDScopeProfile]
redirectURL:KRedirectURI
responseType:OIDResponseTypeCode
additionalParameters:nil];
// performs authentication request
AppDelegate *appDelegate =
(AppDelegate *)[UIApplication sharedApplication].delegate;
appDelegate.currentAuthorizationFlow =
[OIDAuthState authStateByPresentingAuthorizationRequest:request
presentingViewController:self
callback:^(OIDAuthState *_Nullable authState,
NSError *_Nullable error) {
if (authState) {
NSLog(@"Got authorization tokens. Access token: %@",
authState.lastTokenResponse.accessToken);
[self setAuthState:authState];
} else {
NSLog(@"Authorization error: %@", [error localizedDescription]);
[self setAuthState:nil];
}
}];
处理重定向
授权响应 URL 通过 iOS openURL 应用程序代理方法返回到应用,因此您需要将其通过当前授权会话(在之前的会话中创建)。
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary<NSString *, id> *)options {
// Sends the URL to the current authorization flow (if any) which will
// process it if it relates to an authorization response.
if ([_currentAuthorizationFlow resumeExternalUserAgentFlowWithURL:url]) {
_currentAuthorizationFlow = nil;
return YES;
}
// Your additional URL handling (if any) goes here.
return NO;
}
授权 – MacOS
在 macOS 上,获取授权响应重定向最流行的方式是在回环接口上启动本地 HTTP 服务器(仅限于来自用户机器的请求)。授权完成后,用户将被重定向到该本地服务器,应用可以对授权响应进行处理。AppAuth 将负责为您管理本地 HTTP 服务器生命周期。
💡 替代方案:自定义 URI 协议自定义 URI 协议也支持 macOS,但某些浏览器会显示一个拦截页,这降低了可用性。有关在 macOS 上使用自定义 URI 协议的示例,请参见
Example-Mac
。
要使用本地 HTTP 服务器接收授权响应,首先您需要主类中的一个实例变量来保存 HTTP 重定向处理程序。
OIDRedirectHTTPHandler *_redirectHTTPHandler;
然后,由于本地 HTTP 服务器使用的端口可能会有所不同,您需要在构建授权请求之前启动它,以便获取要使用的确切重定向 URI。
static NSString *const kSuccessURLString =
@"http://openid.github.io/AppAuth-iOS/redirect/";
NSURL *successURL = [NSURL URLWithString:kSuccessURLString];
// Starts a loopback HTTP redirect listener to receive the code. This needs to be started first,
// as the exact redirect URI (including port) must be passed in the authorization request.
_redirectHTTPHandler = [[OIDRedirectHTTPHandler alloc] initWithSuccessURL:successURL];
NSURL *redirectURI = [_redirectHTTPHandler startHTTPListener:nil];
然后,发起授权请求。通过使用 authStateByPresentingAuthorizationRequest
便捷方法,将自动执行令牌交换,并且所有内容都将使用 PKCE(如果服务器支持)进行保护。通过将返回值分配给 OIDRedirectHTTPHandler
的 currentAuthorizationFlow
,一旦用户做出选择,授权将自动继续。
// builds authentication request
OIDAuthorizationRequest *request =
[[OIDAuthorizationRequest alloc] initWithConfiguration:configuration
clientId:kClientID
clientSecret:kClientSecret
scopes:@[ OIDScopeOpenID ]
redirectURL:redirectURI
responseType:OIDResponseTypeCode
additionalParameters:nil];
// performs authentication request
__weak __typeof(self) weakSelf = self;
_redirectHTTPHandler.currentAuthorizationFlow =
[OIDAuthState authStateByPresentingAuthorizationRequest:request
callback:^(OIDAuthState *_Nullable authState,
NSError *_Nullable error) {
// Brings this app to the foreground.
[[NSRunningApplication currentApplication]
activateWithOptions:(NSApplicationActivateAllWindows |
NSApplicationActivateIgnoringOtherApps)];
// Processes the authorization response.
if (authState) {
NSLog(@"Got authorization tokens. Access token: %@",
authState.lastTokenResponse.accessToken);
} else {
NSLog(@"Authorization error: %@", error.localizedDescription);
}
[weakSelf setAuthState:authState];
}];
调用API
AppAuth为您提供原始令牌信息,如果您需要的话。但是我们建议使用OIDAuthState
便捷包装类的问题使用提供的performActionWithFreshTokens:
方法来执行API调用,以避免担心令牌的有效性。
[_authState performActionWithFreshTokens:^(NSString *_Nonnull accessToken,
NSString *_Nonnull idToken,
NSError *_Nullable error) {
if (error) {
NSLog(@"Error fetching fresh tokens: %@", [error localizedDescription]);
return;
}
// perform your API request using the tokens
}];
API文档
浏览API文档。
包含的示例
探索AppAuth核心功能的示例应用程序适用于iOS和macOS,按照Examples/README.md中的说明开始。