| Index: remoting/client/ios/facade/remoting_authentication.mm
|
| diff --git a/remoting/client/ios/facade/remoting_authentication.mm b/remoting/client/ios/facade/remoting_authentication.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5abe68fbf6d33602f68c2ece40b8414f8af6b504
|
| --- /dev/null
|
| +++ b/remoting/client/ios/facade/remoting_authentication.mm
|
| @@ -0,0 +1,195 @@
|
| +// 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.
|
| +
|
| +#if !defined(__has_feature) || !__has_feature(objc_arc)
|
| +#error "This file requires ARC support."
|
| +#endif
|
| +
|
| +#import "remoting/client/ios/facade/remoting_authentication.h"
|
| +
|
| +#import <Foundation/Foundation.h>
|
| +#import <Security/Security.h>
|
| +
|
| +#import "base/mac/bind_objc_block.h"
|
| +#import "remoting/client/ios/facade/host_info.h"
|
| +#import "remoting/client/ios/facade/host_list_fetcher.h"
|
| +#import "remoting/client/ios/facade/ios_client_runtime_delegate.h"
|
| +#import "remoting/client/ios/facade/remoting_service.h"
|
| +#import "remoting/client/ios/keychain_wrapper.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"
|
| +
|
| +static NSString* const kCRDAuthenticatedUserEmailKey =
|
| + @"kCRDAuthenticatedUserEmailKey";
|
| +
|
| +const char kOauthRedirectUrl[] =
|
| + "https://chromoting-oauth.talkgadget."
|
| + "google.com/talkgadget/oauth/chrome-remote-desktop/dev";
|
| +
|
| +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 SharedInstance].runtime->url_requester(),
|
| + /*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 SharedInstance].runtime->url_requester(),
|
| + /*auto_refresh=*/true));
|
| + return oauth_tokenGetter;
|
| +}
|
| +
|
| +@interface RemotingAuthentication () {
|
| + std::unique_ptr<remoting::OAuthTokenGetter> _tokenGetter;
|
| + KeychainWrapper* _keychainWrapper;
|
| + BOOL _firstLoadUserAttempt;
|
| +}
|
| +@end
|
| +
|
| +@implementation RemotingAuthentication
|
| +
|
| +@synthesize user = _user;
|
| +@synthesize delegate = _delegate;
|
| +
|
| +- (instancetype)init {
|
| + self = [super init];
|
| + if (self) {
|
| + _keychainWrapper = [[KeychainWrapper alloc] init];
|
| + _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 RemotingAuthentication* weakSelf = self;
|
| + _tokenGetter = CreateOAuthTokenGetterWithAuthorizationCode(
|
| + std::string(base::SysNSStringToUTF8(authorizationCode)),
|
| + base::BindBlockArc(
|
| + ^(const std::string& user_email, const std::string& refresh_token) {
|
| + // TODO(nicholss): Do something with these new creds.
|
| + 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 automaticly in the above block.
|
| + [self callbackWithAccessToken:base::BindBlockArc(^(
|
| + remoting::OAuthTokenGetter::Status status,
|
| + const std::string& user_email,
|
| + const std::string& access_token) {
|
| + if (status == remoting::OAuthTokenGetter::Status::SUCCESS) {
|
| + VLOG(1) << "Success fetching access token from authorization code.";
|
| + } else {
|
| + LOG(ERROR)
|
| + << "Failed to fetch access token from authorization code. ("
|
| + << status << ")";
|
| + // TODO(nicholss): Deal with the sad path for a bad auth token.
|
| + }
|
| + })];
|
| +}
|
| +
|
| +#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:
|
| + (const remoting::OAuthTokenGetter::TokenCallback&)onAccessToken {
|
| + // TODO(nicholss): Be careful here since a failure to reset onAccessToken
|
| + // will end up with retain cycle and memory leakage.
|
| + if (_tokenGetter) {
|
| + _tokenGetter->CallWithToken(onAccessToken);
|
| + }
|
| +}
|
| +
|
| +- (void)logout {
|
| + [self storeUserInfo:nil];
|
| + [self setUser:nil];
|
| +}
|
| +
|
| +#pragma mark - Persistence
|
| +
|
| +- (void)storeUserInfo:(UserInfo*)user {
|
| + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
|
| + if (user) {
|
| + [defaults setObject:user.userEmail forKey:kCRDAuthenticatedUserEmailKey];
|
| + // TODO(nicholss): Need to match the token with the email.
|
| + [_keychainWrapper setRefreshToken:user.refreshToken];
|
| + } else {
|
| + [defaults removeObjectForKey:kCRDAuthenticatedUserEmailKey];
|
| + [_keychainWrapper resetKeychainItem];
|
| + }
|
| + [defaults synchronize];
|
| +}
|
| +
|
| +- (UserInfo*)loadUserInfo {
|
| + UserInfo* user = [[UserInfo alloc] init];
|
| + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
|
| + user.userEmail = [defaults objectForKey:kCRDAuthenticatedUserEmailKey];
|
| + // TODO(nicholss): Need to match the token with the email.
|
| + user.refreshToken = [_keychainWrapper refreshToken];
|
| +
|
| + if (!user || ![user isAuthenticated]) {
|
| + user = nil;
|
| + } else {
|
| + [self authenticateWithRefreshToken:user.refreshToken email:user.userEmail];
|
| + }
|
| + return user;
|
| +}
|
| +
|
| +@end
|
|
|