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

Improve user authentication handling #547

Merged
merged 6 commits into from
Jul 20, 2018
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ The format is based on `Keep a Changelog`_.

Added
-----
- Introduced new options for handling credentials (qiskitrc file, environment
variables) and automatic registration. (#547)

Changed
-------
Expand Down
23 changes: 0 additions & 23 deletions Qconfig.py.default

This file was deleted.

43 changes: 19 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,43 +126,38 @@ your IBM Q Experience account:

#### Configure your API token and QX credentials


1. Create an _[IBM Q Experience](https://quantumexperience.ng.bluemix.net) > Account_ if you haven't already done so.

2. Get an API token from the IBM Q Experience website under _My Account > Advanced > API Token_. This API token allows you to execute your programs with the IBM Q Experience backends. See: [Example](doc/example_real_backend.rst).
3. We are going to create a new file called `Qconfig.py` and insert the API token into it. This file must have these contents:

```python
APItoken = 'MY_API_TOKEN'
3. We are now going to add the necessary credentials to QISKit. Take your token
from step 2, here called `MY_API_TOKEN`, and pass it to the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: it seems we are using soft wrapping so there is no need of manually breaking the lines. I think we should be consistent here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually we are not really using soft-wrapping in most of the README.md - and technically using line wrapping is more in line with the rest of the file (if I recall correctly, we did have full hard-wrapping for the file at one point in time, but lots of changes were made that resulted in the current mix). I'd love indeed to have it fully consistent one way or the other eventually, but seems out of the scope of this PR.

`store_credentials` function:

config = {
'url': 'https://quantumexperience.ng.bluemix.net/api',
# The following should only be needed for IBM Q Network users.
'hub': 'MY_HUB',
'group': 'MY_GROUP',
'project': 'MY_PROJECT'
}
```
```python
from qiskit import store_credentials

4. Substitute `MY_API_TOKEN` with your real API Token extracted in step 2.
store_credentials('MY_API_TOKEN')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we say "you don't need to do this more than once"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I would expect that the reader knows that the steps in this whole section "Configure you API token" is to be done once (create account, create token, set it up, etc).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw this explained later, it is fine.

```

5. If you have access to the IBM Q Network features, you also need to setup the
values for your hub, group, and project. You can do so by filling the
`config` variable with the values you can find on your IBM Q account
page.
4. If you have access to the IBM Q Network features, you also need to pass the
values for your url, hub, group, and project found on your IBM Q account
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be convenient to link IBM Q account with the actual URL and a complete example of how to pass these extra parameters.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same text that was there before, and is only of interest to hub members.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking although It is never late for clarifying the docs.

page to `store_credentials`.

Once the `Qconfig.py` file is set up, you have to move it under the same directory/folder where your program/tutorial resides, so it can be imported and be used to authenticate with the `register()` function. For example:
After calling `store_credentials()`, your credentials will be stored into disk.
Once they are stored, Qiskit will automatically load and use them in your program
via:

```python
from qiskit import register
import Qconfig

register(Qconfig.APItoken, Qconfig.config["url"],
hub=Qconfig.config["hub"],
group=Qconfig.config["group"],
project=Qconfig.config["project"])
register()
```

For more details on this and more information see
For more details on installing Qiskit and for alternative methods for passing
the IBM QX credentials, such as using environment variables, sending them
explicitly and support for the `Qconfig.py` method available in previous
versions, please check
[our Qiskit documentation](https://www.qiskit.org/documentation/).


Expand Down
143 changes: 126 additions & 17 deletions doc/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,113 @@ This will install the latest stable release along with all the dependencies.
you haven't already done so
- Get an API token from the IBM Q experience website under “My
Account” > “Personal Access Token”
- The API token needs to be placed in a file called ``Qconfig.py``. For
convenience, we provide a default version of this file that you
can use as a reference: `Qconfig.py.default`_. After downloading that
file, copy it into the folder where you will be invoking the SDK (on
Windows, replace ``cp`` with ``copy``):

.. code:: sh

cp Qconfig.py.default Qconfig.py
3.1 Automatically loading credentials
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Since Qiskit 0.6, an automatic method that looks for the credentials in several
places can be used for streamlining the setting up of the IBM Q authentication.
This implies that you can set or store your API credentials once after
installation, and when you want to use them, you can simply run:

.. code:: python

from qiskit import register

register()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documenting the new way of registering by providing three sections with exactly the same example can be misleading. The user can think there is an error in the example or something magic is happening behind the scenes. My recommendation is to introduce register() first saying it is an automatic method which will look for configuration in several origins according to an order, then introduce the origins in separate sections.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.


This ``register()`` call (without parameters) performs the automatic loading
of the credentials from several sources, and authenticates against IBM Q,
making the online devices available to your program. Please use one of the
following methods for storing the credentials before calling the automatic
registration:

3.1.1 Store API credentials locally
"""""""""""""""""""""""""""""""""""

For most users, storing your API credentials is the most convenient approach.
Your information is stored locally in a configuration file called `qiskitrc`,
and once stored, you can use the credentials without explicitly passing them
to your program.

To store your information, simply run:

.. code:: python

from qiskit import store_credentials

store_credentials('MY_API_TOKEN')


where `MY_API_TOKEN` should be replaced with your token.

If you are on the IBM Q network, you must also pass `url`,
`hub`, `group`, and `project` arguments to `store_credentials`:


.. code:: python

from qiskit import store_credentials

store_credentials('MY_API_TOKEN', url='http://...', hub='HUB',
group='GROUP', project='PROJECT')

3.1.2 Load API credentials from environment variables
"""""""""""""""""""""""""""""""""""""""""""""""""""""

- Open your ``Qconfig.py``, remove the ``#`` from the beginning of the API
token line, and copy/paste your API token into the space between the
quotation marks on that line. Save and close the file.
For more advanced users, it is possible to load API credentials from
environment variables. Specifically, you can set the following environment
variables:

* `QE_TOKEN`,
* `QE_URL`
* `QE_HUB`
* `QE_GROUP`
* `QE_PROJECT`.

Note that if they are present in your environment, they will take precedence
over the credentials stored in disk.

3.1.3 Load API credentials from Qconfig.py
""""""""""""""""""""""""""""""""""""""""""

For compatibility with configurations set for Qiskit versions earlier than 0.6,
the credentials can also be stored in a file called ``Qconfig.py`` placed in
the directory where your program is invoked from. For convenience, we provide
a default version of this file you can use as a reference - using your favorite
editor, create a ``Qconfig.py`` file in the folder of your program with the
following contents:

.. code:: python

APItoken = 'PUT_YOUR_API_TOKEN_HERE'

config = {
'url': 'https://quantumexperience.ng.bluemix.net/api',

# If you have access to IBM Q features, you also need to fill the "hub",
# "group", and "project" details. Replace "None" on the lines below
# with your details from Quantum Experience, quoting the strings, for
# example: 'hub': 'my_hub'
# You will also need to update the 'url' above, pointing it to your custom
# URL for IBM Q.
'hub': None,
'group': None,
'project': None
}

if 'APItoken' not in locals():
raise Exception('Please set up your access token. See Qconfig.py.')

And customize the following lines:

* copy/paste your API token into the space between the quotation marks on the
first line (``APItoken = 'PUT_YOUR_API_TOKEN_HERE'``).
* if you have access to the IBM Q features, you also need to setup the
values for your url, hub, group, and project. You can do so by filling the
``config`` variable with the values you can find on your IBM Q account
page.

For example, a valid and fully configured ``Qconfig.py`` file would look like:

Expand All @@ -65,13 +159,8 @@ For example, a valid and fully configured ``Qconfig.py`` file would look like:
'url': 'https://quantumexperience.ng.bluemix.net/api'
}

- If you have access to the IBM Q features, you also need to setup the
values for your hub, group, and project. You can do so by filling the
``config`` variable with the values you can find on your IBM Q account
page.

For example, a valid and fully configured ``Qconfig.py`` file for IBM Q
users would look like:
For IBM Q users, a valid and fully configured ``Qconfig.py`` file would look
like:

.. code:: python

Expand All @@ -85,6 +174,26 @@ users would look like:
'project': 'MY_PROJECT'
}

Note that if a ``Qconfig.py`` file is present in your directory, it will take
precedence over the environment variables or the credentials stored in disk.

3.2 Manually loading credentials
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

In more complex scenarios or for users that need finer control over multiple
accounts, please note that you can pass the API token and the other parameters
directly to the ``register()`` function, which will ignore the automatic
loading of the credentials and use the arguments directly. For example::

.. code:: python

from qiskit import register

register('MY_API_TOKEN', url='https://my.url')

will try to authenticate using ``MY_API_TOKEN`` and the specified URL,
regardless of the configuration stored in the config file, the environment
variables, or the ``Qconfig.py`` file, if any.

Install Jupyter-based tutorials
===============================
Expand Down
7 changes: 4 additions & 3 deletions qiskit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

"""Main QISKit public functionality."""

import os

# First, check for required Python and API version
from . import _util

Expand All @@ -35,13 +37,12 @@
from .wrapper._wrapper import (
available_backends, local_backends, remote_backends,
get_backend, compile, execute, register, unregister,
registered_providers, load_qasm_string, load_qasm_file, least_busy)
registered_providers, load_qasm_string, load_qasm_file, least_busy,
store_credentials)

# Import the wrapper, to make it available when doing "import qiskit".
from . import wrapper

import os

ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
with open(os.path.join(ROOT_DIR, "VERSION.txt"), "r") as version_file:
__version__ = version_file.read().strip()
2 changes: 1 addition & 1 deletion qiskit/wrapper/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
from ._wrapper import (available_backends, local_backends, remote_backends,
get_backend, compile, execute, register, unregister,
registered_providers, load_qasm_string, load_qasm_file,
least_busy)
least_busy, store_credentials)
56 changes: 53 additions & 3 deletions qiskit/wrapper/_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@

"""Helper module for simplified QISKit usage."""

import logging
import warnings
from qiskit import transpiler, QISKitError
from qiskit.backends.ibmq import IBMQProvider
from qiskit.wrapper import credentials
from qiskit.wrapper.defaultqiskitprovider import DefaultQISKitProvider
from ._circuittoolkit import circuit_from_qasm_file, circuit_from_qasm_string

Expand All @@ -19,11 +21,22 @@
_DEFAULT_PROVIDER = DefaultQISKitProvider()


logger = logging.getLogger(__name__)


def register(*args, provider_class=IBMQProvider, **kwargs):
"""
Authenticate against an online backend provider.
This is a factory method that returns the provider that gets registered.

Note that if no parameters are passed, this method will try to
automatically discover the credentials for IBMQ in the following places,
in order::

1. in the `Qconfig.py` file in the current working directory.
2. in the environment variables.
3. in the `qiskitrc` configuration file.

Args:
args (tuple): positional arguments passed to provider class initialization
provider_class (BaseProvider): provider class
Expand All @@ -45,9 +58,18 @@ def register(*args, provider_class=IBMQProvider, **kwargs):
BaseProvider: the provider instance that was just registered.

Raises:
QISKitError: if the provider could not be registered
(e.g. due to conflict)
QISKitError: if the provider could not be registered (e.g. due to
conflict, or if no credentials were provided.)
"""
# Try to autodiscover credentials if not passed.
if not args and not kwargs and provider_class == IBMQProvider:
kwargs = credentials.discover_credentials().get(
credentials.get_account_name(IBMQProvider)) or {}
if not kwargs:
raise QISKitError(
'No IBMQ credentials found. Please pass them explicitly or '
'store them before calling register() with store_credentials()')

try:
provider = provider_class(*args, **kwargs)
except Exception as ex:
Expand Down Expand Up @@ -79,8 +101,36 @@ def registered_providers():
return list(_DEFAULT_PROVIDER.providers)


# Functions for inspecting and retrieving backends.
def store_credentials(token, url='https://quantumexperience.ng.bluemix.net/api',
hub=None, group=None, project=None, proxies=None,
verify=True, overwrite=False):
"""
Store credentials for the IBMQ account in the config file.

Args:
token (str): The token used to register on the online backend such
as the quantum experience.
url (str): The url used for online backend such as the quantum
experience.
hub (str): The hub used for online backend.
group (str): The group used for online backend.
project (str): The project used for online backend.
proxies (dict): Proxy configuration for the API, as a dict with
'urls' and credential keys.
verify (bool): If False, ignores SSL certificates errors.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, this is just the old text.

overwrite (bool): overwrite existing credentials.

Raises:
QISKitError: if the credentials already exist and overwrite==False.
"""
credentials.store_credentials(
provider_class=IBMQProvider, overwrite=overwrite,
token=token, url=url, hub=hub, group=group, project=project,
proxies=proxies, verify=verify
)


# Functions for inspecting and retrieving backends.

def available_backends(filters=None, compact=True):
"""
Expand Down
Loading