Skip to content

Commit

Permalink
Add authentication docs (digital-asset#3661)
Browse files Browse the repository at this point in the history
* Add authentication docs

* Use anonymous references

* Update obsolete info on authentication

* Improve authentication intro
  • Loading branch information
rautenrieth-da authored Dec 4, 2019
1 parent 9e2d012 commit a827040
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 10 deletions.
1 change: 1 addition & 0 deletions docs/configs/pdf/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Building applications
app-dev/grpc/index
app-dev/bindings-x-lang/index
app-dev/app-arch
app-dev/authentication

SDK tools
---------
Expand Down
77 changes: 77 additions & 0 deletions docs/source/app-dev/authentication.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
.. Copyright (c) 2019 The DAML Authors. All rights reserved.
.. SPDX-License-Identifier: Apache-2.0
Authentication
##############

When developing DAML applications using SDK tools,
your local setup will most likely not use any authentication -
by default, any valid ledger API request will be accepted by the sandbox.

To run your application against a :doc:`deployed ledger </deploy/index>`, you will need to add authentication.

Introduction
************

The main way for a DAML application to interact with a DAML ledger is through the :doc:`gRPC ledger API </app-dev/grpc/index>`.

This API can be used to request changes to the ledger (e.g., "*Alice wants to exercise choice X on contract Y*),
or to read data from the ledger (e.g., "*Alice wants to see all active contracts*").

What requests are valid is defined by :ref:`integrity <da-model-integrity>` and :ref:`privacy <da-model-privacy>` parts the :ref:`DA Ledger Model <da-ledgers>`.
This model is defined in terms of :ref:`DAML parties <glossary-party>`,
and does not require any cryptographic information to be sent along with requests.

In particular, this model does not talk about authentication ("*Is the request claiming to come from Alice really sent by Alice?*")
and authorization ("*Is Alice authorized to add a new DAML package to the ledger?*").

In practice, DAML ledgers will therefore need to add authentication to the ledger API.

.. note::
Depending on the ledger topology, a DAML ledger may consist of multiple participant nodes,
each having its own ledger API server.
Each participant node typically hosts different DAML parties,
and only sees data visible to the parties hosted on that node (as defined by the DAML privacy model).

For more details on DAML ledger topologies, refer to the :ref:`DAML Ledger Topologies <daml-ledger-topologies>` documentation.

Adding authentication
=====================

How authentication is set up on a particular ledger is defined by the ledger operator.
However, most authentication setups share the following pattern:

First, the DAML application contacts a token issuer to get an access token.
The token issuer verifies the identity of the requesting user
(e.g., by checking the username/password credentials sent with the request),
looks up the priviledges of the user,
and generates a signed access token describing those priviledges.

Then, the DAML application sends the access token along with each ledger API request.
The DAML ledger verifies the signature of the token (to make sure it has not been tampered with),
and then checks that the priviledges described in the token authorize the given ledger API request.

.. image:: ./images/Authentication.svg

Glossary:

- ``Authentication`` is the process of confirming an identity.
- ``Authorization`` is the process of checking permissions to access a resource.
- A ``token`` is a tamper-proof piece of data that contains security information, such as the user identity or its priviledges.
- A ``token issuer`` is a service that generates tokens. Also known as "authentication server" or "Identity and Access Management (IAM) system".

Getting access tokens
*********************

To learn how to receive access tokens for a deployed ledger, contact your ledger operator.
This may be a manual exchange over a secure channel,
or your application may have to request tokens at runtime using an API such as `OAuth <https://oauth.net/2/>`__.

To learn how to generate access tokens for a local sandbox,
read the :ref:`sandbox <sandbox-authentication>` documentation.


Using access tokens
*******************

To learn how to use access tokens in the Scala bindings, read the :ref:`Scala bindings authentication<scala-bindings-authentication>` documentation.
27 changes: 27 additions & 0 deletions docs/source/app-dev/bindings-scala/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,30 @@ To exercise ``IouTransfer_Accept`` choice on the ``IouTransfer`` contract that y
:end-before: // </doc-ref:submit-iou-transfer-accept-exercise-command>

Fore more details on how to subscribe to receive events for a particular party, please refer to the implementation of `com.digitalasset.quickstart.iou.IouMain#newOwnerAcceptsAllTransfers <https://github.com/digital-asset/daml/blob/master/language-support/scala/examples/quickstart-scala/application/src/main/scala/com/digitalasset/quickstart/iou/IouMain.scala>`_.


.. _scala-bindings-authentication:

Authentication
==============

Some ledgers will require you to send an access token along with each request.
To learn more about authentication, read the :doc:`Authentication </app-dev/authentication>` overview.

To use the same token for all ledger API requests,
use the ``token`` field of ``LedgerClientConfiguration``:

.. literalinclude:: ./code-snippets/quickstart-scala/application/src/main/scala/com/digitalasset/quickstart/iou/IouMain.scala
:start-after: // <doc-ref:ledger-client-configuration>
:end-before: // </doc-ref:ledger-client-configuration>

To specify the token for an individual call,
use the ``token`` parameter:

.. code-block:: scala
transactionClient.getLedgerEnd() // Uses the token specified in LedgerClientConfiguration
transactionClient.getLedgerEnd(token = acessToken) // Uses the given token
Note that if your tokens can change at run time (e.g., because they expire or because you switch users),
you will need to specify them on a per-call basis as shown above.
1 change: 1 addition & 0 deletions docs/source/app-dev/images/Authentication.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/source/concepts/glossary.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ A **disjunction choice** has more than one `controller <#controller>`__.

If a contract uses **flexible controllers**, this means you don't specify the controller of the `choice <#choice>`__ at `creation <#create>`__ time of the `contract <#contract-contract-instance>`__, but at `exercise <#exercise>`__ time.


.. _glossary-party:

Party
=====

Expand Down
4 changes: 2 additions & 2 deletions docs/source/concepts/identity-and-package-management.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,12 @@ Identifiers and Ledger Authentication
=====================================

To issue commands or receive transactions on behalf of a newly provisioned party, an application must authenticate itself to the party's hosting participant as someone authorized to represent the party.
Currently, the Ledger API provides no authentication mechanisms.
However, it will soon support authentication through JSON Web Tokens.
Before the newly provisioned party can be used, the application will have to obtain a token for this party.
The issuance of tokens is specific to each ledger and independent of the Ledger API.
The same is true for the policy which the participants use to decide whether to accept a token.

To learn more about ledger API authentication, please read the :doc:`Authentication documentation </app-dev/authentication>`.

.. _identifiers-and-real-world:

Identifiers and the Real World
Expand Down
24 changes: 24 additions & 0 deletions docs/source/daml-integration-kit/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,30 @@ Deploying a DAML Ledger
scripted package upload) can be supported by a uniform admin interface
(`GitHub issue <https://github.com/digital-asset/daml/issues/347>`__).

.. _integration-kit_authorization:

Authorization
=============

To implement authorization on your ledger,
do the following modifications to your code:

- Implement the ``com.digitalasset.ledger.api.auth.AuthService`` (`source code <https://github.com/digital-asset/daml/blob/master/ledger/ledger-api-auth/src/main/scala/com/digitalasset/ledger/api/auth/AuthService.scala>`__) interface.
An AuthService receives all HTTP headers attached to a gRPC ledger API request
and returns a set of ``Claims`` (`source code <https://github.com/digital-asset/daml/blob/master/ledger/ledger-api-auth/src/main/scala/com/digitalasset/ledger/api/auth/Claims.scala>`__), which describe the authorization of the request.
- Instantiate a ``com.digitalasset.ledger.api.auth.interceptor.AuthorizationInterceptor`` (`source code <https://github.com/digital-asset/daml/blob/master/ledger/ledger-api-auth/src/main/scala/com/digitalasset/ledger/api/auth/interceptor/AuthorizationInterceptor.scala>`__),
and pass it an instance of your AuthService implementation.
This interceptor will be responsible for storing the decoded Claims in a place where ledger API services can access them.
- When starting the ``com.digitalasset.ledger.server.apiserver.LedgerApiServer`` (`source code <https://github.com/digital-asset/daml/blob/master/ledger/sandbox/src/main/scala/com/digitalasset/ledger/server/apiserver/LedgerApiServer.scala>`__),
add the above AuthorizationInterceptor to the list of interceptors (see ``interceptors`` parameter of ``LedgerApiServer.create``).

For reference, you can have a look at how authorization is implemented in the sandbox:

- The ``com.digitalasset.ledger.api.auth.AuthServiceJWT`` class (`source code <https://github.com/digital-asset/daml/blob/master/ledger/ledger-api-auth/src/main/scala/com/digitalasset/ledger/api/auth/AuthServiceJWT.scala>`__)
reads a `JWT <https://jwt.io/>`__ token from HTTP headers.
- The ``com.digitalasset.ledger.api.auth.AuthServiceJWTPayload`` class (`source code <https://github.com/digital-asset/daml/blob/master/ledger/ledger-api-auth/src/main/scala/com/digitalasset/ledger/api/auth/AuthServiceJWTPayload.scala>`__)
defines the format of the token payload.
- The token signature algorithm and the corresponding public key is specified as a sandbox command line parameter.

.. _integration-kit_testing:

Expand Down
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ DAML SDK documentation
app-dev/grpc/index
app-dev/bindings-x-lang/index
app-dev/app-arch
app-dev/authentication

.. toctree::
:titlesonly:
Expand Down
53 changes: 46 additions & 7 deletions docs/source/tools/sandbox.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,34 @@ Due to possible conflicts between the ``&`` character and various terminal shell
If you're not familiar with JDBC URLs, see the JDBC docs for more information: https://jdbc.postgresql.org/documentation/head/connect.html

.. _sandbox-authentication:

Running with authentication
***************************

By default, Sandbox does not use any authentication and accepts all valid ledger API requests.

To start Sandbox with authentication based on `JWT <https://jwt.io/>`_, run ``daml sandbox --auth-jwt-hs256-unsafe=<secret>`` where ``<secret>`` is the secret used to sign the token with the HMAC256 algorithm. Please note that this option is there _exclusively_ for testing: for production use cases you are advised to use asymmetric key signing, which is currently being worked on.
To start Sandbox with authentication based on `JWT <https://jwt.io/>`__ tokens,
use one of the following command line options:

- ``--auth-jwt-rs256-crt=<filename>``.
The sandbox will expect all tokens to be signed with RSA256 with the public key loaded from the given X.509 certificate file.
Both PEM-encoded certificates (text files starting with ``-----BEGIN CERTIFICATE-----``)
and DER-encoded certicates (binary files) are supported.

- ``--auth-jwt-rs256-jwks=<url>``.
The sandbox will expect all tokens to be signed with RSA256 with the public key loaded from the given `JWKS <https://tools.ietf.org/html/rfc7517>`__ URL.

.. warning::

For testing purposes only, the following options may also be used.
None of them is considered safe for production:

- ``--auth-jwt-hss256-unsafe=<secret>``.
The sandbox will expect all tokens to be signed with HMAC256 with the given plaintext secret.

Token payload
=============

The JWT payload has the following schema:

Expand All @@ -77,15 +99,32 @@ The JWT payload has the following schema:
"exp": 1300819380,
"admin": true,
"actAs": ["Alice"],
"readAs": ["Alice", "Bob"],
"readAs": ["Bob"]
}
where
``ledgerId``, ``participantId``, ``applicationId`` restricts the validity of the token to the given ledger, participant, or application;
``exp`` is the standard JWT expiration date;
``admin`` determines whether the token bearer is authorized to use admin endpoints of the ledger API;
``actAs`` lists all DAML parties the token bearer can act as (e.g., as submitter of a command); and
``readAs`` lists all DAML parties the token bearer can read data for.

- ``ledgerId``, ``participantId``, ``applicationId`` restricts the validity of the token to the given ledger, participant, or application
- ``exp`` is the standard JWT expiration date (in seconds since EPOCH)
- ``admin`` determines whether the token bearer is authorized to use admin endpoints of the ledger API
- ``actAs`` lists all DAML parties the token bearer can act as (e.g., as submitter of a command) and read data for
- ``readAs`` lists all DAML parties the token bearer can read data for

Generating tokens
=================

To generate tokens for testing purposes, use the `jtw.io <https://jwt.io/>`__ web site.

To generate RSA keys for testing purposes, use the following command

.. code-block:: none
openssl req -nodes -new -x509 -keyout sandbox.key -out sandbox.crt
which generates the following files:

- ``sandbox.key``: the private key in PEM/DER/PKCS#1 format
- ``sandbox.crt``: a self-signed certificate containing the public key, in PEM/DER/X.509 Certificate format

Command-line reference
**********************
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,15 @@ object IouMain extends App with StrictLogging {

private val timeProvider = TimeProvider.Constant(Instant.EPOCH)

// <doc-ref:ledger-client-configuration>
private val clientConfig = LedgerClientConfiguration(
applicationId = ApplicationId.unwrap(applicationId),
ledgerIdRequirement = LedgerIdRequirement("", enabled = false),
commandClient = CommandClientConfiguration.default,
sslContext = None
sslContext = None,
token = None
)
// </doc-ref:ledger-client-configuration>

private val clientF: Future[LedgerClient] =
LedgerClient.singleHost(ledgerHost, ledgerPort, clientConfig)(ec, aesf)
Expand Down

0 comments on commit a827040

Please sign in to comment.