Add AuthManager::CreateAccessTokenFromAuth

In case of local auth client passes Macaroon token and device exchanges
that for access token.

BUG=25768507

Change-Id: Ibf126d9ef470cf7843deed6b0b954c99aa64e78d
Reviewed-on: https://weave-review.googlesource.com/2065
Reviewed-by: Vitaly Buka <vitalybuka@google.com>
diff --git a/src/privet/auth_manager.cc b/src/privet/auth_manager.cc
index 61f5a09..8df7e13 100644
--- a/src/privet/auth_manager.cc
+++ b/src/privet/auth_manager.cc
@@ -4,6 +4,7 @@
 
 #include "src/privet/auth_manager.h"
 
+#include <base/guid.h>
 #include <base/rand_util.h>
 #include <base/strings/string_number_conversions.h>
 
@@ -211,13 +212,13 @@
 bool AuthManager::ConfirmClientAuthToken(const std::vector<uint8_t>& token,
                                          ErrorPtr* error) {
   // Cover case when caller sent confirm twice.
-  if (pending_claims_.empty() && IsValidAuthToken(token))
-    return true;
+  if (pending_claims_.empty())
+    return IsValidAuthToken(token, error);
 
   auto claim =
       std::find_if(pending_claims_.begin(), pending_claims_.end(),
                    [&token](const decltype(pending_claims_)::value_type& auth) {
-                     return auth.first->IsValidAuthToken(token);
+                     return auth.first->IsValidAuthToken(token, nullptr);
                    });
   if (claim == pending_claims_.end()) {
     Error::AddTo(error, FROM_HERE, errors::kDomain, errors::kNotFound,
@@ -256,17 +257,55 @@
   return clock_->Now();
 }
 
-bool AuthManager::IsValidAuthToken(const std::vector<uint8_t>& token) const {
+bool AuthManager::IsValidAuthToken(const std::vector<uint8_t>& token,
+                                   ErrorPtr* error) const {
   std::vector<uint8_t> buffer(kMaxMacaroonSize);
   UwMacaroon macaroon{};
   if (!uw_macaroon_load_(token.data(), token.size(), buffer.data(),
                          buffer.size(), &macaroon)) {
+    Error::AddTo(error, FROM_HERE, errors::kDomain, errors::kInvalidAuthCode,
+                 "Invalid token format");
     return false;
   }
 
   CHECK_EQ(kSha256OutputSize, auth_secret_.size());
-  return uw_macaroon_verify_(&macaroon, auth_secret_.data(),
-                             auth_secret_.size());
+  if (!uw_macaroon_verify_(&macaroon, auth_secret_.data(),
+                           auth_secret_.size())) {
+    Error::AddTo(error, FROM_HERE, errors::kDomain, errors::kInvalidAuthCode,
+                 "Invalid token signature");
+    return false;
+  }
+  return true;
+}
+
+bool AuthManager::CreateAccessTokenFromAuth(
+    const std::vector<uint8_t>& auth_token,
+    base::TimeDelta ttl,
+    std::vector<uint8_t>* access_token,
+    AuthScope* access_token_scope,
+    base::TimeDelta* access_token_ttl,
+    ErrorPtr* error) const {
+  // TODO(vitalybuka): implement token validation.
+  if (!IsValidAuthToken(auth_token, error))
+    return false;
+
+  if (!access_token)
+    return true;
+
+  // TODO(vitalybuka): User and scope must be parsed from auth_token.
+  UserInfo info{config_ ? config_->GetSettings().local_anonymous_access_role
+                        : AuthScope::kViewer,
+                base::GenerateGUID()};
+
+  // TODO(vitalybuka): TTL also should be reduced in accordance with auth_token.
+  *access_token = CreateAccessToken(info, ttl);
+
+  if (access_token_scope)
+    *access_token_scope = info.scope();
+
+  if (access_token_ttl)
+    *access_token_ttl = ttl;
+  return true;
 }
 
 std::vector<uint8_t> AuthManager::CreateSessionId() {
diff --git a/src/privet/auth_manager.h b/src/privet/auth_manager.h
index 00d6e32..6a403b4 100644
--- a/src/privet/auth_manager.h
+++ b/src/privet/auth_manager.h
@@ -55,7 +55,14 @@
                               ErrorPtr* error);
 
   std::vector<uint8_t> GetRootClientAuthToken() const;
-  bool IsValidAuthToken(const std::vector<uint8_t>& token) const;
+  bool IsValidAuthToken(const std::vector<uint8_t>& token,
+                        ErrorPtr* error) const;
+  bool CreateAccessTokenFromAuth(const std::vector<uint8_t>& auth_token,
+                                 base::TimeDelta ttl,
+                                 std::vector<uint8_t>* access_token,
+                                 AuthScope* access_token_scope,
+                                 base::TimeDelta* access_token_ttl,
+                                 ErrorPtr* error) const;
 
   void SetAuthSecret(const std::vector<uint8_t>& secret,
                      RootClientTokenOwner owner);
diff --git a/src/privet/auth_manager_unittest.cc b/src/privet/auth_manager_unittest.cc
index 137e046..ce142c0 100644
--- a/src/privet/auth_manager_unittest.cc
+++ b/src/privet/auth_manager_unittest.cc
@@ -122,7 +122,6 @@
 
     auto token = auth.CreateAccessToken(UserInfo{AuthScope::kUser, "5"},
                                         base::TimeDelta::FromSeconds(i));
-    base::Time time2;
     UserInfo user_info;
     EXPECT_FALSE(auth_.ParseAccessToken(token, &user_info, nullptr));
     EXPECT_TRUE(auth.ParseAccessToken(token, &user_info, nullptr));
@@ -159,14 +158,14 @@
 }
 
 TEST_F(AuthManagerTest, IsValidAuthToken) {
-  EXPECT_TRUE(auth_.IsValidAuthToken(auth_.GetRootClientAuthToken()));
+  EXPECT_TRUE(auth_.IsValidAuthToken(auth_.GetRootClientAuthToken(), nullptr));
   // Multiple attempts with random secrets.
   for (size_t i = 0; i < 1000; ++i) {
     AuthManager auth{{}, {}, {}, &clock_};
 
     auto token = auth.GetRootClientAuthToken();
-    EXPECT_FALSE(auth_.IsValidAuthToken(token));
-    EXPECT_TRUE(auth.IsValidAuthToken(token));
+    EXPECT_FALSE(auth_.IsValidAuthToken(token, nullptr));
+    EXPECT_TRUE(auth.IsValidAuthToken(token, nullptr));
   }
 }
 
@@ -211,12 +210,12 @@
 TEST_F(AuthManagerClaimTest, NormalClaim) {
   auto token =
       auth_.ClaimRootClientAuthToken(RootClientTokenOwner::kCloud, nullptr);
-  EXPECT_FALSE(auth_.IsValidAuthToken(token));
+  EXPECT_FALSE(auth_.IsValidAuthToken(token, nullptr));
   EXPECT_EQ(RootClientTokenOwner::kNone,
             config_.GetSettings().root_client_token_owner);
 
   EXPECT_TRUE(auth_.ConfirmClientAuthToken(token, nullptr));
-  EXPECT_TRUE(auth_.IsValidAuthToken(token));
+  EXPECT_TRUE(auth_.IsValidAuthToken(token, nullptr));
   EXPECT_EQ(RootClientTokenOwner::kCloud,
             config_.GetSettings().root_client_token_owner);
 }
@@ -245,5 +244,18 @@
   EXPECT_FALSE(auth_.ConfirmClientAuthToken(token, nullptr));
 }
 
+TEST_F(AuthManagerClaimTest, CreateAccessTokenFromAuth) {
+  std::vector<uint8_t> access_token;
+  AuthScope scope;
+  base::TimeDelta ttl;
+  EXPECT_TRUE(auth_.CreateAccessTokenFromAuth(
+      auth_.GetRootClientAuthToken(), base::TimeDelta::FromDays(1),
+      &access_token, &scope, &ttl, nullptr));
+  UserInfo user_info;
+  EXPECT_TRUE(auth_.ParseAccessToken(access_token, &user_info, nullptr));
+  EXPECT_EQ(scope, user_info.scope());
+  EXPECT_FALSE(user_info.user_id().empty());
+}
+
 }  // namespace privet
 }  // namespace weave
diff --git a/src/privet/security_manager.cc b/src/privet/security_manager.cc
index bc8dc4f..caed476 100644
--- a/src/privet/security_manager.cc
+++ b/src/privet/security_manager.cc
@@ -111,23 +111,50 @@
     ClosePendingSession(pending_sessions_.begin()->first);
 }
 
-bool SecurityManager::CreateAccessToken(AuthType auth_type,
-                                        const std::string& auth_code,
-                                        AuthScope desired_scope,
-                                        std::string* access_token,
-                                        AuthScope* access_token_scope,
-                                        base::TimeDelta* access_token_ttl,
-                                        ErrorPtr* error) {
+bool SecurityManager::CreateAccessTokenImpl(AuthType auth_type,
+                                            AuthScope desired_scope,
+                                            std::vector<uint8_t>* access_token,
+                                            AuthScope* access_token_scope,
+                                            base::TimeDelta* access_token_ttl) {
+  UserInfo user_info{desired_scope,
+                     std::to_string(static_cast<int>(auth_type)) + "/" +
+                         std::to_string(++last_user_id_)};
+
+  const base::TimeDelta kTtl =
+      base::TimeDelta::FromSeconds(kAccessTokenExpirationSeconds);
+
+  if (access_token)
+    *access_token = auth_manager_->CreateAccessToken(user_info, kTtl);
+
+  if (access_token_scope)
+    *access_token_scope = user_info.scope();
+
+  if (access_token_ttl)
+    *access_token_ttl = kTtl;
+
+  return true;
+}
+
+bool SecurityManager::CreateAccessTokenImpl(
+    AuthType auth_type,
+    const std::vector<uint8_t>& auth_code,
+    AuthScope desired_scope,
+    std::vector<uint8_t>* access_token,
+    AuthScope* access_token_scope,
+    base::TimeDelta* access_token_ttl,
+    ErrorPtr* error) {
   auto disabled_mode = [](ErrorPtr* error) {
     Error::AddTo(error, FROM_HERE, errors::kDomain, errors::kInvalidAuthMode,
                  "Mode is not available");
     return false;
   };
+
   switch (auth_type) {
     case AuthType::kAnonymous:
       if (!auth_manager_->IsAnonymousAuthSupported())
         return disabled_mode(error);
-      break;
+      return CreateAccessTokenImpl(auth_type, desired_scope, access_token,
+                                   access_token_scope, access_token_ttl);
     case AuthType::kPairing:
       if (!auth_manager_->IsPairingAuthSupported())
         return disabled_mode(error);
@@ -136,34 +163,45 @@
                      errors::kInvalidAuthCode, "Invalid authCode");
         return false;
       }
-      break;
+      return CreateAccessTokenImpl(auth_type, desired_scope, access_token,
+                                   access_token_scope, access_token_ttl);
     case AuthType::kLocal:
       if (!auth_manager_->IsLocalAuthSupported())
         return disabled_mode(error);
-      NOTIMPLEMENTED();
-      // no break to fall back to default.
-    default:
-      Error::AddTo(error, FROM_HERE, errors::kDomain, errors::kInvalidAuthMode,
-                   "Unsupported auth mode");
-      return false;
+      const base::TimeDelta kTtl =
+          base::TimeDelta::FromSeconds(kAccessTokenExpirationSeconds);
+      return auth_manager_->CreateAccessTokenFromAuth(
+          auth_code, kTtl, access_token, access_token_scope, access_token_ttl,
+          error);
   }
 
-  UserInfo user_info{desired_scope,
-                     std::to_string(static_cast<int>(auth_type)) + "/" +
-                         std::to_string(++last_user_id_)};
-  base::TimeDelta ttl =
-      base::TimeDelta::FromSeconds(kAccessTokenExpirationSeconds);
+  Error::AddTo(error, FROM_HERE, errors::kDomain, errors::kInvalidAuthMode,
+               "Unsupported auth mode");
+  return false;
+}
 
-  if (access_token) {
-    *access_token =
-        Base64Encode(auth_manager_->CreateAccessToken(user_info, ttl));
+bool SecurityManager::CreateAccessToken(AuthType auth_type,
+                                        const std::string& auth_code,
+                                        AuthScope desired_scope,
+                                        std::string* access_token,
+                                        AuthScope* access_token_scope,
+                                        base::TimeDelta* access_token_ttl,
+                                        ErrorPtr* error) {
+  std::vector<uint8_t> auth_decoded;
+  if (auth_type != AuthType::kAnonymous &&
+      !Base64Decode(auth_code, &auth_decoded)) {
+    return false;
   }
 
-  if (access_token_scope)
-    *access_token_scope = user_info.scope();
+  std::vector<uint8_t> access_token_decoded;
+  if (!CreateAccessTokenImpl(auth_type, auth_decoded, desired_scope,
+                             &access_token_decoded, access_token_scope,
+                             access_token_ttl, error)) {
+    return false;
+  }
 
-  if (access_token_ttl)
-    *access_token_ttl = ttl;
+  if (access_token)
+    *access_token = Base64Encode(access_token_decoded);
 
   return true;
 }
@@ -224,18 +262,15 @@
   return auth_manager_->ConfirmClientAuthToken(token_decoded, error);
 }
 
-bool SecurityManager::IsValidPairingCode(const std::string& auth_code) const {
+bool SecurityManager::IsValidPairingCode(
+    const std::vector<uint8_t>& auth_code) const {
   if (is_security_disabled_)
     return true;
-  std::vector<uint8_t> auth_decoded;
-  if (!Base64Decode(auth_code, &auth_decoded))
-    return false;
   for (const auto& session : confirmed_sessions_) {
     const std::string& key = session.second->GetKey();
     const std::string& id = session.first;
-    if (auth_decoded ==
-        HmacSha256(std::vector<uint8_t>(key.begin(), key.end()),
-                   std::vector<uint8_t>(id.begin(), id.end()))) {
+    if (auth_code == HmacSha256(std::vector<uint8_t>(key.begin(), key.end()),
+                                std::vector<uint8_t>(id.begin(), id.end()))) {
       pairing_attemts_ = 0;
       block_pairing_until_ = base::Time{};
       return true;
diff --git a/src/privet/security_manager.h b/src/privet/security_manager.h
index 5cb884f..79abf33 100644
--- a/src/privet/security_manager.h
+++ b/src/privet/security_manager.h
@@ -64,8 +64,8 @@
                          const std::string& auth_code,
                          AuthScope desired_scope,
                          std::string* access_token,
-                         AuthScope* granted_scope,
-                         base::TimeDelta* ttl,
+                         AuthScope* access_token_scope,
+                         base::TimeDelta* access_token_ttl,
                          ErrorPtr* error) override;
   bool ParseAccessToken(const std::string& token,
                         UserInfo* user_info,
@@ -94,12 +94,24 @@
                                 const PairingEndListener& on_end);
 
  private:
-  bool IsValidPairingCode(const std::string& auth_code) const;
+  bool IsValidPairingCode(const std::vector<uint8_t>& auth_code) const;
   FRIEND_TEST_ALL_PREFIXES(SecurityManagerTest, ThrottlePairing);
   // Allows limited number of new sessions without successful authorization.
   bool CheckIfPairingAllowed(ErrorPtr* error);
   bool ClosePendingSession(const std::string& session_id);
   bool CloseConfirmedSession(const std::string& session_id);
+  bool CreateAccessTokenImpl(AuthType auth_type,
+                             const std::vector<uint8_t>& auth_code,
+                             AuthScope desired_scope,
+                             std::vector<uint8_t>* access_token,
+                             AuthScope* access_token_scope,
+                             base::TimeDelta* access_token_ttl,
+                             ErrorPtr* error);
+  bool CreateAccessTokenImpl(AuthType auth_type,
+                             AuthScope desired_scope,
+                             std::vector<uint8_t>* access_token,
+                             AuthScope* access_token_scope,
+                             base::TimeDelta* access_token_ttl);
 
   AuthManager* auth_manager_{nullptr};