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};