diff --git a/ERROR_CODES.md b/ERROR_CODES.md new file mode 100644 index 0000000..e7283f9 --- /dev/null +++ b/ERROR_CODES.md @@ -0,0 +1,18 @@ +📁 HTTP 4xx Client Errors + +| Code | Error Name | Description | When It's Used | +|------|------------------------|-----------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------| +| 400 | `MISSING_FIELDS` | One or more required fields are missing in the request payload. | `consumerAddress`, `ddo`, `serviceId`, or `sessionId` is not provided. | +| 400 | `SESSION_REQUIRED` | A required session ID is missing in the request. | Used when `sessionId` is a necessary parameter for continuation. | +| 403 | `ADDRESS_DENIED` | Consumer's address was explicitly placed in a deny list. | The address is present in the deny list at either asset or service level. | +| 403 | `ADDRESS_NOT_ALLOWED` | Consumer's address is not included in allow lists, and no wildcard is defined. | Address doesn’t match policy-defined allow rules. | +| 403 | `EMPTY_ALLOW_LIST` | A policy has an allow list defined but it is empty. | Indicates policy misconfiguration at asset/service level. | +| 404 | `SERVICE_NOT_FOUND` | The requested service ID does not match any service in the DDO. | Service ID provided does not exist in the credentialSubject. | +| 422 | `INVALID_JSON` | Failed to parse a JSON string (e.g., `policyServer`) or the structure is not as expected. | JSON parsing fails or fields don’t meet the schema. | +| 422 | `CREDENTIAL_FETCH_FAILED` | SSIpolicy exists but `request_credentials` could not be fetched or is invalid. | A malformed or missing SSI structure inside `ddo.credentials`. | + +⚠️ Default Fallback + +| Code | Error Name | Description | +|------|---------------------|--------------------------------------------| +| 500 | `UNKNOWN_ERROR` | An unexpected internal error occurred. | \ No newline at end of file diff --git a/README.md b/README.md index cb1135b..197d6bd 100644 --- a/README.md +++ b/README.md @@ -54,95 +54,352 @@ MODE_PS="1" ```json { "action": "initiate", - "sessionId": "", //optional - "policyServer": //optional - { - "successRedirectUri": "",//optional - "errorRedirectUri": "",//optional - "responseRedirectUri": "",//optional - "presentationDefinitionUri": ""//optional - }, + "sessionId": "", + "serviceId": "ff294c2e2c7d01bd5f9701abc117737917bb1f91044ba6b2d0903fc806db0d65", + "consumerAddress": "0xd727fb9be39fa019d7c02fea19e54d688da3a662", + "policyServer": { + "successRedirectUri": "", + "errorRedirectUri": "", + "responseRedirectUri": "", + "presentationDefinitionUri": "" + }, "ddo": { + "@context": [ + "https://www.w3.org/ns/credentials/v2" + ], + "id": "did:ope:1ec8435672854acf15ef3e61216900f314f8fae5e04e6b2fb0dc91c0579e0d02", + "version": "5.0.0", "credentialSubject": { - "credentials": [ - { - "allow": [ + "credentials": { + "allow": [ + { + "values": [ + { + "request_credentials": [ + { + "format": "jwt_vc_json", + "policies": [], + "type": "UniversityDegree" + } + ], + "vc_policies": [ + "signature", + "not-before", + "revoked-status-list" + ], + "vp_policies": [] + } + ], + "type": "SSIpolicy" + }, + { + "values": [ + "*" + ], + "type": "address" + } + ], + "deny": [], + "match_deny": "any" + }, + "chainId": 11155111, + "metadata": { + "created": "2025-04-15T19:48:54Z", + "updated": "2025-04-15T19:48:54Z", + "type": "dataset", + "name": "Test data set with SSI credentials - 8", + "description": { + "@value": "Test data set with SSI credentials - 8\n\nAccess to the asset allowed to UniversityDegree holders.", + "@direction": "", + "@language": "" + }, + "tags": [ + "test" + ], + "author": "", + "license": { + "name": "https://github.com/MBadea17/testdata/blob/af26d4f968fdb6e1882c2a3cca16a1480ca44a9c/License%20Agreement.pdf", + "licenseDocuments": [ { - "vp_policies": [ - "signature", - "expired" - ], - "vc_policies": [ - "signature", - "expired" - ], - "request_credentials": [ - { - "type": "VerifiableId", - "format": "jwt_vc_json" - }, + "sha256": "7939fa7e4201a373a3471feccd878c026cad50cc7d4308b6849741782b0691f7", + "mirrors": [ { - "type": "ProofOfResidence", - "format": "jwt_vc_json" - }, - { - "type": "OpenBadgeCredential", - "format": "jwt_vc_json", - "policies": [ - "signature", - { - "policy": "webhook", - "args": "https://example.org/abc/xyz" - } - ] + "method": "get", + "type": "url", + "url": "https://github.com/MBadea17/testdata/blob/af26d4f968fdb6e1882c2a3cca16a1480ca44a9c/License%20Agreement.pdf" } - ] + ], + "name": "https://github.com/MBadea17/testdata/blob/af26d4f968fdb6e1882c2a3cca16a1480ca44a9c/License%20Agreement.pdf", + "fileType": "text/html; charset=utf-8" } ] + }, + "links": {}, + "additionalInformation": { + "termsAndConditions": true + }, + "copyrightHolder": "", + "providedBy": "" + }, + "services": [ + { + "credentials": { + "allow": [ + { + "values": [ + { + "request_credentials": [], + "vc_policies": [ + "signature", + "not-before", + "revoked-status-list" + ], + "vp_policies": [] + } + ], + "type": "SSIpolicy" + }, + { + "values": [ + "0xd727fb9be39fa019d7c02fea19e54d688da3a662" + ], + "type": "address" + } + ], + "match_deny": "any", + "deny": [] + }, + "name": "Service 1", + "files": "0x048d5a53cfc89686e6cde36df81495a3eda3c42044240254fbd7b93e7e11b1272e867e448a64d639d490bdbb9f3a45be4d0a54d53a3b92317c57ca270899dc29b97d3f0fe6fba6a40d3d9ff1438bf8c36732af3b47c4056486e8fcd7dcddba2038bce7fb24ec4e28ae889a4556e61dbeda5344a102d60c88dfbc1fe84e983a0d360c269705071c9191f982c832c93b39d91c688787af4d55b55e5afb38e0ad8eb5d92bd7a51935daa8d4e394436b6f911883516c7f3c875753673acab80859664f5dde7ce7279d7948c5987271989aacd261751cb933e454919f0cd4f1e075e0d138c5425a4fae3e1046b7fdbe8aec7b29bacac922a01f3b37203b67c0ac0a246ac0b0acdb2b5d21c1723daeae63555847c0e9e4b54f9bad995ba8b9098616f82b5a89d9c21fbac07035530a6010ca9ccc94f7474397701b8df23496206402d670cebd2964a8b4a0f45039cf3519b4ce30ae5b0744b22a5ceb5e9db0a8c8431d19356f9284cac87da509da54bc85b1811878bd72de38adbad9b9945a1cf6272d5ed877e498a1", + "description": { + "@value": "Service accessible to address 0xD727FB9Be39fA019d7C02fea19E54d688Da3a662", + "@direction": "", + "@language": "" + }, + "id": "7b2ca00f457ecc21eff766d39f2f35e1ee5e5d427eb3f62aa7297080388eeff6", + "datatokenAddress": "0x879A899d5DCDa773e3cD8188Af45eAf7194c24d2", + "serviceEndpoint": "https://ocean-node-vm3.oceanenterprise.io", + "state": 0, + "type": "access", + "timeout": 0 + }, + { + "credentials": { + "allow": [ + { + "values": [ + { + "request_credentials": [ + { + "format": "jwt_vc_json", + "policies": [], + "type": "VerifiableId" + } + ], + "vc_policies": [ + "signature", + "not-before", + "revoked-status-list" + ], + "vp_policies": [] + } + ], + "type": "SSIpolicy" + } + ], + "match_deny": "any", + "deny": [] + }, + "name": "Service 2", + "description": { + "@value": "Service accessible to holder of VerifiableId credentials", + "@direction": "", + "@language": "" + }, + "files": "0x048d5a53cfc89686e6cde36df81495a3eda3c42044240254fbd7b93e7e11b1272e867e448a64d639d490bdbb9f3a45be4d0a54d53a3b92317c57ca270899dc29b97d3f0fe6fba6a40d3d9ff1438bf8c36732af3b47c4056486e8fcd7dcddba2038bce7fb24ec4e28ae889a4556e61dbeda5344a102d60c88dfbc1fe84e983a0d360c269705071c9191f982c832c93b39d91c688787af4d55b55e5afb38e0ad8eb5d92bd7a51935daa8d4e394436b6f911883516c7f3c875753673acab80859664f5dde7ce7279d7948c5987271989aacd261751cb933e454919f0cd4f1e075e0d138c5425a4fae3e1046b7fdbe8aec7b29bacac922a01f3b37203b67c0ac0a246ac0b0acdb2b5d21c1723daeae63555847c0e9e4b54f9bad995ba8b9098616f82b5a89d9c21fbac07035530a6010ca9ccc94f7474397701b8df23496206402d670cebd2964a8b4a0f45039cf3519b4ce30ae5b0744b22a5ceb5e9db0a8c8431d19356f9284cac87da509da54bc85b1811878bd72de38adbad9b9945a1cf6272d5ed877e498a1", + "id": "ff294c2e2c7d01bd5f9701abc117737917bb1f91044ba6b2d0903fc806db0d65", + "datatokenAddress": "0x18945267E5C9f56f9626206711a31afaCea4Ae6B", + "serviceEndpoint": "https://ocean-node-vm3.oceanenterprise.io", + "state": 0, + "type": "access", + "timeout": 86400 + }, + { + "credentials": { + "allow": [ + { + "values": [ + { + "request_credentials": [], + "vc_policies": [ + "signature", + "not-before", + "revoked-status-list" + ], + "vp_policies": [] + } + ], + "type": "SSIpolicy" + } + ], + "match_deny": "any", + "deny": [ + { + "values": [ + "0x61db12d8b636cb49ea09eca58a893da9480e1f33" + ], + "type": "address" + } + ] + }, + "name": "Service 3", + "description": { + "@value": "Service not accessible to address 0x61DB12d8b636Cb49ea09eCa58a893dA9480E1F33", + "@direction": "", + "@language": "" + }, + "files": "0x048d5a53cfc89686e6cde36df81495a3eda3c42044240254fbd7b93e7e11b1272e867e448a64d639d490bdbb9f3a45be4d0a54d53a3b92317c57ca270899dc29b97d3f0fe6fba6a40d3d9ff1438bf8c36732af3b47c4056486e8fcd7dcddba2038bce7fb24ec4e28ae889a4556e61dbeda5344a102d60c88dfbc1fe84e983a0d360c269705071c9191f982c832c93b39d91c688787af4d55b55e5afb38e0ad8eb5d92bd7a51935daa8d4e394436b6f911883516c7f3c875753673acab80859664f5dde7ce7279d7948c5987271989aacd261751cb933e454919f0cd4f1e075e0d138c5425a4fae3e1046b7fdbe8aec7b29bacac922a01f3b37203b67c0ac0a246ac0b0acdb2b5d21c1723daeae63555847c0e9e4b54f9bad995ba8b9098616f82b5a89d9c21fbac07035530a6010ca9ccc94f7474397701b8df23496206402d670cebd2964a8b4a0f45039cf3519b4ce30ae5b0744b22a5ceb5e9db0a8c8431d19356f9284cac87da509da54bc85b1811878bd72de38adbad9b9945a1cf6272d5ed877e498a1", + "id": "91d8c931ed389ae61d3ed18f89f1db41db381a706b54bfaa1db15f114a2a9cd8", + "datatokenAddress": "0xb0fd7A05b4de95f3FFd31932515e66B7b92ee96a", + "serviceEndpoint": "https://ocean-node-vm3.oceanenterprise.io", + "state": 0, + "type": "access", + "timeout": 86400 } ], - "services": [ + "nftAddress": "0x09e939308A16e1B27088bbf6932D91EC8b5F42b8", + "nft": { + "state": 0, + "address": "0x09e939308A16e1B27088bbf6932D91EC8b5F42b8", + "name": "Data NFT", + "symbol": "OEC-NFT", + "owner": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "created": "2025-04-15T19:59:24Z", + "tokenURI": "" + }, + "stats": { + "allocated": 0, + "orders": 0, + "price": { + "value": 2, + "tokenSymbol": "OCEAN", + "tokenAddress": "0x1B083D8584dd3e6Ff37d04a6e7e82b5F622f3985" + } + }, + "datatokens": [ { - "credentials": [ - { - "allow": [ - { - "vp_policies": [ - "signature", - "expired" - ], - "vc_policies": [ - "signature", - "expired" - ], - "request_credentials": [ - { - "type": "VerifiableId", - "format": "jwt_vc_json" - }, - { - "type": "ProofOfResidence", - "format": "jwt_vc_json" - }, - { - "type": "OpenBadgeCredential", - "format": "jwt_vc_json", - "policies": [ - "signature", - { - "policy": "webhook", - "args": "https://example.org/abc/xyz" - } - ] - } - ] - } - ] - } - ] + "symbol": "DT1", + "address": "0x879A899d5DCDa773e3cD8188Af45eAf7194c24d2", + "name": "Datatoken", + "serviceId": "7b2ca00f457ecc21eff766d39f2f35e1ee5e5d427eb3f62aa7297080388eeff6" + }, + { + "symbol": "DT1", + "address": "0x18945267E5C9f56f9626206711a31afaCea4Ae6B", + "name": "Datatoken", + "serviceId": "ff294c2e2c7d01bd5f9701abc117737917bb1f91044ba6b2d0903fc806db0d65" + }, + { + "symbol": "DT1", + "address": "0xb0fd7A05b4de95f3FFd31932515e66B7b92ee96a", + "name": "Datatoken", + "serviceId": "91d8c931ed389ae61d3ed18f89f1db41db381a706b54bfaa1db15f114a2a9cd8" } - ] - } + ], + "event": { + "txid": "0x71a250f4992c28f09ed9170b70732874eafeef8e8da653faab1c0258ea45fc98", + "from": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "contract": "0x09e939308A16e1B27088bbf6932D91EC8b5F42b8", + "block": 8125544, + "datetime": "2025-04-15T19:59:24.000Z" + }, + "purgatory": { + "state": false + } + }, + "additionalDdos": [], + "type": [ + "VerifiableCredential" + ], + "issuer": "did:jwk:eyJrdHkiOiJPS1AiLCJkIjoiUnBVOU5ONmdFbGtvMXpjYnR1VVRERlVXWEZCeks1Um9FZ3FRaVFHMWN4QSIsImNydiI6IkVkMjU1MTkiLCJraWQiOiI1TS1od19JbTZDalJDZ3NCVXhGX0R2aWxRRnhfdVU5RWpNcUpPbzdQOERnIiwieCI6IklaeXo1WVl6WkpJYWN3R21ockstYXdCa2ZJWmRqbUFWWTViVjFIbGNxYjgifQ", + "proof": { + "signature": "N2GQRLQbDUM7gLlUNweF-JjP9XS1uAWHWZy-8NLdlBdPJFrdvVkZk1z6UntVqATkCZU-l8MMQP_5DyMDzws3DA", + "header": { + "kid": "5M-hw_Im6CjRCgsBUxF_DvilQFx_uU9EjMqJOo7P8Dg", + "alg": "EdDSA" + } + }, + "accessDetails": [ + { + "type": "fixed", + "price": "2.0", + "addressOrId": "0x33d13b46dec069713aa8c3e8b86ee3dd948691ca18ea261b9d464920afa3940f", + "baseToken": { + "address": "0x1B083D8584dd3e6Ff37d04a6e7e82b5F622f3985", + "name": "Ocean Token", + "symbol": "OCEAN", + "decimals": 18 + }, + "datatoken": { + "address": "0x879A899d5DCDa773e3cD8188Af45eAf7194c24d2", + "name": "Access Token", + "symbol": "OEAT", + "decimals": 0 + }, + "paymentCollector": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "templateId": 2, + "isOwned": false, + "validOrderTx": "", + "isPurchasable": true, + "publisherMarketOrderFee": "0" + }, + { + "type": "fixed", + "price": "2.0", + "addressOrId": "0x25b94927c6ab131165d72d45fa3d1fd595ba224e046be56468dbd36dd078ea83", + "baseToken": { + "address": "0x1B083D8584dd3e6Ff37d04a6e7e82b5F622f3985", + "name": "Ocean Token", + "symbol": "OCEAN", + "decimals": 18 + }, + "datatoken": { + "address": "0x18945267E5C9f56f9626206711a31afaCea4Ae6B", + "name": "DataToken", + "symbol": "DT", + "decimals": 0 + }, + "paymentCollector": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "templateId": 2, + "isOwned": false, + "validOrderTx": "", + "isPurchasable": true, + "publisherMarketOrderFee": "0" + }, + { + "type": "fixed", + "price": "5.0", + "addressOrId": "0x4d51f22682adff2aba9854c024de4d0f9abc0943095515cb1cd90b36eba45308", + "baseToken": { + "address": "0x1B083D8584dd3e6Ff37d04a6e7e82b5F622f3985", + "name": "Ocean Token", + "symbol": "OCEAN", + "decimals": 18 + }, + "datatoken": { + "address": "0xb0fd7A05b4de95f3FFd31932515e66B7b92ee96a", + "name": "DataToken", + "symbol": "DT", + "decimals": 0 + }, + "paymentCollector": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "templateId": 2, + "isOwned": false, + "validOrderTx": "", + "isPurchasable": true, + "publisherMarketOrderFee": "0" + } + ] } } @@ -320,11 +577,353 @@ ex. `https://verifier.portal.walt.id/openid4vc/verify/$id`and `http://ocean-node ```json { "action": "download", - "policyServer": - { - "sessionId": "ec64a21c-3d81-44f9-8b1d-099c1ec0c7b6" + "serviceId": "ff294c2e2c7d01bd5f9701abc117737917bb1f91044ba6b2d0903fc806db0d65", + "consumerAddress": "0xd727fb9be39fa019d7c02fea19e54d688da3a662", + "policyServer": { + "successRedirectUri": "", + "sessionId": "ba46717d-e44f-490b-a8d6-522bd9153f9e", + "errorRedirectUri": "", + "responseRedirectUri": "", + "presentationDefinitionUri": "" + }, + "ddo": { + "@context": [ + "https://www.w3.org/ns/credentials/v2" + ], + "id": "did:ope:1ec8435672854acf15ef3e61216900f314f8fae5e04e6b2fb0dc91c0579e0d02", + "version": "5.0.0", + "credentialSubject": { + "credentials": { + "allow": [ + { + "values": [ + { + "request_credentials": [ + { + "format": "jwt_vc_json", + "policies": [], + "type": "UniversityDegree" + } + ], + "vc_policies": [ + "signature", + "not-before", + "revoked-status-list" + ], + "vp_policies": [] + } + ], + "type": "SSIpolicy" + }, + { + "values": [ + "*" + ], + "type": "address" + } + ], + "deny": [], + "match_deny": "any" + }, + "chainId": 11155111, + "metadata": { + "created": "2025-04-15T19:48:54Z", + "updated": "2025-04-15T19:48:54Z", + "type": "dataset", + "name": "Test data set with SSI credentials - 8", + "description": { + "@value": "Test data set with SSI credentials - 8\n\nAccess to the asset allowed to UniversityDegree holders.", + "@direction": "", + "@language": "" + }, + "tags": [ + "test" + ], + "author": "", + "license": { + "name": "https://github.com/MBadea17/testdata/blob/af26d4f968fdb6e1882c2a3cca16a1480ca44a9c/License%20Agreement.pdf", + "licenseDocuments": [ + { + "sha256": "7939fa7e4201a373a3471feccd878c026cad50cc7d4308b6849741782b0691f7", + "mirrors": [ + { + "method": "get", + "type": "url", + "url": "https://github.com/MBadea17/testdata/blob/af26d4f968fdb6e1882c2a3cca16a1480ca44a9c/License%20Agreement.pdf" + } + ], + "name": "https://github.com/MBadea17/testdata/blob/af26d4f968fdb6e1882c2a3cca16a1480ca44a9c/License%20Agreement.pdf", + "fileType": "text/html; charset=utf-8" + } + ] + }, + "links": {}, + "additionalInformation": { + "termsAndConditions": true + }, + "copyrightHolder": "", + "providedBy": "" + }, + "services": [ + { + "credentials": { + "allow": [ + { + "values": [ + { + "request_credentials": [], + "vc_policies": [ + "signature", + "not-before", + "revoked-status-list" + ], + "vp_policies": [] + } + ], + "type": "SSIpolicy" + }, + { + "values": [ + "0xd727fb9be39fa019d7c02fea19e54d688da3a662" + ], + "type": "address" + } + ], + "match_deny": "any", + "deny": [] + }, + "name": "Service 1", + "files": "0x048d5a53cfc89686e6cde36df81495a3eda3c42044240254fbd7b93e7e11b1272e867e448a64d639d490bdbb9f3a45be4d0a54d53a3b92317c57ca270899dc29b97d3f0fe6fba6a40d3d9ff1438bf8c36732af3b47c4056486e8fcd7dcddba2038bce7fb24ec4e28ae889a4556e61dbeda5344a102d60c88dfbc1fe84e983a0d360c269705071c9191f982c832c93b39d91c688787af4d55b55e5afb38e0ad8eb5d92bd7a51935daa8d4e394436b6f911883516c7f3c875753673acab80859664f5dde7ce7279d7948c5987271989aacd261751cb933e454919f0cd4f1e075e0d138c5425a4fae3e1046b7fdbe8aec7b29bacac922a01f3b37203b67c0ac0a246ac0b0acdb2b5d21c1723daeae63555847c0e9e4b54f9bad995ba8b9098616f82b5a89d9c21fbac07035530a6010ca9ccc94f7474397701b8df23496206402d670cebd2964a8b4a0f45039cf3519b4ce30ae5b0744b22a5ceb5e9db0a8c8431d19356f9284cac87da509da54bc85b1811878bd72de38adbad9b9945a1cf6272d5ed877e498a1", + "description": { + "@value": "Service accessible to address 0xD727FB9Be39fA019d7C02fea19E54d688Da3a662", + "@direction": "", + "@language": "" + }, + "id": "7b2ca00f457ecc21eff766d39f2f35e1ee5e5d427eb3f62aa7297080388eeff6", + "datatokenAddress": "0x879A899d5DCDa773e3cD8188Af45eAf7194c24d2", + "serviceEndpoint": "https://ocean-node-vm3.oceanenterprise.io", + "state": 0, + "type": "access", + "timeout": 0 + }, + { + "credentials": { + "allow": [ + { + "values": [ + { + "request_credentials": [ + { + "format": "jwt_vc_json", + "policies": [], + "type": "VerifiableId" + } + ], + "vc_policies": [ + "signature", + "not-before", + "revoked-status-list" + ], + "vp_policies": [] + } + ], + "type": "SSIpolicy" + } + ], + "match_deny": "any", + "deny": [] + }, + "name": "Service 2", + "description": { + "@value": "Service accessible to holder of VerifiableId credentials", + "@direction": "", + "@language": "" + }, + "files": "0x048d5a53cfc89686e6cde36df81495a3eda3c42044240254fbd7b93e7e11b1272e867e448a64d639d490bdbb9f3a45be4d0a54d53a3b92317c57ca270899dc29b97d3f0fe6fba6a40d3d9ff1438bf8c36732af3b47c4056486e8fcd7dcddba2038bce7fb24ec4e28ae889a4556e61dbeda5344a102d60c88dfbc1fe84e983a0d360c269705071c9191f982c832c93b39d91c688787af4d55b55e5afb38e0ad8eb5d92bd7a51935daa8d4e394436b6f911883516c7f3c875753673acab80859664f5dde7ce7279d7948c5987271989aacd261751cb933e454919f0cd4f1e075e0d138c5425a4fae3e1046b7fdbe8aec7b29bacac922a01f3b37203b67c0ac0a246ac0b0acdb2b5d21c1723daeae63555847c0e9e4b54f9bad995ba8b9098616f82b5a89d9c21fbac07035530a6010ca9ccc94f7474397701b8df23496206402d670cebd2964a8b4a0f45039cf3519b4ce30ae5b0744b22a5ceb5e9db0a8c8431d19356f9284cac87da509da54bc85b1811878bd72de38adbad9b9945a1cf6272d5ed877e498a1", + "id": "ff294c2e2c7d01bd5f9701abc117737917bb1f91044ba6b2d0903fc806db0d65", + "datatokenAddress": "0x18945267E5C9f56f9626206711a31afaCea4Ae6B", + "serviceEndpoint": "https://ocean-node-vm3.oceanenterprise.io", + "state": 0, + "type": "access", + "timeout": 86400 + }, + { + "credentials": { + "allow": [ + { + "values": [ + { + "request_credentials": [], + "vc_policies": [ + "signature", + "not-before", + "revoked-status-list" + ], + "vp_policies": [] + } + ], + "type": "SSIpolicy" + } + ], + "match_deny": "any", + "deny": [ + { + "values": [ + "0x61db12d8b636cb49ea09eca58a893da9480e1f33" + ], + "type": "address" + } + ] + }, + "name": "Service 3", + "description": { + "@value": "Service not accessible to address 0x61DB12d8b636Cb49ea09eCa58a893dA9480E1F33", + "@direction": "", + "@language": "" + }, + "files": "0x048d5a53cfc89686e6cde36df81495a3eda3c42044240254fbd7b93e7e11b1272e867e448a64d639d490bdbb9f3a45be4d0a54d53a3b92317c57ca270899dc29b97d3f0fe6fba6a40d3d9ff1438bf8c36732af3b47c4056486e8fcd7dcddba2038bce7fb24ec4e28ae889a4556e61dbeda5344a102d60c88dfbc1fe84e983a0d360c269705071c9191f982c832c93b39d91c688787af4d55b55e5afb38e0ad8eb5d92bd7a51935daa8d4e394436b6f911883516c7f3c875753673acab80859664f5dde7ce7279d7948c5987271989aacd261751cb933e454919f0cd4f1e075e0d138c5425a4fae3e1046b7fdbe8aec7b29bacac922a01f3b37203b67c0ac0a246ac0b0acdb2b5d21c1723daeae63555847c0e9e4b54f9bad995ba8b9098616f82b5a89d9c21fbac07035530a6010ca9ccc94f7474397701b8df23496206402d670cebd2964a8b4a0f45039cf3519b4ce30ae5b0744b22a5ceb5e9db0a8c8431d19356f9284cac87da509da54bc85b1811878bd72de38adbad9b9945a1cf6272d5ed877e498a1", + "id": "91d8c931ed389ae61d3ed18f89f1db41db381a706b54bfaa1db15f114a2a9cd8", + "datatokenAddress": "0xb0fd7A05b4de95f3FFd31932515e66B7b92ee96a", + "serviceEndpoint": "https://ocean-node-vm3.oceanenterprise.io", + "state": 0, + "type": "access", + "timeout": 86400 + } + ], + "nftAddress": "0x09e939308A16e1B27088bbf6932D91EC8b5F42b8", + "nft": { + "state": 0, + "address": "0x09e939308A16e1B27088bbf6932D91EC8b5F42b8", + "name": "Data NFT", + "symbol": "OEC-NFT", + "owner": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "created": "2025-04-15T19:59:24Z", + "tokenURI": "" + }, + "stats": { + "allocated": 0, + "orders": 0, + "price": { + "value": 2, + "tokenSymbol": "OCEAN", + "tokenAddress": "0x1B083D8584dd3e6Ff37d04a6e7e82b5F622f3985" + } + }, + "datatokens": [ + { + "symbol": "DT1", + "address": "0x879A899d5DCDa773e3cD8188Af45eAf7194c24d2", + "name": "Datatoken", + "serviceId": "7b2ca00f457ecc21eff766d39f2f35e1ee5e5d427eb3f62aa7297080388eeff6" + }, + { + "symbol": "DT1", + "address": "0x18945267E5C9f56f9626206711a31afaCea4Ae6B", + "name": "Datatoken", + "serviceId": "ff294c2e2c7d01bd5f9701abc117737917bb1f91044ba6b2d0903fc806db0d65" + }, + { + "symbol": "DT1", + "address": "0xb0fd7A05b4de95f3FFd31932515e66B7b92ee96a", + "name": "Datatoken", + "serviceId": "91d8c931ed389ae61d3ed18f89f1db41db381a706b54bfaa1db15f114a2a9cd8" + } + ], + "event": { + "txid": "0x71a250f4992c28f09ed9170b70732874eafeef8e8da653faab1c0258ea45fc98", + "from": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "contract": "0x09e939308A16e1B27088bbf6932D91EC8b5F42b8", + "block": 8125544, + "datetime": "2025-04-15T19:59:24.000Z" + }, + "purgatory": { + "state": false + } + }, + "additionalDdos": [], + "type": [ + "VerifiableCredential" + ], + "issuer": "did:jwk:eyJrdHkiOiJPS1AiLCJkIjoiUnBVOU5ONmdFbGtvMXpjYnR1VVRERlVXWEZCeks1Um9FZ3FRaVFHMWN4QSIsImNydiI6IkVkMjU1MTkiLCJraWQiOiI1TS1od19JbTZDalJDZ3NCVXhGX0R2aWxRRnhfdVU5RWpNcUpPbzdQOERnIiwieCI6IklaeXo1WVl6WkpJYWN3R21ockstYXdCa2ZJWmRqbUFWWTViVjFIbGNxYjgifQ", + "proof": { + "signature": "N2GQRLQbDUM7gLlUNweF-JjP9XS1uAWHWZy-8NLdlBdPJFrdvVkZk1z6UntVqATkCZU-l8MMQP_5DyMDzws3DA", + "header": { + "kid": "5M-hw_Im6CjRCgsBUxF_DvilQFx_uU9EjMqJOo7P8Dg", + "alg": "EdDSA" + } + }, + "accessDetails": [ + { + "type": "fixed", + "price": "2.0", + "addressOrId": "0x33d13b46dec069713aa8c3e8b86ee3dd948691ca18ea261b9d464920afa3940f", + "baseToken": { + "address": "0x1B083D8584dd3e6Ff37d04a6e7e82b5F622f3985", + "name": "Ocean Token", + "symbol": "OCEAN", + "decimals": 18 + }, + "datatoken": { + "address": "0x879A899d5DCDa773e3cD8188Af45eAf7194c24d2", + "name": "Access Token", + "symbol": "OEAT", + "decimals": 0 + }, + "paymentCollector": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "templateId": 2, + "isOwned": false, + "validOrderTx": "", + "isPurchasable": true, + "publisherMarketOrderFee": "0" + }, + { + "type": "fixed", + "price": "2.0", + "addressOrId": "0x25b94927c6ab131165d72d45fa3d1fd595ba224e046be56468dbd36dd078ea83", + "baseToken": { + "address": "0x1B083D8584dd3e6Ff37d04a6e7e82b5F622f3985", + "name": "Ocean Token", + "symbol": "OCEAN", + "decimals": 18 + }, + "datatoken": { + "address": "0x18945267E5C9f56f9626206711a31afaCea4Ae6B", + "name": "DataToken", + "symbol": "DT", + "decimals": 0 + }, + "paymentCollector": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "templateId": 2, + "isOwned": false, + "validOrderTx": "", + "isPurchasable": true, + "publisherMarketOrderFee": "0" + }, + { + "type": "fixed", + "price": "5.0", + "addressOrId": "0x4d51f22682adff2aba9854c024de4d0f9abc0943095515cb1cd90b36eba45308", + "baseToken": { + "address": "0x1B083D8584dd3e6Ff37d04a6e7e82b5F622f3985", + "name": "Ocean Token", + "symbol": "OCEAN", + "decimals": 18 + }, + "datatoken": { + "address": "0xb0fd7A05b4de95f3FFd31932515e66B7b92ee96a", + "name": "DataToken", + "symbol": "DT", + "decimals": 0 + }, + "paymentCollector": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "templateId": 2, + "isOwned": false, + "validOrderTx": "", + "isPurchasable": true, + "publisherMarketOrderFee": "0" + } + ] } - } ``` diff --git a/src/handlers/waltIdPolicyHandler.ts b/src/handlers/waltIdPolicyHandler.ts index 191e11b..4346e89 100644 --- a/src/handlers/waltIdPolicyHandler.ts +++ b/src/handlers/waltIdPolicyHandler.ts @@ -3,77 +3,116 @@ import axios from 'axios' import { randomUUID } from 'crypto' import { PolicyRequestPayload, PolicyRequestResponse } from '../@types/policy.js' import { PolicyHandler } from '../policyHandler.js' -import { buildInvalidRequestMessage } from '../utils/validateRequests.js' +import { + buildInvalidRequestMessage, + buildVerificationErrorRequestMessage, + ERROR_CODES +} from '../utils/validateRequests.js' import { logError, logInfo } from '../utils/logger.js' + export class WaltIdPolicyHandler extends PolicyHandler { public async initiate( requestPayload: PolicyRequestPayload ): Promise { - if (!requestPayload.ddo.credentialSubject) - return buildInvalidRequestMessage( - 'Request body does not contain ddo.credentialSubject' - ) - - const url = new URL(`/openid4vc/verify`, process.env.WALTID_VERIFIER_URL) - - const requestCredentialsBody = this.parserequest_credentials(requestPayload) - const uuid = requestPayload.sessionId && requestPayload.sessionId !== '' ? requestPayload.sessionId : randomUUID() - const headers = { - stateId: uuid, - successRedirectUri: requestPayload.policyServer?.successRedirectUri - ? requestPayload.policyServer.successRedirectUri - : process.env.WALTID_SUCCESS_REDIRECT_URL || '', + const successRedirectUri = requestPayload.policyServer?.successRedirectUri + ? requestPayload.policyServer.successRedirectUri + : process.env.WALTID_SUCCESS_REDIRECT_URL || '' - errorRedirectUri: requestPayload.policyServer?.errorRedirectUri - ? requestPayload.policyServer.errorRedirectUri - : process.env.WALTID_ERROR_REDIRECT_URL || '' - } + const errorRedirectUri = requestPayload.policyServer?.errorRedirectUri + ? requestPayload.policyServer.errorRedirectUri + : process.env.WALTID_ERROR_REDIRECT_URL || '' - logInfo({ - message: 'WaltId: payload', - url: url.toString(), - headers, - requestCredentialsBody - }) + const web3VerificationResult = this.verifyWeb3Address(requestPayload) - const response = await axios.post(url.toString(), requestCredentialsBody, { headers }) + if (!web3VerificationResult.success) { + return buildVerificationErrorRequestMessage( + web3VerificationResult.message || 'Web3 Address verification error', + web3VerificationResult.code || ERROR_CODES.UNKNOWN_ERROR, + errorRedirectUri.replace('$id', uuid) + ) + } - logInfo({ - message: 'WaltId: response', - url: url.toString(), - response: response.data - }) + const checkSSIPolicy = this.hasSSIPolicyToBeChecked(requestPayload) - const redirectUrl = requestPayload.policyServer?.responseRedirectUri - ? requestPayload.policyServer?.responseRedirectUri.replace('$id', uuid) - : process.env.WALTID_VERIFY_RESPONSE_REDIRECT_URL.replace('$id', uuid) - const definitionUrl = requestPayload.policyServer?.responseRedirectUri - ? requestPayload.policyServer?.presentationDefinitionUri.replace('$id', uuid) - : process.env.WALTID_VERIFY_PRESENTATION_DEFINITION_URL.replace('$id', uuid) - const updatedResponseData = response.data - .replace(/response_uri=([^&]*)/, `response_uri=${encodeURIComponent(redirectUrl)}`) - .replace( - /presentation_definition_uri=([^&]*)/, - `presentation_definition_uri=${encodeURIComponent(definitionUrl)}` + if (checkSSIPolicy.error) { + return buildVerificationErrorRequestMessage( + 'SSIpolicy was found, but failed to fetch request credentials', + ERROR_CODES.CREDENTIAL_FETCH_FAILED, + errorRedirectUri.replace('$id', uuid) ) - - const policyResponse = { - success: response.status === 200, - message: updatedResponseData, - httpStatus: response.status } - logInfo({ - message: 'PS: response', - policyResponse - }) + if (checkSSIPolicy.checkSSIPolicy) { + const url = new URL(`/openid4vc/verify`, process.env.WALTID_VERIFIER_URL) - return policyResponse + const requestCredentialsBody = this.parseRequestCredentials(requestPayload) + + const headers = { + stateId: uuid, + successRedirectUri, + errorRedirectUri + } + + logInfo({ + message: 'WaltId: payload', + url: url.toString(), + headers, + requestCredentialsBody + }) + + const response = await axios.post(url.toString(), requestCredentialsBody, { + headers + }) + + logInfo({ + message: 'WaltId: response', + url: url.toString(), + response: response.data + }) + + const redirectUrl = requestPayload.policyServer?.responseRedirectUri + ? requestPayload.policyServer?.responseRedirectUri.replace('$id', uuid) + : process.env.WALTID_VERIFY_RESPONSE_REDIRECT_URL.replace('$id', uuid) + + const definitionUrl = requestPayload.policyServer?.responseRedirectUri + ? requestPayload.policyServer?.presentationDefinitionUri.replace('$id', uuid) + : process.env.WALTID_VERIFY_PRESENTATION_DEFINITION_URL.replace('$id', uuid) + + const updatedResponseData = response.data + .replace( + /response_uri=([^&]*)/, + `response_uri=${encodeURIComponent(redirectUrl)}` + ) + .replace( + /presentation_definition_uri=([^&]*)/, + `presentation_definition_uri=${encodeURIComponent(definitionUrl)}` + ) + + const policyResponse = { + success: response.status === 200, + message: updatedResponseData, + httpStatus: response.status + } + + logInfo({ message: 'PS: response', policyResponse }) + return policyResponse + } else { + const policyResponse = { + success: true, + message: { + redirectUri: successRedirectUri.replace('$id', uuid) + }, + httpStatus: 200 + } + + logInfo({ message: 'PS: response', policyResponse }) + return policyResponse + } } public async presentationRequest( @@ -283,10 +322,14 @@ export class WaltIdPolicyHandler extends PolicyHandler { }) const policyResponse = { - success: response.status === 200 && response.data.verificationResult, + success: response.status === 200 && response.data.verificationResult === true, message: response.data, - httpStatus: response.status + httpStatus: + response.data.verificationResult === true + ? response.status + : ERROR_CODES.UNKNOWN_ERROR } + logInfo({ message: 'PS: response', policyResponse @@ -305,37 +348,211 @@ export class WaltIdPolicyHandler extends PolicyHandler { } } - if (!requestPayload.policyServer?.sessionId) - return buildInvalidRequestMessage('Request body does not contain sessionId') + const successRedirectUri = requestPayload.policyServer?.successRedirectUri + ? requestPayload.policyServer.successRedirectUri + : process.env.WALTID_SUCCESS_REDIRECT_URL || '' - const url = new URL( - `/openid4vc/session/${requestPayload.policyServer.sessionId}`, - process.env.WALTID_VERIFIER_URL - ) + const errorRedirectUri = requestPayload.policyServer?.errorRedirectUri + ? requestPayload.policyServer.errorRedirectUri + : process.env.WALTID_ERROR_REDIRECT_URL || '' - logInfo({ - message: 'WaltId: payload', - url - }) + const web3VerificationResult = this.verifyWeb3Address(requestPayload) - const response = await axios.get(url.toString()) + if (!web3VerificationResult.success) { + return buildVerificationErrorRequestMessage( + web3VerificationResult.message || 'Web3 Address verification error', + web3VerificationResult.code || ERROR_CODES.UNKNOWN_ERROR, + errorRedirectUri.replace('$id', requestPayload.policyServer.sessionId || '') + ) + } - logInfo({ - message: 'WaltId: response', - url: url.toString(), - response: response.data - }) + const checkSSIPolicy = this.hasSSIPolicyToBeChecked(requestPayload) - const policyResponse = { - success: response.status === 200 && response.data.verificationResult, - message: response.data, - httpStatus: response.status + if (checkSSIPolicy.error) { + return buildVerificationErrorRequestMessage( + 'SSIpolicy was found, but failed to fetch request credentials', + ERROR_CODES.MISSING_FIELDS, + errorRedirectUri.replace('$id', requestPayload.policyServer.sessionId || '') + ) + } + + if (checkSSIPolicy.checkSSIPolicy) { + if (!requestPayload.policyServer?.sessionId) + return buildInvalidRequestMessage('Request body does not contain sessionId') + + const url = new URL( + `/openid4vc/session/${requestPayload.policyServer.sessionId}`, + process.env.WALTID_VERIFIER_URL + ) + + logInfo({ + message: 'WaltId: payload', + url + }) + + try { + const response = await axios.get(url.toString()) + + logInfo({ + message: 'WaltId: response', + url: url.toString(), + response: response.data + }) + + const policyResponse = { + success: response.status === 200 && response.data.verificationResult === true, + message: response.data, + httpStatus: + response.data.verificationResult === true + ? response.status + : ERROR_CODES.UNKNOWN_ERROR + } + logInfo({ + message: 'PS: response', + policyResponse + }) + return policyResponse + } catch (e) { + return { + success: false, + httpStatus: e?.response?.status || ERROR_CODES.UNKNOWN_ERROR, + message: { + error: e?.response?.data?.message || 'Unknown error', + redirectUri: errorRedirectUri.replace( + '$id', + requestPayload.policyServer.sessionId || '' + ) + } + } + } + } else { + const policyResponse = { + success: true, + message: { + redirectUri: successRedirectUri.replace( + '$id', + requestPayload.policyServer.sessionId || '' + ) + }, + httpStatus: 200 + } + + logInfo({ + message: 'PS: response', + policyResponse + }) + + return policyResponse + } + } + + public async startCompute( + requestPayload: PolicyRequestPayload + ): Promise { + if (typeof requestPayload.policyServer === 'string') { + try { + requestPayload.policyServer = JSON.parse(requestPayload.policyServer) + } catch (error) { + return buildInvalidRequestMessage('Failed to parse policyServer JSON string') + } + } + + const successRedirectUri = requestPayload.policyServer?.successRedirectUri + ? requestPayload.policyServer.successRedirectUri + : process.env.WALTID_SUCCESS_REDIRECT_URL || '' + + const errorRedirectUri = requestPayload.policyServer?.errorRedirectUri + ? requestPayload.policyServer.errorRedirectUri + : process.env.WALTID_ERROR_REDIRECT_URL || '' + + const web3VerificationResult = this.verifyWeb3Address(requestPayload) + + if (!web3VerificationResult.success) { + return buildVerificationErrorRequestMessage( + web3VerificationResult.message || 'Web3 Address verification error', + web3VerificationResult.code || ERROR_CODES.UNKNOWN_ERROR, + errorRedirectUri.replace('$id', requestPayload.policyServer.sessionId || '') + ) + } + + const checkSSIPolicy = this.hasSSIPolicyToBeChecked(requestPayload) + + if (checkSSIPolicy.error) { + return buildVerificationErrorRequestMessage( + 'SSIpolicy was found, but failed to fetch request credentials', + ERROR_CODES.MISSING_FIELDS, + errorRedirectUri.replace('$id', requestPayload.policyServer.sessionId || '') + ) + } + + if (checkSSIPolicy.checkSSIPolicy) { + if (!requestPayload.policyServer?.sessionId) + return buildInvalidRequestMessage('Request body does not contain sessionId') + + const url = new URL( + `/openid4vc/session/${requestPayload.policyServer.sessionId}`, + process.env.WALTID_VERIFIER_URL + ) + + logInfo({ + message: 'WaltId: payload', + url + }) + + try { + const response = await axios.get(url.toString()) + + logInfo({ + message: 'WaltId: response', + url: url.toString(), + response: response.data + }) + + const policyResponse = { + success: response.status === 200 && response.data.verificationResult === true, + message: response.data, + httpStatus: + response.data.verificationResult === true + ? response.status + : ERROR_CODES.UNKNOWN_ERROR + } + logInfo({ + message: 'PS: response', + policyResponse + }) + return policyResponse + } catch (e) { + return { + success: false, + httpStatus: e?.response?.status || ERROR_CODES.UNKNOWN_ERROR, + message: { + error: e?.response?.data?.message || 'Unknown error', + redirectUri: errorRedirectUri.replace( + '$id', + requestPayload.policyServer.sessionId || '' + ) + } + } + } + } else { + const policyResponse = { + success: true, + message: { + redirectUri: successRedirectUri.replace( + '$id', + requestPayload.policyServer.sessionId || '' + ) + }, + httpStatus: 200 + } + + logInfo({ + message: 'PS: response', + policyResponse + }) + + return policyResponse } - logInfo({ - message: 'PS: response', - policyResponse - }) - return policyResponse } public async passthrough( @@ -378,7 +595,46 @@ export class WaltIdPolicyHandler extends PolicyHandler { return policyResponse } - private parserequest_credentials(requestPayload: any): any { + private hasSSIPolicyToBeChecked(requestPayload: any): { + checkSSIPolicy: boolean + error?: boolean + } { + const credentialSubject = requestPayload?.ddo?.credentialSubject + const serviceId = requestPayload?.serviceId + if (!credentialSubject) return { checkSSIPolicy: false } + + const targetType = 'SSIpolicy' + + const assetPolicies = + credentialSubject.credentials?.allow?.filter( + (item: any) => item?.type === targetType + ) ?? [] + + const service = credentialSubject.services?.find((s: any) => s.id === serviceId) + + const servicePolicies = + service?.credentials?.allow?.filter((item: any) => item?.type === targetType) ?? [] + + const combinedPolicies = [...assetPolicies, ...servicePolicies] + + for (const policy of combinedPolicies) { + if (!Array.isArray(policy.values)) continue + + for (const val of policy.values) { + if ( + val?.request_credentials && + Array.isArray(val.request_credentials) && + val.request_credentials.length > 0 + ) { + return { checkSSIPolicy: true } + } + } + } + + return { checkSSIPolicy: false, error: combinedPolicies.length > 0 } + } + + private parseRequestCredentials(requestPayload: any): any { const credentialSubject = requestPayload?.ddo?.credentialSubject const targetType = 'SSIpolicy' const credentialSubjectCredentials = @@ -388,6 +644,7 @@ export class WaltIdPolicyHandler extends PolicyHandler { const serviceCredentials = credentialSubject?.services + ?.filter((service: any) => service?.id === requestPayload.serviceId) ?.flatMap( (service: any) => service?.credentials?.allow?.filter( @@ -468,4 +725,129 @@ export class WaltIdPolicyHandler extends PolicyHandler { request_credentials: Array.from(request_credentialsMap.values()) } } + + private verifyWeb3Address(requestPayload: any): { + success: boolean + message?: string + code?: number + } { + const consumerAddress = requestPayload.consumerAddress?.toLowerCase() + const { ddo } = requestPayload + const { serviceId } = requestPayload + + if (!consumerAddress || !ddo || !serviceId) { + return { + success: false, + message: 'Missing required fields (consumerAddress, ddo, serviceId).', + code: ERROR_CODES.MISSING_FIELDS + } + } + + const assetCredentials = ddo.credentialSubject?.credentials || {} + const assetAllowList = this.extractAddressList(assetCredentials.allow) + const assetDenyList = this.extractAddressList(assetCredentials.deny) + + const service = ddo.credentialSubject?.services?.find((s: any) => s.id === serviceId) + + if (!service) { + return { + success: false, + message: 'Service not found in DDO.', + code: ERROR_CODES.SERVICE_NOT_FOUND + } + } + + const serviceAllowList = this.extractAddressList(service.credentials?.allow) + const serviceDenyList = this.extractAddressList(service.credentials?.deny) + + const serviceDefinesAddressCredentials = this.hasAddressCredential( + service.credentials?.allow + ) + + if ( + assetDenyList.includes(consumerAddress) || + serviceDenyList.includes(consumerAddress) + ) { + return { + success: false, + message: 'Access denied: Address is in deny list.', + code: ERROR_CODES.ADDRESS_DENIED + } + } + + if ( + this.hasAddressCredential(assetCredentials.allow) && + assetAllowList.length === 0 + ) { + return { + success: false, + message: 'Access denied: Empty allow list at asset level.', + code: ERROR_CODES.EMPTY_ALLOW_LIST + } + } + + if ( + this.hasAddressCredential(service.credentials?.allow) && + serviceAllowList.length === 0 + ) { + return { + success: false, + message: 'Access denied: Empty allow list at service level.', + code: ERROR_CODES.EMPTY_ALLOW_LIST + } + } + + const assetAllowsAll = assetAllowList.includes('*') + const serviceAllowsAll = serviceAllowList.includes('*') + + if (!serviceDefinesAddressCredentials) { + if (assetAllowsAll) return { success: true } + + return assetAllowList.includes(consumerAddress) + ? { success: true } + : { + success: false, + message: 'Access denied: Address not allowed at asset level.', + code: ERROR_CODES.ADDRESS_NOT_ALLOWED + } + } + + if (assetAllowsAll && serviceAllowsAll) return { success: true } + + const assetMatch = assetAllowsAll || assetAllowList.includes(consumerAddress) + const serviceMatch = serviceAllowsAll || serviceAllowList.includes(consumerAddress) + + if (assetMatch && serviceMatch) return { success: true } + + return { + success: false, + message: 'Access denied: Address not in both allow lists.', + code: ERROR_CODES.ADDRESS_NOT_ALLOWED + } + } + + private extractAddressList(credentialsList: any[]): string[] { + if (!Array.isArray(credentialsList)) return [] + + const addressList: string[] = [] + + for (const credential of credentialsList) { + if (credential.type === 'address' && Array.isArray(credential.values)) { + for (const value of credential.values) { + if (typeof value === 'string') { + addressList.push(value.toLowerCase()) + } else if (value?.address && typeof value.address === 'string') { + addressList.push(value.address.toLowerCase()) + } + } + } + } + + return addressList + } + + private hasAddressCredential(credentialsList: any[]): boolean { + if (!Array.isArray(credentialsList)) return false + return credentialsList.some((c) => c.type === 'address') + } } diff --git a/src/test/WaltIdPolicyHandler.test.ts b/src/test/WaltIdPolicyHandler.test.ts index ebdd5e6..a5523a0 100644 --- a/src/test/WaltIdPolicyHandler.test.ts +++ b/src/test/WaltIdPolicyHandler.test.ts @@ -3,6 +3,7 @@ import { expect } from 'chai' import sinon from 'sinon' import axios from 'axios' import { WaltIdPolicyHandler } from '../handlers/waltIdPolicyHandler.js' + describe('WaltIdPolicyHandler', () => { let handler: WaltIdPolicyHandler @@ -17,52 +18,221 @@ describe('WaltIdPolicyHandler', () => { it('should call initiate with valid payload', async () => { const payload = { action: 'initiate', + sessionId: '', + serviceId: 'ff294c2e2c7d01bd5f9701abc117737917bb1f91044ba6b2d0903fc806db0d65', + consumerAddress: '0xd727fb9be39fa019d7c02fea19e54d688da3a662', + policyServer: { + successRedirectUri: '', + errorRedirectUri: '', + responseRedirectUri: '', + presentationDefinitionUri: '' + }, ddo: { + '@context': ['https://www.w3.org/ns/credentials/v2'], + id: 'did:ope:1ec8435672854acf15ef3e61216900f314f8fae5e04e6b2fb0dc91c0579e0d02', + version: '5.0.0', credentialSubject: { credentials: { allow: [ { - type: 'SSIpolicy', values: [ { - vp_policies: ['signature'], - vc_policies: ['signature'], request_credentials: [ { - type: 'VerifiableId', - format: 'jwt_vc_json' + format: 'jwt_vc_json', + policies: [] as any[], + type: 'UniversityDegree' } - ] + ], + vc_policies: ['signature', 'not-before', 'revoked-status-list'], + vp_policies: [] as any[] } - ] + ], + type: 'SSIpolicy' + }, + { + values: ['*'], + type: 'address' } - ] + ], + deny: [] as any[], + match_deny: 'any' + }, + chainId: 11155111, + metadata: { + created: '2025-04-15T19:48:54Z', + updated: '2025-04-15T19:48:54Z', + type: 'dataset', + name: 'Test data set with SSI credentials - 8', + description: { + '@value': + 'Test data set with SSI credentials - 8\n\nAccess to the asset allowed to UniversityDegree holders.', + '@direction': '', + '@language': '' + }, + tags: ['test'], + author: '', + license: { + name: 'https://github.com/MBadea17/testdata/blob/af26d4f968fdb6e1882c2a3cca16a1480ca44a9c/License%20Agreement.pdf', + licenseDocuments: [ + { + sha256: + '7939fa7e4201a373a3471feccd878c026cad50cc7d4308b6849741782b0691f7', + mirrors: [ + { + method: 'get', + type: 'url', + url: 'https://github.com/MBadea17/testdata/blob/af26d4f968fdb6e1882c2a3cca16a1480ca44a9c/License%20Agreement.pdf' + } + ], + name: 'https://github.com/MBadea17/testdata/blob/af26d4f968fdb6e1882c2a3cca16a1480ca44a9c/License%20Agreement.pdf', + fileType: 'text/html; charset=utf-8' + } + ] + }, + links: {}, + additionalInformation: { + termsAndConditions: true + }, + copyrightHolder: '', + providedBy: '' + }, + services: [ + { + credentials: { + allow: [ + { + values: [ + { + request_credentials: [] as any[], + vc_policies: ['signature', 'not-before', 'revoked-status-list'], + vp_policies: [] as any[] + } + ], + type: 'SSIpolicy' + }, + { + values: ['0xd727fb9be39fa019d7c02fea19e54d688da3a662'], + type: 'address' + } + ], + match_deny: 'any', + deny: [] as any[] + }, + name: 'Service 1', + files: + '0x048d5a53cfc89686e6cde36df81495a3eda3c42044240254fbd7b93e7e11b1272e867e448a64d639d490bdbb9f3a45be4d0a54d53a3b92317c57ca270899dc29b97d3f0fe6fba6a40d3d9ff1438bf8c36732af3b47c4056486e8fcd7dcddba2038bce7fb24ec4e28ae889a4556e61dbeda5344a102d60c88dfbc1fe84e983a0d360c269705071c9191f982c832c93b39d91c688787af4d55b55e5afb38e0ad8eb5d92bd7a51935daa8d4e394436b6f911883516c7f3c875753673acab80859664f5dde7ce7279d7948c5987271989aacd261751cb933e454919f0cd4f1e075e0d138c5425a4fae3e1046b7fdbe8aec7b29bacac922a01f3b37203b67c0ac0a246ac0b0acdb2b5d21c1723daeae63555847c0e9e4b54f9bad995ba8b9098616f82b5a89d9c21fbac07035530a6010ca9ccc94f7474397701b8df23496206402d670cebd2964a8b4a0f45039cf3519b4ce30ae5b0744b22a5ceb5e9db0a8c8431d19356f9284cac87da509da54bc85b1811878bd72de38adbad9b9945a1cf6272d5ed877e498a1', + description: { + '@value': + 'Service accessible to address 0xD727FB9Be39fA019d7C02fea19E54d688Da3a662', + '@direction': '', + '@language': '' + }, + id: '7b2ca00f457ecc21eff766d39f2f35e1ee5e5d427eb3f62aa7297080388eeff6', + datatokenAddress: '0x879A899d5DCDa773e3cD8188Af45eAf7194c24d2', + serviceEndpoint: 'https://ocean-node-vm3.oceanenterprise.io', + state: 0, + type: 'access', + timeout: 0 + }, + { + credentials: { + allow: [ + { + values: [ + { + request_credentials: [ + { + format: 'jwt_vc_json', + policies: [], + type: 'VerifiableId' + } + ], + vc_policies: ['signature', 'not-before', 'revoked-status-list'], + vp_policies: [] + } + ], + type: 'SSIpolicy' + } + ], + match_deny: 'any', + deny: [] + }, + name: 'Service 2', + description: { + '@value': 'Service accessible to holder of VerifiableId credentials', + '@direction': '', + '@language': '' + }, + files: '...', + id: 'ff294c2e2c7d01bd5f9701abc117737917bb1f91044ba6b2d0903fc806db0d65', + datatokenAddress: '0x18945267E5C9f56f9626206711a31afaCea4Ae6B', + serviceEndpoint: 'https://ocean-node-vm3.oceanenterprise.io', + state: 0, + type: 'access', + timeout: 86400 + } + ], + nftAddress: '0x09e939308A16e1B27088bbf6932D91EC8b5F42b8', + nft: { + state: 0, + address: '0x09e939308A16e1B27088bbf6932D91EC8b5F42b8', + name: 'Data NFT', + symbol: 'OEC-NFT', + owner: '0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26', + created: '2025-04-15T19:59:24Z', + tokenURI: '' + } + }, + additionalDdos: [] as any[], + type: ['VerifiableCredential'], + issuer: 'did:jwk:eyJrdHkiOiJPS1Ai...', + proof: { + signature: + 'N2GQRLQbDUM7gLlUNweF-JjP9XS1uAWHWZy-8NLdlBdPJFrdvVkZk1z6UntVqATkCZU-l8MMQP_5DyMDzws3DA', + header: { + kid: '5M-hw_Im6CjRCgsBUxF_DvilQFx_uU9EjMqJOo7P8Dg', + alg: 'EdDSA' } - } + }, + accessDetails: [ + { + type: 'fixed', + price: '2.0', + addressOrId: '...', + baseToken: { + address: '0x1B083D8584dd3e6Ff37d04a6e7e82b5F622f3985', + name: 'Ocean Token', + symbol: 'OCEAN', + decimals: 18 + }, + datatoken: { + address: '...', + name: 'Access Token', + symbol: 'OEAT', + decimals: 0 + }, + paymentCollector: '0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26', + templateId: 2, + isOwned: false, + validOrderTx: '', + isPurchasable: true, + publisherMarketOrderFee: '0' + } + ] } } const stub = sinon.stub(axios, 'post').resolves({ status: 200, data: 'success' }) const response = await handler.initiate(payload as any) - + console.log(JSON.stringify(response)) expect(stub.calledOnce).to.be.true expect(response.success).to.be.true expect(response.message).to.equal('success') expect(response.httpStatus).to.equal(200) }) - it('should return error for invalid payload in initiate', async () => { - const payload = { ddo: {} } - - const response = await handler.initiate(payload as any) - - expect(response.success).to.be.false - expect(response.message).to.include( - 'Request body does not contain ddo.credentialSubject' - ) - }) - it('should call presentationRequest with valid payload', async () => { const payload = { sessionId: 'session123', @@ -88,19 +258,6 @@ describe('WaltIdPolicyHandler', () => { expect(response.message).to.include('Request body does not contain sessionId') }) - it('should call checkSessionId with valid payload', async () => { - const payload = { - sessionId: 'session123' - } - - const stub = sinon.stub(axios, 'get').resolves({ status: 200, data: 'sessionData' }) - - const response = await handler.checkSessionId(payload as any) - - expect(stub.calledOnce).to.be.true - expect(response.httpStatus).to.equal(200) - }) - it('should return error for invalid payload in checkSessionId', async () => { const payload = {} @@ -110,35 +267,6 @@ describe('WaltIdPolicyHandler', () => { expect(response.message).to.include('Request body does not contain sessionId') }) - it('should call download with valid payload', async () => { - const payload = { - policyServer: { - sessionId: 'session123' - } - } - - const stub = sinon.stub(axios, 'get').resolves({ - status: 200, - data: { verificationResult: true } - }) - - const response = await handler.download(payload as any) - - expect(stub.calledOnce).to.be.true - expect(response.success).to.be.true - expect(response.message).to.deep.equal({ verificationResult: true }) - expect(response.httpStatus).to.equal(200) - }) - - it('should return error for invalid payload in download', async () => { - const payload = {} - - const response = await handler.download(payload as any) - - expect(response.success).to.be.false - expect(response.message).to.include('Request body does not contain sessionId') - }) - it('should return error for invalid payload in passthrough', async () => { const payload = { httpMethod: 'POST' } diff --git a/src/utils/validateRequests.ts b/src/utils/validateRequests.ts index c1dcd1e..111b352 100644 --- a/src/utils/validateRequests.ts +++ b/src/utils/validateRequests.ts @@ -4,7 +4,7 @@ import { logError } from './logger.js' export function buildInvalidRequestMessage(cause: string): PolicyRequestResponse { const response = { success: false, - httpStatus: 400, + httpStatus: ERROR_CODES.MISSING_FIELDS, message: cause } logError(response) @@ -14,9 +14,39 @@ export function buildInvalidRequestMessage(cause: string): PolicyRequestResponse export function buildNotImplementedRequestMessage(): PolicyRequestResponse { const response = { success: false, - httpStatus: 501, + httpStatus: ERROR_CODES.NOT_IMPLEMENTED, message: 'Not implemented exception' } logError(response) return response } + +export function buildVerificationErrorRequestMessage( + cause: string, + code: number, + redirectUri?: string +): PolicyRequestResponse { + const response = { + success: false, + httpStatus: code, + message: { + redirectUri, + error: cause + } + } + logError(response) + return response +} + +export const ERROR_CODES = { + MISSING_FIELDS: 400, + INVALID_JSON: 422, + SERVICE_NOT_FOUND: 404, + ADDRESS_DENIED: 403, + ADDRESS_NOT_ALLOWED: 403, + EMPTY_ALLOW_LIST: 403, + SESSION_REQUIRED: 400, + CREDENTIAL_FETCH_FAILED: 422, + UNKNOWN_ERROR: 500, + NOT_IMPLEMENTED: 500 +} diff --git a/swagger.json b/swagger.json index fe96c53..a9a3977 100644 --- a/swagger.json +++ b/swagger.json @@ -39,6 +39,8 @@ "value": { "action": "initiate", "sessionId": "", + "serviceId": "ff294c2e2c7d01bd5f9701abc117737917bb1f91044ba6b2d0903fc806db0d65", + "consumerAddress": "0xd727fb9be39fa019d7c02fea19e54d688da3a662", "policyServer": { "successRedirectUri": "", "errorRedirectUri": "", @@ -46,50 +48,342 @@ "presentationDefinitionUri": "" }, "ddo": { + "@context": [ + "https://www.w3.org/ns/credentials/v2" + ], + "id": "did:ope:1ec8435672854acf15ef3e61216900f314f8fae5e04e6b2fb0dc91c0579e0d02", + "version": "5.0.0", "credentialSubject": { "credentials": { "allow": [ { - "type": "SSIpolicy", "values": [ { - "vp_policies": ["signature", "expired"], - "vc_policies": ["signature", "expired"], "request_credentials": [ { - "type": "UniversityDegree", - "format": "jwt_vc_json" + "format": "jwt_vc_json", + "policies": [], + "type": "UniversityDegree" } - ] + ], + "vc_policies": [ + "signature", + "not-before", + "revoked-status-list" + ], + "vp_policies": [] } - ] + ], + "type": "SSIpolicy" + }, + { + "values": [ + "*" + ], + "type": "address" } - ] + ], + "deny": [], + "match_deny": "any" + }, + "chainId": 11155111, + "metadata": { + "created": "2025-04-15T19:48:54Z", + "updated": "2025-04-15T19:48:54Z", + "type": "dataset", + "name": "Test data set with SSI credentials - 8", + "description": { + "@value": "Test data set with SSI credentials - 8\n\nAccess to the asset allowed to UniversityDegree holders.", + "@direction": "", + "@language": "" + }, + "tags": [ + "test" + ], + "author": "", + "license": { + "name": "https://github.com/MBadea17/testdata/blob/af26d4f968fdb6e1882c2a3cca16a1480ca44a9c/License%20Agreement.pdf", + "licenseDocuments": [ + { + "sha256": "7939fa7e4201a373a3471feccd878c026cad50cc7d4308b6849741782b0691f7", + "mirrors": [ + { + "method": "get", + "type": "url", + "url": "https://github.com/MBadea17/testdata/blob/af26d4f968fdb6e1882c2a3cca16a1480ca44a9c/License%20Agreement.pdf" + } + ], + "name": "https://github.com/MBadea17/testdata/blob/af26d4f968fdb6e1882c2a3cca16a1480ca44a9c/License%20Agreement.pdf", + "fileType": "text/html; charset=utf-8" + } + ] + }, + "links": {}, + "additionalInformation": { + "termsAndConditions": true + }, + "copyrightHolder": "", + "providedBy": "" }, "services": [ { "credentials": { "allow": [ { - "type": "SSIpolicy", "values": [ { - "vp_policies": ["signature", "expired"], - "vc_policies": ["signature", "expired"], + "request_credentials": [], + "vc_policies": [ + "signature", + "not-before", + "revoked-status-list" + ], + "vp_policies": [] + } + ], + "type": "SSIpolicy" + }, + { + "values": [ + "0xd727fb9be39fa019d7c02fea19e54d688da3a662" + ], + "type": "address" + } + ], + "match_deny": "any", + "deny": [] + }, + "name": "Service 1", + "files": "0x048d5a53cfc89686e6cde36df81495a3eda3c42044240254fbd7b93e7e11b1272e867e448a64d639d490bdbb9f3a45be4d0a54d53a3b92317c57ca270899dc29b97d3f0fe6fba6a40d3d9ff1438bf8c36732af3b47c4056486e8fcd7dcddba2038bce7fb24ec4e28ae889a4556e61dbeda5344a102d60c88dfbc1fe84e983a0d360c269705071c9191f982c832c93b39d91c688787af4d55b55e5afb38e0ad8eb5d92bd7a51935daa8d4e394436b6f911883516c7f3c875753673acab80859664f5dde7ce7279d7948c5987271989aacd261751cb933e454919f0cd4f1e075e0d138c5425a4fae3e1046b7fdbe8aec7b29bacac922a01f3b37203b67c0ac0a246ac0b0acdb2b5d21c1723daeae63555847c0e9e4b54f9bad995ba8b9098616f82b5a89d9c21fbac07035530a6010ca9ccc94f7474397701b8df23496206402d670cebd2964a8b4a0f45039cf3519b4ce30ae5b0744b22a5ceb5e9db0a8c8431d19356f9284cac87da509da54bc85b1811878bd72de38adbad9b9945a1cf6272d5ed877e498a1", + "description": { + "@value": "Service accessible to address 0xD727FB9Be39fA019d7C02fea19E54d688Da3a662", + "@direction": "", + "@language": "" + }, + "id": "7b2ca00f457ecc21eff766d39f2f35e1ee5e5d427eb3f62aa7297080388eeff6", + "datatokenAddress": "0x879A899d5DCDa773e3cD8188Af45eAf7194c24d2", + "serviceEndpoint": "https://ocean-node-vm3.oceanenterprise.io", + "state": 0, + "type": "access", + "timeout": 0 + }, + { + "credentials": { + "allow": [ + { + "values": [ + { "request_credentials": [ { - "type": "VerifiableId", - "format": "jwt_vc_json" + "format": "jwt_vc_json", + "policies": [], + "type": "VerifiableId" } - ] + ], + "vc_policies": [ + "signature", + "not-before", + "revoked-status-list" + ], + "vp_policies": [] } - ] + ], + "type": "SSIpolicy" + } + ], + "match_deny": "any", + "deny": [] + }, + "name": "Service 2", + "description": { + "@value": "Service accessible to holder of VerifiableId credentials", + "@direction": "", + "@language": "" + }, + "files": "0x048d5a53cfc89686e6cde36df81495a3eda3c42044240254fbd7b93e7e11b1272e867e448a64d639d490bdbb9f3a45be4d0a54d53a3b92317c57ca270899dc29b97d3f0fe6fba6a40d3d9ff1438bf8c36732af3b47c4056486e8fcd7dcddba2038bce7fb24ec4e28ae889a4556e61dbeda5344a102d60c88dfbc1fe84e983a0d360c269705071c9191f982c832c93b39d91c688787af4d55b55e5afb38e0ad8eb5d92bd7a51935daa8d4e394436b6f911883516c7f3c875753673acab80859664f5dde7ce7279d7948c5987271989aacd261751cb933e454919f0cd4f1e075e0d138c5425a4fae3e1046b7fdbe8aec7b29bacac922a01f3b37203b67c0ac0a246ac0b0acdb2b5d21c1723daeae63555847c0e9e4b54f9bad995ba8b9098616f82b5a89d9c21fbac07035530a6010ca9ccc94f7474397701b8df23496206402d670cebd2964a8b4a0f45039cf3519b4ce30ae5b0744b22a5ceb5e9db0a8c8431d19356f9284cac87da509da54bc85b1811878bd72de38adbad9b9945a1cf6272d5ed877e498a1", + "id": "ff294c2e2c7d01bd5f9701abc117737917bb1f91044ba6b2d0903fc806db0d65", + "datatokenAddress": "0x18945267E5C9f56f9626206711a31afaCea4Ae6B", + "serviceEndpoint": "https://ocean-node-vm3.oceanenterprise.io", + "state": 0, + "type": "access", + "timeout": 86400 + }, + { + "credentials": { + "allow": [ + { + "values": [ + { + "request_credentials": [], + "vc_policies": [ + "signature", + "not-before", + "revoked-status-list" + ], + "vp_policies": [] + } + ], + "type": "SSIpolicy" + } + ], + "match_deny": "any", + "deny": [ + { + "values": [ + "0x61db12d8b636cb49ea09eca58a893da9480e1f33" + ], + "type": "address" } ] - } + }, + "name": "Service 3", + "description": { + "@value": "Service not accessible to address 0x61DB12d8b636Cb49ea09eCa58a893dA9480E1F33", + "@direction": "", + "@language": "" + }, + "files": "0x048d5a53cfc89686e6cde36df81495a3eda3c42044240254fbd7b93e7e11b1272e867e448a64d639d490bdbb9f3a45be4d0a54d53a3b92317c57ca270899dc29b97d3f0fe6fba6a40d3d9ff1438bf8c36732af3b47c4056486e8fcd7dcddba2038bce7fb24ec4e28ae889a4556e61dbeda5344a102d60c88dfbc1fe84e983a0d360c269705071c9191f982c832c93b39d91c688787af4d55b55e5afb38e0ad8eb5d92bd7a51935daa8d4e394436b6f911883516c7f3c875753673acab80859664f5dde7ce7279d7948c5987271989aacd261751cb933e454919f0cd4f1e075e0d138c5425a4fae3e1046b7fdbe8aec7b29bacac922a01f3b37203b67c0ac0a246ac0b0acdb2b5d21c1723daeae63555847c0e9e4b54f9bad995ba8b9098616f82b5a89d9c21fbac07035530a6010ca9ccc94f7474397701b8df23496206402d670cebd2964a8b4a0f45039cf3519b4ce30ae5b0744b22a5ceb5e9db0a8c8431d19356f9284cac87da509da54bc85b1811878bd72de38adbad9b9945a1cf6272d5ed877e498a1", + "id": "91d8c931ed389ae61d3ed18f89f1db41db381a706b54bfaa1db15f114a2a9cd8", + "datatokenAddress": "0xb0fd7A05b4de95f3FFd31932515e66B7b92ee96a", + "serviceEndpoint": "https://ocean-node-vm3.oceanenterprise.io", + "state": 0, + "type": "access", + "timeout": 86400 } - ] - } + ], + "nftAddress": "0x09e939308A16e1B27088bbf6932D91EC8b5F42b8", + "nft": { + "state": 0, + "address": "0x09e939308A16e1B27088bbf6932D91EC8b5F42b8", + "name": "Data NFT", + "symbol": "OEC-NFT", + "owner": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "created": "2025-04-15T19:59:24Z", + "tokenURI": "" + }, + "stats": { + "allocated": 0, + "orders": 0, + "price": { + "value": 2, + "tokenSymbol": "OCEAN", + "tokenAddress": "0x1B083D8584dd3e6Ff37d04a6e7e82b5F622f3985" + } + }, + "datatokens": [ + { + "symbol": "DT1", + "address": "0x879A899d5DCDa773e3cD8188Af45eAf7194c24d2", + "name": "Datatoken", + "serviceId": "7b2ca00f457ecc21eff766d39f2f35e1ee5e5d427eb3f62aa7297080388eeff6" + }, + { + "symbol": "DT1", + "address": "0x18945267E5C9f56f9626206711a31afaCea4Ae6B", + "name": "Datatoken", + "serviceId": "ff294c2e2c7d01bd5f9701abc117737917bb1f91044ba6b2d0903fc806db0d65" + }, + { + "symbol": "DT1", + "address": "0xb0fd7A05b4de95f3FFd31932515e66B7b92ee96a", + "name": "Datatoken", + "serviceId": "91d8c931ed389ae61d3ed18f89f1db41db381a706b54bfaa1db15f114a2a9cd8" + } + ], + "event": { + "txid": "0x71a250f4992c28f09ed9170b70732874eafeef8e8da653faab1c0258ea45fc98", + "from": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "contract": "0x09e939308A16e1B27088bbf6932D91EC8b5F42b8", + "block": 8125544, + "datetime": "2025-04-15T19:59:24.000Z" + }, + "purgatory": { + "state": false + } + }, + "additionalDdos": [], + "type": [ + "VerifiableCredential" + ], + "issuer": "did:jwk:eyJrdHkiOiJPS1AiLCJkIjoiUnBVOU5ONmdFbGtvMXpjYnR1VVRERlVXWEZCeks1Um9FZ3FRaVFHMWN4QSIsImNydiI6IkVkMjU1MTkiLCJraWQiOiI1TS1od19JbTZDalJDZ3NCVXhGX0R2aWxRRnhfdVU5RWpNcUpPbzdQOERnIiwieCI6IklaeXo1WVl6WkpJYWN3R21ockstYXdCa2ZJWmRqbUFWWTViVjFIbGNxYjgifQ", + "proof": { + "signature": "N2GQRLQbDUM7gLlUNweF-JjP9XS1uAWHWZy-8NLdlBdPJFrdvVkZk1z6UntVqATkCZU-l8MMQP_5DyMDzws3DA", + "header": { + "kid": "5M-hw_Im6CjRCgsBUxF_DvilQFx_uU9EjMqJOo7P8Dg", + "alg": "EdDSA" + } + }, + "accessDetails": [ + { + "type": "fixed", + "price": "2.0", + "addressOrId": "0x33d13b46dec069713aa8c3e8b86ee3dd948691ca18ea261b9d464920afa3940f", + "baseToken": { + "address": "0x1B083D8584dd3e6Ff37d04a6e7e82b5F622f3985", + "name": "Ocean Token", + "symbol": "OCEAN", + "decimals": 18 + }, + "datatoken": { + "address": "0x879A899d5DCDa773e3cD8188Af45eAf7194c24d2", + "name": "Access Token", + "symbol": "OEAT", + "decimals": 0 + }, + "paymentCollector": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "templateId": 2, + "isOwned": false, + "validOrderTx": "", + "isPurchasable": true, + "publisherMarketOrderFee": "0" + }, + { + "type": "fixed", + "price": "2.0", + "addressOrId": "0x25b94927c6ab131165d72d45fa3d1fd595ba224e046be56468dbd36dd078ea83", + "baseToken": { + "address": "0x1B083D8584dd3e6Ff37d04a6e7e82b5F622f3985", + "name": "Ocean Token", + "symbol": "OCEAN", + "decimals": 18 + }, + "datatoken": { + "address": "0x18945267E5C9f56f9626206711a31afaCea4Ae6B", + "name": "DataToken", + "symbol": "DT", + "decimals": 0 + }, + "paymentCollector": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "templateId": 2, + "isOwned": false, + "validOrderTx": "", + "isPurchasable": true, + "publisherMarketOrderFee": "0" + }, + { + "type": "fixed", + "price": "5.0", + "addressOrId": "0x4d51f22682adff2aba9854c024de4d0f9abc0943095515cb1cd90b36eba45308", + "baseToken": { + "address": "0x1B083D8584dd3e6Ff37d04a6e7e82b5F622f3985", + "name": "Ocean Token", + "symbol": "OCEAN", + "decimals": 18 + }, + "datatoken": { + "address": "0xb0fd7A05b4de95f3FFd31932515e66B7b92ee96a", + "name": "DataToken", + "symbol": "DT", + "decimals": 0 + }, + "paymentCollector": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "templateId": 2, + "isOwned": false, + "validOrderTx": "", + "isPurchasable": true, + "publisherMarketOrderFee": "0" + } + ] } } }, @@ -121,8 +415,352 @@ "summary": "Download action, returns verification result", "value": { "action": "download", - "policyServer":{ - "sessionId": "" + "serviceId": "ff294c2e2c7d01bd5f9701abc117737917bb1f91044ba6b2d0903fc806db0d65", + "consumerAddress": "0xd727fb9be39fa019d7c02fea19e54d688da3a662", + "policyServer": { + "successRedirectUri": "", + "sessionId": "", + "errorRedirectUri": "", + "responseRedirectUri": "", + "presentationDefinitionUri": "" + }, + "ddo": { + "@context": [ + "https://www.w3.org/ns/credentials/v2" + ], + "id": "did:ope:1ec8435672854acf15ef3e61216900f314f8fae5e04e6b2fb0dc91c0579e0d02", + "version": "5.0.0", + "credentialSubject": { + "credentials": { + "allow": [ + { + "values": [ + { + "request_credentials": [ + { + "format": "jwt_vc_json", + "policies": [], + "type": "UniversityDegree" + } + ], + "vc_policies": [ + "signature", + "not-before", + "revoked-status-list" + ], + "vp_policies": [] + } + ], + "type": "SSIpolicy" + }, + { + "values": [ + "*" + ], + "type": "address" + } + ], + "deny": [], + "match_deny": "any" + }, + "chainId": 11155111, + "metadata": { + "created": "2025-04-15T19:48:54Z", + "updated": "2025-04-15T19:48:54Z", + "type": "dataset", + "name": "Test data set with SSI credentials - 8", + "description": { + "@value": "Test data set with SSI credentials - 8\n\nAccess to the asset allowed to UniversityDegree holders.", + "@direction": "", + "@language": "" + }, + "tags": [ + "test" + ], + "author": "", + "license": { + "name": "https://github.com/MBadea17/testdata/blob/af26d4f968fdb6e1882c2a3cca16a1480ca44a9c/License%20Agreement.pdf", + "licenseDocuments": [ + { + "sha256": "7939fa7e4201a373a3471feccd878c026cad50cc7d4308b6849741782b0691f7", + "mirrors": [ + { + "method": "get", + "type": "url", + "url": "https://github.com/MBadea17/testdata/blob/af26d4f968fdb6e1882c2a3cca16a1480ca44a9c/License%20Agreement.pdf" + } + ], + "name": "https://github.com/MBadea17/testdata/blob/af26d4f968fdb6e1882c2a3cca16a1480ca44a9c/License%20Agreement.pdf", + "fileType": "text/html; charset=utf-8" + } + ] + }, + "links": {}, + "additionalInformation": { + "termsAndConditions": true + }, + "copyrightHolder": "", + "providedBy": "" + }, + "services": [ + { + "credentials": { + "allow": [ + { + "values": [ + { + "request_credentials": [], + "vc_policies": [ + "signature", + "not-before", + "revoked-status-list" + ], + "vp_policies": [] + } + ], + "type": "SSIpolicy" + }, + { + "values": [ + "0xd727fb9be39fa019d7c02fea19e54d688da3a662" + ], + "type": "address" + } + ], + "match_deny": "any", + "deny": [] + }, + "name": "Service 1", + "files": "0x048d5a53cfc89686e6cde36df81495a3eda3c42044240254fbd7b93e7e11b1272e867e448a64d639d490bdbb9f3a45be4d0a54d53a3b92317c57ca270899dc29b97d3f0fe6fba6a40d3d9ff1438bf8c36732af3b47c4056486e8fcd7dcddba2038bce7fb24ec4e28ae889a4556e61dbeda5344a102d60c88dfbc1fe84e983a0d360c269705071c9191f982c832c93b39d91c688787af4d55b55e5afb38e0ad8eb5d92bd7a51935daa8d4e394436b6f911883516c7f3c875753673acab80859664f5dde7ce7279d7948c5987271989aacd261751cb933e454919f0cd4f1e075e0d138c5425a4fae3e1046b7fdbe8aec7b29bacac922a01f3b37203b67c0ac0a246ac0b0acdb2b5d21c1723daeae63555847c0e9e4b54f9bad995ba8b9098616f82b5a89d9c21fbac07035530a6010ca9ccc94f7474397701b8df23496206402d670cebd2964a8b4a0f45039cf3519b4ce30ae5b0744b22a5ceb5e9db0a8c8431d19356f9284cac87da509da54bc85b1811878bd72de38adbad9b9945a1cf6272d5ed877e498a1", + "description": { + "@value": "Service accessible to address 0xD727FB9Be39fA019d7C02fea19E54d688Da3a662", + "@direction": "", + "@language": "" + }, + "id": "7b2ca00f457ecc21eff766d39f2f35e1ee5e5d427eb3f62aa7297080388eeff6", + "datatokenAddress": "0x879A899d5DCDa773e3cD8188Af45eAf7194c24d2", + "serviceEndpoint": "https://ocean-node-vm3.oceanenterprise.io", + "state": 0, + "type": "access", + "timeout": 0 + }, + { + "credentials": { + "allow": [ + { + "values": [ + { + "request_credentials": [ + { + "format": "jwt_vc_json", + "policies": [], + "type": "VerifiableId" + } + ], + "vc_policies": [ + "signature", + "not-before", + "revoked-status-list" + ], + "vp_policies": [] + } + ], + "type": "SSIpolicy" + } + ], + "match_deny": "any", + "deny": [] + }, + "name": "Service 2", + "description": { + "@value": "Service accessible to holder of VerifiableId credentials", + "@direction": "", + "@language": "" + }, + "files": "0x048d5a53cfc89686e6cde36df81495a3eda3c42044240254fbd7b93e7e11b1272e867e448a64d639d490bdbb9f3a45be4d0a54d53a3b92317c57ca270899dc29b97d3f0fe6fba6a40d3d9ff1438bf8c36732af3b47c4056486e8fcd7dcddba2038bce7fb24ec4e28ae889a4556e61dbeda5344a102d60c88dfbc1fe84e983a0d360c269705071c9191f982c832c93b39d91c688787af4d55b55e5afb38e0ad8eb5d92bd7a51935daa8d4e394436b6f911883516c7f3c875753673acab80859664f5dde7ce7279d7948c5987271989aacd261751cb933e454919f0cd4f1e075e0d138c5425a4fae3e1046b7fdbe8aec7b29bacac922a01f3b37203b67c0ac0a246ac0b0acdb2b5d21c1723daeae63555847c0e9e4b54f9bad995ba8b9098616f82b5a89d9c21fbac07035530a6010ca9ccc94f7474397701b8df23496206402d670cebd2964a8b4a0f45039cf3519b4ce30ae5b0744b22a5ceb5e9db0a8c8431d19356f9284cac87da509da54bc85b1811878bd72de38adbad9b9945a1cf6272d5ed877e498a1", + "id": "ff294c2e2c7d01bd5f9701abc117737917bb1f91044ba6b2d0903fc806db0d65", + "datatokenAddress": "0x18945267E5C9f56f9626206711a31afaCea4Ae6B", + "serviceEndpoint": "https://ocean-node-vm3.oceanenterprise.io", + "state": 0, + "type": "access", + "timeout": 86400 + }, + { + "credentials": { + "allow": [ + { + "values": [ + { + "request_credentials": [], + "vc_policies": [ + "signature", + "not-before", + "revoked-status-list" + ], + "vp_policies": [] + } + ], + "type": "SSIpolicy" + } + ], + "match_deny": "any", + "deny": [ + { + "values": [ + "0x61db12d8b636cb49ea09eca58a893da9480e1f33" + ], + "type": "address" + } + ] + }, + "name": "Service 3", + "description": { + "@value": "Service not accessible to address 0x61DB12d8b636Cb49ea09eCa58a893dA9480E1F33", + "@direction": "", + "@language": "" + }, + "files": "0x048d5a53cfc89686e6cde36df81495a3eda3c42044240254fbd7b93e7e11b1272e867e448a64d639d490bdbb9f3a45be4d0a54d53a3b92317c57ca270899dc29b97d3f0fe6fba6a40d3d9ff1438bf8c36732af3b47c4056486e8fcd7dcddba2038bce7fb24ec4e28ae889a4556e61dbeda5344a102d60c88dfbc1fe84e983a0d360c269705071c9191f982c832c93b39d91c688787af4d55b55e5afb38e0ad8eb5d92bd7a51935daa8d4e394436b6f911883516c7f3c875753673acab80859664f5dde7ce7279d7948c5987271989aacd261751cb933e454919f0cd4f1e075e0d138c5425a4fae3e1046b7fdbe8aec7b29bacac922a01f3b37203b67c0ac0a246ac0b0acdb2b5d21c1723daeae63555847c0e9e4b54f9bad995ba8b9098616f82b5a89d9c21fbac07035530a6010ca9ccc94f7474397701b8df23496206402d670cebd2964a8b4a0f45039cf3519b4ce30ae5b0744b22a5ceb5e9db0a8c8431d19356f9284cac87da509da54bc85b1811878bd72de38adbad9b9945a1cf6272d5ed877e498a1", + "id": "91d8c931ed389ae61d3ed18f89f1db41db381a706b54bfaa1db15f114a2a9cd8", + "datatokenAddress": "0xb0fd7A05b4de95f3FFd31932515e66B7b92ee96a", + "serviceEndpoint": "https://ocean-node-vm3.oceanenterprise.io", + "state": 0, + "type": "access", + "timeout": 86400 + } + ], + "nftAddress": "0x09e939308A16e1B27088bbf6932D91EC8b5F42b8", + "nft": { + "state": 0, + "address": "0x09e939308A16e1B27088bbf6932D91EC8b5F42b8", + "name": "Data NFT", + "symbol": "OEC-NFT", + "owner": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "created": "2025-04-15T19:59:24Z", + "tokenURI": "" + }, + "stats": { + "allocated": 0, + "orders": 0, + "price": { + "value": 2, + "tokenSymbol": "OCEAN", + "tokenAddress": "0x1B083D8584dd3e6Ff37d04a6e7e82b5F622f3985" + } + }, + "datatokens": [ + { + "symbol": "DT1", + "address": "0x879A899d5DCDa773e3cD8188Af45eAf7194c24d2", + "name": "Datatoken", + "serviceId": "7b2ca00f457ecc21eff766d39f2f35e1ee5e5d427eb3f62aa7297080388eeff6" + }, + { + "symbol": "DT1", + "address": "0x18945267E5C9f56f9626206711a31afaCea4Ae6B", + "name": "Datatoken", + "serviceId": "ff294c2e2c7d01bd5f9701abc117737917bb1f91044ba6b2d0903fc806db0d65" + }, + { + "symbol": "DT1", + "address": "0xb0fd7A05b4de95f3FFd31932515e66B7b92ee96a", + "name": "Datatoken", + "serviceId": "91d8c931ed389ae61d3ed18f89f1db41db381a706b54bfaa1db15f114a2a9cd8" + } + ], + "event": { + "txid": "0x71a250f4992c28f09ed9170b70732874eafeef8e8da653faab1c0258ea45fc98", + "from": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "contract": "0x09e939308A16e1B27088bbf6932D91EC8b5F42b8", + "block": 8125544, + "datetime": "2025-04-15T19:59:24.000Z" + }, + "purgatory": { + "state": false + } + }, + "additionalDdos": [], + "type": [ + "VerifiableCredential" + ], + "issuer": "did:jwk:eyJrdHkiOiJPS1AiLCJkIjoiUnBVOU5ONmdFbGtvMXpjYnR1VVRERlVXWEZCeks1Um9FZ3FRaVFHMWN4QSIsImNydiI6IkVkMjU1MTkiLCJraWQiOiI1TS1od19JbTZDalJDZ3NCVXhGX0R2aWxRRnhfdVU5RWpNcUpPbzdQOERnIiwieCI6IklaeXo1WVl6WkpJYWN3R21ockstYXdCa2ZJWmRqbUFWWTViVjFIbGNxYjgifQ", + "proof": { + "signature": "N2GQRLQbDUM7gLlUNweF-JjP9XS1uAWHWZy-8NLdlBdPJFrdvVkZk1z6UntVqATkCZU-l8MMQP_5DyMDzws3DA", + "header": { + "kid": "5M-hw_Im6CjRCgsBUxF_DvilQFx_uU9EjMqJOo7P8Dg", + "alg": "EdDSA" + } + }, + "accessDetails": [ + { + "type": "fixed", + "price": "2.0", + "addressOrId": "0x33d13b46dec069713aa8c3e8b86ee3dd948691ca18ea261b9d464920afa3940f", + "baseToken": { + "address": "0x1B083D8584dd3e6Ff37d04a6e7e82b5F622f3985", + "name": "Ocean Token", + "symbol": "OCEAN", + "decimals": 18 + }, + "datatoken": { + "address": "0x879A899d5DCDa773e3cD8188Af45eAf7194c24d2", + "name": "Access Token", + "symbol": "OEAT", + "decimals": 0 + }, + "paymentCollector": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "templateId": 2, + "isOwned": false, + "validOrderTx": "", + "isPurchasable": true, + "publisherMarketOrderFee": "0" + }, + { + "type": "fixed", + "price": "2.0", + "addressOrId": "0x25b94927c6ab131165d72d45fa3d1fd595ba224e046be56468dbd36dd078ea83", + "baseToken": { + "address": "0x1B083D8584dd3e6Ff37d04a6e7e82b5F622f3985", + "name": "Ocean Token", + "symbol": "OCEAN", + "decimals": 18 + }, + "datatoken": { + "address": "0x18945267E5C9f56f9626206711a31afaCea4Ae6B", + "name": "DataToken", + "symbol": "DT", + "decimals": 0 + }, + "paymentCollector": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "templateId": 2, + "isOwned": false, + "validOrderTx": "", + "isPurchasable": true, + "publisherMarketOrderFee": "0" + }, + { + "type": "fixed", + "price": "5.0", + "addressOrId": "0x4d51f22682adff2aba9854c024de4d0f9abc0943095515cb1cd90b36eba45308", + "baseToken": { + "address": "0x1B083D8584dd3e6Ff37d04a6e7e82b5F622f3985", + "name": "Ocean Token", + "symbol": "OCEAN", + "decimals": 18 + }, + "datatoken": { + "address": "0xb0fd7A05b4de95f3FFd31932515e66B7b92ee96a", + "name": "DataToken", + "symbol": "DT", + "decimals": 0 + }, + "paymentCollector": "0x00Dc9e712D3b31Ab5446A5A7CeaDe0a2901E6d26", + "templateId": 2, + "isOwned": false, + "validOrderTx": "", + "isPurchasable": true, + "publisherMarketOrderFee": "0" + } + ] } } },