123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- // Copyright 2017 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #import "remoting/ios/facade/remoting_oauth_authentication.h"
- #import <Foundation/Foundation.h>
- #import <Security/Security.h>
- #import <MaterialComponents/MaterialSnackbar.h>
- #import "base/bind.h"
- #import "remoting/ios/facade/ios_client_runtime_delegate.h"
- #import "remoting/ios/facade/remoting_service.h"
- #import "remoting/ios/persistence/remoting_keychain.h"
- #import "remoting/ios/persistence/remoting_preferences.h"
- #include "base/logging.h"
- #include "base/strings/sys_string_conversions.h"
- #include "net/url_request/url_request_context_getter.h"
- #include "remoting/base/oauth_token_getter.h"
- #include "remoting/base/oauth_token_getter_impl.h"
- #include "services/network/public/cpp/shared_url_loader_factory.h"
- #if !defined(__has_feature) || !__has_feature(objc_arc)
- #error "This file requires ARC support."
- #endif
- static const char kOauthRedirectUrl[] =
- "https://chromoting-oauth.talkgadget."
- "google.com/talkgadget/oauth/chrome-remote-desktop/dev";
- // We currently don't support multi-account sign in for OAuth authentication, so
- // we store the current refresh token for an unspecified account. If we later
- // decide to support multi-account sign in, we may use the user email as the
- // account name when storing the refresh token and store the current user email
- // in UserDefaults.
- static const auto kRefreshTokenAccount =
- remoting::Keychain::kUnspecifiedAccount;
- std::unique_ptr<remoting::OAuthTokenGetter>
- CreateOAuthTokenGetterWithAuthorizationCode(
- const std::string& auth_code,
- const remoting::OAuthTokenGetter::CredentialsUpdatedCallback&
- on_credentials_update) {
- std::unique_ptr<remoting::OAuthTokenGetter::OAuthIntermediateCredentials>
- oauth_credentials(
- new remoting::OAuthTokenGetter::OAuthIntermediateCredentials(
- auth_code, /*is_service_account=*/false));
- oauth_credentials->oauth_redirect_uri = kOauthRedirectUrl;
- std::unique_ptr<remoting::OAuthTokenGetter> oauth_tokenGetter(
- new remoting::OAuthTokenGetterImpl(
- std::move(oauth_credentials), on_credentials_update,
- RemotingService.instance.runtime->url_loader_factory(),
- /*auto_refresh=*/true));
- return oauth_tokenGetter;
- }
- std::unique_ptr<remoting::OAuthTokenGetter> CreateOAuthTokenWithRefreshToken(
- const std::string& refresh_token,
- const std::string& email) {
- std::unique_ptr<remoting::OAuthTokenGetter::OAuthAuthorizationCredentials>
- oauth_credentials(
- new remoting::OAuthTokenGetter::OAuthAuthorizationCredentials(
- email, refresh_token, /*is_service_account=*/false));
- std::unique_ptr<remoting::OAuthTokenGetter> oauth_tokenGetter(
- new remoting::OAuthTokenGetterImpl(
- std::move(oauth_credentials),
- RemotingService.instance.runtime->url_loader_factory(),
- /*auto_refresh=*/true));
- return oauth_tokenGetter;
- }
- RemotingAuthenticationStatus oauthStatusToRemotingAuthenticationStatus(
- remoting::OAuthTokenGetter::Status status) {
- switch (status) {
- case remoting::OAuthTokenGetter::Status::AUTH_ERROR:
- return RemotingAuthenticationStatusAuthError;
- case remoting::OAuthTokenGetter::Status::NETWORK_ERROR:
- return RemotingAuthenticationStatusNetworkError;
- case remoting::OAuthTokenGetter::Status::SUCCESS:
- return RemotingAuthenticationStatusSuccess;
- }
- }
- @interface RemotingOAuthAuthentication () {
- std::unique_ptr<remoting::OAuthTokenGetter> _tokenGetter;
- BOOL _firstLoadUserAttempt;
- }
- @end
- @implementation RemotingOAuthAuthentication
- @synthesize user = _user;
- @synthesize delegate = _delegate;
- - (instancetype)init {
- self = [super init];
- if (self) {
- _user = nil;
- _firstLoadUserAttempt = YES;
- }
- return self;
- }
- #pragma mark - Property Overrides
- - (UserInfo*)user {
- if (_firstLoadUserAttempt && _user == nil) {
- _firstLoadUserAttempt = NO;
- [self setUser:[self loadUserInfo]];
- }
- return _user;
- }
- - (void)setUser:(UserInfo*)user {
- _user = user;
- [self storeUserInfo:_user];
- [_delegate userDidUpdate:_user];
- }
- #pragma mark - Class Implementation
- - (void)authenticateWithAuthorizationCode:(NSString*)authorizationCode {
- __weak RemotingOAuthAuthentication* weakSelf = self;
- _tokenGetter = CreateOAuthTokenGetterWithAuthorizationCode(
- std::string(base::SysNSStringToUTF8(authorizationCode)),
- base::BindRepeating(
- ^(const std::string& user_email, const std::string& refresh_token) {
- VLOG(1) << "New Creds: " << user_email << " " << refresh_token;
- UserInfo* user = [[UserInfo alloc] init];
- user.userEmail = base::SysUTF8ToNSString(user_email);
- user.refreshToken = base::SysUTF8ToNSString(refresh_token);
- [weakSelf setUser:user];
- }));
- // Stimulate the oAuth Token Getter to fetch and access token, this forces it
- // to convert the authorization code into a refresh token, and saving the
- // refresh token will happen automatically in the above block.
- [self callbackWithAccessToken:^(RemotingAuthenticationStatus status,
- NSString* user_email,
- NSString* access_token) {
- if (status == RemotingAuthenticationStatusSuccess) {
- VLOG(1) << "Success fetching access token from authorization code.";
- } else {
- LOG(ERROR) << "Failed to fetch access token from authorization code. ("
- << status << ")";
- [MDCSnackbarManager.defaultManager
- showMessage:
- [MDCSnackbarMessage
- messageWithText:@"Authentication Failed. Please try again."]];
- }
- }];
- }
- #pragma mark - Private
- // Provide the |refreshToken| and |email| to authenticate a user as a returning
- // user of the application.
- - (void)authenticateWithRefreshToken:(NSString*)refreshToken
- email:(NSString*)email {
- _tokenGetter = CreateOAuthTokenWithRefreshToken(
- std::string(base::SysNSStringToUTF8(refreshToken)),
- base::SysNSStringToUTF8(email));
- }
- - (void)callbackWithAccessToken:(AccessTokenCallback)onAccessToken {
- // Be careful here since a failure to reset onAccessToken will end up with
- // retain cycle and memory leakage.
- if (_tokenGetter) {
- _tokenGetter->CallWithToken(base::BindOnce(
- ^(remoting::OAuthTokenGetter::Status status,
- const std::string& user_email, const std::string& access_token) {
- onAccessToken(oauthStatusToRemotingAuthenticationStatus(status),
- base::SysUTF8ToNSString(user_email),
- base::SysUTF8ToNSString(access_token));
- }));
- }
- }
- - (void)logout {
- [self storeUserInfo:nil];
- [self setUser:nil];
- }
- - (void)invalidateCache {
- _tokenGetter->InvalidateCache();
- }
- #pragma mark - Persistence
- - (void)storeUserInfo:(UserInfo*)user {
- if (user) {
- [RemotingPreferences instance].activeUserKey = user.userEmail;
- std::string refreshToken = base::SysNSStringToUTF8(user.refreshToken);
- remoting::RemotingKeychain::GetInstance()->SetData(
- remoting::Keychain::Key::REFRESH_TOKEN, kRefreshTokenAccount,
- refreshToken);
- } else {
- [RemotingPreferences instance].activeUserKey = nil;
- remoting::RemotingKeychain::GetInstance()->RemoveData(
- remoting::Keychain::Key::REFRESH_TOKEN, kRefreshTokenAccount);
- }
- }
- - (UserInfo*)loadUserInfo {
- UserInfo* user = [[UserInfo alloc] init];
- user.userEmail = [RemotingPreferences instance].activeUserKey;
- std::string refreshTokenString =
- remoting::RemotingKeychain::GetInstance()->GetData(
- remoting::Keychain::Key::REFRESH_TOKEN, kRefreshTokenAccount);
- user.refreshToken = base::SysUTF8ToNSString(refreshTokenString);
- if (!user || ![user isAuthenticated]) {
- user = nil;
- } else {
- [self authenticateWithRefreshToken:user.refreshToken email:user.userEmail];
- }
- return user;
- }
- @end
|