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

Dynamic bridge endpoint #34

Open
wants to merge 95 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
b048558
Basic Implementation of Policy Constraints
jfelixh Mar 15, 2024
76df29d
Basic Implementation of Policy Constraints
jfelixh Mar 15, 2024
d1f13f9
Merge branch 'feature_policy_conditions' of https://github.com/GAIA-X…
jfelixh Mar 15, 2024
aca8efe
Build fix
jfelixh Mar 15, 2024
768e9d5
Add new API endpoints for authentication response and test requests
ilaydacansinkoc Apr 18, 2024
f4d23d5
revert: delete unused API endpoints for authentication response and t…
ilaydacansinkoc Jun 9, 2024
48de0dc
OIDC Discovery Fix
jfelixh Jun 10, 2024
3a7a298
Changed default token
jfelixh Jun 10, 2024
0a5deba
Changed default token
jfelixh Jun 10, 2024
22d6cd1
Update extractClaims.test.js
jfelixh Jun 10, 2024
2f4cc92
Update extractClaims.test.js
jfelixh Jun 10, 2024
7971ea4
Merge branch 'feature_policy_conditions' of https://github.com/GAIA-X…
jfelixh Jun 10, 2024
465ae3c
Adopted absolute imports
jfelixh Jun 10, 2024
9325e78
Adopted absolute imports
jfelixh Jun 10, 2024
016eed3
Merge branch 'feature_policy_conditions' of https://github.com/GAIA-X…
jfelixh Jun 10, 2024
bc6ac56
Merge branch 'feature_policy_conditions' of https://github.com/GAIA-X…
jfelixh Jun 10, 2024
e870782
Merge branch 'feature_policy_conditions' of https://github.com/GAIA-X…
jfelixh Jun 11, 2024
aa0ac0f
Fix missing styles source
jfelixh Jun 11, 2024
7287aa6
Readme disclaimer
jfelixh Jun 11, 2024
a7d8579
Less Hydra logs
jfelixh Jun 11, 2024
aa12eb9
Logging improvements with pino
jfelixh Jun 11, 2024
dd5eea3
Text fixes
jfelixh Jun 11, 2024
a8b1d02
Text fixes
jfelixh Jun 11, 2024
46e4a7f
Merge branch 'feature_policy_conditions' of https://github.com/GAIA-X…
jfelixh Jun 11, 2024
1acabb3
add initial dynamic enpoint logic
ilaydacansinkoc Apr 18, 2024
db3d302
fix: move files to dynamic folder
ilaydacansinkoc Apr 18, 2024
ad208dc
enable parametric policy from sp
ilaydacansinkoc Apr 27, 2024
39307f5
fix: define types for input descriptor and presentation definition
ilaydacansinkoc May 1, 2024
5838a46
add verifiable id descriptor
ilaydacansinkoc May 1, 2024
a874fdd
add logic to parametrically use input descriptor for each incr auth
ilaydacansinkoc May 1, 2024
1eeb8ea
update redirect URIs in test_client.sh
ilaydacansinkoc May 1, 2024
502eabf
refactor, create getToken and getMetadata to reuse it in both methods
ilaydacansinkoc May 1, 2024
8e38b37
separete get and post method
ilaydacansinkoc May 1, 2024
4d6cddf
use async/await for execute function
ilaydacansinkoc May 1, 2024
31afb38
chore: flatten credFit array in extractClaims.ts
ilaydacansinkoc May 17, 2024
e13df54
fix: use async await to read file
ilaydacansinkoc May 20, 2024
7b801d1
chore: update OIDC_CLIENT_ID in .env file with client ID from test_cl…
ilaydacansinkoc May 27, 2024
1cf1a09
chore: Update middleware to protect additional paths and use API key …
ilaydacansinkoc Jun 6, 2024
a56d660
chore: Update test_client.sh to use OIDC_CLIENT_ID from .env file
ilaydacansinkoc Jun 6, 2024
d753b23
OIDC Discovery Fix
jfelixh Jun 10, 2024
f9fa302
Changed default token
jfelixh Jun 10, 2024
4184e4e
Changed default token
jfelixh Jun 10, 2024
64f7c7a
Adopted absolute imports
jfelixh Jun 10, 2024
777d90d
Fix missing styles source
jfelixh Jun 11, 2024
600e244
Readme disclaimer
jfelixh Jun 11, 2024
da2deb4
Less Hydra logs
jfelixh Jun 11, 2024
069d8dd
Logging improvements with pino
jfelixh Jun 11, 2024
4830bca
Text fixes
jfelixh Jun 11, 2024
77cd474
Text fixes
jfelixh Jun 11, 2024
3a34b2d
chore: Update dependencies and improve logging
ilaydacansinkoc Jun 11, 2024
41a9c18
revert: delete unused API endpoints for authentication response and t…
ilaydacansinkoc Jun 9, 2024
1dc3cdb
Changed default token
jfelixh Jun 10, 2024
ee00bf5
Adopted absolute imports
jfelixh Jun 10, 2024
5947bce
Adopted absolute imports
jfelixh Jun 10, 2024
26f7282
Fix missing styles source
jfelixh Jun 11, 2024
65b9fc4
Less Hydra logs
jfelixh Jun 11, 2024
a8451bd
Logging improvements with pino
jfelixh Jun 11, 2024
aa73910
Text fixes
jfelixh Jun 11, 2024
4b08dcb
Text fixes
jfelixh Jun 11, 2024
d8b544d
test for dmo
ilaydacansinkoc Jun 11, 2024
3b4ddd0
update to logger.debug, use lib for common piece of code in endpoints
ilaydacansinkoc Jun 11, 2024
d00fbde
Changed default token
jfelixh Jun 10, 2024
1a41415
Changed default token
jfelixh Jun 10, 2024
adfa398
Adopted absolute imports
jfelixh Jun 10, 2024
a41e499
Adopted absolute imports
jfelixh Jun 10, 2024
38c6b2c
Fix missing styles source
jfelixh Jun 11, 2024
1eff760
Logging improvements with pino
jfelixh Jun 11, 2024
0dcd032
Text fixes
jfelixh Jun 11, 2024
0338cd8
Merge branch 'feature_policy_conditions' into dynamic_bridge_endpoint
ilaydacansinkoc Jun 11, 2024
842b6e3
refactor extractClaims.ts
ilaydacansinkoc Jul 7, 2024
6121998
Merge branch 'main' into dynamic_bridge_endpoint
ilaydacansinkoc Jul 8, 2024
9d1e32d
refactor clientMetadata endpoint
ilaydacansinkoc Jul 8, 2024
9566fac
update package-lock.json via npm i
ilaydacansinkoc Jul 8, 2024
2831eb9
use async await to get the loginPolicy
ilaydacansinkoc Jul 8, 2024
f00f9b2
adjust unit tests for multi VC VP and constraints check
ilaydacansinkoc Jul 11, 2024
019d6af
remove old tests that are not needed anymore
ilaydacansinkoc Jul 24, 2024
35fbc11
add a policy with complex constraints
ilaydacansinkoc Jul 24, 2024
cd5cadc
instead of redis.set or get use redisGet and redisSet
ilaydacansinkoc Jul 24, 2024
216e3d4
introduce policy type to order credentials in a presentation
ilaydacansinkoc Jul 24, 2024
41d87d4
add test data and test for triple VC
ilaydacansinkoc Jul 25, 2024
6c8fa50
edit README.md for dynamic_bridge_endpoint approach
ilaydacansinkoc Jul 25, 2024
3eabc09
fix failing test
ilaydacansinkoc Jul 25, 2024
846329c
add swagger for dynamic api endpoint documentation
ilaydacansinkoc Jul 28, 2024
f2115b6
refactor readme.md
ilaydacansinkoc Jul 28, 2024
946be1b
use VC type instead of credential subject type
ilaydacansinkoc Jul 29, 2024
fcfa844
chore: Await extraction of user claims in presentCredential API
ilaydacansinkoc Aug 11, 2024
3d323a5
fix minimal test script
jfelixh Aug 23, 2024
3b3bd20
package maintenance
jfelixh Aug 23, 2024
fa9045e
Fix license headers
jfelixh Aug 23, 2024
8c575f1
Fix more license headers
jfelixh Aug 23, 2024
68cb0b6
Add http logging middleware
jfelixh Aug 23, 2024
7d79109
Logger cleanup
jfelixh Aug 26, 2024
5a71101
First API test
jfelixh Aug 26, 2024
dffdcdc
Version bump
jfelixh Aug 30, 2024
c39c3e6
Fixed test env loading
jfelixh Aug 30, 2024
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
208 changes: 203 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ You operate a service and want to allow your users to sign in using Verifiable
Credentials from a mobile wallet. But building that takes considerable time and
expertise.

<!-- prettier-ignore -->
> [!NOTE]
> As a new feature, the bridge now supports incremental authorization.
> This allows the service provider to request additional Verifiable Credentials
> from the user via the bridge. Please see the
> [Incremental Authorization Flow](#incremental-authorization-flow) section for
> more details.

### The Solution

A service provider can run this dockerized bridge software that acts as a normal
Expand Down Expand Up @@ -147,6 +155,61 @@ sequenceDiagram
Client->>Browser: Provide access to protected service
```

## Incremental Authorization Flow

The user assumed to be logged in via the bridge and the service provider
requests additional VC from user to perform incremental authorization.

```mermaid
sequenceDiagram
autonumber
actor User
participant Wallet
participant B as SSI-to-OIDC Bridge
participant SP as Service Provider

SP ->> B: "POST /api/dynamic/createTempAuthorization"
B-->>SP: "Return UUID"

SP->>B: "GET /api/dynamic/getQRCodeString"
B-->>SP: "Return QR code string"

SP->>User: "Send Auth. page containing QR code"

SP->>B: "GET /api/dynamic/getAuthResponse"
User->>Wallet: "Scan QR code"
Wallet->>B: "GET /api/dynamic/presentCredentialById"
B-->>Wallet: "Return metadata"
Wallet-->>User: "Prompt user"

User ->>Wallet: "Select VC(s)"
Wallet->>B: "POST /api/dynamic/presentCredentialById"
B-->>Wallet: "Success"

B-->>SP: "Return Auth Response"
```

### API Documentation

This documentation provides all the necessary information to interact with the
dynamic API endpoints. The API is documented using Swagger, which provides a
user-friendly interface to explore and test the API.

<!-- prettier-ignore -->
> [!NOTE]
> To access the Swagger documentation, you need to run the bridge in development mode and
> navigate to `http://localhost:5002/api-docs`.

To authenticate requests to the dynamic API in Swagger, you need to provide a
Copy link
Collaborator

Choose a reason for hiding this comment

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

Change this to just API? Or ensure consistent naming of dynamic authorization endpoints.

valid API key. The API key is stored in the `.env` file in the `vclogin` folder.
Copy link
Collaborator

Choose a reason for hiding this comment

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

So the key is passed as an environment variable. Is the name documented somewhere further down?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok, seeing it now. Would still slightly reword this.


<!-- prettier-ignore -->
> [!NOTE]
> To authenticate requests to the dynamic API in Swagger, first click on
> the "Authorize" button in the top right corner of the Swagger UI. Then, enter
> the API key in the "Value" field with the format `API_KEY <api_key>`and click
> on the "Authorize" button.

## Running a Local Deployment

A local deployment is a great way to test the bridge and to use it for
Expand Down Expand Up @@ -175,10 +238,12 @@ proper domain has to be set up.
`/vclogin/.env` with key `PEX_DESCRIPTOR_OVERRIDE` if direct control over
what wallets are asked for is desired (example for quick testing:
`./__tests__/testdata/pex/descriptorEmailFromAltme.json`)
6. at this point, it needs to be ensured that the container for the vclogin
6. to be able to test dynamic endpoint APIs, you need to provide an API key in
the `.env` file in the `vclogin` folder with the key `API_KEY`.
7. at this point, it needs to be ensured that the container for the vclogin
service is freshly built with the new env file:
`docker compose down && docker compose build`
7. `$ docker compose up`
8. `$ docker compose up`

To validate the running bridge with a simple OIDC client:

Expand Down Expand Up @@ -246,6 +311,7 @@ PEX_DESCRIPTOR_OVERRIDE=./__tests__/testdata/pex/descriptorAnything.json
HYDRA_ADMIN_URL=http://localhost:5001
REDIS_HOST=localhost
REDIS_PORT=6379
API_KEY=<api-key>
```

_Note: The PEX_DESCRIPTOR_OVERRIDE is optional and provides a way to override
Expand All @@ -261,7 +327,7 @@ refer to the end of the previous section.

## Running Tests

This repository includes unit tests with `jest` and end-to-end tests with
This repository includes unit tests with `vitest` and end-to-end tests with
`playwright`. You may run them as follows:

```bash
Expand All @@ -278,7 +344,7 @@ credential, while forwarding all subject fields to the `id_token`:
```JSON
[
{
"credentialID": "credential1",
"credentialID": "1",
"patterns": [
{
"issuer": "*",
Expand Down Expand Up @@ -323,7 +389,7 @@ use of this could look like this:
```json
[
{
"credentialId": "one",
"credentialId": "1",
"patterns": [
{
"issuer": "did:web:app.altme.io:issuer",
Expand Down Expand Up @@ -384,6 +450,138 @@ logical operators that can combine multiple constraints:
- _or_ Takes two constraint objects `a` and `b`.
- _not_ Takes one constraint object `a`

### Multiple Policy Objects
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't like the term "policy object" because this already has a name in the code and paper. Otherwise, very good addition.


The bridge also supports multiple policy objects in a policy file. This allows
for more complex scenarios where multiple credentials are needed to perform
authorization. An example of such a policy file is:

```json
[
{
"credentialId": "1",
"type": "EmailPass",
"patterns": [
{
"issuer": "did:web:app.altme.io:issuer",
"claims": [
{
"claimPath": "$.credentialSubject.email"
}
]
}
]
},
{
"credentialId": "2",
"type": "VerifiableId",
"patterns": [
{
"issuer": "did:web:app.altme.io:issuer",
"claims": [
{
"claimPath": "$.credentialSubject.id"
}
]
}
]
}
]
```

<!-- prettier-ignore -->
> [!IMPORTANT]
> Each `credentialId` should be unique across all policy objects,
> and should have integer string values starting from 1, incrementing by 1 for each subsequent policy object. This helps us determine
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does that help us? I thought this worked before. And it allowed people to give more descriptive IDs. Also, now people could mess up configuration by not using numbers?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also, if the credentialID is required to be a number, should it not be a JSON number type?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For the first comment:

The credentialId helps to extract the correct claims from the correct policy.

I guess I added this when I tested a scenario when we want to extract same claims from the different type of VCs.
(2 VCs of different type but the field that we want to extract is the same: $.credentialSubject.name)

In practice, I think we can give unique string IDs again, but then we need to make sure that the constraints are evaluated correctly as well (isValidConstraintFit, and resolveValue functions). In these functions, I use credentialId to locate the correct VC to apply constraints.


For the second comment:

It's required to be an integer-valued string. "1" or "2" for example.

> the correct policy object to apply to the VCs.

<!-- prettier-ignore -->
> [!IMPORTANT]
> Altough the `type` field is an optional parameter, it needs to be
Copy link
Collaborator

Choose a reason for hiding this comment

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

I know that the computational complexity of the previous algorithm was terrible, but I am not a fan of this type field. It leads to edge cases. What if you want to accept a student enrollment VC, but different Universities use different types? Now you cannot simply handle this in the patterns. This might even break that case completely.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You might be right; I think this way: Each organization would have a certain type of VC, and the service provider would accept that type of VC.

The idea is similar to having registries so the verifier can know what credentials to accept.

But then we need to tell the code to apply the correct policy to the correct type of VC.

> present in a policy file that has multiple policy objects. This is crucial for the accurate application of policies.

<!-- prettier-ignore -->
> [!NOTE]
> First we reorder the policy objects in a policy file based on the `credentialId` and
> then we reorder the credentials in the VP based on the `type` field from the reordered policy file.
> This ensures that each credential is matched with the correct policy object.

The `type` field helps to determine which policy object should be applied to
which type of credential. When multiple policy objects are used, this field
becomes important because the order of VCs in the VP is not guaranteed.

Users might submit VCs in a random order, so the type field ensures that each
credential is matched with the correct policy regardless of the submission
order.

In the code snippet above

- The first policy object is applied to the VC with type `EmailPass`.
- The second policy object is applied to the VC with type `VerifiableId`.

If the type fields are the same for multiple policy objects, the bridge will
apply the policy objects to the VCs in the order they are defined in the policy
file.

### Multiple Constraints

For each policy object, you can define constraints as defined in
[Constraints](#constraints). An example of such a policy file is:

```json
[
{
"credentialId": "1",
"type": "EmailPass",
"patterns": [
{
"issuer": "did:web:app.altme.io:issuer",
"claims": [
{
"claimPath": "$.credentialSubject.email"
}
],
"constraint": {
"op": "equalsDID",
"a": "$VP.proof.verificationMethod",
"b": "$1.credentialSubject.id"
}
}
]
},
{
"credentialId": "2",
"type": "VerifiableId",
"patterns": [
{
"issuer": "did:web:app.altme.io:issuer",
"claims": [
{
"claimPath": "$.credentialSubject.id"
}
],
"constraint": {
"op": "equals",
"a": "$2.credentialSubject.id",
"b": "$1.credentialSubject.id"
}
}
]
}
]
```

You can cross reference different VCs using the constraints. As in the example
below, the first VC's `credentialSubject.id` is compared with the second VC's
`credentialSubject.id` in the second policy object.

<!-- prettier-ignore -->
> [!IMPORTANT]
> You need to correctly define the JSONPaths of the constraint
> operands to be able to perform constraints check. The JSONPaths should have a
> structure like `$<credentialId>.<claimPath>` when having multiple policy
> objects.

## Token Introspection

Look into the access token like this:
Expand Down
50 changes: 25 additions & 25 deletions test_client.sh
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
#!/bin/bash
client=$(docker run --rm -it \
--network ory-hydra-net \
oryd/hydra:v2.2.0 \
create client --skip-tls-verify \
--name testclient \
--secret some-secret \
--grant-type authorization_code \
--response-type token,code,id_token \
--scope openid \
--redirect-uri http://localhost:9010/callback \
-e http://hydra:4445 \
--format json )
--network ory-hydra-net \
oryd/hydra:v2.2.0 \
create client --skip-tls-verify \
--name testclient \
--secret some-secret \
--grant-type authorization_code \
--response-type token,code,id_token \
--scope openid \
--redirect-uri "http://localhost:9010/callback" \
-e http://hydra:4445 \
--format json)

echo $client
echo "$client"

client_id=$(echo $client | jq -r '.client_id')
client_id=$(echo "$client" | jq -r ".client_id")

docker run --rm -it \
--network ory-hydra-net \
-p 9010:9010 \
oryd/hydra:v2.2.0 \
perform authorization-code --skip-tls-verify \
--port 9010 \
--client-id $client_id \
--client-secret some-secret \
--redirect http://localhost:9010/callback \
--scope openid \
--auth-url http://localhost:5004/oauth2/auth \
--token-url http://hydra:4444/oauth2/token \
-e http://hydra:4444
--network ory-hydra-net \
-p 9010:9010 \
oryd/hydra:v2.2.0 \
perform authorization-code --skip-tls-verify \
--port 9010 \
--client-id "$client_id" \
--client-secret some-secret \
--redirect "http://localhost:9010/callback" \
--scope openid \
--auth-url http://localhost:5004/oauth2/auth \
--token-url http://hydra:4444/oauth2/token \
-e http://hydra:4444
1 change: 1 addition & 0 deletions vclogin/.env.test
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
LOGIN_POLICY=./__tests__/testdata/policies/acceptAnything.json
DID_KEY_JWK={"kty":"OKP","crv":"Ed25519","x":"cwa3dufHNLg8aQb2eEUqTyoM1cKQW3XnOkMkj_AAl5M","d":"me03qhLByT-NKrfXDeji-lpADSpVOKWoaMUzv5EyzKY"}
EXTERNAL_URL=http://example.com
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"id": "verifiableId",
"name": "Input descriptor for login credential",
"purpose": "Please provide your VerifiableId credential to sign-in.",
"constraints": {
"fields": [
{
"path": ["$.credentialSubject.type"],
"filter": {
"type": "string",
"pattern": "VerifiableId"
}
}
]
}
}
]
2 changes: 1 addition & 1 deletion vclogin/__tests__/testdata/policies/acceptAnything.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[
{
"credentialId": "credential1",
"credentialId": "1",
"patterns": [
{
"issuer": "*",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[
{
"credentialId": "some random string",
"patterns": [
{
"issuer": "*",
"claims": [
{
"claimPath": "$.credentialSubject.id"
}
]
}
]
}
]
Loading
Loading