AppAuth-logout 1.0.4

AppAuth-logout 1.0.4

Yurii KolesnykovJeff Decker 维护。



  • Yurii Kolesnykov、William Denniss 和 Steven E Wright

AppAuth for iOS and macOS Build Status Carthage compatible

AppAuth for iOS 和 macOS 是与 OAuth 2.0 和 OpenID Connect 提供商通信的客户端 SDK。它力图直接映射那些规范中的请求和响应,同时遵循实现语言的惯用风格。除了映射原始协议流程外,还提供便捷方法以帮助执行带新鲜令牌的操作等常见任务。

它遵循 RFC 8252 - OAuth 2.0 for Native Apps 中的最佳实践,包括在 iOS 上使用 SFAuthenticationSessionSFSafariViewController 进行授权请求。由于安全性和可用性原因(见 RFC 8252 第 8.12 节),显式不支持 UIWebViewWKWebView

它还支持 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作为静态库使用。这需要链接库和您的项目,并包含头文件。建议的配置

  1. 创建一个Xcode Workspace。
  2. AppAuth.xcodeproj添加到您的Workspace中。
  3. 将libAppAuth作为链接库包含到您的目标中(在您的目标的“General -> Linked Framework and Libraries”部分)。
  4. 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(如果服务器支持)进行保护。通过将返回值分配给 OIDRedirectHTTPHandlercurrentAuthorizationFlow,一旦用户做出选择,授权将自动继续。

// 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中的说明开始。