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

* weave/master:
  Remove event_http_client example provider.
diff --git a/examples/daemon/common/daemon.h b/examples/daemon/common/daemon.h
index 4cccff3..985c5e5 100644
--- a/examples/daemon/common/daemon.h
+++ b/examples/daemon/common/daemon.h
@@ -20,7 +20,6 @@
  public:
   struct Options {
     bool force_bootstrapping_{false};
-    bool disable_security_{false};
     bool disable_privet_{false};
     std::string registration_ticket_;
     std::string model_id_{"AAAAA"};
@@ -31,7 +30,6 @@
                  << "\t-h,--help                    Show this help message\n"
                  << "\t--v=LEVEL                    Logging level\n"
                  << "\t-b,--bootstrapping           Force WiFi bootstrapping\n"
-                 << "\t-d,--disable_security        Disable privet security\n"
                  << "\t--registration_ticket=TICKET Register device with the "
                     "given ticket\n"
                  << "\t--disable_privet             Disable local privet\n";
@@ -44,8 +42,6 @@
           return false;
         } else if (arg == "-b" || arg == "--bootstrapping") {
           force_bootstrapping_ = true;
-        } else if (arg == "-d" || arg == "--disable_security") {
-          disable_security_ = true;
         } else if (arg == "--disable_privet") {
           disable_privet_ = true;
         } else if (arg.find("--registration_ticket") != std::string::npos) {
@@ -69,10 +65,10 @@
   };
 
   Daemon(const Options& opts)
-      : config_store_{new weave::examples::FileConfigStore(
-            opts.disable_security_,
-            opts.model_id_)},
-        task_runner_{new weave::examples::EventTaskRunner},
+      : task_runner_{new weave::examples::EventTaskRunner},
+        config_store_{
+            new weave::examples::FileConfigStore(opts.model_id_,
+                                                 task_runner_.get())},
         http_client_{new weave::examples::CurlHttpClient(task_runner_.get())},
         network_{new weave::examples::EventNetworkImpl(task_runner_.get())},
         bluetooth_{new weave::examples::BluetoothImpl} {
@@ -114,8 +110,8 @@
       LOG(INFO) << "Device registered: " << device->GetSettings().cloud_id;
   }
 
-  std::unique_ptr<weave::examples::FileConfigStore> config_store_;
   std::unique_ptr<weave::examples::EventTaskRunner> task_runner_;
+  std::unique_ptr<weave::examples::FileConfigStore> config_store_;
   std::unique_ptr<weave::examples::CurlHttpClient> http_client_;
   std::unique_ptr<weave::examples::EventNetworkImpl> network_;
   std::unique_ptr<weave::examples::BluetoothImpl> bluetooth_;
diff --git a/examples/provider/file_config_store.cc b/examples/provider/file_config_store.cc
index 6faa242..a6c2e60 100644
--- a/examples/provider/file_config_store.cc
+++ b/examples/provider/file_config_store.cc
@@ -12,16 +12,25 @@
 #include <string>
 #include <vector>
 
+#include <base/bind.h>
+
 namespace weave {
 namespace examples {
 
 const char kSettingsDir[] = "/var/lib/weave/";
 
-FileConfigStore::FileConfigStore(bool disable_security,
-                                 const std::string& model_id)
-    : disable_security_{disable_security},
-      model_id_{model_id},
-      settings_path_{"/var/lib/weave/weave_settings_" + model_id + ".json"} {}
+FileConfigStore::FileConfigStore(const std::string& model_id,
+                                 provider::TaskRunner* task_runner)
+    : model_id_{model_id},
+      task_runner_{task_runner} {}
+
+std::string FileConfigStore::GetPath(const std::string& name) const {
+  std::string path{kSettingsDir};
+  path += path + "weave_settings_" + model_id_;
+  if (!name.empty())
+    path += "_" + name;
+  return path + ".json";
+}
 
 bool FileConfigStore::LoadDefaults(Settings* settings) {
   char host_name[HOST_NAME_MAX] = {};
@@ -50,22 +59,29 @@
   settings->client_secret = "LS_iPYo_WIOE0m2VnLdduhnx";
   settings->api_key = "AIzaSyACK3oZtmIylUKXiTMqkZqfuRiCgQmQSAQ";
 
-  settings->disable_security = disable_security_;
   return true;
 }
 
 std::string FileConfigStore::LoadSettings() {
-  LOG(INFO) << "Loading settings from " << settings_path_;
-  std::ifstream str(settings_path_);
+  return LoadSettings("");
+}
+
+std::string FileConfigStore::LoadSettings(const std::string& name) {
+  LOG(INFO) << "Loading settings from " << GetPath(name);
+  std::ifstream str(GetPath(name));
   return std::string(std::istreambuf_iterator<char>(str),
                      std::istreambuf_iterator<char>());
 }
 
-void FileConfigStore::SaveSettings(const std::string& settings) {
+void FileConfigStore::SaveSettings(const std::string& name,
+                                   const std::string& settings,
+                                   const DoneCallback& callback) {
   CHECK(mkdir(kSettingsDir, S_IRWXU) == 0 || errno == EEXIST);
-  LOG(INFO) << "Saving settings to " << settings_path_;
-  std::ofstream str(settings_path_);
+  LOG(INFO) << "Saving settings to " << GetPath(name);
+  std::ofstream str(GetPath(name));
   str << settings;
+  if (!callback.is_null())
+    task_runner_->PostDelayedTask(FROM_HERE, base::Bind(callback, nullptr), {});
 }
 
 }  // namespace examples
diff --git a/examples/provider/file_config_store.h b/examples/provider/file_config_store.h
index 578f940..337e82a 100644
--- a/examples/provider/file_config_store.h
+++ b/examples/provider/file_config_store.h
@@ -10,22 +10,28 @@
 #include <vector>
 
 #include <weave/provider/config_store.h>
+#include <weave/provider/task_runner.h>
 
 namespace weave {
 namespace examples {
 
 class FileConfigStore : public provider::ConfigStore {
  public:
-  FileConfigStore(bool disable_security, const std::string& model_id);
+  FileConfigStore(const std::string& model_id,
+                  provider::TaskRunner* task_runner);
 
   bool LoadDefaults(Settings* settings) override;
+  std::string LoadSettings(const std::string& name) override;
+  void SaveSettings(const std::string& name,
+                    const std::string& settings,
+                    const DoneCallback& callback) override;
+
   std::string LoadSettings() override;
-  void SaveSettings(const std::string& settings) override;
 
  private:
-  const bool disable_security_;
+  std::string GetPath(const std::string& name) const;
   const std::string model_id_;
-  const std::string settings_path_;
+  provider::TaskRunner* task_runner_{nullptr};
 };
 
 }  // namespace examples
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/config_store.h b/include/weave/provider/config_store.h
index 1b7988f..128eccc 100644
--- a/include/weave/provider/config_store.h
+++ b/include/weave/provider/config_store.h
@@ -13,6 +13,7 @@
 #include <base/callback.h>
 #include <base/time/time.h>
 #include <weave/enum_to_string.h>
+#include <weave/error.h>
 #include <weave/settings.h>
 
 namespace weave {
@@ -36,8 +37,8 @@
 // Implementation of LoadSettings() method should load previously
 // stored settings from the persistent storage (file, flash, etc).
 // For example:
-//   std::string FileConfigStore::LoadSettings() {
-//     std::ifstream str("/var/lib/weave/weave_settings.json");
+//   std::string FileConfigStore::LoadSettings(const std::string& name) {
+//     std::ifstream str("/var/lib/weave/weave_" + name + ".json");
 //     return std::string(std::istreambuf_iterator<char>(str),
 //                        std::istreambuf_iterator<char>());
 //   }
@@ -47,9 +48,14 @@
 // Implementation of SaveSettings(...) method should store data in the
 // persistent storage (file, flash, etc).
 // For example:
-//   void FileConfigStore::SaveSettings(const std::string& settings) {
-//     std::ofstream str(kSettingsPath);
+//   void FileConfigStore::SaveSettings(const std::string& name,
+//                                      const std::string& settings,
+//                                      const DoneCallback& callback) {
+//     std::ofstream str("/var/lib/weave/weave_" + name + ".json");
 //     str << settings;
+//     if (!callback.is_null())
+//       task_runner_->PostDelayedTask(FROM_HERE, base::Bind(callback, nullptr),
+//                                     {});
 //   }
 // It is highly recommended to protected data using encryption with
 // hardware backed key.
@@ -67,12 +73,20 @@
 
   // Returns settings saved by SaveSettings during last run of libweave.
   // Implementation should return data as-is without parsing or modifications.
-  virtual std::string LoadSettings() = 0;
+  // |name| is the name of settings blob. Could be used as filename.
+  virtual std::string LoadSettings(const std::string& name) = 0;
 
   // Saves settings. Implementation should save data as-is without parsing or
   // modifications. Data stored in settings can be sensitive, so it's highly
   // recommended to protect data, e.g. using encryption.
-  virtual void SaveSettings(const std::string& settings) = 0;
+  // |name| is the name of settings blob. Could be used as filename.
+  // Implementation must call or post callback
+  virtual void SaveSettings(const std::string& name,
+                            const std::string& settings,
+                            const DoneCallback& callback) = 0;
+
+  // Deprecated: only for migration of old configs to version with |name|.
+  virtual std::string LoadSettings() = 0;
 
  protected:
   virtual ~ConfigStore() {}
diff --git a/include/weave/provider/test/mock_config_store.h b/include/weave/provider/test/mock_config_store.h
index 3873251..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,11 +42,21 @@
           "version": 1,
           "device_id": "TEST_DEVICE_ID"
         })"));
-    EXPECT_CALL(*this, SaveSettings(_)).WillRepeatedly(Return());
+    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())
+                callback.Run(nullptr);
+            })));
   }
   MOCK_METHOD1(LoadDefaults, bool(Settings*));
+  MOCK_METHOD1(LoadSettings, std::string(const std::string&));
+  MOCK_METHOD3(SaveSettings,
+               void(const std::string&,
+                    const std::string&,
+                    const DoneCallback&));
   MOCK_METHOD0(LoadSettings, std::string());
-  MOCK_METHOD1(SaveSettings, void(const std::string&));
 };
 
 }  // namespace test
diff --git a/include/weave/settings.h b/include/weave/settings.h
index eeb3f93..741fff2 100644
--- a/include/weave/settings.h
+++ b/include/weave/settings.h
@@ -71,7 +71,6 @@
   // Internal options to tweak some library functionality. External code should
   // avoid using them.
   bool wifi_auto_setup_enabled{true};
-  bool disable_security{false};
   std::string test_privet_ssid;
 };
 
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..a142735
--- /dev/null
+++ b/src/access_api_handler_unittest.cc
@@ -0,0 +1,257 @@
+// 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/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()};
+  }
+
+  ComponentManagerImpl component_manager_;
+  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/config.cc b/src/config.cc
index 76be205..44d20dd 100644
--- a/src/config.cc
+++ b/src/config.cc
@@ -18,9 +18,12 @@
 #include "src/data_encoding.h"
 #include "src/privet/privet_types.h"
 #include "src/string_utils.h"
+#include "src/bind_lambda.h"
 
 namespace weave {
 
+const char kConfigName[] = "config";
+
 namespace config_keys {
 
 const char kVersion[] = "version";
@@ -139,9 +142,12 @@
 void Config::Transaction::LoadState() {
   if (!config_->config_store_)
     return;
-  std::string json_string = config_->config_store_->LoadSettings();
-  if (json_string.empty())
-    return;
+  std::string json_string = config_->config_store_->LoadSettings(kConfigName);
+  if (json_string.empty()) {
+    json_string = config_->config_store_->LoadSettings();
+    if (json_string.empty())
+      return;
+  }
 
   auto value = base::JSONReader::Read(json_string);
   base::DictionaryValue* dict = nullptr;
@@ -266,7 +272,9 @@
   base::JSONWriter::WriteWithOptions(
       dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_string);
 
-  config_store_->SaveSettings(json_string);
+  config_store_->SaveSettings(
+      kConfigName, json_string,
+      base::Bind([](ErrorPtr error) { CHECK(!error); }));
 }
 
 Config::Transaction::~Transaction() {
diff --git a/src/config_unittest.cc b/src/config_unittest.cc
index 0367516..4b0e5b4 100644
--- a/src/config_unittest.cc
+++ b/src/config_unittest.cc
@@ -17,18 +17,20 @@
 using testing::_;
 using testing::Invoke;
 using testing::Return;
+using testing::WithArgs;
 
 namespace weave {
 
+const char kConfigName[] = "config";
+
 class ConfigTest : public ::testing::Test {
  protected:
   void SetUp() override {
-    EXPECT_CALL(*this, OnConfigChanged(_))
-        .Times(1);  // Called from AddOnChangedCallback
     Reload();
   }
 
   void Reload() {
+    EXPECT_CALL(*this, OnConfigChanged(_)).Times(1);
     config_.reset(new Config{&config_store_});
     config_->AddOnChangedCallback(
         base::Bind(&ConfigTest::OnConfigChanged, base::Unretained(this)));
@@ -66,7 +68,6 @@
   EXPECT_FALSE(GetSettings().device_id.empty());
   EXPECT_EQ("", GetSettings().firmware_version);
   EXPECT_TRUE(GetSettings().wifi_auto_setup_enabled);
-  EXPECT_FALSE(GetSettings().disable_security);
   EXPECT_EQ("", GetSettings().test_privet_ssid);
   EXPECT_EQ(std::set<PairingType>{PairingType::kPinCode},
             GetSettings().pairing_modes);
@@ -86,31 +87,45 @@
 }
 
 TEST_F(ConfigTest, LoadStateV0) {
-  EXPECT_CALL(config_store_, LoadSettings())
+  EXPECT_CALL(config_store_, LoadSettings(kConfigName))
       .WillOnce(Return(R"({
     "device_id": "state_device_id"
   })"));
 
-  EXPECT_CALL(*this, OnConfigChanged(_)).Times(1);
   Reload();
 
   EXPECT_EQ("state_device_id", GetSettings().cloud_id);
   EXPECT_FALSE(GetSettings().device_id.empty());
   EXPECT_NE(GetSettings().cloud_id, GetSettings().device_id);
 
-  EXPECT_CALL(config_store_, LoadSettings())
+  EXPECT_CALL(config_store_, LoadSettings(kConfigName))
       .WillOnce(Return(R"({
     "device_id": "state_device_id",
     "cloud_id": "state_cloud_id"
   })"));
 
-  EXPECT_CALL(*this, OnConfigChanged(_)).Times(1);
   Reload();
 
   EXPECT_EQ("state_cloud_id", GetSettings().cloud_id);
   EXPECT_EQ("state_device_id", GetSettings().device_id);
 }
 
+TEST_F(ConfigTest, LoadStateUnnamed) {
+  EXPECT_CALL(config_store_, LoadSettings(kConfigName)).WillOnce(Return(""));
+
+  EXPECT_CALL(config_store_, LoadSettings()).Times(1);
+
+  Reload();
+}
+
+TEST_F(ConfigTest, LoadStateNamed) {
+  EXPECT_CALL(config_store_, LoadSettings(kConfigName)).WillOnce(Return("{}"));
+
+  EXPECT_CALL(config_store_, LoadSettings()).Times(0);
+
+  Reload();
+}
+
 TEST_F(ConfigTest, LoadState) {
   auto state = R"({
     "version": 1,
@@ -133,9 +148,8 @@
     "secret": "c3RhdGVfc2VjcmV0",
     "service_url": "state_service_url"
   })";
-  EXPECT_CALL(config_store_, LoadSettings()).WillOnce(Return(state));
+  EXPECT_CALL(config_store_, LoadSettings(kConfigName)).WillOnce(Return(state));
 
-  EXPECT_CALL(*this, OnConfigChanged(_)).Times(1);
   Reload();
 
   EXPECT_EQ("state_client_id", GetSettings().client_id);
@@ -149,8 +163,6 @@
   EXPECT_EQ("state_device_id", GetSettings().device_id);
   EXPECT_EQ(GetDefaultSettings().wifi_auto_setup_enabled,
             GetSettings().wifi_auto_setup_enabled);
-  EXPECT_EQ(GetDefaultSettings().disable_security,
-            GetSettings().disable_security);
   EXPECT_EQ(GetDefaultSettings().test_privet_ssid,
             GetSettings().test_privet_ssid);
   EXPECT_EQ(GetDefaultSettings().pairing_modes, GetSettings().pairing_modes);
@@ -243,9 +255,10 @@
 
   EXPECT_CALL(*this, OnConfigChanged(_)).Times(1);
 
-  EXPECT_CALL(config_store_, SaveSettings(_))
-      .WillOnce(Invoke([](const std::string& json) {
-        auto expected = R"({
+  EXPECT_CALL(config_store_, SaveSettings(kConfigName, _, _))
+      .WillOnce(WithArgs<1, 2>(
+          Invoke([](const std::string& json, const DoneCallback& callback) {
+            auto expected = R"({
           'version': 1,
           'api_key': 'set_api_key',
           'client_id': 'set_client_id',
@@ -266,8 +279,9 @@
           'secret': 'AQIDBAU=',
           'service_url': 'set_service_url'
         })";
-        EXPECT_JSON_EQ(expected, *test::CreateValue(json));
-      }));
+            EXPECT_JSON_EQ(expected, *test::CreateValue(json));
+            callback.Run(nullptr);
+          })));
 
   change.Commit();
 }
diff --git a/src/device_manager.cc b/src/device_manager.cc
index 04d7a6b..8eed558 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..71a4f39 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,98 @@
 
 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::string& id)
+      : Caveat(kUwMacaroonCaveatTypeDelegateeUser, id.size()) {
+    CHECK(uw_macaroon_caveat_create_delegatee_user_(
+        reinterpret_cast<const uint8_t*>(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 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 +153,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 +207,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 +217,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,15 +311,16 @@
 
 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())};
+  ScopeCaveat scope{ToMacaroonScope(user_info.scope())};
+  UserIdCaveat user{user_info.user_id()};
+  const base::Time now = Now();
+  ExpirationCaveat expiration{now + ttl};
   return CreateMacaroonToken(
-      access_secret_,
+      access_secret_, now,
       {
-          scope.GetCaveat(), user.GetCaveat(), issued.GetCaveat(),
+          &scope.GetCaveat(), &user.GetCaveat(), &expiration.GetCaveat(),
       });
+  return {};
 }
 
 bool AuthManager::ParseAccessToken(const std::vector<uint8_t>& token,
@@ -256,35 +329,28 @@
   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)) {
+      !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(1u, result.num_delegatees);
+  CHECK_EQ(kUwMacaroonDelegateeTypeUser, result.delegatees[0].type);
+  std::string user_id{reinterpret_cast<const char*>(result.delegatees[0].id),
+                      result.delegatees[0].id_len};
   if (user_info)
     *user_info = UserInfo{auth_scope, user_id};
 
@@ -309,7 +375,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 +398,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"
+                                                             : "privet"};
+  return CreateMacaroonToken(
+      auth_secret_, now,
+      {
+          &auth_token.GetCaveat(), &issued.GetCaveat(), &client.GetCaveat(),
+      });
 }
 
 base::Time AuthManager::Now() const {
@@ -350,8 +422,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 +438,54 @@
     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;
+                   });
+
+  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::string user_id{reinterpret_cast<const char*>(last_user_id->id),
+                      last_user_id->id_len};
+  UserInfo info{auth_scope, user_id};
 
-  // TODO(vitalybuka): TTL also should be reduced in accordance with auth_token.
+  ttl = std::min(ttl, FromJ2000Time(result.expiration_time) - now);
   *access_token = CreateAccessToken(info, ttl);
 
   if (access_token_scope)
@@ -388,11 +496,39 @@
   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.user_id()};
+  SessionIdCaveat session{CreateSessionId()};
+
+  return ExtendMacaroonToken(
+      macaroon, now,
+      {
+          &issued.GetCaveat(), &expiration.GetCaveat(), &scope.GetCaveat(),
+          &user.GetCaveat(), &session.GetCaveat(),
+      });
 }
 
 }  // 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..2adb1c2 100644
--- a/src/privet/auth_manager_unittest.cc
+++ b/src/privet/auth_manager_unittest.cc
@@ -29,6 +29,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,18 +69,18 @@
 }
 
 TEST_F(AuthManagerTest, CreateAccessToken) {
-  EXPECT_EQ("UABRUHgcSZDry0bvIsoJv+WDQgEURQJjMjM0RgUaVArkgA==",
+  EXPECT_EQ("WCKDQgEURQlDMjM0RgUaG52hAFA3hFh7TexW1jC96sU4CxvN",
             Base64Encode(auth_.CreateAccessToken(
                 UserInfo{AuthScope::kViewer, "234"}, {})));
-  EXPECT_EQ("UL7YEruLg5QQRDIp2+u1cqCDQgEIRQJjMjU3RgUaVArkgA==",
+  EXPECT_EQ("WCKDQgEIRQlDMjU3RgUaG52hAFD3dEHl3Y9Y28uoUESiYuLq",
             Base64Encode(auth_.CreateAccessToken(
                 UserInfo{AuthScope::kManager, "257"}, {})));
-  EXPECT_EQ("UPFGeZRanR1wLGYLP5ZDkXiDQgECRQJjNDU2RgUaVArkgA==",
+  EXPECT_EQ("WCKDQgECRQlDNDU2RgUaG52hAFBy35bQdtvlqYf+Y/ANyxLU",
             Base64Encode(auth_.CreateAccessToken(
                 UserInfo{AuthScope::kOwner, "456"}, {})));
   auto new_time = clock_.Now() + base::TimeDelta::FromDays(11);
   EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time));
-  EXPECT_EQ("UMm9KlF3OEtZFBmhScJpl4uDQgEORQJjMzQ1RgUaVBllAA==",
+  EXPECT_EQ("WCKDQgEORQlDMzQ1RgUaG6whgFD1HGVxL8+FPaf/U0bOkXr8",
             Base64Encode(auth_.CreateAccessToken(
                 UserInfo{AuthScope::kUser, "345"}, {})));
 }
@@ -129,6 +134,10 @@
         .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, "234"});
+    EXPECT_FALSE(auth.ParseAccessToken(extended, &user_info, nullptr));
+
     EXPECT_CALL(clock_, Now())
         .WillRepeatedly(
             Return(kStartTime + base::TimeDelta::FromSeconds(i + 1)));
@@ -137,35 +146,135 @@
 }
 
 TEST_F(AuthManagerTest, GetRootClientAuthToken) {
-  EXPECT_EQ("UK1ACOc3cWGjGBoTIX2bd3qCQgECRgMaVArkgA==",
-            Base64Encode(auth_.GetRootClientAuthToken()));
+  EXPECT_EQ("WCaDQxkgAUYIGhudoQBIDEZwcml2ZXRQ5aV4jIdY2JGosyU0APnQpA==",
+            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("WCaDQxkgAUYIGhuxZ4BIDEZwcml2ZXRQsDNy7gcfJT/yvRs3/q40oA==",
+            Base64Encode(
+                auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient)));
 }
 
 TEST_F(AuthManagerTest, GetRootClientAuthTokenDifferentSecret) {
   AuthManager auth{kSecret2, {}, kSecret1, &clock_};
-  EXPECT_EQ("UFTBUcgd9d0HnPRnLeroN2mCQgECRgMaVArkgA==",
-            Base64Encode(auth.GetRootClientAuthToken()));
+  EXPECT_EQ(
+      "WCaDQxkgAUYIGhudoQBIDEZwcml2ZXRQKw9xcidyzrelxUkgkLmv1g==",
+      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, "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("234", user_info.user_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, "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, "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, "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 +350,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/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_manager.cc b/src/privet/privet_manager.cc
index edc7907..9c717ce 100644
--- a/src/privet/privet_manager.cc
+++ b/src/privet/privet_manager.cc
@@ -53,8 +53,6 @@
   CHECK(auth_manager);
   CHECK(device);
 
-  disable_security_ = device->GetSettings().disable_security;
-
   device_ = DeviceDelegate::CreateDefault(
       task_runner_, http_server->GetHttpPort(), http_server->GetHttpsPort(),
       http_server->GetRequestTimeout());
@@ -129,9 +127,6 @@
     const std::shared_ptr<provider::HttpServer::Request>& request,
     const std::string& data) {
   std::string auth_header = request->GetFirstHeader(http::kAuthorization);
-  if (auth_header.empty() && disable_security_)
-    auth_header = "Privet anonymous";
-
   base::DictionaryValue empty;
   auto value = base::JSONReader::Read(data);
   const base::DictionaryValue* dictionary = &empty;
diff --git a/src/privet/privet_manager.h b/src/privet/privet_manager.h
index 371d843..06eb89a 100644
--- a/src/privet/privet_manager.h
+++ b/src/privet/privet_manager.h
@@ -79,7 +79,6 @@
   void OnChanged();
   void OnConnectivityChanged();
 
-  bool disable_security_{false};
   provider::TaskRunner* task_runner_{nullptr};
   std::unique_ptr<CloudDelegate> cloud_;
   std::unique_ptr<DeviceDelegate> device_;
diff --git a/src/privet/privet_types.cc b/src/privet/privet_types.cc
index dd291b3..9e50f94 100644
--- a/src/privet/privet_types.cc
+++ b/src/privet/privet_types.cc
@@ -52,7 +52,6 @@
 };
 
 const EnumToStringMap<CryptoType>::Map kCryptoTypeMap[] = {
-    {CryptoType::kNone, "none"},
     {CryptoType::kSpake_p224, "p224_spake2"},
 };
 
diff --git a/src/privet/privet_types.h b/src/privet/privet_types.h
index c738865..49c4522 100644
--- a/src/privet/privet_types.h
+++ b/src/privet/privet_types.h
@@ -15,7 +15,6 @@
 namespace privet {
 
 enum class CryptoType {
-  kNone,
   kSpake_p224,
 };
 
diff --git a/src/privet/security_manager.cc b/src/privet/security_manager.cc
index 358876d..04164b3 100644
--- a/src/privet/security_manager.cc
+++ b/src/privet/security_manager.cc
@@ -67,25 +67,6 @@
   crypto::P224EncryptedKeyExchange spake_;
 };
 
-class UnsecureKeyExchanger : public SecurityManager::KeyExchanger {
- public:
-  explicit UnsecureKeyExchanger(const std::string& password)
-      : password_(password) {}
-  ~UnsecureKeyExchanger() override = default;
-
-  // SecurityManager::KeyExchanger methods.
-  const std::string& GetMessage() override { return password_; }
-
-  bool ProcessMessage(const std::string& message, ErrorPtr* error) override {
-    return true;
-  }
-
-  const std::string& GetKey() const override { return password_; }
-
- private:
-  std::string password_;
-};
-
 }  // namespace
 
 SecurityManager::SecurityManager(const Config* config,
@@ -218,8 +199,6 @@
 
 std::set<CryptoType> SecurityManager::GetCryptoTypes() const {
   std::set<CryptoType> result{CryptoType::kSpake_p224};
-  if (GetSettings().disable_security)
-    result.insert(CryptoType::kNone);
   return result;
 }
 
@@ -259,8 +238,6 @@
 
 bool SecurityManager::IsValidPairingCode(
     const std::vector<uint8_t>& auth_code) const {
-  if (GetSettings().disable_security)
-    return true;
   for (const auto& session : confirmed_sessions_) {
     const std::string& key = session.second->GetKey();
     const std::string& id = session.first;
@@ -309,11 +286,6 @@
     case CryptoType::kSpake_p224:
       spake.reset(new Spakep224Exchanger(code));
       break;
-    case CryptoType::kNone:
-      if (GetSettings().disable_security) {
-        spake.reset(new UnsecureKeyExchanger(code));
-        break;
-      }
     // Fall through...
     default:
       return Error::AddTo(error, FROM_HERE, errors::kInvalidParams,
@@ -416,7 +388,7 @@
 }
 
 std::string SecurityManager::CreateSessionId() {
-  return Base64Encode(auth_manager_->CreateSessionId());
+  return auth_manager_->CreateSessionId();
 }
 
 void SecurityManager::RegisterPairingListeners(
@@ -428,9 +400,6 @@
 }
 
 bool SecurityManager::CheckIfPairingAllowed(ErrorPtr* error) {
-  if (GetSettings().disable_security)
-    return true;
-
   if (block_pairing_until_ > auth_manager_->Now()) {
     return Error::AddTo(error, FROM_HERE, errors::kDeviceBusy,
                         "Too many pairing attempts");
diff --git a/src/weave_unittest.cc b/src/weave_unittest.cc
index 5bef931..ebc66cd 100644
--- a/src/weave_unittest.cc
+++ b/src/weave_unittest.cc
@@ -204,10 +204,6 @@
             })));
   }
 
-  void InitConfigStore() {
-    EXPECT_CALL(config_store_, SaveSettings("")).WillRepeatedly(Return());
-  }
-
   void InitNetwork() {
     EXPECT_CALL(network_, AddConnectionChangedCallback(_))
         .WillRepeatedly(Invoke(
@@ -267,7 +263,6 @@
   }
 
   void InitDefaultExpectations() {
-    InitConfigStore();
     InitNetwork();
     EXPECT_CALL(wifi_, StartAccessPoint(MatchesRegex("TEST_NAME.*prv")))
         .WillOnce(Return());
@@ -360,13 +355,11 @@
 }
 
 TEST_F(WeaveTest, StartMinimal) {
-  InitConfigStore();
   device_ = weave::Device::Create(&config_store_, &task_runner_, &http_client_,
                                   &network_, nullptr, nullptr, &wifi_, nullptr);
 }
 
 TEST_F(WeaveTest, StartNoWifi) {
-  InitConfigStore();
   InitNetwork();
   InitHttpServer();
   InitDnsSd();
@@ -450,7 +443,6 @@
   void SetUp() override {
     WeaveTest::SetUp();
 
-    InitConfigStore();
     InitHttpServer();
     InitNetwork();
     InitDnsSd();
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..aa775c2 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,228 @@
   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->granted_scope = kUwMacaroonCaveatScopeTypeOwner;
+  result->expiration_time = UINT32_MAX;
+  result->weave_app_restricted = false;
+  result->lan_session_id = NULL;
+  result->lan_session_id_len = 0;
+  result->num_delegatees = 0;
+}
+
+/** Reset the result object to the lowest scope when encountering errors */
+static void reset_validation_result(UwMacaroonValidationResult* result) {
+  // Start from the largest scope or highest privilege
+  result->granted_scope =
+      (UwMacaroonCaveatScopeType)UW_MACAROON_CAVEAT_SCOPE_LOWEST_POSSIBLE;
+  result->expiration_time = 0;
+  result->weave_app_restricted = true;
+  result->lan_session_id = NULL;
+  result->lan_session_id_len = 0;
+
+  result->num_delegatees = 0;
+  for (size_t i = 0; i < MAX_NUM_DELEGATEES; i++) {
+    result->delegatees[i].id = NULL;
+    result->delegatees[i].id_len = 0;
+    result->delegatees[i].type = kUwMacaroonDelegateeTypeNone;
+  }
+}
+
+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;
+    }
+  }
+
+  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..c93bbb2 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,78 @@
 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;
+} 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.
+ */
+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..a2b26dc 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,224 @@
   }
 
   *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,
+                                  UwMacaroonValidationResult* result) {
+  if (result->num_delegatees >= MAX_NUM_DELEGATEES) {
+    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->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:
+      state->has_issued_time = true;
+      if (!uw_macaroon_caveat_get_value_uint_(caveat, &issued_time)) {
+        return false;
+      }
+      state->issued_time = issued_time;
+      return true;
+
+    case kUwMacaroonCaveatTypeTTL1Hour:
+      if (!(state->has_issued_time)) {
+        return false;
+      }
+      return update_and_check_expiration_time(
+          context->current_time, state->issued_time + 60 * 60, result);
+
+    case kUwMacaroonCaveatTypeTTL24Hour:
+      if (!(state->has_issued_time)) {
+        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, 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_HIGHEST_POSSIBLE ||
+          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 +539,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 +556,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 +567,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 +584,17 @@
     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->has_issued_time = false;
+  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..b6846e8 100644
--- a/third_party/libuweave/src/macaroon_caveat.h
+++ b/third_party/libuweave/src/macaroon_caveat.h
@@ -15,15 +15,28 @@
 } 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;
 
+// For security sanity checks
+#define UW_MACAROON_CAVEAT_SCOPE_HIGHEST_POSSIBLE 0
+#define UW_MACAROON_CAVEAT_SCOPE_LOWEST_POSSIBLE 127
+
 typedef enum {
   kUwMacaroonCaveatScopeTypeOwner = 2,
   kUwMacaroonCaveatScopeTypeManager = 8,
@@ -31,28 +44,80 @@
   kUwMacaroonCaveatScopeTypeViewer = 20,
 } UwMacaroonCaveatScopeType;
 
-bool uw_macaroon_caveat_create_without_value_(UwMacaroonCaveatType type,
+/** 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..46a72fb
--- /dev/null
+++ b/third_party/libuweave/src/macaroon_caveat_internal.h
@@ -0,0 +1,41 @@
+// 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 {
+  bool has_issued_time;
+  uint32_t issued_time;
+} 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);