remoting_oauth_authentication.mm 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #import "remoting/ios/facade/remoting_oauth_authentication.h"
  5. #import <Foundation/Foundation.h>
  6. #import <Security/Security.h>
  7. #import <MaterialComponents/MaterialSnackbar.h>
  8. #import "base/bind.h"
  9. #import "remoting/ios/facade/ios_client_runtime_delegate.h"
  10. #import "remoting/ios/facade/remoting_service.h"
  11. #import "remoting/ios/persistence/remoting_keychain.h"
  12. #import "remoting/ios/persistence/remoting_preferences.h"
  13. #include "base/logging.h"
  14. #include "base/strings/sys_string_conversions.h"
  15. #include "net/url_request/url_request_context_getter.h"
  16. #include "remoting/base/oauth_token_getter.h"
  17. #include "remoting/base/oauth_token_getter_impl.h"
  18. #include "services/network/public/cpp/shared_url_loader_factory.h"
  19. #if !defined(__has_feature) || !__has_feature(objc_arc)
  20. #error "This file requires ARC support."
  21. #endif
  22. static const char kOauthRedirectUrl[] =
  23. "https://chromoting-oauth.talkgadget."
  24. "google.com/talkgadget/oauth/chrome-remote-desktop/dev";
  25. // We currently don't support multi-account sign in for OAuth authentication, so
  26. // we store the current refresh token for an unspecified account. If we later
  27. // decide to support multi-account sign in, we may use the user email as the
  28. // account name when storing the refresh token and store the current user email
  29. // in UserDefaults.
  30. static const auto kRefreshTokenAccount =
  31. remoting::Keychain::kUnspecifiedAccount;
  32. std::unique_ptr<remoting::OAuthTokenGetter>
  33. CreateOAuthTokenGetterWithAuthorizationCode(
  34. const std::string& auth_code,
  35. const remoting::OAuthTokenGetter::CredentialsUpdatedCallback&
  36. on_credentials_update) {
  37. std::unique_ptr<remoting::OAuthTokenGetter::OAuthIntermediateCredentials>
  38. oauth_credentials(
  39. new remoting::OAuthTokenGetter::OAuthIntermediateCredentials(
  40. auth_code, /*is_service_account=*/false));
  41. oauth_credentials->oauth_redirect_uri = kOauthRedirectUrl;
  42. std::unique_ptr<remoting::OAuthTokenGetter> oauth_tokenGetter(
  43. new remoting::OAuthTokenGetterImpl(
  44. std::move(oauth_credentials), on_credentials_update,
  45. RemotingService.instance.runtime->url_loader_factory(),
  46. /*auto_refresh=*/true));
  47. return oauth_tokenGetter;
  48. }
  49. std::unique_ptr<remoting::OAuthTokenGetter> CreateOAuthTokenWithRefreshToken(
  50. const std::string& refresh_token,
  51. const std::string& email) {
  52. std::unique_ptr<remoting::OAuthTokenGetter::OAuthAuthorizationCredentials>
  53. oauth_credentials(
  54. new remoting::OAuthTokenGetter::OAuthAuthorizationCredentials(
  55. email, refresh_token, /*is_service_account=*/false));
  56. std::unique_ptr<remoting::OAuthTokenGetter> oauth_tokenGetter(
  57. new remoting::OAuthTokenGetterImpl(
  58. std::move(oauth_credentials),
  59. RemotingService.instance.runtime->url_loader_factory(),
  60. /*auto_refresh=*/true));
  61. return oauth_tokenGetter;
  62. }
  63. RemotingAuthenticationStatus oauthStatusToRemotingAuthenticationStatus(
  64. remoting::OAuthTokenGetter::Status status) {
  65. switch (status) {
  66. case remoting::OAuthTokenGetter::Status::AUTH_ERROR:
  67. return RemotingAuthenticationStatusAuthError;
  68. case remoting::OAuthTokenGetter::Status::NETWORK_ERROR:
  69. return RemotingAuthenticationStatusNetworkError;
  70. case remoting::OAuthTokenGetter::Status::SUCCESS:
  71. return RemotingAuthenticationStatusSuccess;
  72. }
  73. }
  74. @interface RemotingOAuthAuthentication () {
  75. std::unique_ptr<remoting::OAuthTokenGetter> _tokenGetter;
  76. BOOL _firstLoadUserAttempt;
  77. }
  78. @end
  79. @implementation RemotingOAuthAuthentication
  80. @synthesize user = _user;
  81. @synthesize delegate = _delegate;
  82. - (instancetype)init {
  83. self = [super init];
  84. if (self) {
  85. _user = nil;
  86. _firstLoadUserAttempt = YES;
  87. }
  88. return self;
  89. }
  90. #pragma mark - Property Overrides
  91. - (UserInfo*)user {
  92. if (_firstLoadUserAttempt && _user == nil) {
  93. _firstLoadUserAttempt = NO;
  94. [self setUser:[self loadUserInfo]];
  95. }
  96. return _user;
  97. }
  98. - (void)setUser:(UserInfo*)user {
  99. _user = user;
  100. [self storeUserInfo:_user];
  101. [_delegate userDidUpdate:_user];
  102. }
  103. #pragma mark - Class Implementation
  104. - (void)authenticateWithAuthorizationCode:(NSString*)authorizationCode {
  105. __weak RemotingOAuthAuthentication* weakSelf = self;
  106. _tokenGetter = CreateOAuthTokenGetterWithAuthorizationCode(
  107. std::string(base::SysNSStringToUTF8(authorizationCode)),
  108. base::BindRepeating(
  109. ^(const std::string& user_email, const std::string& refresh_token) {
  110. VLOG(1) << "New Creds: " << user_email << " " << refresh_token;
  111. UserInfo* user = [[UserInfo alloc] init];
  112. user.userEmail = base::SysUTF8ToNSString(user_email);
  113. user.refreshToken = base::SysUTF8ToNSString(refresh_token);
  114. [weakSelf setUser:user];
  115. }));
  116. // Stimulate the oAuth Token Getter to fetch and access token, this forces it
  117. // to convert the authorization code into a refresh token, and saving the
  118. // refresh token will happen automatically in the above block.
  119. [self callbackWithAccessToken:^(RemotingAuthenticationStatus status,
  120. NSString* user_email,
  121. NSString* access_token) {
  122. if (status == RemotingAuthenticationStatusSuccess) {
  123. VLOG(1) << "Success fetching access token from authorization code.";
  124. } else {
  125. LOG(ERROR) << "Failed to fetch access token from authorization code. ("
  126. << status << ")";
  127. [MDCSnackbarManager.defaultManager
  128. showMessage:
  129. [MDCSnackbarMessage
  130. messageWithText:@"Authentication Failed. Please try again."]];
  131. }
  132. }];
  133. }
  134. #pragma mark - Private
  135. // Provide the |refreshToken| and |email| to authenticate a user as a returning
  136. // user of the application.
  137. - (void)authenticateWithRefreshToken:(NSString*)refreshToken
  138. email:(NSString*)email {
  139. _tokenGetter = CreateOAuthTokenWithRefreshToken(
  140. std::string(base::SysNSStringToUTF8(refreshToken)),
  141. base::SysNSStringToUTF8(email));
  142. }
  143. - (void)callbackWithAccessToken:(AccessTokenCallback)onAccessToken {
  144. // Be careful here since a failure to reset onAccessToken will end up with
  145. // retain cycle and memory leakage.
  146. if (_tokenGetter) {
  147. _tokenGetter->CallWithToken(base::BindOnce(
  148. ^(remoting::OAuthTokenGetter::Status status,
  149. const std::string& user_email, const std::string& access_token) {
  150. onAccessToken(oauthStatusToRemotingAuthenticationStatus(status),
  151. base::SysUTF8ToNSString(user_email),
  152. base::SysUTF8ToNSString(access_token));
  153. }));
  154. }
  155. }
  156. - (void)logout {
  157. [self storeUserInfo:nil];
  158. [self setUser:nil];
  159. }
  160. - (void)invalidateCache {
  161. _tokenGetter->InvalidateCache();
  162. }
  163. #pragma mark - Persistence
  164. - (void)storeUserInfo:(UserInfo*)user {
  165. if (user) {
  166. [RemotingPreferences instance].activeUserKey = user.userEmail;
  167. std::string refreshToken = base::SysNSStringToUTF8(user.refreshToken);
  168. remoting::RemotingKeychain::GetInstance()->SetData(
  169. remoting::Keychain::Key::REFRESH_TOKEN, kRefreshTokenAccount,
  170. refreshToken);
  171. } else {
  172. [RemotingPreferences instance].activeUserKey = nil;
  173. remoting::RemotingKeychain::GetInstance()->RemoveData(
  174. remoting::Keychain::Key::REFRESH_TOKEN, kRefreshTokenAccount);
  175. }
  176. }
  177. - (UserInfo*)loadUserInfo {
  178. UserInfo* user = [[UserInfo alloc] init];
  179. user.userEmail = [RemotingPreferences instance].activeUserKey;
  180. std::string refreshTokenString =
  181. remoting::RemotingKeychain::GetInstance()->GetData(
  182. remoting::Keychain::Key::REFRESH_TOKEN, kRefreshTokenAccount);
  183. user.refreshToken = base::SysUTF8ToNSString(refreshTokenString);
  184. if (!user || ![user isAuthenticated]) {
  185. user = nil;
  186. } else {
  187. [self authenticateWithRefreshToken:user.refreshToken email:user.userEmail];
  188. }
  189. return user;
  190. }
  191. @end