Google APIs Client Library for C++ A C++ library for client applications to access Google APIs.
Credential Store

This document discusses the googleapis::client::CredentialStore abstraction and implementations provided with the SDK. It also discusses how to write and use your own custom CredentialStore.

Contents

  1. Typical use case examples
      Saving credentials Restoring credentials Removing credentials
  2. Provided implementations
      FileCredentialStore
  3. Writing a Codec to encrypt a CredentialStore
  4. Writing a custom CredentialStore

Typical use case examples

The following snippet is applicable to the examples listed below.

// CredentialStore and FileCredentialStore are declared here.
#include "googleapis/client/auth/file_credential_store.h"
#include "googleapis/client/transportu/http_authorization.h"

using googleapis::client::CredentialStore;
using googleapis::client::FileCredentialStoreFactory;

Binding a credential store to an OAuth2AuthorizationFlow

The following binds a CredentialStore to an OAuth2AuthorizationFlow. In this case, it is a file-based CredentialStore. The actual store instance is created from a local factory but ownership of the instance is passed into the authorization flow so will live longer than the scope.

See the section FileCredentialStore below for more explanation about what this sample is doing.

string store_path;
googleapis::util::Status status =
    FileCredentialStoreFactory::GetSystemHomeDirectoryStorePath(&store_path);
if (status.ok()) {
  FileCredentialStoreFactory store_factory(store_path);
  flow->ResetCredentialStore(
      store_factory.NewCredentialStore("CalendarSample", &status));
}
if (!status.ok()) {
  LOG(ERROR)
      << "Failed creating credential store: " << status.error_message();
}

Saving a credential

Normally the OAuth2AuthorizationFlow will automatically store credentials into the credential store bound to it so there is no need to call the CredentialStore directly. You can still explicitly store a credential yourself if you need to.

The credential store interface associates an external user_name key with the credential. This is a key that your application picks to associate with the desired user that this credential is for. When using OAuth 2.0 credentials, this could be the user_id() attribute of the OAuth2Credential.

The Store method takes the user_id and credential instance, stores the serialized credential for later retrieval.

status = credential_store->Store(user_id, credential);
if (!status.ok()) {
  LOG(ERROR) << "Failed storing credentials: " << status.error_message();
}

Restoring a credential

Normally the OAuth2AuthorizationFlow will automatically attempt to load credentials from the CredentialStore bound to it so there is no need to call the CredentialStore yourself. You can still explicitly store a credential yourself if you need to.

Credentials are loaded by giving the desired key to the InitCredential method on the store. The key should be the same as the one used for the Store method. This will re-initialize the credential with the data from the store with any. On failure, the credential will be cleared of any prior data.

status = credential_store->InitCredential(user_id, &credential);
if (!status.ok()) {
  // usually this means the credentials are not known or available,
  // such as a first-time user.
}

Removing a credential

You may explicitly remove a credential from a store by calling the Delete method with the key used to store the credential. This does not revoke the credentials, it just removes them from the store.

The following snippet deletes a credential from the store but does not actually revoke the credential. To revoke an OAuth 2.0 credential, call OAuth2AuthorizationFlow::NewRevokeRefreshTokenRequest.

status = credential_store->Delete(user_id);

Provided implementations

FileCredentialStore

The FileCredentialStore is uses the file system to store credentials. It takes some precautions to protecting the data, such as using user read/write-only files however a filesystem is not as secure as other mechanisms or platform-specific key stores. If the machine and disk is compromised then the credentials can become compromised to gain access to personal data in cloud services.

For programming convenience the CredentialStore interface is organized as a map of client applications to users then users to credentials. However for user maintenance convenience the file credential store internally organizes itself as a map of users to client applications, then client applications to credentials for that user.

Consider a scenario where a user (person) uses multiple client applications (Application1, Application2, Application3). In addition, they have multiple cloud identities so may use different Google Accounts for different applications or the same account for multiple applications. Each of these applications might be using a FileCredentialStore using the same filesystem in their local computer's filesystem because their Google Account cloud identity is different from their native operating system's login id. The physical credential store as stored on disk might look as follows.

The actual FileCredentialStore class is not public. It only exposes the abstract CredentialStore interface. In order to instantiate one, you must use a FileCredentialStoreFactory as illustrated in the snippet below.

string store_path;
googleapis::util::Status status =
    FileCredentialStoreFactory::GetSystemHomeDirectoryStorePath(&store_path);
if (status.ok()) {
  FileCredentialStoreFactory store_factory(store_path);
  flow->ResetCredentialStore(
      store_factory.NewCredentialStore("CalendarSample", &status));
}
if (!status.ok()) {
  LOG(ERROR)
      << "Failed creating credential store: " << status.error_message();
}

The FileCredentialStoreFactory has a root_path attribute specifying the top-level directory where it will store the credentials. The FileCredentialStore::SystemHomeDirectoryStorePath returns a subdirectory off the user $HOME directory, .googleapis/credentials.

Writing a codec to encrypt a CredentialStore

Future releases will provide a standard Codec implementation for encrypting data in credential stores. However at present there is no such implementation so you will need to write your own.

#include "googleapis/client/util/file_utils.h"
#include "googleapis/client/util/status.h"
using googleapis::client::Codec;
using googleapis::util::Status;

class MyEncryptionCodec : public Codec {
public:
  virtual Status Encode(const StringPiece& decoded, string* encoded) {
     *encoded = MyEncryptionAlgorithm(decoded);
     return googleapis::util::StatusOk();
  }
  virtual Status Decode(const StringPiece& encoded, string* decoded) {
     *decoded = MyDecryptionAlgorithm(encoded);
     return googleapis::util::StatusOk();
  }
};
...
credential_store->set_codec(new MyEncryptionCodec);

Writing a custom CredentialStore

In future releases the SDK will probably support native credential stores. In the meantime, or if you have some proprietary mechanism for secure storage, you can write your own specialized CredentialStore by implementing the minimal pure virtual methods in the interface. To get the content of the credential, use the DecodeToEncodingReader and EncodeToDecodingReader methods. These will handle the transformations (such as encryption) that the where externally configured on the store.

using googleapis::client::AuthorizationCredential;
using googleapis::client::DataReader;
class MyCredentialStore : public CredentialStore {
 public:
  MyCredentialStore() {}
  virtual ~MyCredentialStore() {}

  virtual googleapis::util::Status Store(
      const StringPiece& user, const AuthorizationCredential& credential) {
    googleapis::util::Status status;
    scoped_ptr<DataReader>encoder(
        DecodedToEncodingReader(credential.MakeDataReader(), &status));
    if (!status.ok()) return status;

    string serialized = encoder->RemainderToString();
    return StoreStringInMyMechanism(user, serialized);
  }

  virtual googleapis::util::Status InitCredential(
      const StringPiece& user, AuthorizationCredential* credential) {
    string data;
    googleapis::util::Status status = LoadStringFromMyMechanism(user, &data);
    if (!status.ok()) return status;

    scoped_ptr<DataReader> decoder(
        EncodedToDecodingReader(
            googleapis::client::NewUnmanagedInMemoryDataReader(data),
            &status));
    if (!status.ok()) return status;

    return credential->Load(decoder.get());
  }

  virtual googleapis::util::Status Delete(const StringPiece& user) {
    return DeleteStringFromMyMechanism(user);
  }

 private:
  googleapis::util::Status StoreStringInMyMechanism(
      const StringPiece& key, const string& str) {
    // This is where you store the key/str value pair into your mechanism.
    // On failure you'd return an error status with explanation.
    return googleapis::util::StatusOk();
  }
  googleapis::util::Status LoadStringFromMyMechanism(
      const StringPiece& key, string* str) {
    // This is where you load the str value for the key from your mechanism.
    // On failure you'd return an error status with explanation.
    *str = "";
    return googleapis::util::StatusOk();
  }
  googleapis::util::Status DeleteStringFromMyMechanism(
      const StringPiece& key) {
    // This is where you delete the key from your mechanism.
    // On failure you'd return an error status with explanation.
    return googleapis::util::StatusOk();
  }
};