Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding auth doc explaining all the ways to mint credentials. #805

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 19 additions & 32 deletions docs/_components/enabling-a-service-account.rst
Original file line number Diff line number Diff line change
@@ -1,41 +1,28 @@
Now that you have a project,
we need to make sure we are able to access our data.
There are many ways to authenticate,
but we're going to use a Service Account for today.

A *Service Account* is sort of like a username and password
(like when you're connecting to your MySQL database),
except the username is automatically generated
(and is an e-mail address)
and the password is actually a private key file.
A `Service Account`_ handles authentication for a backend service that
needs to talk to other services (e.g. Google APIs).

This comment was marked as spam.


To create a Service Account:

* **Click on Credentials**
under the "APIs & Auth" section.

* **Click the big red button**
that says "Create New Client ID"
under the OAuth section
(the first one).
#. Visit the `Google Developers Console`_.

* **Choose "Service Account"**
and click the blue button
that says "Create Client ID".
#. Create a new project or click on an existing project.

* **This will automatically**
download a private key file.
**Do not lose this.**
#. Navigate to **APIs & auth** > **APIs section** and turn on the following
APIs (you may need to enable billing in order to use these services):

* **Rename your key** something shorter.
I like to name the key ``<project name>.p12``.
* Google Cloud Datastore API
* Google Cloud Storage
* Google Cloud Storage JSON API
* Google Pub/Sub API

This is like your password for the account.
#. Navigate to **APIs & auth** > **Credentials** and then:

* **Copy the long weird e-mail address**
labeled "E-mail address"
in the information section
for the Service Account
you just created.
* If you want to use a new service account, click on **Create new Client ID**
and select **Service account**. After the account is created, you will be
prompted to download the JSON key file that the library uses to authorize
your requests.
* If you want to generate a new key for an existing service account, click
on **Generate new JSON key** and download the JSON key file.

This is like your username for the account.
.. _Google Developers Console: https://console.developers.google.com/project
.. _Service Account: https://developers.google.com/accounts/docs/OAuth2ServiceAccount
301 changes: 301 additions & 0 deletions docs/gcloud-auth.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
.. toctree::
:maxdepth: 1
:hidden:

GCloud Auth
-----------

For the majority of cases, authentication with the ``gcloud`` library
will **"just work"**. Credentials are loaded implicitly from the environment
where the code is running and **no code** is required to enable authentication
within your code.

The core :mod:`gcloud.credentials` module provides helper methods
for any account type needed.

A production application should **use a service account**, but you may
wish to use your own personal user account when first getting started with
the ``gcloud`` library.

=============
Basic Example
=============

With an environment set up to use `Google Default Credentials`_, your
API requests will be authenticated automatically:

.. code-block:: python

from gcloud import datastore
from gcloud import pubsub
from gcloud import storage

entities = datastore.get([
datastore.Key('EntityKind', 1, dataset_id='foo')])

bucket = storage.get_bucket('bucket-name')

pubsub.Topic('topic_name', project_id='my.project').create()

=============
Advanced Uses
=============

A custom connection can be used as the default, but will need to be
explicitly set. The
:func:`set_default_connection() <gcloud.datastore.__init__.set_default_connection>`
function is provided in all sub-packages to set a connection. For
example, in the ``datastore`` sub-package:

.. code-block:: python

credentials = get_custom_credentials()
connection = datastore.Connection(credentials=credentials)
datastore.set_default_connection(connection)

# ... Create keys.
entities = datastore.get([key1, key2])

If no default is set, all functions which make a connection to an API
accept an optional ``connection`` argument. For example, with ``datastore``,
the implicit behavior can be duplicated with:

.. code-block:: python

from gcloud.credentials import get_credentials

credentials = get_credentials().create_scoped(datastore.SCOPE)
connection = datastore.Connection(credentials=credentials)

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.


# ... Create keys.
entities = datastore.get([key1, key2], connection=connection)

If no connection is passed explicity and
:func:`set_default_connection() <gcloud.datastore.__init__.set_default_connection>`
is never called, the request will fail:

.. code-block:: python

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "gcloud/datastore/api.py", line 200, in get
connection = _require_connection(connection)
File "gcloud/datastore/api.py", line 82, in _require_connection
raise EnvironmentError('Connection could not be inferred.')
EnvironmentError: Connection could not be inferred.

---------------------------------
Multiple Simultaneous Connections
---------------------------------

Some applications will need to read and write data into multiple projects.
This may require using more than one set of credentials (either via JSON key
files or other means).

As a result, a single connection will be insufficient to make all API requests.
To use two separate connections, proceed as above by using explicit connections
in each function that makes an API request:

.. code-block:: python

credentials1 = my_custom_credentials_function('foo')
credentials2 = my_custom_credentials_function('bar')

connection1 = datastore.Connection(credentials=credentials1)
connection2 = datastore.Connection(credentials=credentials2)

key1 = datastore.Key('CompanyA', 1337, dataset_id='foo')
entities1 = datastore.get([key1], connection=connection1)

key2 = datastore.Key('CompanyB', 42, dataset_id='bar')
datastore.delete([key2], connection=connection2)

If one is connection used more often than another, it can still be used as the
default:

.. code-block:: python

datastore.set_default_connection(connection=connection1)

entities1 = datastore.get([key1])

datastore.delete([key2], connection=connection2)

=======================
Supported Account Types
=======================

With the helper functions provided, we support the following
OAuth 2.0 account types:

- Google `App Engine Service Accounts`_
- Google `Compute Engine Service Accounts`_
- `Service Accounts`_ with JSON `Service Account Key`_
- User Accounts (3-legged `OAuth`_ 2.0) with refresh token
- Service Accounts with PKCS12 / P12 `Service Account Key`_

==========================
Google Default Credentials
==========================

All but one `supported account type`_ listed above can be handled by using
Google `Application Default`_ credentials via
:func:`get_credentials() <gcloud.credentials.get_credentials>`. This function
can be used directly to obtain ``credentials`` to be passed
to a ``Connection`` or can be used implictly by calling
:func:`get_connection (datastore) <gcloud.datastore.__init__.get_connection>`
and :func:`get_connection (storage) <gcloud.storage.__init__.get_connection>`.

----------------------------------
Google App Engine Service Accounts
----------------------------------

:func:`get_credentials() <gcloud.credentials.get_credentials>`
implicitly handles **Google App Engine Service Accounts** with no user
intervention required.

To explicitly load these credentials:

.. code-block:: python

from oauth2client.appengine import AppAssertionCredentials
credentials = AppAssertionCredentials([])

--------------------------------------
Google Compute Engine Service Accounts
--------------------------------------

:func:`get_credentials() <gcloud.credentials.get_credentials>` implicitly
handles **Google Compute Engine Service Accounts** with no user intervention
required. However, when creating the `instance`_, the service account
must be enabled and the correct set of scopes must be selected for
the services you intend to use.

To explicitly load these credentials:

.. code-block:: python

from oauth2client.gce import AppAssertionCredentials
credentials = AppAssertionCredentials([])

.. _instance: https://cloud.google.com/compute/docs/instances

-------------------------------
Service Accounts with JSON keys
-------------------------------

As mentioned above, support for **Service Accounts with JSON keys** can be done
by setting the ``GOOGLE_APPLICATION_CREDENTIALS`` environment variable:

.. code-block:: bash

$ export GOOGLE_APPLICATION_CREDENTIALS="/path/to/key_file.json"

If you'd like to load these credentials explicitly rather than via the
environment, you can use
:func:`get_for_service_account_json() <gcloud.credentials.get_for_service_account_json>`:

.. code-block:: python

from gcloud.credentials import get_for_service_account_json

private_key_path = '/path/to/key_file.p12'

credentials = get_for_service_account_json(private_key_path)

-------------------------------------------------
User Accounts (3-legged OAuth) with refresh token
-------------------------------------------------

As mentioned in the introduction, a production application should
**use a service account**. However, when just getting started with the library,
it can be useful to just use an authorized user account.

If you've installed the ``gcloud`` command line `tool`_, you can authorize
a user account by running

.. code-block:: bash

$ gcloud auth login

(it's very likely you've already done so).

.. _tool: https://cloud.google.com/sdk/gcloud/

After doing so, you can re-use these credentials within ``gcloud``
just by using :func:`get_credentials() <gcloud.credentials.get_credentials>`:

.. code-block:: python

from gcloud.credentials import get_credentials
credentials = get_credentials()

.. note::

The ``gcloud`` CLI tool will create an authorized user JSON credentials
file on your system. On \*nix systems, this will typically reside in
``${HOME}/.config/gcloud/application_default_credentials.json``.

In addition to using the credentials from the ``gcloud`` CLI, you can use your
own credentials file. This file will contain a JSON object with four keys:

.. code-block:: json

{
"client_id": "123",
"client_secret": "secret",
"refresh_token": "FOO",
"type": "authorized_user"
}

This can be used just as a JSON key file can be used for a service account

.. code-block:: bash

$ export GOOGLE_APPLICATION_CREDENTIALS="/path/to/authorized_user_credentials.json"

As with Service Accounts with JSON keys, you can explicitly load an authorized
user account key with
:func:`get_for_service_account_json() <gcloud.credentials.get_for_service_account_json>`.

=================================
PKCS12 / P12 Service Account Keys
=================================

PKCS12 / P12 Service Account keys are the only `supported account type`_ above
that is not covered by Google Default Credentials.

.. _supported account type: #supported-account-types

This is because **JSON Service Account Credentials** are the preferred format.

If for some reason you need (or want) to use a P12 key,
:func:`get_for_service_account_p12() <gcloud.credentials.get_for_service_account_p12>`
allows you to load service account credentials for such a key:

.. code-block:: python

from gcloud.credentials import get_for_service_account_p12

client_email = '[email protected]'
private_key_path = '/path/to/key_file.p12'

credentials = get_for_service_account_p12(client_email, private_key_path)

Notice that unlike the JSON Service Account Key, the P12 key also
**requires the email address** for the service account. (This is because the
JSON key file contains the email address, while the P12 key file only contains
the private key bytes.)

==========================
Enabling a service account
==========================

.. include:: _components/enabling-a-service-account.rst

.. _Service Account Key: https://cloud.google.com/storage/docs/authentication#generating-a-private-key
.. _Application Default: https://developers.google.com/accounts/docs/application-default-credentials
.. _App Engine Service Accounts: https://cloud.google.com/appengine/docs/python/appidentity/
.. _Compute Engine Service Accounts: https://cloud.google.com/compute/docs/authentication#metadata
.. _OAuth: https://developers.google.com/accounts/docs/OAuth2WebServer
.. _Service Accounts: #enabling-a-service-account
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
:hidden:

gcloud-api
gcloud-auth
datastore-api
datastore-entities
datastore-keys
Expand Down