Merge remote-tracking branch 'weave/dev' into 'weave/master'

ff46c93 Merge remote-tracking branch 'weave/master' into dev_review
08be74d Update libuweave/macaroon code
4efdf46 Fix GCC warning
4fe71e3 Make App ID a part of User ID
3cbb686 Update macaroon lib with version supporting empty strings
8585d30 Remove unused line
69dd2e1 Merge remote-tracking branch 'weave/master' into dev_dev
70c8642 Add kUwMacaroonDelegateeTypeService caveat
d5f7aab Add session ID validation
d7c6deb Remove crypto type "None"
d74a732 Update macaroon lib
a821f2e Integrate new macaroon library
7d29a5a Update macaroon lib
1c83377 Fix build errors introduced by
9ac4c6c Merge remote-tracking branch 'weave/master' into 'weave/dev'
5a7c4f5 Add black list manager implementation
484b6e4 Update AccessBlackListManager interface
81ac16e Add MockConfigStore argument to disabled default expectations
07bb755 Merge remote-tracking branch 'weave/master' into dev_dev2
7329b74 Fix unittest compilation on GCC
f533677 Implemented _accessControlBlackList trait
6741755 Merge remote-tracking branch 'weave/master' into dev_dev2
42e508f Add write callback into SaveSettings function
7ecdf95 Add |name| into LoadSettings/SaveSettings
8023b80 Merge remote-tracking branch 'weave/master' into dev_dev2
diff --git a/file_lists.mk b/file_lists.mk
index b944c3a..9a015e2 100644
--- a/file_lists.mk
+++ b/file_lists.mk
@@ -3,6 +3,8 @@
 # found in the LICENSE file.
 
 WEAVE_SRC_FILES := \
+	src/access_api_handler.cc \
+	src/access_black_list_manager_impl.cc \
 	src/backoff_entry.cc \
 	src/base_api_handler.cc \
 	src/commands/cloud_command_proxy.cc \
@@ -48,6 +50,8 @@
 	src/test/unittest_utils.cc
 
 WEAVE_UNITTEST_SRC_FILES := \
+	src/access_api_handler_unittest.cc \
+	src/access_black_list_manager_impl_unittest.cc \
 	src/backoff_entry_unittest.cc \
 	src/base_api_handler_unittest.cc \
 	src/commands/cloud_command_proxy_unittest.cc \
@@ -161,4 +165,3 @@
 	third_party/libuweave/src/macaroon_caveat.c \
 	third_party/libuweave/src/macaroon_context.c \
 	third_party/libuweave/src/macaroon_encoding.c
-
diff --git a/include/weave/provider/test/mock_config_store.h b/include/weave/provider/test/mock_config_store.h
index e6411d6..a7eb374 100644
--- a/include/weave/provider/test/mock_config_store.h
+++ b/include/weave/provider/test/mock_config_store.h
@@ -18,10 +18,13 @@
 
 class MockConfigStore : public ConfigStore {
  public:
-  MockConfigStore() {
+  explicit MockConfigStore(bool set_expectations = true) {
     using testing::_;
     using testing::Return;
 
+    if (!set_expectations)
+      return;
+
     EXPECT_CALL(*this, LoadDefaults(_))
         .WillRepeatedly(testing::Invoke([](Settings* settings) {
           settings->firmware_version = "TEST_FIRMWARE";
@@ -39,8 +42,8 @@
           "version": 1,
           "device_id": "TEST_DEVICE_ID"
         })"));
-    EXPECT_CALL(*this, LoadSettings("config")).WillRepeatedly(Return(""));
-    EXPECT_CALL(*this, SaveSettings("config", _, _))
+    EXPECT_CALL(*this, LoadSettings(_)).WillRepeatedly(Return(""));
+    EXPECT_CALL(*this, SaveSettings(_, _, _))
         .WillRepeatedly(testing::WithArgs<1, 2>(testing::Invoke(
             [](const std::string& json, const DoneCallback& callback) {
               if (!callback.is_null())
diff --git a/src/access_api_handler.cc b/src/access_api_handler.cc
new file mode 100644
index 0000000..7c39b20
--- /dev/null
+++ b/src/access_api_handler.cc
@@ -0,0 +1,227 @@
+// Copyright 2016 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/access_api_handler.h"
+
+#include <base/bind.h>
+#include <weave/device.h>
+
+#include "src/access_black_list_manager.h"
+#include "src/commands/schema_constants.h"
+#include "src/data_encoding.h"
+#include "src/json_error_codes.h"
+
+namespace weave {
+
+namespace {
+
+const char kComponent[] = "accessControl";
+const char kTrait[] = "_accessControlBlackList";
+const char kStateSize[] = "_accessControlBlackList.size";
+const char kStateCapacity[] = "_accessControlBlackList.capacity";
+const char kUserId[] = "userId";
+const char kApplicationId[] = "applicationId";
+const char kExpirationTimeout[] = "expirationTimeoutSec";
+const char kBlackList[] = "blackList";
+
+bool GetIds(const base::DictionaryValue& parameters,
+            std::vector<uint8_t>* user_id_decoded,
+            std::vector<uint8_t>* app_id_decoded,
+            ErrorPtr* error) {
+  std::string user_id;
+  parameters.GetString(kUserId, &user_id);
+  if (!Base64Decode(user_id, user_id_decoded)) {
+    Error::AddToPrintf(error, FROM_HERE, errors::commands::kInvalidPropValue,
+                       "Invalid user id '%s'", user_id.c_str());
+    return false;
+  }
+
+  std::string app_id;
+  parameters.GetString(kApplicationId, &app_id);
+  if (!Base64Decode(app_id, app_id_decoded)) {
+    Error::AddToPrintf(error, FROM_HERE, errors::commands::kInvalidPropValue,
+                       "Invalid app id '%s'", user_id.c_str());
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace
+
+AccessApiHandler::AccessApiHandler(Device* device,
+                                   AccessBlackListManager* manager)
+    : device_{device}, manager_{manager} {
+  device_->AddTraitDefinitionsFromJson(R"({
+    "_accessControlBlackList": {
+      "commands": {
+        "block": {
+          "minimalRole": "owner",
+          "parameters": {
+            "userId": {
+              "type": "string"
+            },
+            "applicationId": {
+              "type": "string"
+            },
+            "expirationTimeoutSec": {
+              "type": "integer"
+            }
+          }
+        },
+        "unblock": {
+          "minimalRole": "owner",
+          "parameters": {
+            "userId": {
+              "type": "string"
+            },
+            "applicationId": {
+              "type": "string"
+            }
+          }
+        },
+        "list": {
+          "minimalRole": "owner",
+          "parameters": {},
+          "results": {
+            "blackList": {
+              "type": "array",
+              "items": {
+                "type": "object",
+                "properties": {
+                  "userId": {
+                    "type": "string"
+                  },
+                  "applicationId": {
+                    "type": "string"
+                  }
+                },
+                "additionalProperties": false
+              }
+            }
+          }
+        }
+      },
+      "state": {
+        "size": {
+          "type": "integer",
+          "isRequired": true
+        },
+        "capacity": {
+          "type": "integer",
+          "isRequired": true
+        }
+      }
+    }
+  })");
+  CHECK(device_->AddComponent(kComponent, {kTrait}, nullptr));
+  UpdateState();
+
+  device_->AddCommandHandler(
+      kComponent, "_accessControlBlackList.block",
+      base::Bind(&AccessApiHandler::Block, weak_ptr_factory_.GetWeakPtr()));
+  device_->AddCommandHandler(
+      kComponent, "_accessControlBlackList.unblock",
+      base::Bind(&AccessApiHandler::Unblock, weak_ptr_factory_.GetWeakPtr()));
+  device_->AddCommandHandler(
+      kComponent, "_accessControlBlackList.list",
+      base::Bind(&AccessApiHandler::List, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void AccessApiHandler::Block(const std::weak_ptr<Command>& cmd) {
+  auto command = cmd.lock();
+  if (!command)
+    return;
+
+  CHECK(command->GetState() == Command::State::kQueued)
+      << EnumToString(command->GetState());
+  command->SetProgress(base::DictionaryValue{}, nullptr);
+
+  const auto& parameters = command->GetParameters();
+  std::vector<uint8_t> user_id;
+  std::vector<uint8_t> app_id;
+  ErrorPtr error;
+  if (!GetIds(parameters, &user_id, &app_id, &error)) {
+    command->Abort(error.get(), nullptr);
+    return;
+  }
+
+  int timeout_sec = 0;
+  parameters.GetInteger(kExpirationTimeout, &timeout_sec);
+
+  base::Time expiration =
+      base::Time::Now() + base::TimeDelta::FromSeconds(timeout_sec);
+
+  manager_->Block(user_id, app_id, expiration,
+                  base::Bind(&AccessApiHandler::OnCommandDone,
+                             weak_ptr_factory_.GetWeakPtr(), cmd));
+}
+
+void AccessApiHandler::Unblock(const std::weak_ptr<Command>& cmd) {
+  auto command = cmd.lock();
+  if (!command)
+    return;
+
+  CHECK(command->GetState() == Command::State::kQueued)
+      << EnumToString(command->GetState());
+  command->SetProgress(base::DictionaryValue{}, nullptr);
+
+  const auto& parameters = command->GetParameters();
+  std::vector<uint8_t> user_id;
+  std::vector<uint8_t> app_id;
+  ErrorPtr error;
+  if (!GetIds(parameters, &user_id, &app_id, &error)) {
+    command->Abort(error.get(), nullptr);
+    return;
+  }
+
+  manager_->Unblock(user_id, app_id,
+                    base::Bind(&AccessApiHandler::OnCommandDone,
+                               weak_ptr_factory_.GetWeakPtr(), cmd));
+}
+
+void AccessApiHandler::List(const std::weak_ptr<Command>& cmd) {
+  auto command = cmd.lock();
+  if (!command)
+    return;
+
+  CHECK(command->GetState() == Command::State::kQueued)
+      << EnumToString(command->GetState());
+  command->SetProgress(base::DictionaryValue{}, nullptr);
+
+  std::unique_ptr<base::ListValue> entries{new base::ListValue};
+  for (const auto& e : manager_->GetEntries()) {
+    std::unique_ptr<base::DictionaryValue> entry{new base::DictionaryValue};
+    entry->SetString(kUserId, Base64Encode(e.user_id));
+    entry->SetString(kApplicationId, Base64Encode(e.app_id));
+    entries->Append(entry.release());
+  }
+
+  base::DictionaryValue result;
+  result.Set(kBlackList, entries.release());
+
+  command->Complete(result, nullptr);
+}
+
+void AccessApiHandler::OnCommandDone(const std::weak_ptr<Command>& cmd,
+                                     ErrorPtr error) {
+  auto command = cmd.lock();
+  if (!command)
+    return;
+  UpdateState();
+  if (error) {
+    command->Abort(error.get(), nullptr);
+    return;
+  }
+  command->Complete({}, nullptr);
+}
+
+void AccessApiHandler::UpdateState() {
+  base::DictionaryValue state;
+  state.SetInteger(kStateSize, manager_->GetSize());
+  state.SetInteger(kStateCapacity, manager_->GetCapacity());
+  device_->SetStateProperties(kComponent, state, nullptr);
+}
+
+}  // namespace weave
diff --git a/src/access_api_handler.h b/src/access_api_handler.h
new file mode 100644
index 0000000..821ce02
--- /dev/null
+++ b/src/access_api_handler.h
@@ -0,0 +1,47 @@
+// Copyright 2016 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBWEAVE_SRC_ACCESS_API_HANDLER_H_
+#define LIBWEAVE_SRC_ACCESS_API_HANDLER_H_
+
+#include <memory>
+
+#include <base/memory/weak_ptr.h>
+#include <weave/error.h>
+
+namespace weave {
+
+class AccessBlackListManager;
+class Command;
+class Device;
+
+// Handles commands for 'accessControlBlackList' trait.
+// Objects of the class subscribe for notification from CommandManager and
+// execute incoming commands.
+// Handled commands:
+//  accessControlBlackList.block
+//  accessControlBlackList.unblock
+//  accessControlBlackList.list
+class AccessApiHandler final {
+ public:
+  AccessApiHandler(Device* device, AccessBlackListManager* manager);
+
+ private:
+  void Block(const std::weak_ptr<Command>& command);
+  void Unblock(const std::weak_ptr<Command>& command);
+  void List(const std::weak_ptr<Command>& command);
+  void UpdateState();
+
+  void OnCommandDone(const std::weak_ptr<Command>& command, ErrorPtr error);
+
+  Device* device_{nullptr};
+  AccessBlackListManager* manager_{nullptr};
+
+  base::WeakPtrFactory<AccessApiHandler> weak_ptr_factory_{this};
+  DISALLOW_COPY_AND_ASSIGN(AccessApiHandler);
+};
+
+}  // namespace weave
+
+#endif  // LIBWEAVE_SRC_ACCESS_API_HANDLER_H_
diff --git a/src/access_api_handler_unittest.cc b/src/access_api_handler_unittest.cc
new file mode 100644
index 0000000..3e7f5d7
--- /dev/null
+++ b/src/access_api_handler_unittest.cc
@@ -0,0 +1,259 @@
+// Copyright 2016 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/access_api_handler.h"
+
+#include <gtest/gtest.h>
+#include <weave/provider/test/fake_task_runner.h>
+#include <weave/test/mock_device.h>
+#include <weave/test/unittest_utils.h>
+
+#include "src/component_manager_impl.h"
+#include "src/access_black_list_manager.h"
+#include "src/data_encoding.h"
+
+using testing::_;
+using testing::AnyOf;
+using testing::Invoke;
+using testing::Return;
+using testing::StrictMock;
+using testing::WithArgs;
+
+namespace weave {
+
+class MockAccessBlackListManager : public AccessBlackListManager {
+ public:
+  MOCK_METHOD4(Block,
+               void(const std::vector<uint8_t>&,
+                    const std::vector<uint8_t>&,
+                    const base::Time&,
+                    const DoneCallback&));
+  MOCK_METHOD3(Unblock,
+               void(const std::vector<uint8_t>&,
+                    const std::vector<uint8_t>&,
+                    const DoneCallback&));
+  MOCK_CONST_METHOD2(IsBlocked,
+                     bool(const std::vector<uint8_t>&,
+                          const std::vector<uint8_t>&));
+  MOCK_CONST_METHOD0(GetEntries, std::vector<Entry>());
+  MOCK_CONST_METHOD0(GetSize, size_t());
+  MOCK_CONST_METHOD0(GetCapacity, size_t());
+};
+
+class AccessApiHandlerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    EXPECT_CALL(device_, AddTraitDefinitionsFromJson(_))
+        .WillRepeatedly(Invoke([this](const std::string& json) {
+          EXPECT_TRUE(component_manager_.LoadTraits(json, nullptr));
+        }));
+    EXPECT_CALL(device_, SetStateProperties(_, _, _))
+        .WillRepeatedly(
+            Invoke(&component_manager_, &ComponentManager::SetStateProperties));
+    EXPECT_CALL(device_, SetStateProperty(_, _, _, _))
+        .WillRepeatedly(
+            Invoke(&component_manager_, &ComponentManager::SetStateProperty));
+    EXPECT_CALL(device_, AddComponent(_, _, _))
+        .WillRepeatedly(Invoke([this](const std::string& name,
+                                      const std::vector<std::string>& traits,
+                                      ErrorPtr* error) {
+          return component_manager_.AddComponent("", name, traits, error);
+        }));
+
+    EXPECT_CALL(device_,
+                AddCommandHandler(_, AnyOf("_accessControlBlackList.block",
+                                           "_accessControlBlackList.unblock",
+                                           "_accessControlBlackList.list"),
+                                  _))
+        .WillRepeatedly(
+            Invoke(&component_manager_, &ComponentManager::AddCommandHandler));
+
+    EXPECT_CALL(access_manager_, GetSize()).WillRepeatedly(Return(0));
+
+    EXPECT_CALL(access_manager_, GetCapacity()).WillRepeatedly(Return(10));
+
+    handler_.reset(new AccessApiHandler{&device_, &access_manager_});
+  }
+
+  const base::DictionaryValue& AddCommand(const std::string& command) {
+    std::string id;
+    auto command_instance = component_manager_.ParseCommandInstance(
+        *test::CreateDictionaryValue(command.c_str()), Command::Origin::kLocal,
+        UserRole::kOwner, &id, nullptr);
+    EXPECT_NE(nullptr, command_instance.get());
+    component_manager_.AddCommand(std::move(command_instance));
+    EXPECT_EQ(Command::State::kDone,
+              component_manager_.FindCommand(id)->GetState());
+    return component_manager_.FindCommand(id)->GetResults();
+  }
+
+  std::unique_ptr<base::DictionaryValue> GetState() {
+    std::string path =
+        component_manager_.FindComponentWithTrait("_accessControlBlackList");
+    EXPECT_FALSE(path.empty());
+    const auto* component = component_manager_.FindComponent(path, nullptr);
+    EXPECT_TRUE(component);
+    const base::DictionaryValue* state = nullptr;
+    EXPECT_TRUE(
+        component->GetDictionary("state._accessControlBlackList", &state));
+    return std::unique_ptr<base::DictionaryValue>{state->DeepCopy()};
+  }
+
+  StrictMock<provider::test::FakeTaskRunner> task_runner_;
+  ComponentManagerImpl component_manager_{&task_runner_};
+  StrictMock<test::MockDevice> device_;
+  StrictMock<MockAccessBlackListManager> access_manager_;
+  std::unique_ptr<AccessApiHandler> handler_;
+};
+
+TEST_F(AccessApiHandlerTest, Initialization) {
+  const base::DictionaryValue* trait = nullptr;
+  ASSERT_TRUE(component_manager_.GetTraits().GetDictionary(
+      "_accessControlBlackList", &trait));
+
+  auto expected = R"({
+    "commands": {
+      "block": {
+        "minimalRole": "owner",
+        "parameters": {
+          "userId": {
+            "type": "string"
+          },
+          "applicationId": {
+            "type": "string"
+          },
+          "expirationTimeoutSec": {
+            "type": "integer"
+          }
+        }
+      },
+      "unblock": {
+        "minimalRole": "owner",
+        "parameters": {
+          "userId": {
+            "type": "string"
+          },
+          "applicationId": {
+            "type": "string"
+          }
+        }
+      },
+      "list": {
+        "minimalRole": "owner",
+        "parameters": {},
+        "results": {
+          "blackList": {
+            "type": "array",
+            "items": {
+              "type": "object",
+              "properties": {
+                "userId": {
+                  "type": "string"
+                },
+                "applicationId": {
+                  "type": "string"
+                }
+              },
+              "additionalProperties": false
+            }
+          }
+        }
+      }
+    },
+    "state": {
+      "size": {
+        "type": "integer",
+        "isRequired": true
+      },
+      "capacity": {
+        "type": "integer",
+        "isRequired": true
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(expected, *trait);
+
+  expected = R"({
+    "capacity": 10,
+    "size": 0
+  })";
+  EXPECT_JSON_EQ(expected, *GetState());
+}
+
+TEST_F(AccessApiHandlerTest, Block) {
+  EXPECT_CALL(access_manager_, Block(std::vector<uint8_t>{1, 2, 3},
+                                     std::vector<uint8_t>{3, 4, 5}, _, _))
+      .WillOnce(WithArgs<3>(
+          Invoke([](const DoneCallback& callback) { callback.Run(nullptr); })));
+  EXPECT_CALL(access_manager_, GetSize()).WillRepeatedly(Return(1));
+
+  AddCommand(R"({
+    'name' : '_accessControlBlackList.block',
+    'component': 'accessControl',
+    'parameters': {
+      'userId': 'AQID',
+      'applicationId': 'AwQF',
+      'expirationTimeoutSec': 1234
+    }
+  })");
+
+  auto expected = R"({
+    "capacity": 10,
+    "size": 1
+  })";
+  EXPECT_JSON_EQ(expected, *GetState());
+}
+
+TEST_F(AccessApiHandlerTest, Unblock) {
+  EXPECT_CALL(access_manager_, Unblock(std::vector<uint8_t>{1, 2, 3},
+                                       std::vector<uint8_t>{3, 4, 5}, _))
+      .WillOnce(WithArgs<2>(
+          Invoke([](const DoneCallback& callback) { callback.Run(nullptr); })));
+  EXPECT_CALL(access_manager_, GetSize()).WillRepeatedly(Return(4));
+
+  AddCommand(R"({
+    'name' : '_accessControlBlackList.unblock',
+    'component': 'accessControl',
+    'parameters': {
+      'userId': 'AQID',
+      'applicationId': 'AwQF',
+      'expirationTimeoutSec': 1234
+    }
+  })");
+
+  auto expected = R"({
+    "capacity": 10,
+    "size": 4
+  })";
+  EXPECT_JSON_EQ(expected, *GetState());
+}
+
+TEST_F(AccessApiHandlerTest, List) {
+  std::vector<AccessBlackListManager::Entry> entries{
+      {{11, 12, 13}, {21, 22, 23}, base::Time::FromTimeT(1410000000)},
+      {{31, 32, 33}, {41, 42, 43}, base::Time::FromTimeT(1420000000)},
+  };
+  EXPECT_CALL(access_manager_, GetEntries()).WillOnce(Return(entries));
+  EXPECT_CALL(access_manager_, GetSize()).WillRepeatedly(Return(4));
+
+  auto expected = R"({
+    "blackList": [ {
+      "applicationId": "FRYX",
+      "userId": "CwwN"
+    }, {
+       "applicationId": "KSor",
+       "userId": "HyAh"
+    } ]
+  })";
+
+  const auto& results = AddCommand(R"({
+    'name' : '_accessControlBlackList.list',
+    'component': 'accessControl',
+    'parameters': {
+    }
+  })");
+
+  EXPECT_JSON_EQ(expected, results);
+}
+}  // namespace weave
diff --git a/src/access_black_list_manager.h b/src/access_black_list_manager.h
new file mode 100644
index 0000000..b56226a
--- /dev/null
+++ b/src/access_black_list_manager.h
@@ -0,0 +1,56 @@
+// Copyright 2016 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBWEAVE_SRC_ACCESS_BLACK_LIST_H_
+#define LIBWEAVE_SRC_ACCESS_BLACK_LIST_H_
+
+#include <vector>
+
+#include <base/time/time.h>
+
+namespace weave {
+
+class AccessBlackListManager {
+ public:
+  struct Entry {
+    // user_id is empty, app_id is empty: block everything.
+    // user_id is not empty, app_id is empty: block if user_id matches.
+    // user_id is empty, app_id is not empty: block if app_id matches.
+    // user_id is not empty, app_id is not empty: block if both match.
+    std::vector<uint8_t> user_id;
+    std::vector<uint8_t> app_id;
+
+    // Time after which to discard the rule.
+    base::Time expiration;
+  };
+  virtual ~AccessBlackListManager() = default;
+
+  virtual void Block(const std::vector<uint8_t>& user_id,
+                     const std::vector<uint8_t>& app_id,
+                     const base::Time& expiration,
+                     const DoneCallback& callback) = 0;
+  virtual void Unblock(const std::vector<uint8_t>& user_id,
+                       const std::vector<uint8_t>& app_id,
+                       const DoneCallback& callback) = 0;
+  virtual bool IsBlocked(const std::vector<uint8_t>& user_id,
+                         const std::vector<uint8_t>& app_id) const = 0;
+  virtual std::vector<Entry> GetEntries() const = 0;
+  virtual size_t GetSize() const = 0;
+  virtual size_t GetCapacity() const = 0;
+};
+
+inline bool operator==(const AccessBlackListManager::Entry& l,
+                       const AccessBlackListManager::Entry& r) {
+  return l.user_id == r.user_id && l.app_id == r.app_id &&
+         l.expiration == r.expiration;
+}
+
+inline bool operator!=(const AccessBlackListManager::Entry& l,
+                       const AccessBlackListManager::Entry& r) {
+  return !(l == r);
+}
+
+}  // namespace weave
+
+#endif  // LIBWEAVE_SRC_ACCESS_BLACK_LIST_H_
diff --git a/src/access_black_list_manager_impl.cc b/src/access_black_list_manager_impl.cc
new file mode 100644
index 0000000..992a680
--- /dev/null
+++ b/src/access_black_list_manager_impl.cc
@@ -0,0 +1,163 @@
+// Copyright 2016 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/access_black_list_manager_impl.h"
+
+#include <base/json/json_reader.h>
+#include <base/json/json_writer.h>
+#include <base/values.h>
+
+#include "src/commands/schema_constants.h"
+#include "src/data_encoding.h"
+
+namespace weave {
+
+namespace {
+const char kConfigFileName[] = "black_list";
+
+const char kUser[] = "user";
+const char kApp[] = "app";
+const char kExpiration[] = "expiration";
+}
+
+AccessBlackListManagerImpl::AccessBlackListManagerImpl(
+    provider::ConfigStore* store,
+    size_t capacity,
+    base::Clock* clock)
+    : capacity_{capacity}, clock_{clock}, store_{store} {
+  Load();
+}
+
+void AccessBlackListManagerImpl::Load() {
+  if (!store_)
+    return;
+  if (auto list = base::ListValue::From(
+          base::JSONReader::Read(store_->LoadSettings(kConfigFileName)))) {
+    for (const auto& e : *list) {
+      const base::DictionaryValue* entry{nullptr};
+      std::string user;
+      std::string app;
+      decltype(entries_)::key_type key;
+      int expiration;
+      if (e->GetAsDictionary(&entry) && entry->GetString(kUser, &user) &&
+          Base64Decode(user, &key.first) && entry->GetString(kApp, &app) &&
+          Base64Decode(app, &key.second) &&
+          entry->GetInteger(kExpiration, &expiration)) {
+        base::Time expiration_time = base::Time::FromTimeT(expiration);
+        if (expiration_time > clock_->Now())
+          entries_[key] = expiration_time;
+      }
+    }
+    if (entries_.size() < list->GetSize()) {
+      // Save some storage space by saving without expired entries.
+      Save({});
+    }
+  }
+}
+
+void AccessBlackListManagerImpl::Save(const DoneCallback& callback) {
+  if (!store_) {
+    if (!callback.is_null())
+      callback.Run(nullptr);
+    return;
+  }
+
+  base::ListValue list;
+  for (const auto& e : entries_) {
+    scoped_ptr<base::DictionaryValue> entry{new base::DictionaryValue};
+    entry->SetString(kUser, Base64Encode(e.first.first));
+    entry->SetString(kApp, Base64Encode(e.first.second));
+    entry->SetInteger(kExpiration, e.second.ToTimeT());
+    list.Append(std::move(entry));
+  }
+
+  std::string json;
+  base::JSONWriter::Write(list, &json);
+  store_->SaveSettings(kConfigFileName, json, callback);
+}
+
+void AccessBlackListManagerImpl::RemoveExpired() {
+  for (auto i = begin(entries_); i != end(entries_);) {
+    if (i->second <= clock_->Now())
+      i = entries_.erase(i);
+    else
+      ++i;
+  }
+}
+
+void AccessBlackListManagerImpl::Block(const std::vector<uint8_t>& user_id,
+                                       const std::vector<uint8_t>& app_id,
+                                       const base::Time& expiration,
+                                       const DoneCallback& callback) {
+  // Iterating is OK as Save below is more expensive.
+  RemoveExpired();
+  if (expiration <= clock_->Now()) {
+    if (!callback.is_null()) {
+      ErrorPtr error;
+      Error::AddTo(&error, FROM_HERE, "aleady_expired",
+                   "Entry already expired");
+      callback.Run(std::move(error));
+    }
+    return;
+  }
+  if (entries_.size() >= capacity_) {
+    if (!callback.is_null()) {
+      ErrorPtr error;
+      Error::AddTo(&error, FROM_HERE, "blacklist_is_full",
+                   "Unable to store more entries");
+      callback.Run(std::move(error));
+    }
+    return;
+  }
+  auto& value = entries_[std::make_pair(user_id, app_id)];
+  value = std::max(value, expiration);
+  Save(callback);
+}
+
+void AccessBlackListManagerImpl::Unblock(const std::vector<uint8_t>& user_id,
+                                         const std::vector<uint8_t>& app_id,
+                                         const DoneCallback& callback) {
+  if (!entries_.erase(std::make_pair(user_id, app_id))) {
+    if (!callback.is_null()) {
+      ErrorPtr error;
+      Error::AddTo(&error, FROM_HERE, "entry_not_found", "Unknown entry");
+      callback.Run(std::move(error));
+    }
+    return;
+  }
+  // Iterating is OK as Save below is more expensive.
+  RemoveExpired();
+  Save(callback);
+}
+
+bool AccessBlackListManagerImpl::IsBlocked(
+    const std::vector<uint8_t>& user_id,
+    const std::vector<uint8_t>& app_id) const {
+  for (const auto& user : {{}, user_id}) {
+    for (const auto& app : {{}, app_id}) {
+      auto both = entries_.find(std::make_pair(user, app));
+      if (both != end(entries_) && both->second > clock_->Now())
+        return true;
+    }
+  }
+  return false;
+}
+
+std::vector<AccessBlackListManager::Entry>
+AccessBlackListManagerImpl::GetEntries() const {
+  std::vector<Entry> result;
+  for (const auto& e : entries_)
+    result.push_back({e.first.first, e.first.second, e.second});
+  return result;
+}
+
+size_t AccessBlackListManagerImpl::GetSize() const {
+  return entries_.size();
+}
+
+size_t AccessBlackListManagerImpl::GetCapacity() const {
+  return capacity_;
+}
+
+}  // namespace weave
diff --git a/src/access_black_list_manager_impl.h b/src/access_black_list_manager_impl.h
new file mode 100644
index 0000000..1c175db
--- /dev/null
+++ b/src/access_black_list_manager_impl.h
@@ -0,0 +1,58 @@
+// Copyright 2016 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBWEAVE_SRC_ACCESS_BLACK_LIST_IMPL_H_
+#define LIBWEAVE_SRC_ACCESS_BLACK_LIST_IMPL_H_
+
+#include <map>
+#include <utility>
+
+#include <base/time/default_clock.h>
+#include <base/time/time.h>
+#include <weave/error.h>
+#include <weave/provider/config_store.h>
+
+#include "src/access_black_list_manager.h"
+
+namespace weave {
+
+class AccessBlackListManagerImpl : public AccessBlackListManager {
+ public:
+  explicit AccessBlackListManagerImpl(provider::ConfigStore* store,
+                                      size_t capacity = 1024,
+                                      base::Clock* clock = nullptr);
+
+  // AccessBlackListManager implementation.
+  void Block(const std::vector<uint8_t>& user_id,
+             const std::vector<uint8_t>& app_id,
+             const base::Time& expiration,
+             const DoneCallback& callback) override;
+  void Unblock(const std::vector<uint8_t>& user_id,
+               const std::vector<uint8_t>& app_id,
+               const DoneCallback& callback) override;
+  bool IsBlocked(const std::vector<uint8_t>& user_id,
+                 const std::vector<uint8_t>& app_id) const override;
+  std::vector<Entry> GetEntries() const override;
+  size_t GetSize() const override;
+  size_t GetCapacity() const override;
+
+ private:
+  void Load();
+  void Save(const DoneCallback& callback);
+  void RemoveExpired();
+
+  const size_t capacity_{0};
+  base::DefaultClock default_clock_;
+  base::Clock* clock_{&default_clock_};
+
+  provider::ConfigStore* store_{nullptr};
+  std::map<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>, base::Time>
+      entries_;
+
+  DISALLOW_COPY_AND_ASSIGN(AccessBlackListManagerImpl);
+};
+
+}  // namespace weave
+
+#endif  // LIBWEAVE_SRC_ACCESS_BLACK_LIST_IMPL_H_
diff --git a/src/access_black_list_manager_impl_unittest.cc b/src/access_black_list_manager_impl_unittest.cc
new file mode 100644
index 0000000..fd9f226
--- /dev/null
+++ b/src/access_black_list_manager_impl_unittest.cc
@@ -0,0 +1,165 @@
+// Copyright 2016 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/access_black_list_manager_impl.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <weave/provider/test/mock_config_store.h>
+#include <weave/test/unittest_utils.h>
+
+#include "src/test/mock_clock.h"
+#include "src/bind_lambda.h"
+
+using testing::_;
+using testing::Return;
+using testing::StrictMock;
+
+namespace weave {
+
+class AccessBlackListManagerImplTest : public testing::Test {
+ protected:
+  void SetUp() {
+    std::string to_load = R"([{
+      "user": "BQID",
+      "app": "BwQF",
+      "expiration": 1410000000
+    }, {
+      "user": "AQID",
+      "app": "AwQF",
+      "expiration": 1419999999
+    }])";
+
+    EXPECT_CALL(config_store_, LoadSettings("black_list"))
+        .WillOnce(Return(to_load));
+
+    EXPECT_CALL(config_store_, SaveSettings("black_list", _, _))
+        .WillOnce(testing::WithArgs<1, 2>(testing::Invoke(
+            [](const std::string& json, const DoneCallback& callback) {
+              std::string to_save = R"([{
+                "user": "AQID",
+                "app": "AwQF",
+                "expiration": 1419999999
+              }])";
+              EXPECT_JSON_EQ(to_save, *test::CreateValue(json));
+              if (!callback.is_null())
+                callback.Run(nullptr);
+            })));
+
+    EXPECT_CALL(clock_, Now())
+        .WillRepeatedly(Return(base::Time::FromTimeT(1412121212)));
+    manager_.reset(new AccessBlackListManagerImpl{&config_store_, 10, &clock_});
+  }
+  StrictMock<test::MockClock> clock_;
+  StrictMock<provider::test::MockConfigStore> config_store_{false};
+  std::unique_ptr<AccessBlackListManagerImpl> manager_;
+};
+
+TEST_F(AccessBlackListManagerImplTest, Init) {
+  EXPECT_EQ(1u, manager_->GetSize());
+  EXPECT_EQ(10u, manager_->GetCapacity());
+  EXPECT_EQ((std::vector<AccessBlackListManagerImpl::Entry>{{
+                {1, 2, 3}, {3, 4, 5}, base::Time::FromTimeT(1419999999),
+            }}),
+            manager_->GetEntries());
+}
+
+TEST_F(AccessBlackListManagerImplTest, Block) {
+  EXPECT_CALL(config_store_, SaveSettings("black_list", _, _))
+      .WillOnce(testing::WithArgs<1, 2>(testing::Invoke(
+          [](const std::string& json, const DoneCallback& callback) {
+            std::string to_save = R"([{
+                "user": "AQID",
+                "app": "AwQF",
+                "expiration": 1419999999
+              }, {
+                "app": "CAgI",
+                "user": "BwcH",
+                "expiration": 1419990000
+              }])";
+            EXPECT_JSON_EQ(to_save, *test::CreateValue(json));
+            if (!callback.is_null())
+              callback.Run(nullptr);
+          })));
+  manager_->Block({7, 7, 7}, {8, 8, 8}, base::Time::FromTimeT(1419990000), {});
+}
+
+TEST_F(AccessBlackListManagerImplTest, BlockExpired) {
+  manager_->Block({}, {}, base::Time::FromTimeT(1400000000),
+                  base::Bind([](ErrorPtr error) {
+                    EXPECT_TRUE(error->HasError("aleady_expired"));
+                  }));
+}
+
+TEST_F(AccessBlackListManagerImplTest, BlockListIsFull) {
+  EXPECT_CALL(config_store_, SaveSettings("black_list", _, _))
+      .WillRepeatedly(testing::WithArgs<1, 2>(testing::Invoke(
+          [](const std::string& json, const DoneCallback& callback) {
+            if (!callback.is_null())
+              callback.Run(nullptr);
+          })));
+  for (size_t i = manager_->GetSize(); i < manager_->GetCapacity(); ++i) {
+    manager_->Block(
+        {99, static_cast<uint8_t>(i / 256), static_cast<uint8_t>(i % 256)},
+        {8, 8, 8}, base::Time::FromTimeT(1419990000), {});
+    EXPECT_EQ(i + 1, manager_->GetSize());
+  }
+  manager_->Block({99}, {8, 8, 8}, base::Time::FromTimeT(1419990000),
+                  base::Bind([](ErrorPtr error) {
+                    EXPECT_TRUE(error->HasError("blacklist_is_full"));
+                  }));
+}
+
+TEST_F(AccessBlackListManagerImplTest, Unblock) {
+  EXPECT_CALL(config_store_, SaveSettings("black_list", _, _))
+      .WillOnce(testing::WithArgs<1, 2>(testing::Invoke(
+          [](const std::string& json, const DoneCallback& callback) {
+            EXPECT_JSON_EQ("[]", *test::CreateValue(json));
+            if (!callback.is_null())
+              callback.Run(nullptr);
+          })));
+  manager_->Unblock({1, 2, 3}, {3, 4, 5}, {});
+}
+
+TEST_F(AccessBlackListManagerImplTest, UnblockNotFound) {
+  manager_->Unblock({5, 2, 3}, {5, 4, 5}, base::Bind([](ErrorPtr error) {
+                      EXPECT_TRUE(error->HasError("entry_not_found"));
+                    }));
+}
+
+TEST_F(AccessBlackListManagerImplTest, IsBlockedFalse) {
+  EXPECT_FALSE(manager_->IsBlocked({7, 7, 7}, {8, 8, 8}));
+}
+
+class AccessBlackListManagerImplIsBlockedTest
+    : public AccessBlackListManagerImplTest,
+      public testing::WithParamInterface<
+          std::tuple<std::vector<uint8_t>, std::vector<uint8_t>>> {
+ public:
+  void SetUp() override {
+    AccessBlackListManagerImplTest::SetUp();
+    EXPECT_CALL(config_store_, SaveSettings("black_list", _, _))
+        .WillOnce(testing::WithArgs<2>(
+            testing::Invoke([](const DoneCallback& callback) {
+              if (!callback.is_null())
+                callback.Run(nullptr);
+            })));
+    manager_->Block(std::get<0>(GetParam()), std::get<1>(GetParam()),
+                    base::Time::FromTimeT(1419990000), {});
+  }
+};
+
+TEST_P(AccessBlackListManagerImplIsBlockedTest, IsBlocked) {
+  EXPECT_TRUE(manager_->IsBlocked({7, 7, 7}, {8, 8, 8}));
+}
+
+INSTANTIATE_TEST_CASE_P(
+    Filters,
+    AccessBlackListManagerImplIsBlockedTest,
+    testing::Combine(testing::Values(std::vector<uint8_t>{},
+                                     std::vector<uint8_t>{7, 7, 7}),
+                     testing::Values(std::vector<uint8_t>{},
+                                     std::vector<uint8_t>{8, 8, 8})));
+
+}  // namespace weave
diff --git a/src/base_api_handler.h b/src/base_api_handler.h
index 1dbbac8..6eebfca 100644
--- a/src/base_api_handler.h
+++ b/src/base_api_handler.h
@@ -33,7 +33,7 @@
   void OnConfigChanged(const Settings& settings);
 
   DeviceRegistrationInfo* device_info_;
-  Device* device_;
+  Device* device_{nullptr};
 
   base::WeakPtrFactory<BaseApiHandler> weak_ptr_factory_{this};
   DISALLOW_COPY_AND_ASSIGN(BaseApiHandler);
diff --git a/src/device_manager.cc b/src/device_manager.cc
index 097f854..deb5404 100644
--- a/src/device_manager.cc
+++ b/src/device_manager.cc
@@ -8,6 +8,8 @@
 
 #include <base/bind.h>
 
+#include "src/access_api_handler.h"
+#include "src/access_black_list_manager_impl.h"
 #include "src/base_api_handler.h"
 #include "src/commands/schema_constants.h"
 #include "src/component_manager_impl.h"
@@ -40,6 +42,10 @@
       network, auth_manager_.get()));
   base_api_handler_.reset(new BaseApiHandler{device_info_.get(), this});
 
+  black_list_manager_.reset(new AccessBlackListManagerImpl{config_store});
+  access_api_handler_.reset(
+      new AccessApiHandler{this, black_list_manager_.get()});
+
   device_info_->Start();
 
   if (http_server) {
diff --git a/src/device_manager.h b/src/device_manager.h
index d40ba8e..d77bacc 100644
--- a/src/device_manager.h
+++ b/src/device_manager.h
@@ -10,6 +10,8 @@
 
 namespace weave {
 
+class AccessApiHandler;
+class AccessBlackListManager;
 class BaseApiHandler;
 class Config;
 class ComponentManager;
@@ -107,6 +109,8 @@
   std::unique_ptr<ComponentManager> component_manager_;
   std::unique_ptr<DeviceRegistrationInfo> device_info_;
   std::unique_ptr<BaseApiHandler> base_api_handler_;
+  std::unique_ptr<AccessBlackListManager> black_list_manager_;
+  std::unique_ptr<AccessApiHandler> access_api_handler_;
   std::unique_ptr<privet::Manager> privet_;
 
   base::WeakPtrFactory<DeviceManager> weak_ptr_factory_{this};
diff --git a/src/privet/auth_manager.cc b/src/privet/auth_manager.cc
index 66d04c4..c82887e 100644
--- a/src/privet/auth_manager.cc
+++ b/src/privet/auth_manager.cc
@@ -18,6 +18,7 @@
 
 extern "C" {
 #include "third_party/libuweave/src/macaroon.h"
+#include "third_party/libuweave/src/macaroon_caveat_internal.h"
 }
 
 namespace weave {
@@ -25,9 +26,19 @@
 
 namespace {
 
+const time_t kJ2000ToTimeT = 946684800;
 const size_t kMaxMacaroonSize = 1024;
 const size_t kMaxPendingClaims = 10;
 const char kInvalidTokenError[] = "invalid_token";
+const int kSessionIdTtlMinutes = 1;
+
+uint32_t ToJ2000Time(const base::Time& time) {
+  return std::max(time.ToTimeT(), kJ2000ToTimeT) - kJ2000ToTimeT;
+}
+
+base::Time FromJ2000Time(uint32_t time) {
+  return base::Time::FromTimeT(time + kJ2000ToTimeT);
+}
 
 template <class T>
 void AppendToArray(T value, std::vector<uint8_t>* array) {
@@ -37,78 +48,108 @@
 
 class Caveat {
  public:
-  // TODO(vitalybuka): Use _get_buffer_size_ when available.
-  Caveat(UwMacaroonCaveatType type, uint32_t value) : buffer(8) {
-    CHECK(uw_macaroon_caveat_create_with_uint_(type, value, buffer.data(),
-                                               buffer.size(), &caveat));
+  Caveat(UwMacaroonCaveatType type, size_t str_len)
+      : buffer_(uw_macaroon_caveat_creation_get_buffsize_(type, str_len)) {
+    CHECK(!buffer_.empty());
   }
+  const UwMacaroonCaveat& GetCaveat() const { return caveat_; }
 
-  // TODO(vitalybuka): Use _get_buffer_size_ when available.
-  Caveat(UwMacaroonCaveatType type, const std::string& value)
-      : buffer(std::max<size_t>(value.size(), 32u) * 2) {
-    CHECK(uw_macaroon_caveat_create_with_str_(
-        type, reinterpret_cast<const uint8_t*>(value.data()), value.size(),
-        buffer.data(), buffer.size(), &caveat));
-  }
-
-  const UwMacaroonCaveat& GetCaveat() const { return caveat; }
-
- private:
-  UwMacaroonCaveat caveat;
-  std::vector<uint8_t> buffer;
+ protected:
+  UwMacaroonCaveat caveat_{};
+  std::vector<uint8_t> buffer_;
 
   DISALLOW_COPY_AND_ASSIGN(Caveat);
 };
 
-bool CheckCaveatType(const UwMacaroonCaveat& caveat,
-                     UwMacaroonCaveatType type,
-                     ErrorPtr* error) {
-  UwMacaroonCaveatType caveat_type{};
-  if (!uw_macaroon_caveat_get_type_(&caveat, &caveat_type)) {
-    return Error::AddTo(error, FROM_HERE, kInvalidTokenError,
-                        "Unable to get type");
+class ScopeCaveat : public Caveat {
+ public:
+  explicit ScopeCaveat(UwMacaroonCaveatScopeType scope)
+      : Caveat(kUwMacaroonCaveatTypeScope, 0) {
+    CHECK(uw_macaroon_caveat_create_scope_(scope, buffer_.data(),
+                                           buffer_.size(), &caveat_));
   }
 
-  if (caveat_type != type) {
-    return Error::AddTo(error, FROM_HERE, kInvalidTokenError,
-                        "Unexpected caveat type");
+  DISALLOW_COPY_AND_ASSIGN(ScopeCaveat);
+};
+
+class TimestampCaveat : public Caveat {
+ public:
+  explicit TimestampCaveat(const base::Time& timestamp)
+      : Caveat(kUwMacaroonCaveatTypeDelegationTimestamp, 0) {
+    CHECK(uw_macaroon_caveat_create_delegation_timestamp_(
+        ToJ2000Time(timestamp), buffer_.data(), buffer_.size(), &caveat_));
   }
 
-  return true;
-}
+  DISALLOW_COPY_AND_ASSIGN(TimestampCaveat);
+};
 
-bool ReadCaveat(const UwMacaroonCaveat& caveat,
-                UwMacaroonCaveatType type,
-                uint32_t* value,
-                ErrorPtr* error) {
-  if (!CheckCaveatType(caveat, type, error))
-    return false;
-
-  if (!uw_macaroon_caveat_get_value_uint_(&caveat, value)) {
-    return Error::AddTo(error, FROM_HERE, kInvalidTokenError,
-                        "Unable to read caveat");
+class ExpirationCaveat : public Caveat {
+ public:
+  explicit ExpirationCaveat(const base::Time& timestamp)
+      : Caveat(kUwMacaroonCaveatTypeExpirationAbsolute, 0) {
+    CHECK(uw_macaroon_caveat_create_expiration_absolute_(
+        ToJ2000Time(timestamp), buffer_.data(), buffer_.size(), &caveat_));
   }
 
-  return true;
-}
+  DISALLOW_COPY_AND_ASSIGN(ExpirationCaveat);
+};
 
-bool ReadCaveat(const UwMacaroonCaveat& caveat,
-                UwMacaroonCaveatType type,
-                std::string* value,
-                ErrorPtr* error) {
-  if (!CheckCaveatType(caveat, type, error))
-    return false;
-
-  const uint8_t* start{nullptr};
-  size_t size{0};
-  if (!uw_macaroon_caveat_get_value_str_(&caveat, &start, &size)) {
-    return Error::AddTo(error, FROM_HERE, kInvalidTokenError,
-                        "Unable to read caveat");
+class UserIdCaveat : public Caveat {
+ public:
+  explicit UserIdCaveat(const std::vector<uint8_t>& id)
+      : Caveat(kUwMacaroonCaveatTypeDelegateeUser, id.size()) {
+    CHECK(uw_macaroon_caveat_create_delegatee_user_(
+        id.data(), id.size(), buffer_.data(), buffer_.size(), &caveat_));
   }
 
-  value->assign(reinterpret_cast<const char*>(start), size);
-  return true;
-}
+  DISALLOW_COPY_AND_ASSIGN(UserIdCaveat);
+};
+
+class AppIdCaveat : public Caveat {
+ public:
+  explicit AppIdCaveat(const std::vector<uint8_t>& id)
+      : Caveat(kUwMacaroonCaveatTypeDelegateeApp, id.size()) {
+    CHECK(uw_macaroon_caveat_create_delegatee_app_(
+        id.data(), id.size(), buffer_.data(), buffer_.size(), &caveat_));
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(AppIdCaveat);
+};
+
+class ServiceCaveat : public Caveat {
+ public:
+  explicit ServiceCaveat(const std::string& id)
+      : Caveat(kUwMacaroonCaveatTypeDelegateeService, id.size()) {
+    CHECK(uw_macaroon_caveat_create_delegatee_service_(
+        reinterpret_cast<const uint8_t*>(id.data()), id.size(), buffer_.data(),
+        buffer_.size(), &caveat_));
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(ServiceCaveat);
+};
+
+class SessionIdCaveat : public Caveat {
+ public:
+  explicit SessionIdCaveat(const std::string& id)
+      : Caveat(kUwMacaroonCaveatTypeLanSessionID, id.size()) {
+    CHECK(uw_macaroon_caveat_create_lan_session_id_(
+        reinterpret_cast<const uint8_t*>(id.data()), id.size(), buffer_.data(),
+        buffer_.size(), &caveat_));
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(SessionIdCaveat);
+};
+
+class ClientAuthTokenCaveat : public Caveat {
+ public:
+  ClientAuthTokenCaveat()
+      : Caveat(kUwMacaroonCaveatTypeClientAuthorizationTokenV1, 0) {
+    CHECK(uw_macaroon_caveat_create_client_authorization_token_(
+        nullptr, 0, buffer_.data(), buffer_.size(), &caveat_));
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(ClientAuthTokenCaveat);
+};
 
 std::vector<uint8_t> CreateSecret() {
   std::vector<uint8_t> secret(kSha256OutputSize);
@@ -122,18 +163,53 @@
 
 std::vector<uint8_t> CreateMacaroonToken(
     const std::vector<uint8_t>& secret,
-    const std::vector<UwMacaroonCaveat>& caveats) {
+    const base::Time& time,
+    const std::vector<const UwMacaroonCaveat*>& caveats) {
   CHECK_EQ(kSha256OutputSize, secret.size());
+
+  UwMacaroonContext context{};
+  CHECK(uw_macaroon_context_create_(ToJ2000Time(time), nullptr, 0, &context));
+
   UwMacaroon macaroon{};
-  CHECK(uw_macaroon_new_from_root_key_(&macaroon, secret.data(), secret.size(),
-                                       caveats.data(), caveats.size()));
+  CHECK(uw_macaroon_create_from_root_key_(&macaroon, secret.data(),
+                                          secret.size(), &context,
+                                          caveats.data(), caveats.size()));
 
-  std::vector<uint8_t> token(kMaxMacaroonSize);
+  std::vector<uint8_t> serialized_token(kMaxMacaroonSize);
   size_t len = 0;
-  CHECK(uw_macaroon_dump_(&macaroon, token.data(), token.size(), &len));
-  token.resize(len);
+  CHECK(uw_macaroon_serialize_(&macaroon, serialized_token.data(),
+                               serialized_token.size(), &len));
+  serialized_token.resize(len);
 
-  return token;
+  return serialized_token;
+}
+
+std::vector<uint8_t> ExtendMacaroonToken(
+    const UwMacaroon& macaroon,
+    const base::Time& time,
+    const std::vector<const UwMacaroonCaveat*>& caveats) {
+  UwMacaroonContext context{};
+  CHECK(uw_macaroon_context_create_(ToJ2000Time(time), nullptr, 0, &context));
+
+  UwMacaroon prev_macaroon = macaroon;
+  std::vector<uint8_t> prev_buffer(kMaxMacaroonSize);
+  std::vector<uint8_t> new_buffer(kMaxMacaroonSize);
+
+  for (auto caveat : caveats) {
+    UwMacaroon new_macaroon{};
+    CHECK(uw_macaroon_extend_(&prev_macaroon, &new_macaroon, &context, caveat,
+                              new_buffer.data(), new_buffer.size()));
+    new_buffer.swap(prev_buffer);
+    prev_macaroon = new_macaroon;
+  }
+
+  std::vector<uint8_t> serialized_token(kMaxMacaroonSize);
+  size_t len = 0;
+  CHECK(uw_macaroon_serialize_(&prev_macaroon, serialized_token.data(),
+                               serialized_token.size(), &len));
+  serialized_token.resize(len);
+
+  return serialized_token;
 }
 
 bool LoadMacaroon(const std::vector<uint8_t>& token,
@@ -141,8 +217,8 @@
                   UwMacaroon* macaroon,
                   ErrorPtr* error) {
   buffer->resize(kMaxMacaroonSize);
-  if (!uw_macaroon_load_(token.data(), token.size(), buffer->data(),
-                         buffer->size(), macaroon)) {
+  if (!uw_macaroon_deserialize_(token.data(), token.size(), buffer->data(),
+                                buffer->size(), macaroon)) {
     return Error::AddTo(error, FROM_HERE, kInvalidTokenError,
                         "Invalid token format");
   }
@@ -151,10 +227,16 @@
 
 bool VerifyMacaroon(const std::vector<uint8_t>& secret,
                     const UwMacaroon& macaroon,
+                    const base::Time& time,
+                    UwMacaroonValidationResult* result,
                     ErrorPtr* error) {
   CHECK_EQ(kSha256OutputSize, secret.size());
-  if (!uw_macaroon_verify_(&macaroon, secret.data(), secret.size())) {
-    return Error::AddTo(error, FROM_HERE, "invalid_signature",
+  UwMacaroonContext context = {};
+  CHECK(uw_macaroon_context_create_(ToJ2000Time(time), nullptr, 0, &context));
+
+  if (!uw_macaroon_validate_(&macaroon, secret.data(), secret.size(), &context,
+                             result)) {
+    return Error::AddTo(error, FROM_HERE, "invalid_token",
                         "Invalid token signature");
   }
   return true;
@@ -239,14 +321,22 @@
 
 std::vector<uint8_t> AuthManager::CreateAccessToken(const UserInfo& user_info,
                                                     base::TimeDelta ttl) const {
-  Caveat scope{kUwMacaroonCaveatTypeScope, ToMacaroonScope(user_info.scope())};
-  Caveat user{kUwMacaroonCaveatTypeIdentifier, user_info.user_id()};
-  Caveat issued{kUwMacaroonCaveatTypeExpiration,
-                static_cast<uint32_t>((Now() + ttl).ToTimeT())};
+  const base::Time now = Now();
+  TimestampCaveat issued{now};
+  ScopeCaveat scope{ToMacaroonScope(user_info.scope())};
+  // Macaroons have no caveats for auth type. So we just append the type to the
+  // user ID.
+  std::vector<uint8_t> id_with_type{user_info.id().user};
+  id_with_type.push_back(static_cast<uint8_t>(user_info.id().type));
+  UserIdCaveat user{id_with_type};
+  AppIdCaveat app{user_info.id().app};
+  ExpirationCaveat expiration{now + ttl};
   return CreateMacaroonToken(
-      access_secret_,
+      access_secret_, now,
       {
-          scope.GetCaveat(), user.GetCaveat(), issued.GetCaveat(),
+
+          &issued.GetCaveat(), &scope.GetCaveat(), &user.GetCaveat(),
+          &app.GetCaveat(), &expiration.GetCaveat(),
       });
 }
 
@@ -256,37 +346,40 @@
   std::vector<uint8_t> buffer;
   UwMacaroon macaroon{};
 
-  uint32_t scope{0};
-  std::string user_id;
-  uint32_t expiration{0};
-
+  UwMacaroonValidationResult result{};
+  const base::Time now = Now();
   if (!LoadMacaroon(token, &buffer, &macaroon, error) ||
-      !VerifyMacaroon(access_secret_, macaroon, error) ||
-      macaroon.num_caveats != 3 ||
-      !ReadCaveat(macaroon.caveats[0], kUwMacaroonCaveatTypeScope, &scope,
-                  error) ||
-      !ReadCaveat(macaroon.caveats[1], kUwMacaroonCaveatTypeIdentifier,
-                  &user_id, error) ||
-      !ReadCaveat(macaroon.caveats[2], kUwMacaroonCaveatTypeExpiration,
-                  &expiration, error)) {
+      macaroon.num_caveats != 5 ||
+      !VerifyMacaroon(access_secret_, macaroon, now, &result, error)) {
     return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthorization,
                         "Invalid token");
   }
 
-  AuthScope auth_scope{FromMacaroonScope(scope)};
+  AuthScope auth_scope{FromMacaroonScope(result.granted_scope)};
   if (auth_scope == AuthScope::kNone) {
     return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthorization,
                         "Invalid token data");
   }
 
-  base::Time time{base::Time::FromTimeT(expiration)};
-  if (time < clock_->Now()) {
-    return Error::AddTo(error, FROM_HERE, errors::kAuthorizationExpired,
-                        "Token is expired");
-  }
+  // If token is valid and token was not extended, it should has precisely this
+  // values.
+  CHECK_GE(FromJ2000Time(result.expiration_time), now);
+  CHECK_EQ(2u, result.num_delegatees);
+  CHECK_EQ(kUwMacaroonDelegateeTypeUser, result.delegatees[0].type);
+  CHECK_EQ(kUwMacaroonDelegateeTypeApp, result.delegatees[1].type);
+  CHECK_GT(result.delegatees[0].id_len, 1u);
+  std::vector<uint8_t> user_id{
+      result.delegatees[0].id,
+      result.delegatees[0].id + result.delegatees[0].id_len};
+  // Last byte is used for type. See |CreateAccessToken|.
+  AuthType type = static_cast<AuthType>(user_id.back());
+  user_id.pop_back();
 
+  std::vector<uint8_t> app_id{
+      result.delegatees[1].id,
+      result.delegatees[1].id + result.delegatees[1].id_len};
   if (user_info)
-    *user_info = UserInfo{auth_scope, user_id};
+    *user_info = UserInfo{auth_scope, UserAppId{type, user_id, app_id}};
 
   return true;
 }
@@ -309,7 +402,7 @@
       std::unique_ptr<AuthManager>{new AuthManager{nullptr, {}}}, owner));
   if (pending_claims_.size() > kMaxPendingClaims)
     pending_claims_.pop_front();
-  return pending_claims_.back().first->GetRootClientAuthToken();
+  return pending_claims_.back().first->GetRootClientAuthToken(owner);
 }
 
 bool AuthManager::ConfirmClientAuthToken(const std::vector<uint8_t>& token,
@@ -332,14 +425,20 @@
   return true;
 }
 
-std::vector<uint8_t> AuthManager::GetRootClientAuthToken() const {
-  Caveat scope{kUwMacaroonCaveatTypeScope, kUwMacaroonCaveatScopeTypeOwner};
-  Caveat issued{kUwMacaroonCaveatTypeIssued,
-                static_cast<uint32_t>(Now().ToTimeT())};
-  return CreateMacaroonToken(auth_secret_,
-                             {
-                                 scope.GetCaveat(), issued.GetCaveat(),
-                             });
+std::vector<uint8_t> AuthManager::GetRootClientAuthToken(
+    RootClientTokenOwner owner) const {
+  CHECK(RootClientTokenOwner::kNone != owner);
+  ClientAuthTokenCaveat auth_token;
+  const base::Time now = Now();
+  TimestampCaveat issued{now};
+
+  ServiceCaveat client{owner == RootClientTokenOwner::kCloud ? "google.com"
+                                                             : ""};
+  return CreateMacaroonToken(
+      auth_secret_, now,
+      {
+          &auth_token.GetCaveat(), &issued.GetCaveat(), &client.GetCaveat(),
+      });
 }
 
 base::Time AuthManager::Now() const {
@@ -350,8 +449,9 @@
                                    ErrorPtr* error) const {
   std::vector<uint8_t> buffer;
   UwMacaroon macaroon{};
+  UwMacaroonValidationResult result{};
   if (!LoadMacaroon(token, &buffer, &macaroon, error) ||
-      !VerifyMacaroon(auth_secret_, macaroon, error)) {
+      !VerifyMacaroon(auth_secret_, macaroon, Now(), &result, error)) {
     return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode,
                         "Invalid token");
   }
@@ -365,19 +465,63 @@
     AuthScope* access_token_scope,
     base::TimeDelta* access_token_ttl,
     ErrorPtr* error) const {
-  // TODO(vitalybuka): implement token validation.
-  if (!IsValidAuthToken(auth_token, error))
-    return false;
+  std::vector<uint8_t> buffer;
+  UwMacaroon macaroon{};
+  UwMacaroonValidationResult result{};
+  const base::Time now = Now();
+  if (!LoadMacaroon(auth_token, &buffer, &macaroon, error) ||
+      !VerifyMacaroon(auth_secret_, macaroon, now, &result, error)) {
+    return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode,
+                        "Invalid token");
+  }
+
+  AuthScope auth_scope{FromMacaroonScope(result.granted_scope)};
+  if (auth_scope == AuthScope::kNone) {
+    return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode,
+                        "Invalid token data");
+  }
+
+  // TODO: Integrate black list checks.
+  auto delegates_rbegin = std::reverse_iterator<const UwMacaroonDelegateeInfo*>(
+      result.delegatees + result.num_delegatees);
+  auto delegates_rend =
+      std::reverse_iterator<const UwMacaroonDelegateeInfo*>(result.delegatees);
+  auto last_user_id =
+      std::find_if(delegates_rbegin, delegates_rend,
+                   [](const UwMacaroonDelegateeInfo& delegatee) {
+                     return delegatee.type == kUwMacaroonDelegateeTypeUser;
+                   });
+  auto last_app_id =
+      std::find_if(delegates_rbegin, delegates_rend,
+                   [](const UwMacaroonDelegateeInfo& delegatee) {
+                     return delegatee.type == kUwMacaroonDelegateeTypeApp;
+                   });
+
+  if (last_user_id == delegates_rend || !last_user_id->id_len) {
+    return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode,
+                        "User ID is missing");
+  }
+
+  const char* session_id = reinterpret_cast<const char*>(result.lan_session_id);
+  if (!IsValidSessionId({session_id, session_id + result.lan_session_id_len})) {
+    return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode,
+                        "Invalid session id");
+  }
+
+  CHECK_GE(FromJ2000Time(result.expiration_time), now);
 
   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()};
+  std::vector<uint8_t> user_id{last_user_id->id,
+                               last_user_id->id + last_user_id->id_len};
+  std::vector<uint8_t> app_id;
+  if (last_app_id != delegates_rend)
+    app_id.assign(last_app_id->id, last_app_id->id + last_app_id->id_len);
 
-  // TODO(vitalybuka): TTL also should be reduced in accordance with auth_token.
+  UserInfo info{auth_scope, {AuthType::kLocal, user_id, app_id}};
+
+  ttl = std::min(ttl, FromJ2000Time(result.expiration_time) - now);
   *access_token = CreateAccessToken(info, ttl);
 
   if (access_token_scope)
@@ -388,11 +532,45 @@
   return true;
 }
 
-std::vector<uint8_t> AuthManager::CreateSessionId() {
-  std::vector<uint8_t> result;
-  AppendToArray(Now().ToTimeT(), &result);
-  AppendToArray(++session_counter_, &result);
-  return result;
+std::string AuthManager::CreateSessionId() const {
+  return std::to_string(ToJ2000Time(Now())) + ":" +
+         std::to_string(++session_counter_);
+}
+
+bool AuthManager::IsValidSessionId(const std::string& session_id) const {
+  base::Time ssid_time = FromJ2000Time(std::atoi(session_id.c_str()));
+  return Now() - base::TimeDelta::FromMinutes(kSessionIdTtlMinutes) <=
+             ssid_time &&
+         ssid_time <= Now();
+}
+
+std::vector<uint8_t> AuthManager::DelegateToUser(
+    const std::vector<uint8_t>& token,
+    base::TimeDelta ttl,
+    const UserInfo& user_info) const {
+  std::vector<uint8_t> buffer;
+  UwMacaroon macaroon{};
+  CHECK(LoadMacaroon(token, &buffer, &macaroon, nullptr));
+
+  const base::Time now = Now();
+  TimestampCaveat issued{now};
+  ExpirationCaveat expiration{now + ttl};
+  ScopeCaveat scope{ToMacaroonScope(user_info.scope())};
+  UserIdCaveat user{user_info.id().user};
+  AppIdCaveat app{user_info.id().app};
+  SessionIdCaveat session{CreateSessionId()};
+
+  std::vector<const UwMacaroonCaveat*> caveats{
+      &issued.GetCaveat(), &expiration.GetCaveat(), &scope.GetCaveat(),
+      &user.GetCaveat(),
+  };
+
+  if (!user_info.id().app.empty())
+    caveats.push_back(&app.GetCaveat());
+
+  caveats.push_back(&session.GetCaveat());
+
+  return ExtendMacaroonToken(macaroon, now, caveats);
 }
 
 }  // namespace privet
diff --git a/src/privet/auth_manager.h b/src/privet/auth_manager.h
index 309d80e..f0a5761 100644
--- a/src/privet/auth_manager.h
+++ b/src/privet/auth_manager.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include <base/gtest_prod_util.h>
 #include <base/time/default_clock.h>
 #include <base/time/time.h>
 #include <weave/error.h>
@@ -54,7 +55,7 @@
   bool ConfirmClientAuthToken(const std::vector<uint8_t>& token,
                               ErrorPtr* error);
 
-  std::vector<uint8_t> GetRootClientAuthToken() const;
+  std::vector<uint8_t> GetRootClientAuthToken(RootClientTokenOwner owner) const;
   bool IsValidAuthToken(const std::vector<uint8_t>& token,
                         ErrorPtr* error) const;
   bool CreateAccessTokenFromAuth(const std::vector<uint8_t>& auth_token,
@@ -67,13 +68,21 @@
   void SetAuthSecret(const std::vector<uint8_t>& secret,
                      RootClientTokenOwner owner);
 
-  std::vector<uint8_t> CreateSessionId();
+  std::string CreateSessionId() const;
+  bool IsValidSessionId(const std::string& session_id) const;
 
  private:
+  friend class AuthManagerTest;
+
+  // Test helpers. Device does not need to implement delegation.
+  std::vector<uint8_t> DelegateToUser(const std::vector<uint8_t>& token,
+                                      base::TimeDelta ttl,
+                                      const UserInfo& user_info) const;
+
   Config* config_{nullptr};  // Can be nullptr for tests.
   base::DefaultClock default_clock_;
   base::Clock* clock_{&default_clock_};
-  uint32_t session_counter_{0};
+  mutable uint32_t session_counter_{0};
 
   std::vector<uint8_t> auth_secret_;  // Persistent.
   std::vector<uint8_t> certificate_fingerprint_;
diff --git a/src/privet/auth_manager_unittest.cc b/src/privet/auth_manager_unittest.cc
index 70750ad..294aefa 100644
--- a/src/privet/auth_manager_unittest.cc
+++ b/src/privet/auth_manager_unittest.cc
@@ -10,6 +10,7 @@
 
 #include "src/config.h"
 #include "src/data_encoding.h"
+#include "src/privet/mock_delegates.h"
 #include "src/test/mock_clock.h"
 
 using testing::Return;
@@ -29,6 +30,11 @@
   }
 
  protected:
+  std::vector<uint8_t> DelegateToUser(const std::vector<uint8_t>& token,
+                                      base::TimeDelta ttl,
+                                      const UserInfo& user_info) const {
+    return auth_.DelegateToUser(token, ttl, user_info);
+  }
   const std::vector<uint8_t> kSecret1{
       78, 40, 39, 68, 29, 19, 70, 86, 38, 61, 13, 55, 33, 32, 51, 52,
       34, 43, 97, 48, 8,  56, 11, 99, 50, 59, 24, 26, 31, 71, 76, 28};
@@ -64,49 +70,90 @@
 }
 
 TEST_F(AuthManagerTest, CreateAccessToken) {
-  EXPECT_EQ("UABRUHgcSZDry0bvIsoJv+WDQgEURQJjMjM0RgUaVArkgA==",
+  EXPECT_EQ("WC2FRggaG52hAEIBFEYJRDIzNABCCkBGBRobnaEAUFAF46oQlMmXgnLstt7wU2w=",
             Base64Encode(auth_.CreateAccessToken(
-                UserInfo{AuthScope::kViewer, "234"}, {})));
-  EXPECT_EQ("UL7YEruLg5QQRDIp2+u1cqCDQgEIRQJjMjU3RgUaVArkgA==",
+                UserInfo{AuthScope::kViewer, TestUserId{"234"}}, {})));
+  EXPECT_EQ("WC2FRggaG52hAEIBCEYJRDI1NwBCCkBGBRobnaEAUEdWRNHcu/0mA6c3e0tgDrk=",
             Base64Encode(auth_.CreateAccessToken(
-                UserInfo{AuthScope::kManager, "257"}, {})));
-  EXPECT_EQ("UPFGeZRanR1wLGYLP5ZDkXiDQgECRQJjNDU2RgUaVArkgA==",
+                UserInfo{AuthScope::kManager, TestUserId{"257"}}, {})));
+  EXPECT_EQ("WC2FRggaG52hAEIBAkYJRDQ1NgBCCkBGBRobnaEAUH2ZLgUPdTtjNRa+PoDkMW4=",
             Base64Encode(auth_.CreateAccessToken(
-                UserInfo{AuthScope::kOwner, "456"}, {})));
+                UserInfo{AuthScope::kOwner, TestUserId{"456"}}, {})));
   auto new_time = clock_.Now() + base::TimeDelta::FromDays(11);
   EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time));
-  EXPECT_EQ("UMm9KlF3OEtZFBmhScJpl4uDQgEORQJjMzQ1RgUaVBllAA==",
+  EXPECT_EQ("WC2FRggaG6whgEIBDkYJRDM0NQBCCkBGBRobrCGAUDAFptj7bbYmbpaa6Wpb1Wo=",
             Base64Encode(auth_.CreateAccessToken(
-                UserInfo{AuthScope::kUser, "345"}, {})));
+                UserInfo{AuthScope::kUser, TestUserId{"345"}}, {})));
 }
 
 TEST_F(AuthManagerTest, CreateSameToken) {
-  EXPECT_EQ(auth_.CreateAccessToken(UserInfo{AuthScope::kViewer, "555"}, {}),
-            auth_.CreateAccessToken(UserInfo{AuthScope::kViewer, "555"}, {}));
+  EXPECT_EQ(auth_.CreateAccessToken(
+                UserInfo{AuthScope::kViewer, TestUserId{"555"}}, {}),
+            auth_.CreateAccessToken(
+                UserInfo{AuthScope::kViewer, TestUserId{"555"}}, {}));
+}
+
+TEST_F(AuthManagerTest, CreateSameTokenWithApp) {
+  EXPECT_EQ(auth_.CreateAccessToken(
+                UserInfo{AuthScope::kViewer,
+                         {AuthType::kLocal, {1, 2, 3}, {4, 5, 6}}},
+                {}),
+            auth_.CreateAccessToken(
+                UserInfo{AuthScope::kViewer,
+                         {AuthType::kLocal, {1, 2, 3}, {4, 5, 6}}},
+                {}));
+}
+
+TEST_F(AuthManagerTest, CreateSameTokenWithDifferentType) {
+  EXPECT_NE(auth_.CreateAccessToken(
+                UserInfo{AuthScope::kViewer,
+                         {AuthType::kLocal, {1, 2, 3}, {4, 5, 6}}},
+                {}),
+            auth_.CreateAccessToken(
+                UserInfo{AuthScope::kViewer,
+                         {AuthType::kPairing, {1, 2, 3}, {4, 5, 6}}},
+                {}));
+}
+
+TEST_F(AuthManagerTest, CreateSameTokenWithDifferentApp) {
+  EXPECT_NE(auth_.CreateAccessToken(
+                UserInfo{AuthScope::kViewer,
+                         {AuthType::kLocal, {1, 2, 3}, {4, 5, 6}}},
+                {}),
+            auth_.CreateAccessToken(
+                UserInfo{AuthScope::kViewer,
+                         {AuthType::kLocal, {1, 2, 3}, {4, 5, 7}}},
+                {}));
 }
 
 TEST_F(AuthManagerTest, CreateTokenDifferentScope) {
-  EXPECT_NE(auth_.CreateAccessToken(UserInfo{AuthScope::kViewer, "456"}, {}),
-            auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, "456"}, {}));
+  EXPECT_NE(auth_.CreateAccessToken(
+                UserInfo{AuthScope::kViewer, TestUserId{"456"}}, {}),
+            auth_.CreateAccessToken(
+                UserInfo{AuthScope::kOwner, TestUserId{"456"}}, {}));
 }
 
 TEST_F(AuthManagerTest, CreateTokenDifferentUser) {
-  EXPECT_NE(auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, "456"}, {}),
-            auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, "789"}, {}));
+  EXPECT_NE(auth_.CreateAccessToken(
+                UserInfo{AuthScope::kOwner, TestUserId{"456"}}, {}),
+            auth_.CreateAccessToken(
+                UserInfo{AuthScope::kOwner, TestUserId{"789"}}, {}));
 }
 
 TEST_F(AuthManagerTest, CreateTokenDifferentTime) {
-  auto token = auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, "567"}, {});
+  auto token = auth_.CreateAccessToken(
+      UserInfo{AuthScope::kOwner, TestUserId{"567"}}, {});
   EXPECT_CALL(clock_, Now())
       .WillRepeatedly(Return(base::Time::FromTimeT(1400000000)));
-  EXPECT_NE(token,
-            auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, "567"}, {}));
+  EXPECT_NE(token, auth_.CreateAccessToken(
+                       UserInfo{AuthScope::kOwner, TestUserId{"567"}}, {}));
 }
 
 TEST_F(AuthManagerTest, CreateTokenDifferentInstance) {
-  EXPECT_NE(auth_.CreateAccessToken(UserInfo{AuthScope::kUser, "123"}, {}),
+  EXPECT_NE(auth_.CreateAccessToken(
+                UserInfo{AuthScope::kUser, TestUserId{"123"}}, {}),
             AuthManager({}, {}).CreateAccessToken(
-                UserInfo{AuthScope::kUser, "123"}, {}));
+                UserInfo{AuthScope::kUser, TestUserId{"123"}}, {}));
 }
 
 TEST_F(AuthManagerTest, ParseAccessToken) {
@@ -117,18 +164,24 @@
 
     AuthManager auth{{}, {}, {}, &clock_};
 
-    auto token = auth.CreateAccessToken(UserInfo{AuthScope::kUser, "5"},
-                                        base::TimeDelta::FromSeconds(i));
+    auto token =
+        auth.CreateAccessToken(UserInfo{AuthScope::kUser, TestUserId{"5"}},
+                               base::TimeDelta::FromSeconds(i));
     UserInfo user_info;
     EXPECT_FALSE(auth_.ParseAccessToken(token, &user_info, nullptr));
     EXPECT_TRUE(auth.ParseAccessToken(token, &user_info, nullptr));
     EXPECT_EQ(AuthScope::kUser, user_info.scope());
-    EXPECT_EQ("5", user_info.user_id());
+    EXPECT_EQ(TestUserId{"5"}, user_info.id());
 
     EXPECT_CALL(clock_, Now())
         .WillRepeatedly(Return(kStartTime + base::TimeDelta::FromSeconds(i)));
     EXPECT_TRUE(auth.ParseAccessToken(token, &user_info, nullptr));
 
+    auto extended =
+        DelegateToUser(token, base::TimeDelta::FromSeconds(1000),
+                       UserInfo{AuthScope::kUser, TestUserId{"234"}});
+    EXPECT_FALSE(auth.ParseAccessToken(extended, &user_info, nullptr));
+
     EXPECT_CALL(clock_, Now())
         .WillRepeatedly(
             Return(kStartTime + base::TimeDelta::FromSeconds(i + 1)));
@@ -137,35 +190,135 @@
 }
 
 TEST_F(AuthManagerTest, GetRootClientAuthToken) {
-  EXPECT_EQ("UK1ACOc3cWGjGBoTIX2bd3qCQgECRgMaVArkgA==",
-            Base64Encode(auth_.GetRootClientAuthToken()));
+  EXPECT_EQ("WCCDQxkgAUYIGhudoQBCDEBQZgRhYq78I8GtFUZHNBbfGw==",
+            Base64Encode(
+                auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient)));
+}
+
+TEST_F(AuthManagerTest, GetRootClientAuthTokenDifferentOwner) {
+  EXPECT_EQ(
+      "WCqDQxkgAUYIGhudoQBMDEpnb29nbGUuY29tUOoLAxSUAZAAv54drarqhag=",
+      Base64Encode(auth_.GetRootClientAuthToken(RootClientTokenOwner::kCloud)));
 }
 
 TEST_F(AuthManagerTest, GetRootClientAuthTokenDifferentTime) {
   auto new_time = clock_.Now() + base::TimeDelta::FromDays(15);
   EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time));
-  EXPECT_EQ("UBpNF8g/GbNUmAyHg1qqJr+CQgECRgMaVB6rAA==",
-            Base64Encode(auth_.GetRootClientAuthToken()));
+  EXPECT_EQ("WCCDQxkgAUYIGhuxZ4BCDEBQjO+OTbjjTzZ/Dvk66nfQqg==",
+            Base64Encode(
+                auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient)));
 }
 
 TEST_F(AuthManagerTest, GetRootClientAuthTokenDifferentSecret) {
   AuthManager auth{kSecret2, {}, kSecret1, &clock_};
-  EXPECT_EQ("UFTBUcgd9d0HnPRnLeroN2mCQgECRgMaVArkgA==",
-            Base64Encode(auth.GetRootClientAuthToken()));
+  EXPECT_EQ(
+      "WCCDQxkgAUYIGhudoQBCDEBQ2MZF8YXv5pbtmMxwz9VtLA==",
+      Base64Encode(auth.GetRootClientAuthToken(RootClientTokenOwner::kClient)));
 }
 
 TEST_F(AuthManagerTest, IsValidAuthToken) {
-  EXPECT_TRUE(auth_.IsValidAuthToken(auth_.GetRootClientAuthToken(), nullptr));
+  EXPECT_TRUE(auth_.IsValidAuthToken(
+      auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient), nullptr));
   // Multiple attempts with random secrets.
   for (size_t i = 0; i < 1000; ++i) {
     AuthManager auth{{}, {}, {}, &clock_};
 
-    auto token = auth.GetRootClientAuthToken();
+    auto token = auth.GetRootClientAuthToken(RootClientTokenOwner::kClient);
     EXPECT_FALSE(auth_.IsValidAuthToken(token, nullptr));
     EXPECT_TRUE(auth.IsValidAuthToken(token, nullptr));
   }
 }
 
+TEST_F(AuthManagerTest, CreateSessionId) {
+  EXPECT_EQ("463315200:1", auth_.CreateSessionId());
+}
+
+TEST_F(AuthManagerTest, IsValidSessionId) {
+  EXPECT_TRUE(auth_.IsValidSessionId("463315200:1"));
+  EXPECT_TRUE(auth_.IsValidSessionId("463315200:2"));
+  EXPECT_TRUE(auth_.IsValidSessionId("463315150"));
+
+  // Future
+  EXPECT_FALSE(auth_.IsValidSessionId("463315230:1"));
+
+  // Expired
+  EXPECT_FALSE(auth_.IsValidSessionId("463315100:1"));
+}
+
+TEST_F(AuthManagerTest, CreateAccessTokenFromAuth) {
+  std::vector<uint8_t> access_token;
+  AuthScope scope;
+  base::TimeDelta ttl;
+  auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kCloud);
+  auto extended = DelegateToUser(root, base::TimeDelta::FromSeconds(1000),
+                                 UserInfo{AuthScope::kUser, TestUserId{"234"}});
+  EXPECT_EQ(
+      "WE+IQxkgAUYIGhudoQBMDEpnb29nbGUuY29tRggaG52hAEYFGhudpOhCAQ5FCUMyMzRNEUs0"
+      "NjMzMTUyMDA6MVCRVKU+0SpOoBppnwqdKMwP",
+      Base64Encode(extended));
+  EXPECT_TRUE(
+      auth_.CreateAccessTokenFromAuth(extended, 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_EQ(AuthScope::kUser, user_info.scope());
+
+  EXPECT_EQ(TestUserId{"234"}, user_info.id());
+}
+
+TEST_F(AuthManagerTest, CreateAccessTokenFromAuthNotMinted) {
+  std::vector<uint8_t> access_token;
+  auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient);
+  ErrorPtr error;
+  EXPECT_FALSE(auth_.CreateAccessTokenFromAuth(
+      root, base::TimeDelta::FromDays(1), nullptr, nullptr, nullptr, &error));
+  EXPECT_TRUE(error->HasError("invalidAuthCode"));
+}
+
+TEST_F(AuthManagerTest, CreateAccessTokenFromAuthValidateAfterSomeTime) {
+  auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient);
+  auto extended = DelegateToUser(root, base::TimeDelta::FromSeconds(1000),
+                                 UserInfo{AuthScope::kUser, TestUserId{"234"}});
+
+  // new_time < session_id_expiration < token_expiration.
+  auto new_time = clock_.Now() + base::TimeDelta::FromSeconds(15);
+  EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time));
+  EXPECT_TRUE(
+      auth_.CreateAccessTokenFromAuth(extended, base::TimeDelta::FromDays(1),
+                                      nullptr, nullptr, nullptr, nullptr));
+}
+
+TEST_F(AuthManagerTest, CreateAccessTokenFromAuthExpired) {
+  auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient);
+  auto extended = DelegateToUser(root, base::TimeDelta::FromSeconds(10),
+                                 UserInfo{AuthScope::kUser, TestUserId{"234"}});
+  ErrorPtr error;
+
+  // token_expiration < new_time < session_id_expiration.
+  auto new_time = clock_.Now() + base::TimeDelta::FromSeconds(15);
+  EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time));
+  EXPECT_FALSE(
+      auth_.CreateAccessTokenFromAuth(extended, base::TimeDelta::FromDays(1),
+                                      nullptr, nullptr, nullptr, &error));
+  EXPECT_TRUE(error->HasError("invalidAuthCode"));
+}
+
+TEST_F(AuthManagerTest, CreateAccessTokenFromAuthExpiredSessionid) {
+  auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient);
+  auto extended = DelegateToUser(root, base::TimeDelta::FromSeconds(1000),
+                                 UserInfo{AuthScope::kUser, TestUserId{"234"}});
+  ErrorPtr error;
+
+  // session_id_expiration < new_time < token_expiration.
+  auto new_time = clock_.Now() + base::TimeDelta::FromSeconds(200);
+  EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time));
+  EXPECT_FALSE(
+      auth_.CreateAccessTokenFromAuth(extended, base::TimeDelta::FromDays(1),
+                                      nullptr, nullptr, nullptr, &error));
+  EXPECT_TRUE(error->HasError("invalidAuthCode"));
+}
+
 class AuthManagerClaimTest : public testing::Test {
  public:
   void SetUp() override { EXPECT_EQ(auth_.GetAuthSecret().size(), 32u); }
@@ -241,18 +394,5 @@
   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/cloud_delegate.cc b/src/privet/cloud_delegate.cc
index 5f31fee..49fceaa 100644
--- a/src/privet/cloud_delegate.cc
+++ b/src/privet/cloud_delegate.cc
@@ -165,7 +165,7 @@
                   const UserInfo& user_info,
                   const CommandDoneCallback& callback) override {
     CHECK(user_info.scope() != AuthScope::kNone);
-    CHECK(!user_info.user_id().empty());
+    CHECK(!user_info.id().IsEmpty());
 
     ErrorPtr error;
     UserRole role;
@@ -182,7 +182,7 @@
     if (!command_instance)
       return callback.Run({}, std::move(error));
     component_manager_->AddCommand(std::move(command_instance));
-    command_owners_[id] = user_info.user_id();
+    command_owners_[id] = user_info.id();
     callback.Run(*component_manager_->FindCommand(id)->ToJson(), nullptr);
   }
 
@@ -230,7 +230,7 @@
  private:
   void OnCommandAdded(Command* command) {
     // Set to "" for any new unknown command.
-    command_owners_.insert(std::make_pair(command->GetID(), ""));
+    command_owners_.insert(std::make_pair(command->GetID(), UserAppId{}));
   }
 
   void OnCommandRemoved(Command* command) {
@@ -309,14 +309,17 @@
     return command;
   }
 
-  bool CanAccessCommand(const std::string& owner_id,
+  bool CanAccessCommand(const UserAppId& owner,
                         const UserInfo& user_info,
                         ErrorPtr* error) const {
     CHECK(user_info.scope() != AuthScope::kNone);
-    CHECK(!user_info.user_id().empty());
+    CHECK(!user_info.id().IsEmpty());
 
     if (user_info.scope() == AuthScope::kManager ||
-        owner_id == user_info.user_id()) {
+        (owner.type == user_info.id().type &&
+         owner.user == user_info.id().user &&
+         (user_info.id().app.empty() ||  // Token is not restricted to the app.
+          owner.app == user_info.id().app))) {
       return true;
     }
 
@@ -341,7 +344,7 @@
   int registation_retry_count_{0};
 
   // Map of command IDs to user IDs.
-  std::map<std::string, std::string> command_owners_;
+  std::map<std::string, UserAppId> command_owners_;
 
   // Backoff entry for retrying device registration.
   BackoffEntry backoff_entry_{&register_backoff_policy};
diff --git a/src/privet/mock_delegates.h b/src/privet/mock_delegates.h
index c75d438..c2e9a89 100644
--- a/src/privet/mock_delegates.h
+++ b/src/privet/mock_delegates.h
@@ -28,6 +28,11 @@
 
 namespace privet {
 
+struct TestUserId : public UserAppId {
+  TestUserId(const std::string& user_id)
+      : UserAppId{AuthType::kAnonymous, {user_id.begin(), user_id.end()}, {}} {}
+};
+
 ACTION_TEMPLATE(RunCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
                 AND_0_VALUE_PARAMS()) {
@@ -103,9 +108,12 @@
         .WillRepeatedly(Return(true));
 
     EXPECT_CALL(*this, ParseAccessToken(_, _, _))
-        .WillRepeatedly(
-            DoAll(SetArgPointee<1>(UserInfo{AuthScope::kViewer, "1234567"}),
-                  Return(true)));
+        .WillRepeatedly(DoAll(SetArgPointee<1>(UserInfo{
+                                  AuthScope::kViewer,
+                                  UserAppId{AuthType::kLocal,
+                                            {'1', '2', '3', '4', '5', '6', '7'},
+                                            {}}}),
+                              Return(true)));
 
     EXPECT_CALL(*this, GetPairingTypes())
         .WillRepeatedly(Return(std::set<PairingType>{
diff --git a/src/privet/openssl_utils.cc b/src/privet/openssl_utils.cc
index f38fd1a..17ebf70 100644
--- a/src/privet/openssl_utils.cc
+++ b/src/privet/openssl_utils.cc
@@ -18,13 +18,9 @@
 std::vector<uint8_t> HmacSha256(const std::vector<uint8_t>& key,
                                 const std::vector<uint8_t>& data) {
   std::vector<uint8_t> mac(kSha256OutputSize);
-  uint8_t hmac_state[uw_crypto_hmac_required_buffer_size_()];
-  CHECK(uw_crypto_hmac_init_(hmac_state, sizeof(hmac_state), key.data(),
-                             key.size()));
-  CHECK(uw_crypto_hmac_update_(hmac_state, sizeof(hmac_state), data.data(),
-                               data.size()));
-  CHECK(uw_crypto_hmac_final_(hmac_state, sizeof(hmac_state), mac.data(),
-                              mac.size()));
+  const UwCryptoHmacMsg messages[] = {{data.data(), data.size()}};
+  CHECK(uw_crypto_hmac_(key.data(), key.size(), messages, arraysize(messages),
+                        mac.data(), mac.size()));
   return mac;
 }
 
diff --git a/src/privet/privet_handler_unittest.cc b/src/privet/privet_handler_unittest.cc
index fa79e77..20f5aa0 100644
--- a/src/privet/privet_handler_unittest.cc
+++ b/src/privet/privet_handler_unittest.cc
@@ -484,7 +484,8 @@
     auth_header_ = "Privet 123";
     EXPECT_CALL(security_, ParseAccessToken(_, _, _))
         .WillRepeatedly(DoAll(
-            SetArgPointee<1>(UserInfo{AuthScope::kOwner, "1"}), Return(true)));
+            SetArgPointee<1>(UserInfo{AuthScope::kOwner, TestUserId{"1"}}),
+            Return(true)));
   }
 };
 
@@ -658,7 +659,8 @@
 TEST_F(PrivetHandlerSetupTest, GcdSetupAsMaster) {
   EXPECT_CALL(security_, ParseAccessToken(_, _, _))
       .WillRepeatedly(DoAll(
-          SetArgPointee<1>(UserInfo{AuthScope::kManager, "1"}), Return(true)));
+          SetArgPointee<1>(UserInfo{AuthScope::kManager, TestUserId{"1"}}),
+          Return(true)));
   const char kInput[] = R"({
     'gcd': {
       'ticketId': 'testTicket',
diff --git a/src/privet/privet_types.h b/src/privet/privet_types.h
index 49c4522..0f51862 100644
--- a/src/privet/privet_types.h
+++ b/src/privet/privet_types.h
@@ -29,17 +29,42 @@
   kWifi50,
 };
 
+struct UserAppId {
+  UserAppId() = default;
+
+  UserAppId(AuthType auth_type,
+            const std::vector<uint8_t>& user_id,
+            const std::vector<uint8_t>& app_id)
+      : type{auth_type},
+        user{user_id},
+        app{user_id.empty() ? user_id : app_id} {}
+
+  bool IsEmpty() const { return user.empty(); }
+
+  AuthType type{};
+  std::vector<uint8_t> user;
+  std::vector<uint8_t> app;
+};
+
+inline bool operator==(const UserAppId& l, const UserAppId& r) {
+  return l.user == r.user && l.app == r.app;
+}
+
+inline bool operator!=(const UserAppId& l, const UserAppId& r) {
+  return l.user != r.user || l.app != r.app;
+}
+
 class UserInfo {
  public:
   explicit UserInfo(AuthScope scope = AuthScope::kNone,
-                    const std::string& user_id = {})
-      : scope_{scope}, user_id_{scope == AuthScope::kNone ? "" : user_id} {}
+                    const UserAppId& id = {})
+      : scope_{scope}, id_{scope == AuthScope::kNone ? UserAppId{} : id} {}
   AuthScope scope() const { return scope_; }
-  const std::string& user_id() const { return user_id_; }
+  const UserAppId& id() const { return id_; }
 
  private:
   AuthScope scope_;
-  std::string user_id_;
+  UserAppId id_;
 };
 
 class ConnectionState final {
diff --git a/src/privet/security_manager.cc b/src/privet/security_manager.cc
index 0f00699..3b08613 100644
--- a/src/privet/security_manager.cc
+++ b/src/privet/security_manager.cc
@@ -91,9 +91,10 @@
                                             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_)};
+  auto user_id = std::to_string(++last_user_id_);
+  UserInfo user_info{
+      desired_scope,
+      UserAppId{auth_type, {user_id.begin(), user_id.end()}, {}}};
 
   const base::TimeDelta kTtl =
       base::TimeDelta::FromSeconds(kAccessTokenExpirationSeconds);
@@ -388,7 +389,7 @@
 }
 
 std::string SecurityManager::CreateSessionId() {
-  return Base64Encode(auth_manager_->CreateSessionId());
+  return auth_manager_->CreateSessionId();
 }
 
 void SecurityManager::RegisterPairingListeners(
diff --git a/src/privet/security_manager_unittest.cc b/src/privet/security_manager_unittest.cc
index 43b7f00..f596de9 100644
--- a/src/privet/security_manager_unittest.cc
+++ b/src/privet/security_manager_unittest.cc
@@ -25,6 +25,7 @@
 #include "src/config.h"
 #include "src/data_encoding.h"
 #include "src/privet/auth_manager.h"
+#include "src/privet/mock_delegates.h"
 #include "src/privet/openssl_utils.h"
 #include "src/test/mock_clock.h"
 #include "third_party/chromium/crypto/p224_spake.h"
@@ -170,7 +171,7 @@
     UserInfo info;
     EXPECT_TRUE(security_.ParseAccessToken(token, &info, nullptr));
     EXPECT_EQ(requested_scope, info.scope());
-    EXPECT_EQ("0/" + std::to_string(i), info.user_id());
+    EXPECT_EQ(TestUserId{std::to_string(i)}, info.id());
   }
 }
 
diff --git a/third_party/libuweave/src/crypto_hmac.c b/third_party/libuweave/src/crypto_hmac.c
index 8b75133..d3dca65 100644
--- a/third_party/libuweave/src/crypto_hmac.c
+++ b/third_party/libuweave/src/crypto_hmac.c
@@ -11,41 +11,24 @@
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
 
-size_t uw_crypto_hmac_required_buffer_size_() {
-  return sizeof(HMAC_CTX);
-}
-
-bool uw_crypto_hmac_init_(uint8_t* state_buffer,
-                          size_t state_buffer_len,
-                          const uint8_t* key,
-                          size_t key_len) {
-  if (sizeof(HMAC_CTX) > state_buffer_len) {
+bool uw_crypto_hmac_(const uint8_t* key,
+                     size_t key_len,
+                     const UwCryptoHmacMsg messages[],
+                     size_t num_messages,
+                     uint8_t* truncated_digest,
+                     size_t truncated_digest_len) {
+  HMAC_CTX context = {0};
+  HMAC_CTX_init(&context);
+  if (!HMAC_Init(&context, key, key_len, EVP_sha256()))
     return false;
-  }
-  HMAC_CTX* context = (HMAC_CTX*)state_buffer;
-  HMAC_CTX_init(context);
-  return HMAC_Init(context, key, key_len, EVP_sha256());
-}
 
-bool uw_crypto_hmac_update_(uint8_t* state_buffer,
-                            size_t state_buffer_len,
-                            const uint8_t* data,
-                            size_t data_len) {
-  if (sizeof(HMAC_CTX) > state_buffer_len) {
-    return false;
+  for (size_t i = 0; i < num_messages; ++i) {
+    if (messages[i].num_bytes &&
+        (!messages[i].bytes ||
+         !HMAC_Update(&context, messages[i].bytes, messages[i].num_bytes))) {
+      return false;
+    }
   }
-  HMAC_CTX* context = (HMAC_CTX*)state_buffer;
-  return HMAC_Update(context, data, data_len);
-}
-
-bool uw_crypto_hmac_final_(uint8_t* state_buffer,
-                           size_t state_buffer_len,
-                           uint8_t* truncated_digest,
-                           size_t truncated_digest_len) {
-  if (sizeof(HMAC_CTX) > state_buffer_len) {
-    return false;
-  }
-  HMAC_CTX* context = (HMAC_CTX*)state_buffer;
 
   const size_t kFullDigestLen = (size_t)EVP_MD_size(EVP_sha256());
   if (truncated_digest_len > kFullDigestLen) {
@@ -55,8 +38,8 @@
   uint8_t digest[kFullDigestLen];
   uint32_t len = kFullDigestLen;
 
-  bool result = HMAC_Final(context, digest, &len) && kFullDigestLen == len;
-  HMAC_CTX_cleanup(context);
+  bool result = HMAC_Final(&context, digest, &len) && kFullDigestLen == len;
+  HMAC_CTX_cleanup(&context);
   if (result) {
     memcpy(truncated_digest, digest, truncated_digest_len);
   }
diff --git a/third_party/libuweave/src/crypto_hmac.h b/third_party/libuweave/src/crypto_hmac.h
index bac634a..6f76ed0 100644
--- a/third_party/libuweave/src/crypto_hmac.h
+++ b/third_party/libuweave/src/crypto_hmac.h
@@ -9,21 +9,21 @@
 #include <stddef.h>
 #include <stdint.h>
 
-// Return the minimum required number of bytes for the state_buffer used in the
-// init, update and final functions.
-size_t uw_crypto_hmac_required_buffer_size_();
+typedef struct {
+  const uint8_t* bytes;
+  size_t num_bytes;
+} UwCryptoHmacMsg;
 
-bool uw_crypto_hmac_init_(uint8_t* state_buffer,
-                          size_t state_buffer_len,
-                          const uint8_t* key,
-                          size_t key_len);
-bool uw_crypto_hmac_update_(uint8_t* state_buffer,
-                            size_t state_buffer_len,
-                            const uint8_t* data,
-                            size_t data_len);
-bool uw_crypto_hmac_final_(uint8_t* state_buffer,
-                           size_t state_buffer_len,
-                           uint8_t* truncated_digest,
-                           size_t truncated_digest_len);
+/**
+ * Compute HMAC over a list of messages, which is equivalent to computing HMAC
+ * over the concatenation of all the messages. The HMAC output will be truncated
+ * to the desired length truncated_digest_len, and written into trucated_digest.
+ */
+bool uw_crypto_hmac_(const uint8_t* key,
+                     size_t key_len,
+                     const UwCryptoHmacMsg messages[],
+                     size_t num_messages,
+                     uint8_t* truncated_digest,
+                     size_t truncated_digest_len);
 
 #endif  // LIBUWEAVE_SRC_CRYPTO_HMAC_H_
diff --git a/third_party/libuweave/src/crypto_utils.c b/third_party/libuweave/src/crypto_utils.c
index 76b8068..7a6e38f 100644
--- a/third_party/libuweave/src/crypto_utils.c
+++ b/third_party/libuweave/src/crypto_utils.c
@@ -7,13 +7,6 @@
 bool uw_crypto_utils_equal_(const uint8_t* arr1,
                             const uint8_t* arr2,
                             size_t len) {
-  if (arr1 == NULL || arr2 == NULL) {
-    if (arr1 == NULL && arr2 == NULL && len == 0) {
-      return true;
-    }
-    return false;
-  }
-
   uint8_t diff = 0;
   for (size_t i = 0; i < len; i++) {
     diff |= arr1[i] ^ arr2[i];
diff --git a/third_party/libuweave/src/macaroon.c b/third_party/libuweave/src/macaroon.c
index 70afda1..c823804 100644
--- a/third_party/libuweave/src/macaroon.c
+++ b/third_party/libuweave/src/macaroon.c
@@ -8,13 +8,17 @@
 
 #include "src/crypto_utils.h"
 #include "src/macaroon_caveat.h"
+#include "src/macaroon_caveat_internal.h"
 #include "src/macaroon_encoding.h"
 
-static bool create_mac_tag_(const uint8_t* key, size_t key_len,
-                            const UwMacaroonCaveat* caveats, size_t num_caveats,
+static bool create_mac_tag_(const uint8_t* key,
+                            size_t key_len,
+                            const UwMacaroonContext* context,
+                            const UwMacaroonCaveat* const caveats[],
+                            size_t num_caveats,
                             uint8_t mac_tag[UW_MACAROON_MAC_LEN]) {
-  if (key == NULL || key_len == 0 || caveats == NULL || num_caveats == 0 ||
-      mac_tag == NULL) {
+  if (key == NULL || key_len == 0 || context == NULL || caveats == NULL ||
+      num_caveats == 0 || mac_tag == NULL) {
     return false;
   }
 
@@ -26,15 +30,15 @@
   uint8_t mac_tag_buff[UW_MACAROON_MAC_LEN];
 
   // Compute the first tag by using the key
-  if (!uw_macaroon_caveat_sign_(key, key_len, &(caveats[0]), mac_tag_buff,
+  if (!uw_macaroon_caveat_sign_(key, key_len, context, caveats[0], mac_tag_buff,
                                 UW_MACAROON_MAC_LEN)) {
     return false;
   }
 
   // Compute the rest of the tags by using the tag as the key
   for (size_t i = 1; i < num_caveats; i++) {
-    if (!uw_macaroon_caveat_sign_(mac_tag_buff, UW_MACAROON_MAC_LEN,
-                                  &(caveats[i]), mac_tag_buff,
+    if (!uw_macaroon_caveat_sign_(mac_tag_buff, UW_MACAROON_MAC_LEN, context,
+                                  caveats[i], mac_tag_buff,
                                   UW_MACAROON_MAC_LEN)) {
       return false;
     }
@@ -44,33 +48,38 @@
   return true;
 }
 
-bool uw_macaroon_new_from_mac_tag_(UwMacaroon* new_macaroon,
-                                   const uint8_t mac_tag[UW_MACAROON_MAC_LEN],
-                                   const UwMacaroonCaveat* caveats,
-                                   size_t num_caveats) {
-  if (new_macaroon == NULL || mac_tag == NULL || caveats == NULL ||
-      num_caveats == 0) {
+static bool verify_mac_tag_(const uint8_t* root_key,
+                            size_t root_key_len,
+                            const UwMacaroonContext* context,
+                            const UwMacaroonCaveat* const caveats[],
+                            size_t num_caveats,
+                            const uint8_t mac_tag[UW_MACAROON_MAC_LEN]) {
+  if (root_key == NULL || root_key_len == 0 || context == NULL ||
+      caveats == NULL || num_caveats == 0 || mac_tag == 0) {
     return false;
   }
 
-  memcpy(new_macaroon->mac_tag, mac_tag, UW_MACAROON_MAC_LEN);
-  new_macaroon->num_caveats = num_caveats;
-  new_macaroon->caveats = caveats;
+  uint8_t computed_mac_tag[UW_MACAROON_MAC_LEN] = {0};
+  if (!create_mac_tag_(root_key, root_key_len, context, caveats, num_caveats,
+                       computed_mac_tag)) {
+    return false;
+  }
 
-  return true;
+  return uw_crypto_utils_equal_(mac_tag, computed_mac_tag, UW_MACAROON_MAC_LEN);
 }
 
-bool uw_macaroon_new_from_root_key_(UwMacaroon* new_macaroon,
-                                    const uint8_t* root_key,
-                                    size_t root_key_len,
-                                    const UwMacaroonCaveat* caveats,
-                                    size_t num_caveats) {
-  if (new_macaroon == NULL || root_key == NULL || root_key_len == 0 ||
-      caveats == NULL || num_caveats == 0) {
+bool uw_macaroon_create_from_root_key_(UwMacaroon* new_macaroon,
+                                       const uint8_t* root_key,
+                                       size_t root_key_len,
+                                       const UwMacaroonContext* context,
+                                       const UwMacaroonCaveat* const caveats[],
+                                       size_t num_caveats) {
+  if (new_macaroon == NULL || root_key == NULL || context == NULL ||
+      root_key_len == 0 || caveats == NULL || num_caveats == 0) {
     return false;
   }
 
-  if (!create_mac_tag_(root_key, root_key_len, caveats, num_caveats,
+  if (!create_mac_tag_(root_key, root_key_len, context, caveats, num_caveats,
                        new_macaroon->mac_tag)) {
     return false;
   }
@@ -81,139 +90,231 @@
   return true;
 }
 
-bool uw_macaroon_verify_(const UwMacaroon* macaroon,
-                         const uint8_t* root_key,
-                         size_t root_key_len) {
-  if (macaroon == NULL || root_key == NULL) {
-    return false;
-  }
-
-  uint8_t mac_tag[UW_MACAROON_MAC_LEN] = {0};
-  if (!create_mac_tag_(root_key, root_key_len, macaroon->caveats,
-                       macaroon->num_caveats, mac_tag)) {
-    return false;
-  }
-
-  return uw_crypto_utils_equal_(mac_tag, macaroon->mac_tag,
-                                UW_MACAROON_MAC_LEN);
-}
-
 bool uw_macaroon_extend_(const UwMacaroon* old_macaroon,
                          UwMacaroon* new_macaroon,
+                         const UwMacaroonContext* context,
                          const UwMacaroonCaveat* additional_caveat,
-                         uint8_t* buffer, size_t buffer_size) {
-  if (old_macaroon == NULL || new_macaroon == NULL ||
+                         uint8_t* buffer,
+                         size_t buffer_size) {
+  if (old_macaroon == NULL || new_macaroon == NULL || context == NULL ||
       additional_caveat == NULL || buffer == NULL || buffer_size == 0) {
     return false;
   }
 
   new_macaroon->num_caveats = old_macaroon->num_caveats + 1;
 
-  // Extend the caveat list
-  if ((new_macaroon->num_caveats) * sizeof(UwMacaroonCaveat) > buffer_size) {
-    // Not enough memory to store the extended caveat list
+  // Extend the caveat pointer list
+  if ((new_macaroon->num_caveats) * sizeof(UwMacaroonCaveat*) > buffer_size) {
+    // Not enough memory to store the extended caveat pointer list
     return false;
   }
-  UwMacaroonCaveat* extended_list = (UwMacaroonCaveat*)buffer;
-  if (old_macaroon->caveats != NULL && extended_list != old_macaroon->caveats) {
+  const UwMacaroonCaveat** extended_list = (const UwMacaroonCaveat**)buffer;
+  if (new_macaroon->caveats != old_macaroon->caveats) {
     memcpy(extended_list, old_macaroon->caveats,
-           (old_macaroon->num_caveats) * sizeof(UwMacaroonCaveat));
+           old_macaroon->num_caveats * sizeof(old_macaroon->caveats[0]));
   }
-  extended_list[old_macaroon->num_caveats] = *additional_caveat;
-  new_macaroon->caveats = extended_list;
+  extended_list[old_macaroon->num_caveats] = additional_caveat;
+  new_macaroon->caveats = (const UwMacaroonCaveat* const*)extended_list;
 
   // Compute the new MAC tag
-  return create_mac_tag_(old_macaroon->mac_tag, UW_MACAROON_MAC_LEN,
-                         additional_caveat, 1, new_macaroon->mac_tag);
+  return create_mac_tag_(old_macaroon->mac_tag, UW_MACAROON_MAC_LEN, context,
+                         new_macaroon->caveats + old_macaroon->num_caveats, 1,
+                         new_macaroon->mac_tag);
+}
+
+static void init_validation_result(UwMacaroonValidationResult* result) {
+  // Start from the largest scope
+  *result = (UwMacaroonValidationResult){
+      .granted_scope = kUwMacaroonCaveatScopeTypeOwner,
+      .expiration_time = UINT32_MAX,
+  };
+}
+
+/** Reset the result object to the lowest scope when encountering errors */
+static void reset_validation_result(UwMacaroonValidationResult* result) {
+  *result = (UwMacaroonValidationResult){
+      .weave_app_restricted = true,
+      .granted_scope = UW_MACAROON_CAVEAT_SCOPE_LOWEST_POSSIBLE};
+}
+
+/** Get the next closest scope (to the narrower side). */
+static UwMacaroonCaveatScopeType get_closest_scope(
+    UwMacaroonCaveatScopeType scope) {
+  if (scope <= kUwMacaroonCaveatScopeTypeOwner) {
+    return kUwMacaroonCaveatScopeTypeOwner;
+  } else if (scope <= kUwMacaroonCaveatScopeTypeManager) {
+    return kUwMacaroonCaveatScopeTypeManager;
+  } else if (scope <= kUwMacaroonCaveatScopeTypeUser) {
+    return kUwMacaroonCaveatScopeTypeUser;
+  } else if (scope <= kUwMacaroonCaveatScopeTypeViewer) {
+    return kUwMacaroonCaveatScopeTypeViewer;
+  }
+  return scope;
+}
+
+bool uw_macaroon_validate_(const UwMacaroon* macaroon,
+                           const uint8_t* root_key,
+                           size_t root_key_len,
+                           const UwMacaroonContext* context,
+                           UwMacaroonValidationResult* result) {
+  if (result == NULL) {
+    return false;
+  }
+  init_validation_result(result);
+
+  if (root_key == NULL || root_key_len == 0 || macaroon == NULL ||
+      context == NULL || result == NULL ||
+      !verify_mac_tag_(root_key, root_key_len, context, macaroon->caveats,
+                       macaroon->num_caveats, macaroon->mac_tag)) {
+    return false;
+  }
+
+  UwMacaroonValidationState state;
+  if (!uw_macaroon_caveat_init_validation_state_(&state)) {
+    return false;
+  }
+  for (size_t i = 0; i < macaroon->num_caveats; i++) {
+    if (!uw_macaroon_caveat_validate_(macaroon->caveats[i], context, &state,
+                                      result)) {
+      reset_validation_result(result);  // Reset the result object
+      return false;
+    }
+  }
+
+  result->granted_scope = get_closest_scope(result->granted_scope);
+  return true;
 }
 
 // Encode a Macaroon to a byte string
-bool uw_macaroon_dump_(const UwMacaroon* macaroon,
-                       uint8_t* out,
-                       size_t out_len,
-                       size_t* resulting_str_len) {
-  if (macaroon == NULL || out == NULL || out_len == 0 ||
+bool uw_macaroon_serialize_(const UwMacaroon* macaroon,
+                            uint8_t* out,
+                            size_t out_len,
+                            size_t* resulting_str_len) {
+  if (macaroon == NULL || out == NULL ||
+      out_len < UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN ||
       resulting_str_len == NULL) {
     return false;
   }
 
-  size_t offset = 0, item_len;
+  // Need to encode the whole Macaroon again into a byte string.
 
-  if (!uw_macaroon_encoding_encode_byte_str_(
-          macaroon->mac_tag, UW_MACAROON_MAC_LEN, out, out_len, &item_len)) {
-    return false;
-  }
-  offset += item_len;
-
-  if (!uw_macaroon_encoding_encode_array_len_(
-          (uint32_t)(macaroon->num_caveats), out + offset, out_len - offset, &item_len)) {
+  // First encode the part without the overall byte string header to the buffer
+  // to get the total length.
+  size_t item_len = 0;
+  // Start with an offset
+  size_t offset = UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN;
+  if (!uw_macaroon_encoding_encode_array_len_((uint32_t)(macaroon->num_caveats),
+                                              out + offset, out_len - offset,
+                                              &item_len)) {
     return false;
   }
   offset += item_len;
 
   for (size_t i = 0; i < macaroon->num_caveats; i++) {
     if (!uw_macaroon_encoding_encode_byte_str_(
-            macaroon->caveats[i].bytes, macaroon->caveats[i].num_bytes,
+            macaroon->caveats[i]->bytes, macaroon->caveats[i]->num_bytes,
             out + offset, out_len - offset, &item_len)) {
       return false;
     }
     offset += item_len;
   }
 
-  *resulting_str_len = offset;
+  if (!uw_macaroon_encoding_encode_byte_str_(macaroon->mac_tag,
+                                             UW_MACAROON_MAC_LEN, out + offset,
+                                             out_len - offset, &item_len)) {
+    return false;
+  }
+  offset += item_len;
+
+  // Encode the length of the body at the beginning of the buffer
+  size_t bstr_len = offset - UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN;
+  if (!uw_macaroon_encoding_encode_byte_str_len_(
+          bstr_len, out, UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN, &item_len)) {
+    return false;
+  }
+
+  // Move the body part to be adjacent to the byte string header part
+  memmove(out + item_len, out + UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN,
+          bstr_len);
+
+  *resulting_str_len = item_len + bstr_len;
   return true;
 }
 
 // Decode a byte string to a Macaroon
-bool uw_macaroon_load_(const uint8_t* in,
-                       size_t in_len,
-                       uint8_t* caveats_buffer,
-                       size_t caveats_buffer_size,
-                       UwMacaroon* macaroon) {
-  if (in == NULL || in_len == 0 || caveats_buffer == NULL ||
-      caveats_buffer_size == 0 || macaroon == NULL) {
+bool uw_macaroon_deserialize_(const uint8_t* in,
+                              size_t in_len,
+                              uint8_t* buffer,
+                              size_t buffer_size,
+                              UwMacaroon* macaroon) {
+  if (in == NULL || in_len == 0 || buffer == NULL || buffer_size == 0 ||
+      macaroon == NULL) {
     return false;
   }
 
-  const uint8_t* tag;
-  size_t tag_len;
-  if (!uw_macaroon_encoding_decode_byte_str_(in, in_len, &tag, &tag_len) ||
-      tag_len != UW_MACAROON_MAC_LEN) {
+  size_t offset = 0;
+  size_t item_len = 0;
+
+  const uint8_t* bstr = NULL;
+  size_t bstr_len = 0;
+  if (!uw_macaroon_encoding_decode_byte_str_(in + offset, in_len - offset,
+                                             &bstr, &bstr_len)) {
     return false;
   }
-  memcpy(macaroon->mac_tag, tag, UW_MACAROON_MAC_LEN);
+  item_len = bstr - in;  // The length of the first byte string header
+  offset += item_len;
 
-  size_t offset = 0, cbor_item_len;
-  if (!uw_macaroon_encoding_get_item_len_(in, in_len, &cbor_item_len)) {
+  if (item_len + bstr_len != in_len) {
+    // The string length doesn't match
     return false;
   }
-  offset += cbor_item_len;
 
-  uint32_t array_len;
+  uint32_t array_len = 0;
   if (!uw_macaroon_encoding_decode_array_len_(in + offset, in_len - offset,
                                               &array_len)) {
     return false;
   }
   macaroon->num_caveats = (size_t)array_len;
-  if (caveats_buffer_size < array_len * sizeof(UwMacaroonCaveat)) {
+  if (buffer_size <
+      (array_len * (sizeof(UwMacaroonCaveat) + sizeof(UwMacaroonCaveat*)))) {
+    // Need two levels of abstraction, one for structs and one for pointers
     return false;
   }
 
-  UwMacaroonCaveat* caveats = (UwMacaroonCaveat*)caveats_buffer;
-  for (size_t i = 0; i < array_len; i++) {
-    if (!uw_macaroon_encoding_get_item_len_(in + offset, in_len - offset,
-                                            &cbor_item_len)) {
-      return false;
-    }
-    offset += cbor_item_len;
-
-    if (!uw_macaroon_encoding_decode_byte_str_(in + offset, in_len - offset,
-                                               &(caveats[i].bytes),
-                                               &(caveats[i].num_bytes))) {
-      return false;
-    }
+  if (!uw_macaroon_encoding_get_item_len_(in + offset, in_len - offset,
+                                          &item_len)) {
+    return false;
   }
-  macaroon->caveats = caveats;
+  offset += item_len;
+
+  const UwMacaroonCaveat** caveat_pointers = (const UwMacaroonCaveat**)buffer;
+  buffer += array_len * sizeof(UwMacaroonCaveat*);
+  UwMacaroonCaveat* caveat_structs = (UwMacaroonCaveat*)buffer;
+  for (size_t i = 0; i < array_len; i++) {
+    caveat_pointers[i] = &(caveat_structs[i]);
+
+    if (!uw_macaroon_encoding_decode_byte_str_(
+            in + offset, in_len - offset, &(caveat_structs[i].bytes),
+            &(caveat_structs[i].num_bytes))) {
+      return false;
+    }
+
+    if (!uw_macaroon_encoding_get_item_len_(in + offset, in_len - offset,
+                                            &item_len)) {
+      return false;
+    }
+    offset += item_len;
+  }
+  macaroon->caveats = caveat_pointers;
+
+  const uint8_t* tag;
+  size_t tag_len;
+  if (!uw_macaroon_encoding_decode_byte_str_(in + offset, in_len - offset, &tag,
+                                             &tag_len) ||
+      tag_len != UW_MACAROON_MAC_LEN) {
+    return false;
+  }
+  memcpy(macaroon->mac_tag, tag, UW_MACAROON_MAC_LEN);
 
   return true;
 }
diff --git a/third_party/libuweave/src/macaroon.h b/third_party/libuweave/src/macaroon.h
index 61242f7..c739bca 100644
--- a/third_party/libuweave/src/macaroon.h
+++ b/third_party/libuweave/src/macaroon.h
@@ -9,7 +9,8 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include "macaroon_caveat.h"
+#include "src/macaroon_caveat.h"
+#include "src/macaroon_context.h"
 
 #define UW_MACAROON_MAC_LEN 16
 
@@ -20,45 +21,80 @@
 typedef struct {
   uint8_t mac_tag[UW_MACAROON_MAC_LEN];
   size_t num_caveats;
-  const UwMacaroonCaveat* caveats;
+  const UwMacaroonCaveat* const* caveats;
 } UwMacaroon;
 
-bool uw_macaroon_new_from_mac_tag_(UwMacaroon* new_macaroon,
-                                   const uint8_t mac_tag[UW_MACAROON_MAC_LEN],
-                                   const UwMacaroonCaveat* caveats,
-                                   size_t num_caveats);
+// For the delegatee list in the validation result object
+typedef enum {
+  kUwMacaroonDelegateeTypeNone = 0,
+  kUwMacaroonDelegateeTypeUser = 1,
+  kUwMacaroonDelegateeTypeApp = 2,
+  kUwMacaroonDelegateeTypeService = 3,
+} UwMacaroonDelegateeType;
 
-bool uw_macaroon_new_from_root_key_(UwMacaroon* new_macaroon,
-                                    const uint8_t* root_key,
-                                    size_t root_key_len,
-                                    const UwMacaroonCaveat* caveats,
-                                    size_t num_caveats);
+typedef struct {
+  const uint8_t* id;
+  size_t id_len;
+  UwMacaroonDelegateeType type;
+  uint32_t timestamp;
+} UwMacaroonDelegateeInfo;
 
-bool uw_macaroon_verify_(const UwMacaroon* macaroon,
-                         const uint8_t* root_key,
-                         size_t root_key_len);
+#define MAX_NUM_DELEGATEES 10
 
-// Create a new macaroon with a new caveat
+typedef struct {
+  UwMacaroonCaveatScopeType granted_scope;
+  uint32_t expiration_time;
+  bool weave_app_restricted;
+  const uint8_t* lan_session_id;
+  size_t lan_session_id_len;
+  UwMacaroonDelegateeInfo delegatees[MAX_NUM_DELEGATEES];
+  size_t num_delegatees;
+} UwMacaroonValidationResult;
+
+bool uw_macaroon_create_from_root_key_(UwMacaroon* new_macaroon,
+                                       const uint8_t* root_key,
+                                       size_t root_key_len,
+                                       const UwMacaroonContext* context,
+                                       const UwMacaroonCaveat* const caveats[],
+                                       size_t num_caveats);
+
+/** Creates a new macaroon with a new caveat. */
 bool uw_macaroon_extend_(const UwMacaroon* old_macaroon,
                          UwMacaroon* new_macaroon,
+                         const UwMacaroonContext* context,
                          const UwMacaroonCaveat* additional_caveat,
-                         uint8_t* buffer, size_t buffer_size);
+                         uint8_t* buffer,
+                         size_t buffer_size);
 
-// Encode a Macaroon to a byte string
-bool uw_macaroon_dump_(const UwMacaroon* macaroon,
-                       uint8_t* out,
-                       size_t out_len,
-                       size_t* resulting_str_len);
+/**
+ * Verify and validate the Macaroon, and put relevant information into the
+ * result object. Note that the resulting granted_scope will be the closest
+ * valid scope type (to the narrower side) defined in macaroon_caveat.h.
+ */
+bool uw_macaroon_validate_(
+    const UwMacaroon* macaroon,
+    const uint8_t* root_key,
+    size_t root_key_len,
+    const UwMacaroonContext* context,
+    UwMacaroonValidationResult* result);
 
-// Decode a byte string to a Macaroon (the caveats_buffer here is used only for
-// the caveat pointer list *caveats in the UwMacaroon *macaroon). One note is
-// that the function doesn't copy string values to new buffers, so the caller
-// may maintain the input string around to make caveats with string values to
-// be usuable.
-bool uw_macaroon_load_(const uint8_t* in,
-                       size_t in_len,
-                       uint8_t* caveats_buffer,
-                       size_t caveats_buffer_size,
-                       UwMacaroon* macaroon);
+/** Encode a Macaroon to a byte string. */
+bool uw_macaroon_serialize_(const UwMacaroon* macaroon,
+                            uint8_t* out,
+                            size_t out_len,
+                            size_t* resulting_str_len);
+
+/**
+ * Decodes a byte string to a Macaroon.
+ *
+ * One note is that the function doesn't copy string values to new buffers, so
+ * the caller must maintain the input string around to make caveats with string
+ * values to be usable.
+ */
+bool uw_macaroon_deserialize_(const uint8_t* in,
+                              size_t in_len,
+                              uint8_t* buffer,
+                              size_t buffer_size,
+                              UwMacaroon* new_macaroon);
 
 #endif  // LIBUWEAVE_SRC_MACAROON_H_
diff --git a/third_party/libuweave/src/macaroon_caveat.c b/third_party/libuweave/src/macaroon_caveat.c
index 594f9de..dc4ee3b 100644
--- a/third_party/libuweave/src/macaroon_caveat.c
+++ b/third_party/libuweave/src/macaroon_caveat.c
@@ -3,125 +3,298 @@
 // found in the LICENSE file.
 
 #include "src/macaroon_caveat.h"
+#include "src/macaroon_caveat_internal.h"
 
 #include <string.h>
 
 #include "src/crypto_hmac.h"
+#include "src/macaroon.h"
 #include "src/macaroon_context.h"
 #include "src/macaroon_encoding.h"
 
-// TODO(bozhu): Find a better way to pre-allocate memory for HMACc computations?
-// Are C99 variable-length arrays allowed on embedded devices?
-#define HMAC_STATE_BUFFER_SIZE 1024
+static bool is_valid_caveat_type_(UwMacaroonCaveatType type) {
+  switch (type) {
+    case kUwMacaroonCaveatTypeNonce:
+    case kUwMacaroonCaveatTypeScope:
+    case kUwMacaroonCaveatTypeExpirationAbsolute:
+    case kUwMacaroonCaveatTypeTTL1Hour:
+    case kUwMacaroonCaveatTypeTTL24Hour:
+    case kUwMacaroonCaveatTypeDelegationTimestamp:
+    case kUwMacaroonCaveatTypeDelegateeUser:
+    case kUwMacaroonCaveatTypeDelegateeApp:
+    case kUwMacaroonCaveatTypeAppCommandsOnly:
+    case kUwMacaroonCaveatTypeDelegateeService:
+    case kUwMacaroonCaveatTypeBleSessionID:
+    case kUwMacaroonCaveatTypeLanSessionID:
+    case kUwMacaroonCaveatTypeClientAuthorizationTokenV1:
+    case kUwMacaroonCaveatTypeServerAuthenticationTokenV1:
+      return true;
+  }
+  return false;
+}
 
-static bool create_caveat_(UwMacaroonCaveatType type, const void* value,
-                           size_t value_len, uint8_t* buffer,
-                           size_t buffer_size, UwMacaroonCaveat* caveat) {
-  if (buffer == NULL || buffer_size == 0 || caveat == NULL) {
-    // Here value can be NULL, and value_len can be 0
+static bool is_valid_scope_type_(UwMacaroonCaveatScopeType type) {
+  switch (type) {
+    case kUwMacaroonCaveatScopeTypeOwner:
+    case kUwMacaroonCaveatScopeTypeManager:
+    case kUwMacaroonCaveatScopeTypeUser:
+    case kUwMacaroonCaveatScopeTypeViewer:
+      return true;
+  }
+  return false;
+}
+
+static bool create_caveat_no_value_(UwMacaroonCaveatType type,
+                                    uint8_t* buffer,
+                                    size_t buffer_size,
+                                    UwMacaroonCaveat* new_caveat) {
+  // (buffer_size == 0 || get_buffer_size_() > buffer_size) will conver the case
+  // that get_buffer_size_() returns 0 (for errors), so there is no need to
+  // check get_buffer_size_() == 0 again.
+  if (buffer == NULL || buffer_size == 0 || new_caveat == NULL ||
+      uw_macaroon_caveat_creation_get_buffsize_(type, 0) > buffer_size) {
     return false;
   }
 
-  caveat->bytes = buffer;
-  size_t encoded_str_len, total_str_len;
-
-  uint32_t unsigned_int = (uint32_t)type;
-  if (!uw_macaroon_encoding_encode_uint_(unsigned_int, buffer, buffer_size,
+  size_t encoded_str_len = 0, total_str_len = 0;
+  if (!uw_macaroon_encoding_encode_uint_((uint32_t)type, buffer, buffer_size,
                                          &encoded_str_len)) {
     return false;
   }
-  total_str_len = encoded_str_len;
-  buffer += encoded_str_len;
-  buffer_size -= encoded_str_len;
-
-  switch (type) {
-    case kUwMacaroonCaveatTypeStop:
-    case kUwMacaroonCaveatTypeSessionIdentifier:
-      // No value
-      encoded_str_len = 0;
-      break;
-
-    case kUwMacaroonCaveatTypeScope:
-    case kUwMacaroonCaveatTypeIssued:
-    case kUwMacaroonCaveatTypeTTL:
-    case kUwMacaroonCaveatTypeExpiration:
-      // Integer
-      if (value_len != sizeof(uint32_t)) {
-        // Wrong size for integers
-        return false;
-      }
-      unsigned_int = *((uint32_t*)value);
-      if (!uw_macaroon_encoding_encode_uint_(unsigned_int, buffer, buffer_size,
-                                             &encoded_str_len)) {
-        return false;
-      }
-      break;
-
-    case kUwMacaroonCaveatTypeIdentifier:
-      // Text string
-      if (!uw_macaroon_encoding_encode_text_str_((uint8_t*)value, value_len,
-                                                 buffer, buffer_size,
-                                                 &encoded_str_len)) {
-        return false;
-      }
-      break;
-
-    default:
-      // Should never reach here
-      return false;
-  }
-
   total_str_len += encoded_str_len;
-  caveat->num_bytes = total_str_len;
+
+  new_caveat->bytes = buffer;
+  new_caveat->num_bytes = total_str_len;
   return true;
 }
 
-bool uw_macaroon_caveat_create_without_value_(UwMacaroonCaveatType type,
+static bool create_caveat_uint_value_(UwMacaroonCaveatType type,
+                                      uint32_t unsigned_int,
+                                      uint8_t* buffer,
+                                      size_t buffer_size,
+                                      UwMacaroonCaveat* new_caveat) {
+  if (buffer == NULL || buffer_size == 0 || new_caveat == NULL ||
+      uw_macaroon_caveat_creation_get_buffsize_(type, 0) > buffer_size) {
+    return false;
+  }
+
+  size_t encoded_str_len = 0, total_str_len = 0;
+  if (!uw_macaroon_encoding_encode_uint_((uint32_t)type, buffer, buffer_size,
+                                         &encoded_str_len)) {
+    return false;
+  }
+  total_str_len += encoded_str_len;
+  if (!uw_macaroon_encoding_encode_uint_(unsigned_int, buffer + total_str_len,
+                                         buffer_size - total_str_len,
+                                         &encoded_str_len)) {
+    return false;
+  }
+  total_str_len += encoded_str_len;
+
+  new_caveat->bytes = buffer;
+  new_caveat->num_bytes = total_str_len;
+  return true;
+}
+
+static bool create_caveat_bstr_value_(UwMacaroonCaveatType type,
+                                      const uint8_t* str,
+                                      size_t str_len,
+                                      uint8_t* buffer,
+                                      size_t buffer_size,
+                                      UwMacaroonCaveat* new_caveat) {
+  if ((str == NULL && str_len != 0) || buffer == NULL || buffer_size == 0 ||
+      new_caveat == NULL ||
+      uw_macaroon_caveat_creation_get_buffsize_(type, str_len) > buffer_size) {
+    return false;
+  }
+
+  size_t encoded_str_len = 0, total_str_len = 0;
+  if (!uw_macaroon_encoding_encode_uint_((uint32_t)type, buffer, buffer_size,
+                                         &encoded_str_len)) {
+    return false;
+  }
+  total_str_len += encoded_str_len;
+  if (!uw_macaroon_encoding_encode_byte_str_(
+          str, str_len, buffer + total_str_len, buffer_size - total_str_len,
+          &encoded_str_len)) {
+    return false;
+  }
+  total_str_len += encoded_str_len;
+
+  new_caveat->bytes = buffer;
+  new_caveat->num_bytes = total_str_len;
+  return true;
+}
+
+size_t uw_macaroon_caveat_creation_get_buffsize_(UwMacaroonCaveatType type,
+                                                 size_t str_len) {
+  switch (type) {
+    // No values
+    case kUwMacaroonCaveatTypeTTL1Hour:
+    case kUwMacaroonCaveatTypeTTL24Hour:
+    case kUwMacaroonCaveatTypeAppCommandsOnly:
+    case kUwMacaroonCaveatTypeBleSessionID:
+      return UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN;
+
+    // Unsigned integers
+    case kUwMacaroonCaveatTypeScope:
+    case kUwMacaroonCaveatTypeExpirationAbsolute:
+    case kUwMacaroonCaveatTypeDelegationTimestamp:
+      return 2 * UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN;
+
+    // Byte strings
+    case kUwMacaroonCaveatTypeNonce:
+    case kUwMacaroonCaveatTypeDelegateeUser:
+    case kUwMacaroonCaveatTypeDelegateeApp:
+    case kUwMacaroonCaveatTypeDelegateeService:
+    case kUwMacaroonCaveatTypeLanSessionID:
+    case kUwMacaroonCaveatTypeClientAuthorizationTokenV1:
+    case kUwMacaroonCaveatTypeServerAuthenticationTokenV1:
+      return str_len + UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN;
+
+    default:
+      return 0;  // For errors
+  }
+}
+
+bool uw_macaroon_caveat_create_nonce_(const uint8_t* nonce,
+                                      size_t nonce_size,
+                                      uint8_t* buffer,
+                                      size_t buffer_size,
+                                      UwMacaroonCaveat* new_caveat) {
+  return create_caveat_bstr_value_(kUwMacaroonCaveatTypeNonce, nonce,
+                                   nonce_size, buffer, buffer_size, new_caveat);
+}
+
+bool uw_macaroon_caveat_create_scope_(UwMacaroonCaveatScopeType scope,
+                                      uint8_t* buffer,
+                                      size_t buffer_size,
+                                      UwMacaroonCaveat* new_caveat) {
+  if (!is_valid_scope_type_(scope)) {
+    return false;
+  }
+
+  return create_caveat_uint_value_(kUwMacaroonCaveatTypeScope, scope, buffer,
+                                   buffer_size, new_caveat);
+}
+
+bool uw_macaroon_caveat_create_expiration_absolute_(
+    uint32_t expiration_time,
+    uint8_t* buffer,
+    size_t buffer_size,
+    UwMacaroonCaveat* new_caveat) {
+  return create_caveat_uint_value_(kUwMacaroonCaveatTypeExpirationAbsolute,
+                                   expiration_time, buffer, buffer_size,
+                                   new_caveat);
+}
+
+bool uw_macaroon_caveat_create_ttl_1_hour_(uint8_t* buffer,
+                                           size_t buffer_size,
+                                           UwMacaroonCaveat* new_caveat) {
+  return create_caveat_no_value_(kUwMacaroonCaveatTypeTTL1Hour, buffer,
+                                 buffer_size, new_caveat);
+}
+
+bool uw_macaroon_caveat_create_ttl_24_hour_(uint8_t* buffer,
+                                            size_t buffer_size,
+                                            UwMacaroonCaveat* new_caveat) {
+  return create_caveat_no_value_(kUwMacaroonCaveatTypeTTL24Hour, buffer,
+                                 buffer_size, new_caveat);
+}
+
+bool uw_macaroon_caveat_create_delegation_timestamp_(
+    uint32_t timestamp,
+    uint8_t* buffer,
+    size_t buffer_size,
+    UwMacaroonCaveat* new_caveat) {
+  return create_caveat_uint_value_(kUwMacaroonCaveatTypeDelegationTimestamp,
+                                   timestamp, buffer, buffer_size, new_caveat);
+}
+
+bool uw_macaroon_caveat_create_delegatee_user_(const uint8_t* id_str,
+                                               size_t id_str_len,
+                                               uint8_t* buffer,
+                                               size_t buffer_size,
+                                               UwMacaroonCaveat* new_caveat) {
+  return create_caveat_bstr_value_(kUwMacaroonCaveatTypeDelegateeUser, id_str,
+                                   id_str_len, buffer, buffer_size, new_caveat);
+}
+
+bool uw_macaroon_caveat_create_delegatee_app_(const uint8_t* id_str,
+                                              size_t id_str_len,
                                               uint8_t* buffer,
                                               size_t buffer_size,
-                                              UwMacaroonCaveat* caveat) {
-  if (buffer == NULL || buffer_size == 0 || caveat == NULL) {
-    return false;
-  }
-  if (type != kUwMacaroonCaveatTypeStop &&
-      type != kUwMacaroonCaveatTypeSessionIdentifier) {
-    return false;
-  }
-
-  return create_caveat_(type, NULL, 0, buffer, buffer_size, caveat);
+                                              UwMacaroonCaveat* new_caveat) {
+  return create_caveat_bstr_value_(kUwMacaroonCaveatTypeDelegateeApp, id_str,
+                                   id_str_len, buffer, buffer_size, new_caveat);
 }
 
-bool uw_macaroon_caveat_create_with_uint_(UwMacaroonCaveatType type,
-                                          uint32_t value, uint8_t* buffer,
-                                          size_t buffer_size,
-                                          UwMacaroonCaveat* caveat) {
-  if (buffer == NULL || buffer_size == 0 || caveat == NULL) {
-    return false;
-  }
-  if (type != kUwMacaroonCaveatTypeScope &&
-      type != kUwMacaroonCaveatTypeIssued &&
-      type != kUwMacaroonCaveatTypeTTL &&
-      type != kUwMacaroonCaveatTypeExpiration) {
-    return false;
-  }
-
-  return create_caveat_(type, &value, sizeof(uint32_t), buffer, buffer_size,
-                        caveat);
+bool uw_macaroon_caveat_create_app_commands_only_(
+    uint8_t* buffer,
+    size_t buffer_size,
+    UwMacaroonCaveat* new_caveat) {
+  return create_caveat_no_value_(kUwMacaroonCaveatTypeAppCommandsOnly, buffer,
+                                 buffer_size, new_caveat);
 }
 
-bool uw_macaroon_caveat_create_with_str_(UwMacaroonCaveatType type,
-                                         const uint8_t* str, size_t str_len,
-                                         uint8_t* buffer, size_t buffer_size,
-                                         UwMacaroonCaveat* caveat) {
-  if (buffer == NULL || buffer_size == 0 || caveat == NULL ||
-      (str == NULL && str_len != 0)) {
-    return false;
-  }
-  if (type != kUwMacaroonCaveatTypeIdentifier) {
-    return false;
-  }
+bool uw_macaroon_caveat_create_delegatee_service_(
+    const uint8_t* id_str,
+    size_t id_str_len,
+    uint8_t* buffer,
+    size_t buffer_size,
+    UwMacaroonCaveat* new_caveat) {
+  return create_caveat_bstr_value_(kUwMacaroonCaveatTypeDelegateeService,
+                                   id_str, id_str_len, buffer, buffer_size,
+                                   new_caveat);
+}
 
-  return create_caveat_(type, str, str_len, buffer, buffer_size, caveat);
+bool uw_macaroon_caveat_create_ble_session_id_(uint8_t* buffer,
+                                               size_t buffer_size,
+                                               UwMacaroonCaveat* new_caveat) {
+  return create_caveat_no_value_(kUwMacaroonCaveatTypeBleSessionID, buffer,
+                                 buffer_size, new_caveat);
+}
+
+bool uw_macaroon_caveat_create_lan_session_id_(const uint8_t* session_id,
+                                               size_t session_id_len,
+                                               uint8_t* buffer,
+                                               size_t buffer_size,
+                                               UwMacaroonCaveat* new_caveat) {
+  return create_caveat_bstr_value_(kUwMacaroonCaveatTypeLanSessionID,
+                                   session_id, session_id_len, buffer,
+                                   buffer_size, new_caveat);
+}
+
+bool uw_macaroon_caveat_create_client_authorization_token_(
+    const uint8_t* str,
+    size_t str_len,
+    uint8_t* buffer,
+    size_t buffer_size,
+    UwMacaroonCaveat* new_caveat) {
+  if (str_len == 0) {
+    return create_caveat_no_value_(
+        kUwMacaroonCaveatTypeClientAuthorizationTokenV1, buffer, buffer_size,
+        new_caveat);
+  }
+  return create_caveat_bstr_value_(
+      kUwMacaroonCaveatTypeClientAuthorizationTokenV1, str, str_len, buffer,
+      buffer_size, new_caveat);
+}
+
+bool uw_macaroon_caveat_create_server_authentication_token_(
+    const uint8_t* str,
+    size_t str_len,
+    uint8_t* buffer,
+    size_t buffer_size,
+    UwMacaroonCaveat* new_caveat) {
+  if (str_len == 0) {
+    return create_caveat_no_value_(
+        kUwMacaroonCaveatTypeServerAuthenticationTokenV1, buffer, buffer_size,
+        new_caveat);
+  }
+  return create_caveat_bstr_value_(
+      kUwMacaroonCaveatTypeServerAuthenticationTokenV1, str, str_len, buffer,
+      buffer_size, new_caveat);
 }
 
 bool uw_macaroon_caveat_get_type_(const UwMacaroonCaveat* caveat,
@@ -137,20 +310,226 @@
   }
 
   *type = (UwMacaroonCaveatType)unsigned_int;
+  return is_valid_caveat_type_(*type);
+}
 
-  if (*type != kUwMacaroonCaveatTypeStop &&
-      *type != kUwMacaroonCaveatTypeScope &&
-      *type != kUwMacaroonCaveatTypeIdentifier &&
-      *type != kUwMacaroonCaveatTypeIssued &&
-      *type != kUwMacaroonCaveatTypeTTL &&
-      *type != kUwMacaroonCaveatTypeExpiration &&
-      *type != kUwMacaroonCaveatTypeSessionIdentifier) {
+/* === Some internal functions defined in macaroon_caveat_internal.h === */
+
+bool uw_macaroon_caveat_sign_(const uint8_t* key,
+                              size_t key_len,
+                              const UwMacaroonContext* context,
+                              const UwMacaroonCaveat* caveat,
+                              uint8_t* mac_tag,
+                              size_t mac_tag_size) {
+  if (key == NULL || key_len == 0 || context == NULL || caveat == NULL ||
+      mac_tag == NULL || mac_tag_size == 0) {
     return false;
   }
 
+  UwMacaroonCaveatType caveat_type;
+  if (!uw_macaroon_caveat_get_type_(caveat, &caveat_type) ||
+      !is_valid_caveat_type_(caveat_type)) {
+    return false;
+  }
+
+  // Need to encode the whole caveat as a byte string and then sign it
+
+  // If there is no additional value from the context, just compute the HMAC on
+  // the current byte string.
+  uint8_t bstr_cbor_prefix[UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN] = {0};
+  size_t bstr_cbor_prefix_len = 0;
+  if (caveat_type != kUwMacaroonCaveatTypeBleSessionID) {
+    if (!uw_macaroon_encoding_encode_byte_str_len_(
+            (uint32_t)(caveat->num_bytes), bstr_cbor_prefix,
+            sizeof(bstr_cbor_prefix), &bstr_cbor_prefix_len)) {
+      return false;
+    }
+
+    UwCryptoHmacMsg messages[] = {
+        {bstr_cbor_prefix, bstr_cbor_prefix_len},
+        {caveat->bytes, caveat->num_bytes},
+    };
+
+    return uw_crypto_hmac_(key, key_len, messages,
+                           sizeof(messages) / sizeof(messages[0]), mac_tag,
+                           mac_tag_size);
+  }
+
+  // If there is additional value from the context.
+  if (context->ble_session_id == NULL || context->ble_session_id_len == 0) {
+    return false;
+  }
+
+  // The length here includes the length of the BLE session ID string.
+  if (!uw_macaroon_encoding_encode_byte_str_len_(
+          (uint32_t)(context->ble_session_id_len + caveat->num_bytes),
+          bstr_cbor_prefix, sizeof(bstr_cbor_prefix), &bstr_cbor_prefix_len)) {
+    return false;
+  }
+
+  uint8_t value_cbor_prefix[UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN] = {0};
+  size_t value_cbor_prefix_len = 0;
+  if (!uw_macaroon_encoding_encode_byte_str_len_(
+          (uint32_t)(context->ble_session_id_len), value_cbor_prefix,
+          sizeof(value_cbor_prefix), &value_cbor_prefix_len)) {
+    return false;
+  }
+
+  UwCryptoHmacMsg messages[] = {
+      {bstr_cbor_prefix, bstr_cbor_prefix_len},
+      {caveat->bytes, caveat->num_bytes},
+      {value_cbor_prefix, value_cbor_prefix_len},
+      {context->ble_session_id, context->ble_session_id_len},
+  };
+
+  return uw_crypto_hmac_(key, key_len, messages,
+                         sizeof(messages) / sizeof(messages[0]), mac_tag,
+                         mac_tag_size);
+}
+
+static bool update_and_check_expiration_time(
+    uint32_t current_time,
+    uint32_t new_expiration_time,
+    UwMacaroonValidationResult* result) {
+  if (result->expiration_time > new_expiration_time) {
+    result->expiration_time = new_expiration_time;
+  }
+
+  return current_time <= result->expiration_time;
+}
+
+static bool update_delegatee_list(UwMacaroonCaveatType caveat_type,
+                                  const UwMacaroonCaveat* caveat,
+                                  uint32_t issued_time,
+                                  UwMacaroonValidationResult* result) {
+  if (result->num_delegatees >= MAX_NUM_DELEGATEES || issued_time == 0) {
+    return false;
+  }
+
+  UwMacaroonDelegateeType delegatee_type = kUwMacaroonDelegateeTypeNone;
+  switch (caveat_type) {
+    case kUwMacaroonCaveatTypeDelegateeUser:
+      delegatee_type = kUwMacaroonDelegateeTypeUser;
+      break;
+
+    case kUwMacaroonCaveatTypeDelegateeApp:
+      delegatee_type = kUwMacaroonDelegateeTypeApp;
+      break;
+
+    case kUwMacaroonCaveatTypeDelegateeService:
+      delegatee_type = kUwMacaroonDelegateeTypeService;
+      break;
+
+    default:
+      return false;
+  }
+
+  if (caveat_type != kUwMacaroonCaveatTypeDelegateeUser) {
+    for (size_t i = 0; i < result->num_delegatees; i++) {
+      // There must have at most one DelegateeApp or DelegateeService
+      if (result->delegatees[i].type == delegatee_type) {
+        return false;
+      }
+    }
+  }
+
+  if (!uw_macaroon_caveat_get_value_bstr_(
+          caveat, &(result->delegatees[result->num_delegatees].id),
+          &(result->delegatees[result->num_delegatees].id_len))) {
+    return false;
+  }
+  result->delegatees[result->num_delegatees].type = delegatee_type;
+  result->delegatees[result->num_delegatees].timestamp = issued_time;
+  result->num_delegatees++;
   return true;
 }
 
+bool uw_macaroon_caveat_validate_(const UwMacaroonCaveat* caveat,
+                                  const UwMacaroonContext* context,
+                                  UwMacaroonValidationState* state,
+                                  UwMacaroonValidationResult* result) {
+  if (caveat == NULL || context == NULL || state == NULL || result == NULL) {
+    return false;
+  }
+
+  uint32_t expiration_time = 0;
+  uint32_t issued_time = 0;
+  uint32_t scope = UW_MACAROON_CAVEAT_SCOPE_LOWEST_POSSIBLE;
+
+  UwMacaroonCaveatType caveat_type;
+  if (!uw_macaroon_caveat_get_type_(caveat, &caveat_type)) {
+    return false;
+  }
+
+  switch (caveat_type) {
+    // The types that always validate
+    case kUwMacaroonCaveatTypeClientAuthorizationTokenV1:
+    case kUwMacaroonCaveatTypeServerAuthenticationTokenV1:
+    case kUwMacaroonCaveatTypeNonce:
+    case kUwMacaroonCaveatTypeBleSessionID:
+      return true;
+
+    case kUwMacaroonCaveatTypeDelegationTimestamp:
+      if (!uw_macaroon_caveat_get_value_uint_(caveat, &issued_time) ||
+          issued_time < state->issued_time) {
+        return false;
+      }
+      state->issued_time = issued_time;
+      return true;
+
+    case kUwMacaroonCaveatTypeTTL1Hour:
+      if (state->issued_time == 0) {
+        return false;
+      }
+      return update_and_check_expiration_time(
+          context->current_time, state->issued_time + 60 * 60, result);
+
+    case kUwMacaroonCaveatTypeTTL24Hour:
+      if (state->issued_time == 0) {
+        return false;
+      }
+      return update_and_check_expiration_time(
+          context->current_time, state->issued_time + 24 * 60 * 60, result);
+
+    // Need to create a list of delegatees
+    case kUwMacaroonCaveatTypeDelegateeUser:
+    case kUwMacaroonCaveatTypeDelegateeApp:
+    case kUwMacaroonCaveatTypeDelegateeService:
+      return update_delegatee_list(caveat_type, caveat, state->issued_time,
+                                   result);
+
+    // Time related caveats
+    case kUwMacaroonCaveatTypeExpirationAbsolute:
+      if (!uw_macaroon_caveat_get_value_uint_(caveat, &expiration_time)) {
+        return false;
+      }
+      return update_and_check_expiration_time(context->current_time,
+                                              expiration_time, result);
+
+    // The caveats that update the values of the result object
+    case kUwMacaroonCaveatTypeScope:
+      if (!uw_macaroon_caveat_get_value_uint_(caveat, &scope) ||
+          // Larger value means less priviledge
+          scope > UW_MACAROON_CAVEAT_SCOPE_LOWEST_POSSIBLE) {
+        return false;
+      }
+      if (scope > (uint32_t)(result->granted_scope)) {
+        result->granted_scope = (UwMacaroonCaveatScopeType)scope;
+      }
+      return true;
+
+    case kUwMacaroonCaveatTypeAppCommandsOnly:
+      result->weave_app_restricted = true;
+      return true;
+
+    case kUwMacaroonCaveatTypeLanSessionID:
+      return uw_macaroon_caveat_get_value_bstr_(
+          caveat, &(result->lan_session_id), &(result->lan_session_id_len));
+  }
+
+  return false;
+}
+
 bool uw_macaroon_caveat_get_value_uint_(const UwMacaroonCaveat* caveat,
                                         uint32_t* unsigned_int) {
   if (caveat == NULL || unsigned_int == NULL) {
@@ -162,13 +541,13 @@
     return false;
   }
   if (type != kUwMacaroonCaveatTypeScope &&
-      type != kUwMacaroonCaveatTypeIssued &&
-      type != kUwMacaroonCaveatTypeTTL &&
-      type != kUwMacaroonCaveatTypeExpiration) {
+      type != kUwMacaroonCaveatTypeExpirationAbsolute &&
+      type != kUwMacaroonCaveatTypeDelegationTimestamp) {
     // Wrong type
     return false;
   }
 
+  // Skip the portion for CBOR type
   size_t offset;
   if (!uw_macaroon_encoding_get_item_len_(caveat->bytes, caveat->num_bytes,
                                           &offset)) {
@@ -179,8 +558,9 @@
       caveat->bytes + offset, caveat->num_bytes - offset, unsigned_int);
 }
 
-bool uw_macaroon_caveat_get_value_str_(const UwMacaroonCaveat* caveat,
-                                       const uint8_t** str, size_t* str_len) {
+bool uw_macaroon_caveat_get_value_bstr_(const UwMacaroonCaveat* caveat,
+                                        const uint8_t** str,
+                                        size_t* str_len) {
   if (caveat == NULL || str == NULL || str_len == NULL) {
     return false;
   }
@@ -189,7 +569,13 @@
   if (!uw_macaroon_caveat_get_type_(caveat, &type)) {
     return false;
   }
-  if (type != kUwMacaroonCaveatTypeIdentifier) {
+  if (type != kUwMacaroonCaveatTypeNonce &&
+      type != kUwMacaroonCaveatTypeDelegateeUser &&
+      type != kUwMacaroonCaveatTypeDelegateeApp &&
+      type != kUwMacaroonCaveatTypeDelegateeService &&
+      type != kUwMacaroonCaveatTypeLanSessionID &&
+      type != kUwMacaroonCaveatTypeClientAuthorizationTokenV1 &&
+      type != kUwMacaroonCaveatTypeServerAuthenticationTokenV1) {
     // Wrong type
     return false;
   }
@@ -200,48 +586,16 @@
     return false;
   }
 
-  return uw_macaroon_encoding_decode_text_str_(
+  return uw_macaroon_encoding_decode_byte_str_(
       caveat->bytes + offset, caveat->num_bytes - offset, str, str_len);
 }
 
-bool uw_macaroon_caveat_sign_(const uint8_t* key, size_t key_len,
-                              const UwMacaroonCaveat* caveat, uint8_t* mac_tag,
-                              size_t mac_tag_size) {
-  if (key == NULL || key_len == 0 || caveat == NULL || mac_tag == NULL ||
-      mac_tag_size == 0) {
+bool uw_macaroon_caveat_init_validation_state_(
+    UwMacaroonValidationState* state) {
+  if (state == NULL) {
     return false;
   }
 
-  uint8_t hmac_state_buffer[HMAC_STATE_BUFFER_SIZE];
-  if (HMAC_STATE_BUFFER_SIZE < uw_crypto_hmac_required_buffer_size_()) {
-    return false;
-  }
-
-  if (!uw_crypto_hmac_init_(hmac_state_buffer, HMAC_STATE_BUFFER_SIZE, key,
-                            key_len)) {
-    return false;
-  }
-
-  if (!uw_crypto_hmac_update_(hmac_state_buffer, HMAC_STATE_BUFFER_SIZE,
-                              caveat->bytes, caveat->num_bytes)) {
-    return false;
-  }
-
-  const uint8_t* context;
-  size_t context_len;
-  UwMacaroonCaveatType caveat_type;
-
-  if ((!uw_macaroon_caveat_get_type_(caveat, &caveat_type)) ||
-      (!uw_macaroon_context_get_(caveat_type, &context, &context_len))) {
-    return false;
-  }
-  if (context != NULL && context_len != 0) {
-    if (!uw_crypto_hmac_update_(hmac_state_buffer, HMAC_STATE_BUFFER_SIZE,
-                                context, context_len)) {
-      return false;
-    }
-  }
-
-  return uw_crypto_hmac_final_(hmac_state_buffer, HMAC_STATE_BUFFER_SIZE,
-                               mac_tag, mac_tag_size);
+  state->issued_time = 0;
+  return true;
 }
diff --git a/third_party/libuweave/src/macaroon_caveat.h b/third_party/libuweave/src/macaroon_caveat.h
index 2e01742..4905667 100644
--- a/third_party/libuweave/src/macaroon_caveat.h
+++ b/third_party/libuweave/src/macaroon_caveat.h
@@ -15,13 +15,22 @@
 } UwMacaroonCaveat;
 
 typedef enum {
-  kUwMacaroonCaveatTypeStop = 0,
-  kUwMacaroonCaveatTypeScope = 1,
-  kUwMacaroonCaveatTypeIdentifier = 2,
-  kUwMacaroonCaveatTypeIssued = 3,
-  kUwMacaroonCaveatTypeTTL = 4,
-  kUwMacaroonCaveatTypeExpiration = 5,
-  kUwMacaroonCaveatTypeSessionIdentifier = 16,
+  kUwMacaroonCaveatTypeNonce = 0,                // bstr
+  kUwMacaroonCaveatTypeScope = 1,                // uint
+  kUwMacaroonCaveatTypeExpirationAbsolute = 5,   // uint
+  kUwMacaroonCaveatTypeTTL1Hour = 6,             // no value
+  kUwMacaroonCaveatTypeTTL24Hour = 7,            // no value
+  kUwMacaroonCaveatTypeDelegationTimestamp = 8,  // uint
+
+  kUwMacaroonCaveatTypeDelegateeUser = 9,      // bstr
+  kUwMacaroonCaveatTypeDelegateeApp = 10,      // bstr
+  kUwMacaroonCaveatTypeDelegateeService = 12,  // bstr
+
+  kUwMacaroonCaveatTypeAppCommandsOnly = 11,                 // no value
+  kUwMacaroonCaveatTypeBleSessionID = 16,                    // no value
+  kUwMacaroonCaveatTypeLanSessionID = 17,                    // bstr
+  kUwMacaroonCaveatTypeClientAuthorizationTokenV1 = 8193,    // bstr (0x2001)
+  kUwMacaroonCaveatTypeServerAuthenticationTokenV1 = 12289,  // bstr (0x3001)
 } UwMacaroonCaveatType;
 
 typedef enum {
@@ -31,28 +40,83 @@
   kUwMacaroonCaveatScopeTypeViewer = 20,
 } UwMacaroonCaveatScopeType;
 
-bool uw_macaroon_caveat_create_without_value_(UwMacaroonCaveatType type,
+// For security sanity checks
+#define UW_MACAROON_CAVEAT_SCOPE_LOWEST_POSSIBLE 127
+
+/** Compute the buffer sizes that are enough for caveat creation functions. */
+size_t uw_macaroon_caveat_creation_get_buffsize_(UwMacaroonCaveatType type,
+                                                 size_t str_len);
+
+// Caveat creation functions
+bool uw_macaroon_caveat_create_nonce_(const uint8_t* nonce,
+                                      size_t nonce_size,
+                                      uint8_t* buffer,
+                                      size_t buffer_size,
+                                      UwMacaroonCaveat* new_caveat);
+bool uw_macaroon_caveat_create_scope_(UwMacaroonCaveatScopeType scope,
+                                      uint8_t* buffer,
+                                      size_t buffer_size,
+                                      UwMacaroonCaveat* new_caveat);
+bool uw_macaroon_caveat_create_expiration_absolute_(
+    uint32_t expiration_time,
+    uint8_t* buffer,
+    size_t buffer_size,
+    UwMacaroonCaveat* new_caveat);
+bool uw_macaroon_caveat_create_ttl_1_hour_(uint8_t* buffer,
+                                           size_t buffer_size,
+                                           UwMacaroonCaveat* new_caveat);
+bool uw_macaroon_caveat_create_ttl_24_hour_(uint8_t* buffer,
+                                            size_t buffer_size,
+                                            UwMacaroonCaveat* new_caveat);
+bool uw_macaroon_caveat_create_delegation_timestamp_(
+    uint32_t timestamp,
+    uint8_t* buffer,
+    size_t buffer_size,
+    UwMacaroonCaveat* new_caveat);
+bool uw_macaroon_caveat_create_delegatee_user_(const uint8_t* id_str,
+                                               size_t id_str_len,
+                                               uint8_t* buffer,
+                                               size_t buffer_size,
+                                               UwMacaroonCaveat* new_caveat);
+bool uw_macaroon_caveat_create_delegatee_app_(const uint8_t* id_str,
+                                              size_t id_str_len,
                                               uint8_t* buffer,
                                               size_t buffer_size,
                                               UwMacaroonCaveat* new_caveat);
-bool uw_macaroon_caveat_create_with_uint_(UwMacaroonCaveatType type,
-                                          uint32_t value, uint8_t* buffer,
-                                          size_t buffer_size,
-                                          UwMacaroonCaveat* new_caveat);
-bool uw_macaroon_caveat_create_with_str_(UwMacaroonCaveatType type,
-                                         const uint8_t* str, size_t str_len,
-                                         uint8_t* buffer, size_t buffer_size,
-                                         UwMacaroonCaveat* new_caveat);
+bool uw_macaroon_caveat_create_delegatee_service_(const uint8_t* id_str,
+                                                  size_t id_str_len,
+                                                  uint8_t* buffer,
+                                                  size_t buffer_size,
+                                                  UwMacaroonCaveat* new_caveat);
+bool uw_macaroon_caveat_create_app_commands_only_(uint8_t* buffer,
+                                                  size_t buffer_size,
+                                                  UwMacaroonCaveat* new_caveat);
+bool uw_macaroon_caveat_create_ble_session_id_(uint8_t* buffer,
+                                               size_t buffer_size,
+                                               UwMacaroonCaveat* new_caveat);
+bool uw_macaroon_caveat_create_lan_session_id_(const uint8_t* session_id,
+                                               size_t session_id_len,
+                                               uint8_t* buffer,
+                                               size_t buffer_size,
+                                               UwMacaroonCaveat* new_caveat);
 
+// The string values for these two token types are optional.
+// Use str_len = 0 to indicate creating the caveats without string values.
+bool uw_macaroon_caveat_create_client_authorization_token_(
+    const uint8_t* str,
+    size_t str_len,
+    uint8_t* buffer,
+    size_t buffer_size,
+    UwMacaroonCaveat* new_caveat);
+bool uw_macaroon_caveat_create_server_authentication_token_(
+    const uint8_t* str,
+    size_t str_len,
+    uint8_t* buffer,
+    size_t buffer_size,
+    UwMacaroonCaveat* new_caveat);
+
+/** Get the type for the given caveat. */
 bool uw_macaroon_caveat_get_type_(const UwMacaroonCaveat* caveat,
                                   UwMacaroonCaveatType* type);
-bool uw_macaroon_caveat_get_value_uint_(const UwMacaroonCaveat* caveat,
-                                        uint32_t* unsigned_int);
-bool uw_macaroon_caveat_get_value_str_(const UwMacaroonCaveat* caveat,
-                                       const uint8_t** str, size_t* str_len);
-
-bool uw_macaroon_caveat_sign_(const uint8_t* key, size_t key_len,
-                              const UwMacaroonCaveat* caveat, uint8_t* mac_tag,
-                              size_t mac_tag_size);
 
 #endif  // LIBUWEAVE_SRC_MACAROON_CAVEAT_H_
diff --git a/third_party/libuweave/src/macaroon_caveat_internal.h b/third_party/libuweave/src/macaroon_caveat_internal.h
new file mode 100644
index 0000000..d6e7b07
--- /dev/null
+++ b/third_party/libuweave/src/macaroon_caveat_internal.h
@@ -0,0 +1,40 @@
+// Copyright 2015 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBUWEAVE_SRC_MACAROON_CAVEAT_INTERNAL_H_
+#define LIBUWEAVE_SRC_MACAROON_CAVEAT_INTERNAL_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "src/macaroon.h"
+#include "src/macaroon_caveat.h"
+
+bool uw_macaroon_caveat_sign_(const uint8_t* key,
+                              size_t key_len,
+                              const UwMacaroonContext* context,
+                              const UwMacaroonCaveat* caveat,
+                              uint8_t* mac_tag,
+                              size_t mac_tag_size);
+
+typedef struct {
+  uint32_t issued_time;  // 0 when invalid or not set.
+} UwMacaroonValidationState;
+
+bool uw_macaroon_caveat_init_validation_state_(
+    UwMacaroonValidationState* state);
+
+bool uw_macaroon_caveat_validate_(const UwMacaroonCaveat* caveat,
+                                  const UwMacaroonContext* context,
+                                  UwMacaroonValidationState* state,
+                                  UwMacaroonValidationResult* result);
+
+bool uw_macaroon_caveat_get_value_uint_(const UwMacaroonCaveat* caveat,
+                                        uint32_t* unsigned_int);
+bool uw_macaroon_caveat_get_value_bstr_(const UwMacaroonCaveat* caveat,
+                                        const uint8_t** str,
+                                        size_t* str_len);
+
+#endif  // LIBUWEAVE_SRC_MACAROON_CAVEAT_INTERNAL_H_
diff --git a/third_party/libuweave/src/macaroon_context.c b/third_party/libuweave/src/macaroon_context.c
index 7477784..2f1685d 100644
--- a/third_party/libuweave/src/macaroon_context.c
+++ b/third_party/libuweave/src/macaroon_context.c
@@ -4,19 +4,19 @@
 
 #include "src/macaroon_context.h"
 
-#include "src/macaroon_caveat.h"
-
-bool uw_macaroon_context_get_(UwMacaroonCaveatType type,
-                              const uint8_t** context, size_t* context_len) {
-  if (type != kUwMacaroonCaveatTypeSessionIdentifier) {
-    *context = NULL;
-    *context_len = 0;
+bool uw_macaroon_context_create_(uint32_t current_time,
+                                 const uint8_t* ble_session_id,
+                                 size_t ble_session_id_len,
+                                 UwMacaroonContext* new_context) {
+  if (ble_session_id == NULL && ble_session_id_len != 0) {
+    return false;
+  }
+  if (new_context == NULL) {
+    return false;
   }
 
-  // TODO(bozhu): Waiting for a proper way to obtain the session identifier.
-  // Have we already implemented something related to session identifiers?
-  *context = NULL;
-  *context_len = 0;
-
+  new_context->current_time = current_time;
+  new_context->ble_session_id = ble_session_id;
+  new_context->ble_session_id_len = ble_session_id_len;
   return true;
 }
diff --git a/third_party/libuweave/src/macaroon_context.h b/third_party/libuweave/src/macaroon_context.h
index 8522b69..c230eb7 100644
--- a/third_party/libuweave/src/macaroon_context.h
+++ b/third_party/libuweave/src/macaroon_context.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UW_LIBUWEAVE_SRC_MACAROON_CONTEXT_
-#define UW_LIBUWEAVE_SRC_MACAROON_CONTEXT_
+#ifndef LIBUWEAVE_SRC_MACAROON_CONTEXT_
+#define LIBUWEAVE_SRC_MACAROON_CONTEXT_
 
 #include <stdbool.h>
 #include <stddef.h>
@@ -11,7 +11,15 @@
 
 #include "src/macaroon_caveat.h"
 
-bool uw_macaroon_context_get_(UwMacaroonCaveatType type,
-                              const uint8_t** context, size_t* context_len);
+typedef struct {
+  uint32_t current_time;  // In number of seconds since Jan 1st 2000 00:00:00
+  const uint8_t* ble_session_id;  // Only for BLE
+  size_t ble_session_id_len;
+} UwMacaroonContext;
 
-#endif  // UW_LIBUWEAVE_SRC_MACAROON_CONTEXT_
+bool uw_macaroon_context_create_(uint32_t current_time,
+                                 const uint8_t* ble_session_id,
+                                 size_t ble_session_id_len,
+                                 UwMacaroonContext* new_context);
+
+#endif  // LIBUWEAVE_SRC_MACAROON_CONTEXT_
diff --git a/third_party/libuweave/src/macaroon_encoding.c b/third_party/libuweave/src/macaroon_encoding.c
index 3fb5323..29adc52 100644
--- a/third_party/libuweave/src/macaroon_encoding.c
+++ b/third_party/libuweave/src/macaroon_encoding.c
@@ -21,28 +21,34 @@
   kCborMajorTypeArray = 4 << 5,    // type 4 -- arrays
 } CborMajorType;
 
-// -- Prototypes begin --
 static inline CborMajorType get_type_(const uint8_t* cbor);
 static inline uint8_t get_addtl_data_(const uint8_t* cbor);
 static inline void set_type_(CborMajorType type, uint8_t* cbor);
 static inline void set_addtl_data_(uint8_t addtl_data, uint8_t* cbor);
 
-// Compute the minimum number of bytes to store the unsigned integer.
+/** Computes the minimum number of bytes to store the unsigned integer. */
 static inline size_t uint_min_len_(uint32_t unsigned_int);
 
-// Encoding or decoding without checking types
-static bool blindly_encode_uint_(uint32_t unsigned_int, uint8_t* buffer,
-                                 size_t buffer_size, size_t* result_len);
-static bool blindly_encode_str_(const uint8_t* str, size_t str_len,
-                                uint8_t* buffer, size_t buffer_size,
+/** Encoding or decoding without checking types */
+static bool blindly_encode_uint_(uint32_t unsigned_int,
+                                 uint8_t* buffer,
+                                 size_t buffer_size,
+                                 size_t* result_len);
+static bool blindly_encode_str_(const uint8_t* str,
+                                size_t str_len,
+                                uint8_t* buffer,
+                                size_t buffer_size,
                                 size_t* result_len);
-static bool blindly_decode_uint_(const uint8_t* cbor, size_t cbor_len,
+static bool blindly_decode_uint_(const uint8_t* cbor,
+                                 size_t cbor_len,
                                  uint32_t* unsigned_int);
-static bool blindly_decode_str_(const uint8_t* cbor, size_t cbor_len,
-                                const uint8_t** out_str, size_t* out_str_len);
-// -- Prototypes end --
+static bool blindly_decode_str_(const uint8_t* cbor,
+                                size_t cbor_len,
+                                const uint8_t** out_str,
+                                size_t* out_str_len);
 
-bool uw_macaroon_encoding_get_item_len_(const uint8_t* cbor, size_t cbor_len,
+bool uw_macaroon_encoding_get_item_len_(const uint8_t* cbor,
+                                        size_t cbor_len,
                                         size_t* first_item_len) {
   if (cbor == NULL || cbor_len == 0 || first_item_len == NULL) {
     return false;
@@ -76,7 +82,8 @@
 }
 
 bool uw_macaroon_encoding_encode_uint_(const uint32_t unsigned_int,
-                                       uint8_t* buffer, size_t buffer_size,
+                                       uint8_t* buffer,
+                                       size_t buffer_size,
                                        size_t* resulting_cbor_len) {
   if (buffer == NULL || buffer_size == 0 || resulting_cbor_len == NULL) {
     return false;
@@ -88,7 +95,8 @@
 }
 
 bool uw_macaroon_encoding_encode_array_len_(const uint32_t array_len,
-                                            uint8_t* buffer, size_t buffer_size,
+                                            uint8_t* buffer,
+                                            size_t buffer_size,
                                             size_t* resulting_cbor_len) {
   if (buffer == NULL || buffer_size == 0 || resulting_cbor_len == NULL) {
     return false;
@@ -99,8 +107,10 @@
                               resulting_cbor_len);
 }
 
-bool uw_macaroon_encoding_encode_byte_str_(const uint8_t* str, size_t str_len,
-                                           uint8_t* buffer, size_t buffer_size,
+bool uw_macaroon_encoding_encode_byte_str_(const uint8_t* str,
+                                           size_t str_len,
+                                           uint8_t* buffer,
+                                           size_t buffer_size,
                                            size_t* resulting_cbor_len) {
   if (buffer == NULL || buffer_size == 0 || resulting_cbor_len == NULL) {
     return false;
@@ -111,8 +121,10 @@
                              resulting_cbor_len);
 }
 
-bool uw_macaroon_encoding_encode_text_str_(const uint8_t* str, size_t str_len,
-                                           uint8_t* buffer, size_t buffer_size,
+bool uw_macaroon_encoding_encode_text_str_(const uint8_t* str,
+                                           size_t str_len,
+                                           uint8_t* buffer,
+                                           size_t buffer_size,
                                            size_t* resulting_cbor_len) {
   if (buffer == NULL || buffer_size == 0 || resulting_cbor_len == NULL) {
     return false;
@@ -123,7 +135,19 @@
                              resulting_cbor_len);
 }
 
-bool uw_macaroon_encoding_decode_uint_(const uint8_t* cbor, size_t cbor_len,
+bool uw_macaroon_encoding_encode_byte_str_len_(size_t str_len,
+                                               uint8_t* buffer,
+                                               size_t buffer_size,
+                                               size_t* resulting_cbor_len) {
+  if (buffer == NULL || buffer_size == 0 || resulting_cbor_len == NULL) {
+    return false;
+  }
+  set_type_(kCborMajorTypeByteStr, buffer);
+  return blindly_encode_uint_(str_len, buffer, buffer_size, resulting_cbor_len);
+}
+
+bool uw_macaroon_encoding_decode_uint_(const uint8_t* cbor,
+                                       size_t cbor_len,
                                        uint32_t* unsigned_int) {
   if (cbor == NULL || cbor_len == 0 || unsigned_int == NULL ||
       get_type_(cbor) != kCborMajorTypeUint) {
@@ -144,7 +168,8 @@
   return blindly_decode_uint_(cbor, cbor_len, array_len);
 }
 
-bool uw_macaroon_encoding_decode_byte_str_(const uint8_t* cbor, size_t cbor_len,
+bool uw_macaroon_encoding_decode_byte_str_(const uint8_t* cbor,
+                                           size_t cbor_len,
                                            const uint8_t** out_str,
                                            size_t* out_str_len) {
   if (cbor == NULL || cbor_len == 0 || out_str == NULL || out_str_len == NULL ||
@@ -155,7 +180,8 @@
   return blindly_decode_str_(cbor, cbor_len, out_str, out_str_len);
 }
 
-bool uw_macaroon_encoding_decode_text_str_(const uint8_t* cbor, size_t cbor_len,
+bool uw_macaroon_encoding_decode_text_str_(const uint8_t* cbor,
+                                           size_t cbor_len,
                                            const uint8_t** out_str,
                                            size_t* out_str_len) {
   if (cbor == NULL || cbor_len == 0 || out_str == NULL || out_str_len == NULL ||
@@ -193,9 +219,12 @@
   return 4;
 }
 
-// Write the unsigned int in the big-endian fashion by using the minimum number
-// of bytes in CBOR
-static inline bool write_uint_big_endian_(uint32_t unsigned_int, uint8_t* buff,
+/**
+ * Writes the unsigned int in the big-endian fashion by using the minimum number
+ * of bytes in CBOR
+ */
+static inline bool write_uint_big_endian_(uint32_t unsigned_int,
+                                          uint8_t* buff,
                                           size_t buff_len) {
   if (buff == NULL || buff_len == 0) {
     return false;
@@ -225,8 +254,9 @@
   return true;
 }
 
-// Read the unsigned int written in big-endian
-static inline bool read_uint_big_endian_(const uint8_t* bytes, size_t num_bytes,
+/** Reads the unsigned int written in big-endian. */
+static inline bool read_uint_big_endian_(const uint8_t* bytes,
+                                         size_t num_bytes,
                                          uint32_t* unsigned_int) {
   if (bytes == NULL || num_bytes == 0 || num_bytes > 4 ||
       unsigned_int == NULL) {
@@ -252,8 +282,10 @@
   return true;
 }
 
-static bool blindly_encode_uint_(uint32_t unsigned_int, uint8_t* buffer,
-                                 size_t buffer_size, size_t* result_len) {
+static bool blindly_encode_uint_(uint32_t unsigned_int,
+                                 uint8_t* buffer,
+                                 size_t buffer_size,
+                                 size_t* result_len) {
   if (buffer == NULL || buffer_size == 0 || result_len == NULL) {
     return false;
   }
@@ -288,8 +320,10 @@
   return write_uint_big_endian_(unsigned_int, buffer + 1, buffer_size - 1);
 }
 
-static bool blindly_encode_str_(const uint8_t* str, size_t str_len,
-                                uint8_t* buffer, size_t buffer_size,
+static bool blindly_encode_str_(const uint8_t* str,
+                                size_t str_len,
+                                uint8_t* buffer,
+                                size_t buffer_size,
                                 size_t* result_len) {
   if (buffer == NULL || buffer_size == 0) {
     return false;
@@ -320,7 +354,8 @@
   return true;
 }
 
-static bool blindly_decode_uint_(const uint8_t* cbor, size_t cbor_len,
+static bool blindly_decode_uint_(const uint8_t* cbor,
+                                 size_t cbor_len,
                                  uint32_t* unsigned_int) {
   if (cbor == NULL || cbor_len == 0 || unsigned_int == NULL) {
     return false;
@@ -344,8 +379,10 @@
   return read_uint_big_endian_(cbor + 1, uint_num_bytes, unsigned_int);
 }
 
-static bool blindly_decode_str_(const uint8_t* cbor, size_t cbor_len,
-                                const uint8_t** out_str, size_t* out_str_len) {
+static bool blindly_decode_str_(const uint8_t* cbor,
+                                size_t cbor_len,
+                                const uint8_t** out_str,
+                                size_t* out_str_len) {
   if (cbor == NULL || cbor_len == 0 || out_str == NULL || out_str == NULL) {
     return false;
   }
diff --git a/third_party/libuweave/src/macaroon_encoding.h b/third_party/libuweave/src/macaroon_encoding.h
index edddfc1..60f80a6 100644
--- a/third_party/libuweave/src/macaroon_encoding.h
+++ b/third_party/libuweave/src/macaroon_encoding.h
@@ -17,32 +17,53 @@
 #include <stddef.h>
 #include <stdint.h>
 
-// Get the number of bytes that is occupied by the first data item in the give
-// CBOR string.
-bool uw_macaroon_encoding_get_item_len_(const uint8_t* cbor, size_t cbor_len,
+#define UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN 5
+
+/**
+ * Gets the number of bytes that is occupied by the first data item in the give
+ * CBOR string.
+ */
+bool uw_macaroon_encoding_get_item_len_(const uint8_t* cbor,
+                                        size_t cbor_len,
                                         size_t* first_item_len);
 
 bool uw_macaroon_encoding_encode_uint_(const uint32_t unsigned_int,
-                                       uint8_t* buffer, size_t buffer_size,
+                                       uint8_t* buffer,
+                                       size_t buffer_size,
                                        size_t* resulting_cbor_len);
 bool uw_macaroon_encoding_encode_array_len_(const uint32_t array_len,
-                                            uint8_t* buffer, size_t buffer_size,
+                                            uint8_t* buffer,
+                                            size_t buffer_size,
                                             size_t* resulting_cbor_len);
-bool uw_macaroon_encoding_encode_byte_str_(const uint8_t* str, size_t str_len,
-                                           uint8_t* buffer, size_t buffer_size,
+bool uw_macaroon_encoding_encode_byte_str_(const uint8_t* str,
+                                           size_t str_len,
+                                           uint8_t* buffer,
+                                           size_t buffer_size,
                                            size_t* resulting_cbor_len);
-bool uw_macaroon_encoding_encode_text_str_(const uint8_t* str, size_t str_len,
-                                           uint8_t* buffer, size_t buffer_size,
+bool uw_macaroon_encoding_encode_text_str_(const uint8_t* str,
+                                           size_t str_len,
+                                           uint8_t* buffer,
+                                           size_t buffer_size,
                                            size_t* resulting_cbor_len);
 
-bool uw_macaroon_encoding_decode_uint_(const uint8_t* cbor, size_t cbor_len,
+/** Only encode the header (major type and length) of the byte string */
+bool uw_macaroon_encoding_encode_byte_str_len_(size_t str_len,
+                                               uint8_t* buffer,
+                                               size_t buffer_size,
+                                               size_t* resulting_cbor_len);
+
+bool uw_macaroon_encoding_decode_uint_(const uint8_t* cbor,
+                                       size_t cbor_len,
                                        uint32_t* unsigned_int);
 bool uw_macaroon_encoding_decode_array_len_(const uint8_t* cbor,
-                                            size_t cbor_len, uint32_t* array_len);
-bool uw_macaroon_encoding_decode_byte_str_(const uint8_t* cbor, size_t cbor_len,
+                                            size_t cbor_len,
+                                            uint32_t* array_len);
+bool uw_macaroon_encoding_decode_byte_str_(const uint8_t* cbor,
+                                           size_t cbor_len,
                                            const uint8_t** str,
                                            size_t* str_len);
-bool uw_macaroon_encoding_decode_text_str_(const uint8_t* cbor, size_t cbor_len,
+bool uw_macaroon_encoding_decode_text_str_(const uint8_t* cbor,
+                                           size_t cbor_len,
                                            const uint8_t** str,
                                            size_t* str_len);