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

Add domain linkage grpc #1337

Merged
merged 9 commits into from
Mar 22, 2024
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
Loading