通过AppAuth iOS理解Google OIDC服务

转载于:http://www.jianshu.com/p/00ae475fb041

通过AppAuth-iOS体验Google OIDC服务非常容易,只需要配置下面三个配置项。

static NSString *const kIssuer = @"https://accounts.google.com";

static NSString *const kClientID = @"24408797720-p6d3uj34k564kl4s85o2d1rdgvlp7nef.apps.googleusercontent.com";

static NSString *const kRedirectURI = @"com.googleusercontent.apps.24408797720-p6d3uj34k564kl4s85o2d1rdgvlp7nef:/oauth2redirect/";

通过https://accounts.google.com/.well-known/openid-configuration可以获取到Google Auth所有的相关信息,非常丰富的信息。这个配置文件的详细介绍可以参考:OpenID Connect Discovery 1.0 incorporating errata set 1

从配置信息可以看出Google把auth和login放在accounts.google.com域下面,token单独放在www.googleapis.com域下面。


Paste_Image.png

接下来看看如何发起授权。构造OIDAuthorizationRequest请求之后,使用In-App browser(iOS使用SFSafariViewController)打开相应的URL。

// builds authentication request
OIDAuthorizationRequest *request =
  [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration
                                                clientId:clientID
                                            clientSecret:clientSecret
                                                  scopes:@[ OIDScopeOpenID, OIDScopeProfile ]
                                             redirectURL:redirectURI
                                            responseType:OIDResponseTypeCode
                                    additionalParameters:nil];
// performs authentication request
AppDelegate *appDelegate = (AppDelegate *) [UIApplication sharedApplication].delegate;
[self logMessage:@"Initiating authorization request %@", request];
appDelegate.currentAuthorizationFlow =
  [OIDAuthorizationService presentAuthorizationRequest:request
      presentingViewController:self
                      callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse,
                                 NSError *_Nullable error) {
    if (authorizationResponse) {
      OIDAuthState *authState =
          [[OIDAuthState alloc] initWithAuthorizationResponse:authorizationResponse];
      [self setAuthState:authState];

      [self logMessage:@"Authorization response with code: %@",
                       authorizationResponse.authorizationCode];
      // could just call [self tokenExchange:nil] directly, but will let the user initiate it.
    } else {
      [self logMessage:@"Authorization error: %@", [error localizedDescription]];
    }
  }];

- (SFSafariViewController *)safariViewControllerWithURL:(NSURL *)URL {
  SFSafariViewController *safariViewController =
      [[SFSafariViewController alloc] initWithURL:URL entersReaderIfAvailable:NO];
  return safariViewController;
}

本来我还想将SFSafariViewController改成UIWebView,以观察所有URL的流转关系。结果没法使用UIWebView,Google发现是UIWebView会直接报错。


Paste_Image.png

Google Auth返回的id_token已经包含了用户信息,但是还是提供一个单独的userinfo_endpoint去换取用户信息。


Paste_Image.png

手动authorize流程如下所示。

11:09:02: Fetching configuration for issuer: https://accounts.google.com
11:09:02: Got configuration: OIDServiceConfiguration authorizationEndpoint: https://accounts.google.com/o/oauth2/v2/auth, tokenEndpoint: https://www.googleapis.com/oauth2/v4/token, registrationEndpoint: (null), discoveryDocument: [<OIDServiceDiscovery: 0x608000012970>]

11:09:02: Initiating authorization request <OIDAuthorizationRequest: 0x6000000ac180, request: 

//第一步,获取auth code,参数都在URL里面。
https://accounts.google.com/o/oauth2/v2/auth?
response_type=code
&code_challenge_method=S256
&scope=openid%20profile
&code_challenge=zkJhG3SZa9vHx8P8TvikiEozUDNQwlJXnlskEe0wJGA
&redirect_uri=com.googleusercontent.apps.24408797720-p6d3uj34k564kl4s85o2d1rdgvlp7nef:/oauth2redirect/
&client_id=24408797720-p6d3uj34k564kl4s85o2d1rdgvlp7nef.apps.googleusercontent.com
&state=KX_4c-UQCxrshPrtHD75CLPoj80gipzxTr1r2hGNhus>

//拿到auth code
11:09:51: Authorization response with code: 4/SS-PVx_JADts0XLrN4VDQiK5QUOTd0qvKtYO_UZJO2E

//第二步,获取access token和refresh token,参数都通过post发出去。
//Google Auth比较有特色的一点是没有client_secret
11:09:52: Performing authorization code exchange with request [<OIDTokenRequest: 0x6080000ac360, request: <URL: https://www.googleapis.com/oauth2/v4/token, 
HTTPBody: code=4/SS-PVx_JADts0XLrN4VDQiK5QUOTd0qvKtYO_UZJO2E
&code_verifier=9zUuBzJkfD4y8Ei-424Cx-lWIwObhnSbC5_dOGZXSCk
&redirect_uri=com.googleusercontent.apps.24408797720-p6d3uj34k564kl4s85o2d1rdgvlp7nef:/oauth2redirect/
&client_id=24408797720-p6d3uj34k564kl4s85o2d1rdgvlp7nef.apps.googleusercontent.com
&grant_type=authorization_code>>]

11:09:53: Received token response with accessToken: ya29.GltBBBYWx0rhh87WWK1oV0peHCI9_EwwoWWf4lUiwBc_--bTXv_Ag6OWdBU6SAHhYx5O6RdOsX1HMCPcELfeIlw-5rESYDuGmyRTqAuchGbHl1Ws-hC10O80YAmi

上面的参数中,code_challenge_methodcode_challengecode_verifier这三个看起来很神秘。它们是为了避免auth code被拦截而做了保护措施。第一步通过code_challenge_methodcode_verifier做一次计算得到code_challenge。Authorization Endpoint会保存code_challenge相关的信息。第二步直接把code_verifier传递给token endpoint,服务器端做一次对比。如果对不上,就报错。详细信息请参看:Proof Key for Code Exchange by OAuth Public Clients


Paste_Image.png

Paste_Image.png

继续往下分析一下state这个参数的作用吧。state参数是为了防止CSRF攻击,详细信息请参看:OAuth2:忽略 state 参数引发的 csrf 漏洞 #68。In-App browser回跳到App时,要检查一下URL里面的state是否跟之前的state一致。


Paste_Image.png

OpenID Connect标准集里面包含一个Dynamic Client Registration的标准,用于动态注册客户端,不过我目前还没有看到哪个OIDC服务器的Discovery里面有registration_endpoint,包括Google OIDC服务器。

AppAuth客户端是支持这个特性的,很多地方都会判断是否配置了client_id。如果没有配置的话,那么通过Dynamic Client Registration注册一个客户端。

if (!kClientID) {
  [self doClientRegistration:configuration
                    callback:^(OIDServiceConfiguration *configuration,
                               OIDRegistrationResponse *registrationResponse) {
    [self doAuthWithAutoCodeExchange:configuration
                            clientID:registrationResponse.clientID
                        clientSecret:registrationResponse.clientSecret];
  }];
} else {
  [self doAuthWithAutoCodeExchange:configuration clientID:kClientID clientSecret:nil];
}
}];

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值