Skip to content

Add Documentation for OpenDID #313

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

Merged
merged 16 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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: 1 addition & 1 deletion docs/develop/02_chain/04_fullnode.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ This can either be `peregrine` or `spiritnet`.

Hence, to start a full node for the Spiritnet network, the parameter would be `--chain=spiritnet`.
Unfortunately, there is no hardcoded chain spec for the Peregrine network, so the full path of the chainspec file must be provided `--chain=/node/dev-specs/kilt-parachain/peregrine-kilt.json`.
Please refer to the [KILT node repository](https://github.com/KILTprotocol/kilt-node/blob/develop/dev-specs/kilt-parachain/peregrine-kilt.json) or the [Docker image](https://hub.docker.com/r/kiltprotocol/kilt-node/tags) for more information.
Please refer to the [KILT node repository](https://github.com/KILTprotocol/kilt-node/blob/master/dev-specs/kilt-parachain/peregrine-kilt.json) or the [Docker image](https://hub.docker.com/r/kiltprotocol/kilt-node/tags) for more information.

### Specify the Blockchain Storage Path

Expand Down
40 changes: 40 additions & 0 deletions docs/develop/08_opendid/01_overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
id: what-is-opendid
title: Overview
---

[OpenDID](https://github.com/KILTprotocol/opendid) is an OpenID Provider implementation capable of authenticating users through their [Decentralized Identifier (DID)](../../concepts/02_did.md) and Verifiable Credentials.

It follows the [OpenID Connect 1.0 Specification](https://openid.net/specs/openid-connect-core-1_0.html#Introduction) and acts as a bridge between the decentralized identity world and the centralized authentication world supporting both the implicit and Authorization Code Flow.

A major use of OpenDID is Single Sign-On (SSO) is to use the same DID and credentials to sign into multiple platforms and web services. For instance, by adding a "Sign in with KILT" button to a webpage.

Although integrating that functionality into a webpage is relatively simple, configuring and running OpenDID is more involved.

:::info

To learn more about the flow of OpenDID, see the [OpenDID Flow](./02_opendid_flow.md) documentation.

:::

## Project container structure

The project consist of multiple parts that supplement and interact with each other all shipped as docker containers and released to docker hub.

### opendid-setup container

The OpenDID Service needs configuration to run, which you can apply using this
container.
For example, it requires a DID to establish a session with an identity wallet.
This container creates a DID and the necessary configuration by providing an account with enough funds.

Learn more in the [run setup container documentation](./03_opendid_service.md#run-setup-container).

### kiltprotocol/opendid container

This container [runs the OpenDID Service](./03_opendid_service.md#run-the-service), both the OpenDID front and back end.
This container requires the configuration file created from the `opendid-setup` container.

### kiltprotocol/opendid-demo

This container is a [web app demo](./05_demo_project.md), including front and back end services to demonstrate the use of OpenDID.
74 changes: 74 additions & 0 deletions docs/develop/08_opendid/02_opendid_flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
id: flow
title: OpenDID Flow
---

This guide explains the internal workings of OpenDID.
Understanding this flow is helpful for setting up and configuring an OpenDID Service but less important if you only need to integrate it in an application.

OpenDID includes interactions between multiple apps to authenticate and authorize users.
Common use cases include the following:

- Web app Frontend (app that includes the login button, for example, the demo app)
- Web app back end
- OpenDID front end
- OpenDID back end
- Identity wallet (a browser extension, for example, [Sporran](https://www.sporran.org/))

The following steps outline the interactions necessary to implement the Implicit Flow:

1. The user clicks the login button on the *web app front end*.
2. The *web app front end* redirects the user to the *OpenDID front end*.
3. The user chooses what wallet to authenticate with.
4. The *OpenDID back end* establishes a secure session with the *identity wallet*.
5. The *OpenDID back end* optionally requests a credential that implements a specific CType.
6. The *identity wallet* provides the *OpenDID back end* with the requested credential, after authenticating the DID holder.
7. The *OpenDID back end* returns a `id_token` as a JSON web token (JWT) to the *OpenDID front end*.
8. *OpenDID front end* redirects the user back to a specific `redirect_url` on the *web app front end* including the `id_token`.
9. The *web app front end* detects the `id_token` and sends it to the *web app back end*.
10. The *web app back end* verifies the `id_token` and ensures the validity of the credential.

The following sequence diagram summarizes the flow:

```mermaid
sequenceDiagram

participant AB as WebApp Backend
participant AF as WebApp Frontend
participant OF as OpenDID Frontend
participant OB as OpenDID Backend
participant IW as Identity Wallet

AF->>OF: (1, 2) Authorize (redirect_uri: /callback)
OF->>OF: (3) Pick Identity Wallet
critical (4) Key Exchange
OF->>OB: GET Challenge
OB-->>OF: Challenge
OF->>IW: Start Session
IW-->>OF: Encrypted Challenge
OF->>OB: POST Challenge
OB-->>OF: OK
end

critical Authenticate
OF->>OB: (5) GET Credential Requirements
OB-->>OF: Credential Requirements
OF->>IW: (6) Request Credential
IW->>IW: Authenticate User
IW->>OF: Credential
OF->>OB: POST Credential
OB->>OB: Verify Credential
OB->>OF: (7) `id_token`)
end

OF->>AF: redirect to /callback with `id_token`
AF->>AB: (8) `id_token`
AB->>AB: (9) verify `id_token`
AB->>AF: (10) Access granted.

```

:::info
Although this example describes the implicit flow, the authorization code flow is similar.
Instead of returning an `id_token` directly, the OpenDID service instead returns a `code` to exhange for an `id_token` using the `token` endpoint.
:::
114 changes: 114 additions & 0 deletions docs/develop/08_opendid/03_opendid_service.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
id: opendid_service
title: Run OpenDID Service
---

<!-- TODO: Overview and steps -->

## Configuration

Running the OpenDID service requires some configuration and a DID.
The DID establishes a secure session with an identity wallet using a key agreement key of type `X25519KeyAgreementKey2019` included in the DID Document generated by the setup container.

OpenDID serves a [well-known DID configuration](https://identity.foundation/.well-known/resources/did-configuration/), which the identity wallet uses to ensure that the domain is linked to the specified DID.

### Run setup container

Before running the `opendid-setup` container, set two environment variables:

1. `SEED` to provide an account with funds (minimum of 3 KILT) for the DID generation.

```bash
export SEED="dont try this seed its completely made up for this nice example"
```

2. `ENDPOINT`

Set to "spiritnet" if the account is on the spiritnet production network.

```bash
export ENDPOINT="spiritnet"
```

Set to "peregrine" if the account is on the peregrine test network.

```bash
export ENDPOINT="peregrine"
```

Then run the setup with the following command:

```bash
docker run --rm -it -e "ENDPOINT=${ENDPOINT}" -v $(pwd):/data docker.io/kiltprotocol/opendid-setup:latest "${SEED}"
```

The command generates a set of new mnemonics and then derives a DID from them and generates multiple files into the current directory:

1. `config.yaml` The configuration file used by the OpenDID service.
2. `did-secrets.json` This file contains the public and secret keys in the DID Document.

Keep a secure backup of this file as it contains all the secret keys.

3. `did-document.json` contains the DID Document generated by this setup.

:::danger
You only need the `config.yaml` to run the OpenDID service.
This file includes the generated mnemonic and secret keys and you should protect it from unauthorized access.
:::

The container generates sensible defaults in the `config.yaml` file, but here are some values you might want to change:

- Set `production` to true, this only allows secure connections.
- Set the `WellKnownDid` > `origin`, which should match the host running the OpenDID service.
- Set the keys used for JWT issuance in the `jwt` section.
- The `client` section, including:

- The client ID as a key (The default is: `example-client`).
- The `requirements` section, including:
- What CTypes are required for authentication.
- The trusted attesters as an address (The default is for the [SocialKYC attester](https://socialkyc.io/)).

:::note info

The generated `config.yaml` requires an [email credential](https://test.ctypehub.galaniprojects.de/ctype/kilt:ctype:0x3291bb126e33b4862d421bfaa1d2f272e6cdfc4f96658988fbcffea8914bd9ac) issued by an attester.

:::

- What `redirect_url`s the service accepts (The default is `http://localhost:1606/callback.html` for the demo project).
- The `clientSecret` is optional but recommended. If you use the authorization code flow, the `token` endpoint requires it.

## Run the service

When you've made changes to the `config.yaml` file, you can run the OpenDID service.

1. Specify the runtime through the `RUNTIME` environment variable:

Set to `"spiritnet"` for production KILT

```bash
export RUNTIME="spiritnet"
```

Set to `"peregrine"` for the KILT test net.

```bash
export RUNTIME="peregrine"
```

2. Run the `docker.io/kiltprotocol/opendid` docker image .

```bash
docker run -d --rm \
-v $(pwd)/config.yaml:/app/config.yaml \
-v $(pwd)/checks:/app/checks \
-e "RUNTIME=${RUNTIME}" \
-p 3001:3001 \
docker.io/kiltprotocol/opendid:latest
```

3. Open the login page at _http://localhost:3001_.

## Next steps

With configuration in place and a service running, next you need to [integrate OpenDID into an application](./04_integrate_opendid.md) so that a user can use the login page.

117 changes: 117 additions & 0 deletions docs/develop/08_opendid/04_integrate_opendid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
---
id: integrate_opendid
title: Integrate OpenDID
---

OpenDID follows the [OpenID Connect 1.0 Specification](https://openid.net/specs/openid-connect-core-1_0.html#Introduction) and implements both the [implicit flow](https://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowSteps)
and the [authorization code flow](https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth).
Read the [demo project guide](05_demo_project.md) for an example of integrating OpenDID.

## Authorization code flow

Initiate the flow by redirecting to the **GET** `/api/v1/authorize` endpoint on the OpenDID service and setting the following query
parameters:

- `response_type`: set value to `code` to indicate Authorization Code Flow.
- `client_id`: The client ID set in the config.yaml file.
- `redirect_uri`: OpenDID will redirect to this URL after authentication.
- `scope`: set value to `openid`.
- `state`: set to a secure random number.
- `nonce`: optional value, set to a secure random number.

**Example**:

```
GET /api/v1/authorize?
response_type=code&
client_id=example-client&
redirect_uri=http://localhost:1606/callback.html&
scope=openid&
state=rkw49cbvd4azu5dsln1xbl&
nonce=vedur4om49ei8w91jt7wt HTTP/1.1
```

After successful authentication, the OpenDID service redirects back to the provided `redirect_uri` with `code` and `state` query parameters.

**Example**:

```
/callback.html?
code=lwDS1ZpQBwR4Vdm53_L8bWpUJ1mx9A0mA_-86dubTqzqzwGazx1RyLX4Z_qf&
state=rkw49cbvd4azu5dsln1xbl
```

You can retrieve the `id_token` by calling the **POST** `/api/v1/token` and providing the following values in the form serialization:

- `code`: code value returned from `authorize`.
- `grant_type`: set value to `authorization_code`.
- `redirect_uri`: the same `redirect_uri` used in `authorize`.
- `client_id`: the client ID set in the `config.yaml` file.
- `client_secret`: the client secret value set in the `config.yaml` file.

**Example**:

```
POST /api/v1/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

code=lwDS1ZpQBwR4Vdm53_L8bWpUJ1mx9A0mA_-86dubTqzqzwGazx1RyLX4Z_qf&
grant_type=authorization_code&
redirect_uri=http%3A%2F%2Flocalhost%3A1606%2Fcallback.html&
client_id=example-client&
client_secret=insecure_client_secret
```

The OpenDID service returns the `id_token` in the response body serialized as a JSON object.

```json
{
"access_token": "SsFhhSBMWsLeDMxVUVGreKARNwYxMZtGFfBr0-ZiH6iondSmwPRvQDqkG6Fh",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkaWQ6a2lsdDo0b0VkNENVV3RwbkxUVnZENVBFd2lMUmlqMWdzQmprS1JMbVpES2lCOEdqN2I2V0wiLCJ3M24iOiJjdXN0b20iLCJleHAiOjE3MTY4MTYwNjQsImlhdCI6MTcxNjgxNTQ2NCwiaXNzIjoiZGlkOmtpbHQ6NHJzQkE3dEQ1S1E4TDlXSGpGallRdUhrTWtha2NmSGRDNUNhUVVjVXh5VWpEVkhBIiwiYXVkIjoiYXV0aGVudGljYXRpb24iLCJwcm8iOnsiRW1haWwiOiJhYmR1bEBraWx0LmlvIn0sIm5vbmNlIjoidmVkdXI0b200OWVpOHc5MWp0N3d0In0.yOmE_9jWKcAu8LpjVx7IsFyOOvlKbgo2oC4Imf-qrLY",
"id_token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkaWQ6a2lsdDo0b0VkNENVV3RwbkxUVnZENVBFd2lMUmlqMWdzQmprS1JMbVpES2lCOEdqN2I2V0wiLCJ3M24iOiJjdXN0b20iLCJleHAiOjE3MTY4MTU1MjQsImlhdCI6MTcxNjgxNTQ2NCwiaXNzIjoiZGlkOmtpbHQ6NHJzQkE3dEQ1S1E4TDlXSGpGallRdUhrTWtha2NmSGRDNUNhUVVjVXh5VWpEVkhBIiwiYXVkIjoiYXBwbGljYXRpb24iLCJwcm8iOnsiRW1haWwiOiJhYmR1bEBraWx0LmlvIn0sIm5vbmNlIjoidmVkdXI0b200OWVpOHc5MWp0N3d0In0.YlRE9EGnSExQCb5m2iy4__58PZJlZdCZMsSvsuW4oj8"
}
```

:::note
In full-stack applications, calling the `token` endpoint is usually done through the back end to improve security.
:::

The `id_token` is a JSON web token (JWT) signed by the JWT key-pair specified in the `config.yaml` file of the OpenDID service. Yout can be verified using the JWT public key, for example, by the backend of the Web app.

## Implicit Flow

Initiate the flow by redirecting to the **GET** `/api/v1/authorize` endpoint on the OpenDID Service and setting the following query
parameters:

- `response_type`: set value to `id_token` to indicate Implicit Flow.
- `client_id`: The client ID set in the config.yaml file.
- `redirect_uri`: OpenDID will redirect to this URL after authentication.
- `scope`: set value to `openid`.
- `state`: set to a secure random number.
- `nonce`: optional value, set to a secure random number.

**Example**:

```
GET /api/v1/authorize?
response_type=id_token&
client_id=example-client&
redirect_uri=http://localhost:1606/callback.html&
scope=openid&
state=o0fl4c9gwylymzw5f4ik&
nonce=ia7sa06ungxdfzaqphk2 HTTP/1.1
```

After successful authentication, OpenDID will redirect back to the provided `redirect_uri` with `id_token` and `state`
**fragment components**.

**Example**:

```
/callback.html#
id_token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkaWQ6a2lsdDo0b0VkNENVV3RwbkxUVnZENVBFd2lMUmlqMWdzQmprS1JMbVpES2lCOEdqN2I2V0wiLCJ3M24iOiJjdXN0b20iLCJleHAiOjE3MTY4ODQ5MDYsImlhdCI6MTcxNjg4NDg0NiwiaXNzIjoiZGlkOmtpbHQ6NHJzQkE3dEQ1S1E4TDlXSGpGallRdUhrTWtha2NmSGRDNUNhUVVjVXh5VWpEVkhBIiwiYXVkIjoiYXBwbGljYXRpb24iLCJwcm8iOnsiRW1haWwiOiJhYmR1bEBraWx0LmlvIn0sIm5vbmNlIjoiOTFzN2ZnZDZvcjR3c2NkdGVtcXQifQ.xTy3Oyc5e-vlP10mGy0f9GqNU4LV97s77s-l7w5EwF0&
refresh_token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkaWQ6a2lsdDo0b0VkNENVV3RwbkxUVnZENVBFd2lMUmlqMWdzQmprS1JMbVpES2lCOEdqN2I2V0wiLCJ3M24iOiJjdXN0b20iLCJleHAiOjE3MTY4ODU0NDYsImlhdCI6MTcxNjg4NDg0NiwiaXNzIjoiZGlkOmtpbHQ6NHJzQkE3dEQ1S1E4TDlXSGpGallRdUhrTWtha2NmSGRDNUNhUVVjVXh5VWpEVkhBIiwiYXVkIjoiYXV0aGVudGljYXRpb24iLCJwcm8iOnsiRW1haWwiOiJhYmR1bEBraWx0LmlvIn0sIm5vbmNlIjoiOTFzN2ZnZDZvcjR3c2NkdGVtcXQifQ.87UHGid3OotxO8Wpfuw-1sc5fsQJVt5gc2cqp9dVHiw&
state=nitctpl7nmqcpvob7xthrw&
token_type=bearer
```
30 changes: 30 additions & 0 deletions docs/develop/08_opendid/05_demo_project.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
id: demo_project
title: Demo Project
---

The example code at [demo-project](https://github.com/KILTprotocol/opendid/tree/main/demo-project) contains a minimal application that uses OpenDID.
It's an [express](https://expressjs.com) application that exposes three things:

- A login page that handles the dispatching of the user to the opendid.
- A callback page for the openid connect flow to accept the token.
- A protected resource that only authenticated users can access.

Run the pre-configured demo application with the following command:

```bash
docker run -d -it --rm \
--name demo-frontend \
-p 1606:1606 \
docker.io/kiltprotocol/opendid-demo
```

The demo page will run on _http://localhost:1606_.

For this demo to work a running OpenDID Service is needed, an Identity Wallet (e.g. [Sporran](https://www.sporran.org/))
with a DID and Credential issued by the required attester specified in the `config.yaml` file (Default is SocialKYC).

:::note
The JWT secret can be set with the `TOKEN_SECRET` environment variable inside the docker container. It must match
the one specified in the `config.yaml` file to correctly verify the `id_token`. Default is `super-secret-jwt-secret`.
:::
Loading
Loading