Google APIs Client Library for C++ | A C++ library for client applications to access Google APIs. |
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
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.
- UserHomeDirectory
.googleapis/credentials/
- user_identityA/
- Application1
- Application2
- user_identityB/
- Application1
- Application3
- user_identityA/
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(); } };