Home | History | Annotate | Download | only in privet
      1 // Copyright 2015 The Weave 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 
      5 #include "src/privet/auth_manager.h"
      6 
      7 #include <algorithm>
      8 
      9 #include <base/guid.h>
     10 #include <base/rand_util.h>
     11 #include <base/strings/string_number_conversions.h>
     12 
     13 #include "src/config.h"
     14 #include "src/data_encoding.h"
     15 #include "src/privet/constants.h"
     16 #include "src/privet/openssl_utils.h"
     17 #include "src/string_utils.h"
     18 
     19 extern "C" {
     20 #include "third_party/libuweave/src/macaroon.h"
     21 #include "third_party/libuweave/src/macaroon_caveat_internal.h"
     22 }
     23 
     24 namespace weave {
     25 namespace privet {
     26 
     27 namespace {
     28 
     29 const time_t kJ2000ToTimeT = 946684800;
     30 const size_t kMaxMacaroonSize = 1024;
     31 const size_t kMaxPendingClaims = 10;
     32 const char kInvalidTokenError[] = "invalid_token";
     33 const int kSessionIdTtlMinutes = 1;
     34 
     35 uint32_t ToJ2000Time(const base::Time& time) {
     36   return std::max(time.ToTimeT(), kJ2000ToTimeT) - kJ2000ToTimeT;
     37 }
     38 
     39 base::Time FromJ2000Time(uint32_t time) {
     40   return base::Time::FromTimeT(time + kJ2000ToTimeT);
     41 }
     42 
     43 template <class T>
     44 void AppendToArray(T value, std::vector<uint8_t>* array) {
     45   auto begin = reinterpret_cast<const uint8_t*>(&value);
     46   array->insert(array->end(), begin, begin + sizeof(value));
     47 }
     48 
     49 class Caveat {
     50  public:
     51   Caveat(UwMacaroonCaveatType type, size_t str_len)
     52       : buffer_(uw_macaroon_caveat_creation_get_buffsize_(type, str_len)) {
     53     CHECK(!buffer_.empty());
     54   }
     55   const UwMacaroonCaveat& GetCaveat() const { return caveat_; }
     56 
     57  protected:
     58   UwMacaroonCaveat caveat_{};
     59   std::vector<uint8_t> buffer_;
     60 
     61   DISALLOW_COPY_AND_ASSIGN(Caveat);
     62 };
     63 
     64 class ScopeCaveat : public Caveat {
     65  public:
     66   explicit ScopeCaveat(UwMacaroonCaveatScopeType scope)
     67       : Caveat(kUwMacaroonCaveatTypeScope, 0) {
     68     CHECK(uw_macaroon_caveat_create_scope_(scope, buffer_.data(),
     69                                            buffer_.size(), &caveat_));
     70   }
     71 
     72   DISALLOW_COPY_AND_ASSIGN(ScopeCaveat);
     73 };
     74 
     75 class TimestampCaveat : public Caveat {
     76  public:
     77   explicit TimestampCaveat(const base::Time& timestamp)
     78       : Caveat(kUwMacaroonCaveatTypeDelegationTimestamp, 0) {
     79     CHECK(uw_macaroon_caveat_create_delegation_timestamp_(
     80         ToJ2000Time(timestamp), buffer_.data(), buffer_.size(), &caveat_));
     81   }
     82 
     83   DISALLOW_COPY_AND_ASSIGN(TimestampCaveat);
     84 };
     85 
     86 class ExpirationCaveat : public Caveat {
     87  public:
     88   explicit ExpirationCaveat(const base::Time& timestamp)
     89       : Caveat(kUwMacaroonCaveatTypeExpirationAbsolute, 0) {
     90     CHECK(uw_macaroon_caveat_create_expiration_absolute_(
     91         ToJ2000Time(timestamp), buffer_.data(), buffer_.size(), &caveat_));
     92   }
     93 
     94   DISALLOW_COPY_AND_ASSIGN(ExpirationCaveat);
     95 };
     96 
     97 class UserIdCaveat : public Caveat {
     98  public:
     99   explicit UserIdCaveat(const std::vector<uint8_t>& id)
    100       : Caveat(kUwMacaroonCaveatTypeDelegateeUser, id.size()) {
    101     CHECK(uw_macaroon_caveat_create_delegatee_user_(
    102         id.data(), id.size(), buffer_.data(), buffer_.size(), &caveat_));
    103   }
    104 
    105   DISALLOW_COPY_AND_ASSIGN(UserIdCaveat);
    106 };
    107 
    108 class AppIdCaveat : public Caveat {
    109  public:
    110   explicit AppIdCaveat(const std::vector<uint8_t>& id)
    111       : Caveat(kUwMacaroonCaveatTypeDelegateeApp, id.size()) {
    112     CHECK(uw_macaroon_caveat_create_delegatee_app_(
    113         id.data(), id.size(), buffer_.data(), buffer_.size(), &caveat_));
    114   }
    115 
    116   DISALLOW_COPY_AND_ASSIGN(AppIdCaveat);
    117 };
    118 
    119 class ServiceCaveat : public Caveat {
    120  public:
    121   explicit ServiceCaveat(const std::string& id)
    122       : Caveat(kUwMacaroonCaveatTypeDelegateeService, id.size()) {
    123     CHECK(uw_macaroon_caveat_create_delegatee_service_(
    124         reinterpret_cast<const uint8_t*>(id.data()), id.size(), buffer_.data(),
    125         buffer_.size(), &caveat_));
    126   }
    127 
    128   DISALLOW_COPY_AND_ASSIGN(ServiceCaveat);
    129 };
    130 
    131 class SessionIdCaveat : public Caveat {
    132  public:
    133   explicit SessionIdCaveat(const std::string& id)
    134       : Caveat(kUwMacaroonCaveatTypeLanSessionID, id.size()) {
    135     CHECK(uw_macaroon_caveat_create_lan_session_id_(
    136         reinterpret_cast<const uint8_t*>(id.data()), id.size(), buffer_.data(),
    137         buffer_.size(), &caveat_));
    138   }
    139 
    140   DISALLOW_COPY_AND_ASSIGN(SessionIdCaveat);
    141 };
    142 
    143 class ClientAuthTokenCaveat : public Caveat {
    144  public:
    145   ClientAuthTokenCaveat()
    146       : Caveat(kUwMacaroonCaveatTypeClientAuthorizationTokenV1, 0) {
    147     CHECK(uw_macaroon_caveat_create_client_authorization_token_(
    148         nullptr, 0, buffer_.data(), buffer_.size(), &caveat_));
    149   }
    150 
    151   DISALLOW_COPY_AND_ASSIGN(ClientAuthTokenCaveat);
    152 };
    153 
    154 std::vector<uint8_t> CreateSecret() {
    155   std::vector<uint8_t> secret(kSha256OutputSize);
    156   base::RandBytes(secret.data(), secret.size());
    157   return secret;
    158 }
    159 
    160 bool IsClaimAllowed(RootClientTokenOwner curret, RootClientTokenOwner claimer) {
    161   return claimer > curret || claimer == RootClientTokenOwner::kCloud;
    162 }
    163 
    164 std::vector<uint8_t> CreateMacaroonToken(
    165     const std::vector<uint8_t>& secret,
    166     const base::Time& time,
    167     const std::vector<const UwMacaroonCaveat*>& caveats) {
    168   CHECK_EQ(kSha256OutputSize, secret.size());
    169 
    170   UwMacaroonContext context{};
    171   CHECK(uw_macaroon_context_create_(ToJ2000Time(time), nullptr, 0, &context));
    172 
    173   UwMacaroon macaroon{};
    174   CHECK(uw_macaroon_create_from_root_key_(&macaroon, secret.data(),
    175                                           secret.size(), &context,
    176                                           caveats.data(), caveats.size()));
    177 
    178   std::vector<uint8_t> serialized_token(kMaxMacaroonSize);
    179   size_t len = 0;
    180   CHECK(uw_macaroon_serialize_(&macaroon, serialized_token.data(),
    181                                serialized_token.size(), &len));
    182   serialized_token.resize(len);
    183 
    184   return serialized_token;
    185 }
    186 
    187 std::vector<uint8_t> ExtendMacaroonToken(
    188     const UwMacaroon& macaroon,
    189     const base::Time& time,
    190     const std::vector<const UwMacaroonCaveat*>& caveats) {
    191   UwMacaroonContext context{};
    192   CHECK(uw_macaroon_context_create_(ToJ2000Time(time), nullptr, 0, &context));
    193 
    194   UwMacaroon prev_macaroon = macaroon;
    195   std::vector<uint8_t> prev_buffer(kMaxMacaroonSize);
    196   std::vector<uint8_t> new_buffer(kMaxMacaroonSize);
    197 
    198   for (auto caveat : caveats) {
    199     UwMacaroon new_macaroon{};
    200     CHECK(uw_macaroon_extend_(&prev_macaroon, &new_macaroon, &context, caveat,
    201                               new_buffer.data(), new_buffer.size()));
    202     new_buffer.swap(prev_buffer);
    203     prev_macaroon = new_macaroon;
    204   }
    205 
    206   std::vector<uint8_t> serialized_token(kMaxMacaroonSize);
    207   size_t len = 0;
    208   CHECK(uw_macaroon_serialize_(&prev_macaroon, serialized_token.data(),
    209                                serialized_token.size(), &len));
    210   serialized_token.resize(len);
    211 
    212   return serialized_token;
    213 }
    214 
    215 bool LoadMacaroon(const std::vector<uint8_t>& token,
    216                   std::vector<uint8_t>* buffer,
    217                   UwMacaroon* macaroon,
    218                   ErrorPtr* error) {
    219   buffer->resize(kMaxMacaroonSize);
    220   if (!uw_macaroon_deserialize_(token.data(), token.size(), buffer->data(),
    221                                 buffer->size(), macaroon)) {
    222     return Error::AddTo(error, FROM_HERE, kInvalidTokenError,
    223                         "Invalid token format");
    224   }
    225   return true;
    226 }
    227 
    228 bool VerifyMacaroon(const std::vector<uint8_t>& secret,
    229                     const UwMacaroon& macaroon,
    230                     const base::Time& time,
    231                     UwMacaroonValidationResult* result,
    232                     ErrorPtr* error) {
    233   CHECK_EQ(kSha256OutputSize, secret.size());
    234   UwMacaroonContext context = {};
    235   CHECK(uw_macaroon_context_create_(ToJ2000Time(time), nullptr, 0, &context));
    236 
    237   if (!uw_macaroon_validate_(&macaroon, secret.data(), secret.size(), &context,
    238                              result)) {
    239     return Error::AddTo(error, FROM_HERE, "invalid_token",
    240                         "Invalid token signature");
    241   }
    242   return true;
    243 }
    244 
    245 UwMacaroonCaveatScopeType ToMacaroonScope(AuthScope scope) {
    246   switch (scope) {
    247     case AuthScope::kViewer:
    248       return kUwMacaroonCaveatScopeTypeViewer;
    249     case AuthScope::kUser:
    250       return kUwMacaroonCaveatScopeTypeUser;
    251     case AuthScope::kManager:
    252       return kUwMacaroonCaveatScopeTypeManager;
    253     case AuthScope::kOwner:
    254       return kUwMacaroonCaveatScopeTypeOwner;
    255     default:
    256       NOTREACHED() << EnumToString(scope);
    257   }
    258   return kUwMacaroonCaveatScopeTypeViewer;
    259 }
    260 
    261 AuthScope FromMacaroonScope(uint32_t scope) {
    262   if (scope <= kUwMacaroonCaveatScopeTypeOwner)
    263     return AuthScope::kOwner;
    264   if (scope <= kUwMacaroonCaveatScopeTypeManager)
    265     return AuthScope::kManager;
    266   if (scope <= kUwMacaroonCaveatScopeTypeUser)
    267     return AuthScope::kUser;
    268   if (scope <= kUwMacaroonCaveatScopeTypeViewer)
    269     return AuthScope::kViewer;
    270   return AuthScope::kNone;
    271 }
    272 
    273 }  // namespace
    274 
    275 AuthManager::AuthManager(Config* config,
    276                          const std::vector<uint8_t>& certificate_fingerprint)
    277     : config_{config},
    278       certificate_fingerprint_{certificate_fingerprint},
    279       access_secret_{CreateSecret()} {
    280   if (config_) {
    281     SetAuthSecret(config_->GetSettings().secret,
    282                   config_->GetSettings().root_client_token_owner);
    283   } else {
    284     SetAuthSecret({}, RootClientTokenOwner::kNone);
    285   }
    286 }
    287 
    288 AuthManager::AuthManager(const std::vector<uint8_t>& auth_secret,
    289                          const std::vector<uint8_t>& certificate_fingerprint,
    290                          const std::vector<uint8_t>& access_secret,
    291                          base::Clock* clock)
    292     : AuthManager(nullptr, certificate_fingerprint) {
    293   access_secret_ = access_secret.size() == kSha256OutputSize ? access_secret
    294                                                              : CreateSecret();
    295   SetAuthSecret(auth_secret, RootClientTokenOwner::kNone);
    296   if (clock)
    297     clock_ = clock;
    298 }
    299 
    300 void AuthManager::SetAuthSecret(const std::vector<uint8_t>& secret,
    301                                 RootClientTokenOwner owner) {
    302   auth_secret_ = secret;
    303 
    304   if (auth_secret_.size() != kSha256OutputSize) {
    305     auth_secret_ = CreateSecret();
    306     owner = RootClientTokenOwner::kNone;
    307   }
    308 
    309   if (!config_ || (config_->GetSettings().secret == auth_secret_ &&
    310                    config_->GetSettings().root_client_token_owner == owner)) {
    311     return;
    312   }
    313 
    314   Config::Transaction change{config_};
    315   change.set_secret(secret);
    316   change.set_root_client_token_owner(owner);
    317   change.Commit();
    318 }
    319 
    320 AuthManager::~AuthManager() {}
    321 
    322 std::vector<uint8_t> AuthManager::CreateAccessToken(const UserInfo& user_info,
    323                                                     base::TimeDelta ttl) const {
    324   const base::Time now = Now();
    325   TimestampCaveat issued{now};
    326   ScopeCaveat scope{ToMacaroonScope(user_info.scope())};
    327   // Macaroons have no caveats for auth type. So we just append the type to the
    328   // user ID.
    329   std::vector<uint8_t> id_with_type{user_info.id().user};
    330   id_with_type.push_back(static_cast<uint8_t>(user_info.id().type));
    331   UserIdCaveat user{id_with_type};
    332   AppIdCaveat app{user_info.id().app};
    333   ExpirationCaveat expiration{now + ttl};
    334   return CreateMacaroonToken(
    335       access_secret_, now,
    336       {
    337 
    338           &issued.GetCaveat(), &scope.GetCaveat(), &user.GetCaveat(),
    339           &app.GetCaveat(), &expiration.GetCaveat(),
    340       });
    341 }
    342 
    343 bool AuthManager::ParseAccessToken(const std::vector<uint8_t>& token,
    344                                    UserInfo* user_info,
    345                                    ErrorPtr* error) const {
    346   std::vector<uint8_t> buffer;
    347   UwMacaroon macaroon{};
    348 
    349   UwMacaroonValidationResult result{};
    350   const base::Time now = Now();
    351   if (!LoadMacaroon(token, &buffer, &macaroon, error) ||
    352       macaroon.num_caveats != 5 ||
    353       !VerifyMacaroon(access_secret_, macaroon, now, &result, error)) {
    354     return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthorization,
    355                         "Invalid token");
    356   }
    357 
    358   AuthScope auth_scope{FromMacaroonScope(result.granted_scope)};
    359   if (auth_scope == AuthScope::kNone) {
    360     return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthorization,
    361                         "Invalid token data");
    362   }
    363 
    364   // If token is valid and token was not extended, it should has precisely this
    365   // values.
    366   CHECK_GE(FromJ2000Time(result.expiration_time), now);
    367   CHECK_EQ(2u, result.num_delegatees);
    368   CHECK_EQ(kUwMacaroonDelegateeTypeUser, result.delegatees[0].type);
    369   CHECK_EQ(kUwMacaroonDelegateeTypeApp, result.delegatees[1].type);
    370   CHECK_GT(result.delegatees[0].id_len, 1u);
    371   std::vector<uint8_t> user_id{
    372       result.delegatees[0].id,
    373       result.delegatees[0].id + result.delegatees[0].id_len};
    374   // Last byte is used for type. See |CreateAccessToken|.
    375   AuthType type = static_cast<AuthType>(user_id.back());
    376   user_id.pop_back();
    377 
    378   std::vector<uint8_t> app_id{
    379       result.delegatees[1].id,
    380       result.delegatees[1].id + result.delegatees[1].id_len};
    381   if (user_info)
    382     *user_info = UserInfo{auth_scope, UserAppId{type, user_id, app_id}};
    383 
    384   return true;
    385 }
    386 
    387 std::vector<uint8_t> AuthManager::ClaimRootClientAuthToken(
    388     RootClientTokenOwner owner,
    389     ErrorPtr* error) {
    390   CHECK(RootClientTokenOwner::kNone != owner);
    391   if (config_) {
    392     auto current = config_->GetSettings().root_client_token_owner;
    393     if (!IsClaimAllowed(current, owner)) {
    394       Error::AddToPrintf(error, FROM_HERE, errors::kAlreadyClaimed,
    395                          "Device already claimed by '%s'",
    396                          EnumToString(current).c_str());
    397       return {};
    398     }
    399   };
    400 
    401   pending_claims_.push_back(std::make_pair(
    402       std::unique_ptr<AuthManager>{new AuthManager{nullptr, {}}}, owner));
    403   if (pending_claims_.size() > kMaxPendingClaims)
    404     pending_claims_.pop_front();
    405   return pending_claims_.back().first->GetRootClientAuthToken(owner);
    406 }
    407 
    408 bool AuthManager::ConfirmClientAuthToken(const std::vector<uint8_t>& token,
    409                                          ErrorPtr* error) {
    410   // Cover case when caller sent confirm twice.
    411   if (pending_claims_.empty())
    412     return IsValidAuthToken(token, error);
    413 
    414   auto claim =
    415       std::find_if(pending_claims_.begin(), pending_claims_.end(),
    416                    [&token](const decltype(pending_claims_)::value_type& auth) {
    417                      return auth.first->IsValidAuthToken(token, nullptr);
    418                    });
    419   if (claim == pending_claims_.end()) {
    420     return Error::AddTo(error, FROM_HERE, errors::kNotFound, "Unknown claim");
    421   }
    422 
    423   SetAuthSecret(claim->first->GetAuthSecret(), claim->second);
    424   pending_claims_.clear();
    425   return true;
    426 }
    427 
    428 std::vector<uint8_t> AuthManager::GetRootClientAuthToken(
    429     RootClientTokenOwner owner) const {
    430   CHECK(RootClientTokenOwner::kNone != owner);
    431   ClientAuthTokenCaveat auth_token;
    432   const base::Time now = Now();
    433   TimestampCaveat issued{now};
    434 
    435   ServiceCaveat client{owner == RootClientTokenOwner::kCloud ? "google.com"
    436                                                              : ""};
    437   return CreateMacaroonToken(
    438       auth_secret_, now,
    439       {
    440           &auth_token.GetCaveat(), &issued.GetCaveat(), &client.GetCaveat(),
    441       });
    442 }
    443 
    444 base::Time AuthManager::Now() const {
    445   return clock_->Now();
    446 }
    447 
    448 bool AuthManager::IsValidAuthToken(const std::vector<uint8_t>& token,
    449                                    ErrorPtr* error) const {
    450   std::vector<uint8_t> buffer;
    451   UwMacaroon macaroon{};
    452   UwMacaroonValidationResult result{};
    453   if (!LoadMacaroon(token, &buffer, &macaroon, error) ||
    454       !VerifyMacaroon(auth_secret_, macaroon, Now(), &result, error)) {
    455     return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode,
    456                         "Invalid token");
    457   }
    458   return true;
    459 }
    460 
    461 bool AuthManager::CreateAccessTokenFromAuth(
    462     const std::vector<uint8_t>& auth_token,
    463     base::TimeDelta ttl,
    464     std::vector<uint8_t>* access_token,
    465     AuthScope* access_token_scope,
    466     base::TimeDelta* access_token_ttl,
    467     ErrorPtr* error) const {
    468   std::vector<uint8_t> buffer;
    469   UwMacaroon macaroon{};
    470   UwMacaroonValidationResult result{};
    471   const base::Time now = Now();
    472   if (!LoadMacaroon(auth_token, &buffer, &macaroon, error) ||
    473       !VerifyMacaroon(auth_secret_, macaroon, now, &result, error)) {
    474     return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode,
    475                         "Invalid token");
    476   }
    477 
    478   AuthScope auth_scope{FromMacaroonScope(result.granted_scope)};
    479   if (auth_scope == AuthScope::kNone) {
    480     return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode,
    481                         "Invalid token data");
    482   }
    483 
    484   // TODO: Integrate black list checks.
    485   auto delegates_rbegin = std::reverse_iterator<const UwMacaroonDelegateeInfo*>(
    486       result.delegatees + result.num_delegatees);
    487   auto delegates_rend =
    488       std::reverse_iterator<const UwMacaroonDelegateeInfo*>(result.delegatees);
    489   auto last_user_id =
    490       std::find_if(delegates_rbegin, delegates_rend,
    491                    [](const UwMacaroonDelegateeInfo& delegatee) {
    492                      return delegatee.type == kUwMacaroonDelegateeTypeUser;
    493                    });
    494   auto last_app_id =
    495       std::find_if(delegates_rbegin, delegates_rend,
    496                    [](const UwMacaroonDelegateeInfo& delegatee) {
    497                      return delegatee.type == kUwMacaroonDelegateeTypeApp;
    498                    });
    499 
    500   if (last_user_id == delegates_rend || !last_user_id->id_len) {
    501     return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode,
    502                         "User ID is missing");
    503   }
    504 
    505   const char* session_id = reinterpret_cast<const char*>(result.lan_session_id);
    506   if (!IsValidSessionId({session_id, session_id + result.lan_session_id_len})) {
    507     return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode,
    508                         "Invalid session id");
    509   }
    510 
    511   CHECK_GE(FromJ2000Time(result.expiration_time), now);
    512 
    513   if (!access_token)
    514     return true;
    515 
    516   std::vector<uint8_t> user_id{last_user_id->id,
    517                                last_user_id->id + last_user_id->id_len};
    518   std::vector<uint8_t> app_id;
    519   if (last_app_id != delegates_rend)
    520     app_id.assign(last_app_id->id, last_app_id->id + last_app_id->id_len);
    521 
    522   UserInfo info{auth_scope, {AuthType::kLocal, user_id, app_id}};
    523 
    524   ttl = std::min(ttl, FromJ2000Time(result.expiration_time) - now);
    525   *access_token = CreateAccessToken(info, ttl);
    526 
    527   if (access_token_scope)
    528     *access_token_scope = info.scope();
    529 
    530   if (access_token_ttl)
    531     *access_token_ttl = ttl;
    532   return true;
    533 }
    534 
    535 std::string AuthManager::CreateSessionId() const {
    536   return std::to_string(ToJ2000Time(Now())) + ":" +
    537          std::to_string(++session_counter_);
    538 }
    539 
    540 bool AuthManager::IsValidSessionId(const std::string& session_id) const {
    541   base::Time ssid_time = FromJ2000Time(std::atoi(session_id.c_str()));
    542   return Now() - base::TimeDelta::FromMinutes(kSessionIdTtlMinutes) <=
    543              ssid_time &&
    544          ssid_time <= Now();
    545 }
    546 
    547 std::vector<uint8_t> AuthManager::DelegateToUser(
    548     const std::vector<uint8_t>& token,
    549     base::TimeDelta ttl,
    550     const UserInfo& user_info) const {
    551   std::vector<uint8_t> buffer;
    552   UwMacaroon macaroon{};
    553   CHECK(LoadMacaroon(token, &buffer, &macaroon, nullptr));
    554 
    555   const base::Time now = Now();
    556   TimestampCaveat issued{now};
    557   ExpirationCaveat expiration{now + ttl};
    558   ScopeCaveat scope{ToMacaroonScope(user_info.scope())};
    559   UserIdCaveat user{user_info.id().user};
    560   AppIdCaveat app{user_info.id().app};
    561   SessionIdCaveat session{CreateSessionId()};
    562 
    563   std::vector<const UwMacaroonCaveat*> caveats{
    564       &issued.GetCaveat(), &expiration.GetCaveat(), &scope.GetCaveat(),
    565       &user.GetCaveat(),
    566   };
    567 
    568   if (!user_info.id().app.empty())
    569     caveats.push_back(&app.GetCaveat());
    570 
    571   caveats.push_back(&session.GetCaveat());
    572 
    573   return ExtendMacaroonToken(macaroon, now, caveats);
    574 }
    575 
    576 }  // namespace privet
    577 }  // namespace weave
    578