Skip to content

Commit

Permalink
Add domain linkage grpc (#1337)
Browse files Browse the repository at this point in the history
* add domain linkage proto and implementation for domain verification

* add domain linkage verification for dids, refactor error/status handling

* add endpoints with pre-fetched configurations, add tests

* add error details to DomainLinkageError message

* add getter for `id` in `LinkedDomainService`

* align error detail format

* update readme with test setup description and add tooling scripts/files

* fix clippy suggestions
  • Loading branch information
wulfraem authored Mar 22, 2024
1 parent 0980069 commit 1ea3fb2
Show file tree
Hide file tree
Showing 13 changed files with 743 additions and 10 deletions.
4 changes: 3 additions & 1 deletion bindings/grpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ path = "src/main.rs"

[dependencies]
anyhow = "1.0.75"
futures = { version = "0.3" }
identity_eddsa_verifier = { path = "../../identity_eddsa_verifier" }
identity_iota = { path = "../../identity_iota", features = ["resolver", "sd-jwt"] }
identity_iota = { path = "../../identity_iota", features = ["resolver", "sd-jwt", "domain-linkage", "domain-linkage-fetch"] }
identity_stronghold = { path = "../../identity_stronghold", features = ["send-sync-storage"] }
iota-sdk = { version = "1.1.2", features = ["stronghold"] }
prost = "0.12"
Expand All @@ -32,6 +33,7 @@ tokio-stream = { version = "0.1.14", features = ["net"] }
tonic = "0.10"
tracing = { version = "0.1.40", features = ["async-await"] }
tracing-subscriber = "0.3.18"
url = { version = "2.5", default-features = false }

[dev-dependencies]
identity_storage = { path = "../../identity_storage", features = ["memstore"] }
Expand Down
116 changes: 109 additions & 7 deletions bindings/grpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,113 @@ The provided docker image requires the following variables to be set in order to
Make sure to provide a valid stronghold snapshot at the provided `SNAPSHOT_PATH` prefilled with all the needed key material.

### Available services
| Service description | Service Id | Proto File |
|--------------------------------|------------------------------------------|------------|
| Credential Revocation Checking | `credentials/CredentialRevocation.check` | [credentials.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/credentials.proto) |
| SD-JWT Validation | `sd_jwt/Verification.verify` | [sd_jwt.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/sd_jwt.proto) |
| Credential JWT creation | `credentials/Jwt.create` | [credentials.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/credentials.proto) |
| Credential JWT validation | `credentials/VcValidation.validate` | [credentials.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/credentials.proto) |
| DID Document Creation | `document/DocumentService.create` | [document.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/document.proto) |
| Service description | Service Id | Proto File |
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- |
| Credential Revocation Checking | `credentials/CredentialRevocation.check` | [credentials.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/credentials.proto) |
| SD-JWT Validation | `sd_jwt/Verification.verify` | [sd_jwt.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/sd_jwt.proto) |
| Credential JWT creation | `credentials/Jwt.create` | [credentials.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/credentials.proto) |
| Credential JWT validation | `credentials/VcValidation.validate` | [credentials.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/credentials.proto) |
| DID Document Creation | `document/DocumentService.create` | [document.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/document.proto) |
| Domain Linkage - validate domain, let server fetch did-configuration | `domain_linkage/DomainLinkage.validate_domain` | [domain_linkage.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/domain_linkage.proto) |
| Domain Linkage - validate domain, pass did-configuration to service | `domain_linkage/DomainLinkage.validate_domain_against_did_configuration` | [domain_linkage.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/domain_linkage.proto) |
| Domain Linkage - validate endpoints in DID, let server fetch did-configuration | `domain_linkage/DomainLinkage.validate_did` | [domain_linkage.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/domain_linkage.proto) |
| Domain Linkage - validate endpoints in DID, pass did-configuration to service | `domain_linkage/DomainLinkage.validate_did_against_did_configurations` | [domain_linkage.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/domain_linkage.proto) |

## Testing
### Domain Linkage
Following is a description about how to manually test the domain linkage service. The steps for the other services might vary a bit.

#### Http server
If you want to test domain linkage, you need a server, that's reachable via HTTPS. If you already have one, ignore the server setup steps here and just make sure your server provides the `did-configuration.json` file as described here.

- create test server folder with did configuration in it, e.g. (you can also use the template in `./tooling/domain-linkage-test-server`)
```raw
test-server/
└── .well-known
└── did-configuration.json
```
`did-configuration` looks like this for now:
```json
{
"@context": "https://identity.foundation/.well-known/did-configuration/v1",
"linked_dids": [
"add your domain linkage credential here"
]
}
```
- start a server, that will serve this folder, e.g. with a "http-server" from NodeJs : `http-server ./test-server/`, in this example the server should now be running on local port 8080
- now tunnel your server's port (here 8080) to a public domain with https, e.g. with ngrok:
`ngrok http http://127.0.0.1:8080`
the output should now have a line like
`Forwarding https://0d40-2003-d3-2710-e200-485f-e8bb-7431-79a7.ngrok-free.app -> http://127.0.0.1:8080`
check that the https url is reachable, this will be used in the next step. you can also start ngrok with a static domain, that you do not have to update credentials after each http server restart
- for convenience, you can find a script to start the HTTP server, that you can adjust in `tooling/start-http-server.sh`, don't forget to insert your static domain or to remove the `--domain` parameter
#### Domain linkage credential
- copy this public url and insert it into the advanced test 6 (the one for domain linkage) as domain 1, e.g. `let domain_1: Url = Url::parse("https://0d40-2003-d3-2710-e200-485f-e8bb-7431-79a7.ngrok-free.app")?;`
- run the example with `cargo run --release --example 6_domain_linkage`
#### GRPC server
- grab the configuration resource from the log and replace the contents of your `did-configuration.json` with it
- you now have a publicly reachable (sub)domain, that serves a `did-configuration` file containing a credential pointing to your DID
- to verify this, run the server via Docker or with the following command, remember to replace the placeholders ;) `API_ENDPOINT=replace_me STRONGHOLD_PWD=replace_me SNAPSHOT_PATH=replace_me cargo run --release`, arguments can be taken from examples, e.g. after running a `6_domain_linkage.rs`, that also logs snapshot path passed to secret manager (`let snapshot_path = random_stronghold_path(); dbg!(&snapshot_path.to_str());`), for example
- API_ENDPOINT: `"http://localhost"`
- STRONGHOLD_PWD: `"secure_password"`
- SNAPSHOT_PATH: `"/var/folders/41/s1sm86jx0xl4x435t81j81440000gn/T/test_strongholds/8o2Nyiv5ENBi7Ik3dEDq9gNzSrqeUdqi.stronghold"`
- for convenience, you can find a script to start the GRPC server, that you can adjust in `tooling/start-rpc-server.sh`, don't forget to insert the env variables as described above
#### Calling the endpoints
- call the `validate_domain` endpoint with your domain, e.g with:
```json
{
"domain": "https://0d40-2003-d3-2710-e200-485f-e8bb-7431-79a7.ngrok-free.app"
}
```
you should now receive a response like this:
```json
{
"linked_dids": [
{
"document": "... (compact JWT domain linkage credential)",
"status": "ok"
}
]
}
```
- to call the `validate_did` endpoint, you need a DID to check, you can find a testable in you domain linkage credential. for this just decode it (e.g. on jwt.io) and get the `iss` value, then you can submit as "did" like following
```json
{
"did": "did:iota:snd:0x967bf8f0c7487f61378611b6a1c6a59cb99e65b839681ee70be691b09a024ab9"
}
```
you should not receive a response like this:
```json
{
"service": [
{
"service_endpoint": [
{
"valid": true,
"document": "eyJraWQiOiJkaWQ6aW90YTpzbmQ6MHg5NjdiZjhmMGM3NDg3ZjYxMzc4NjExYjZhMWM2YTU5Y2I5OWU2NWI4Mzk2ODFlZTcwYmU2OTFiMDlhMDI0YWI5IzA3QjVWRkxBa0FabkRhaC1OTnYwYUN3TzJ5ZnRzX09ZZ0YzNFNudUloMlUiLCJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSJ9.eyJleHAiOjE3NDE2NzgyNzUsImlzcyI6ImRpZDppb3RhOnNuZDoweDk2N2JmOGYwYzc0ODdmNjEzNzg2MTFiNmExYzZhNTljYjk5ZTY1YjgzOTY4MWVlNzBiZTY5MWIwOWEwMjRhYjkiLCJuYmYiOjE3MTAxNDIyNzUsInN1YiI6ImRpZDppb3RhOnNuZDoweDk2N2JmOGYwYzc0ODdmNjEzNzg2MTFiNmExYzZhNTljYjk5ZTY1YjgzOTY4MWVlNzBiZTY5MWIwOWEwMjRhYjkiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vaWRlbnRpdHkuZm91bmRhdGlvbi8ud2VsbC1rbm93bi9kaWQtY29uZmlndXJhdGlvbi92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiRG9tYWluTGlua2FnZUNyZWRlbnRpYWwiXSwiY3JlZGVudGlhbFN1YmplY3QiOnsib3JpZ2luIjoiaHR0cHM6Ly9ob3QtYnVsbGRvZy1wcm9mb3VuZC5uZ3Jvay1mcmVlLmFwcC8ifX19.69e7T0DbRw9Kz7eEQ96P9E5HWbEo5F1fLuMjyQN6_Oa1lwBdbfj0wLlhS1j_d8AuNmvu60lMdLVixjMZJLQ5AA"
},
{
"valid": false,
"error": "domain linkage error: error sending request for url (https://bar.example.com/.well-known/did-configuration.json): error trying to connect: dns error: failed to lookup address information: nodename nor servname provided, or not known"
}
],
"id": "did:iota:snd:0x967bf8f0c7487f61378611b6a1c6a59cb99e65b839681ee70be691b09a024ab9"
}
]
}
```
Which tells us that it found a DID document with one matching service with a serviceEndpoint, that contains two domains. Out of these domains one links back to the given DID, the other domain could not be resolved.
1 change: 0 additions & 1 deletion bindings/grpc/build.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
//tonic_build::compile_protos("proto/helloworld.proto")?;
let proto_files = std::fs::read_dir("./proto")?
.filter_map(|entry| entry.ok().map(|e| e.path()))
.filter(|path| path.extension().and_then(|ext| ext.to_str()) == Some("proto"));
Expand Down
60 changes: 60 additions & 0 deletions bindings/grpc/proto/domain_linkage.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
syntax = "proto3";
package domain_linkage;

message ValidateDomainRequest {
// domain to validate
string domain = 1;
}

message ValidateDomainAgainstDidConfigurationRequest {
// domain to validate
string domain = 1;
// already resolved domain linkage config
string did_configuration = 2;
}

message LinkedDidValidationStatus {
// validation succeeded or not, `error` property is added for `false` cases
bool valid = 1;
// credential from `linked_dids` as compact JWT domain linkage credential if it could be retrieved
optional string document = 2;
// an error message, that occurred when validated, omitted if valid
optional string error = 3;
}

message ValidateDomainResponse {
// list of JWT domain linkage credential, uses the same order as the `did-configuration.json` file for domain
repeated LinkedDidValidationStatus linked_dids = 1;
}

message LinkedDidEndpointValidationStatus {
// id of service endpoint entry
string id = 1;
// list of JWT domain linkage credential, uses the same order as the `did-configuration.json` file for domain
repeated LinkedDidValidationStatus service_endpoint = 2;
}

message ValidateDidRequest {
// DID to validate
string did = 1;
}

message ValidateDidAgainstDidConfigurationsRequest {
// DID to validate
string did = 1;
// already resolved domain linkage configs
repeated ValidateDomainAgainstDidConfigurationRequest did_configurations = 2;
}

message ValidateDidResponse {
// mapping of service entries from DID with validation status for endpoint URLs
repeated LinkedDidEndpointValidationStatus service = 1;
}

service DomainLinkage {
rpc validate_domain(ValidateDomainRequest) returns (ValidateDomainResponse);
rpc validate_domain_against_did_configuration(ValidateDomainAgainstDidConfigurationRequest) returns (ValidateDomainResponse);

rpc validate_did(ValidateDidRequest) returns (ValidateDidResponse);
rpc validate_did_against_did_configurations(ValidateDidAgainstDidConfigurationsRequest) returns (ValidateDidResponse);
}
Loading

0 comments on commit 1ea3fb2

Please sign in to comment.