@enbox/agent 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.js +2215 -0
- package/dist/browser.js.map +7 -0
- package/dist/browser.mjs +2215 -0
- package/dist/browser.mjs.map +7 -0
- package/dist/cjs/index.js +8530 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/agent-did-resolver-cache.js +87 -0
- package/dist/esm/agent-did-resolver-cache.js.map +1 -0
- package/dist/esm/bearer-identity.js +41 -0
- package/dist/esm/bearer-identity.js.map +1 -0
- package/dist/esm/connect.js +191 -0
- package/dist/esm/connect.js.map +1 -0
- package/dist/esm/crypto-api.js +346 -0
- package/dist/esm/crypto-api.js.map +1 -0
- package/dist/esm/did-api.js +278 -0
- package/dist/esm/did-api.js.map +1 -0
- package/dist/esm/dwn-api.js +336 -0
- package/dist/esm/dwn-api.js.map +1 -0
- package/dist/esm/dwn-registrar.js +120 -0
- package/dist/esm/dwn-registrar.js.map +1 -0
- package/dist/esm/hd-identity-vault.js +729 -0
- package/dist/esm/hd-identity-vault.js.map +1 -0
- package/dist/esm/identity-api.js +262 -0
- package/dist/esm/identity-api.js.map +1 -0
- package/dist/esm/index.js +23 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/local-key-manager.js +498 -0
- package/dist/esm/local-key-manager.js.map +1 -0
- package/dist/esm/oidc.js +507 -0
- package/dist/esm/oidc.js.map +1 -0
- package/dist/esm/permissions-api.js +322 -0
- package/dist/esm/permissions-api.js.map +1 -0
- package/dist/esm/prototyping/clients/dwn-rpc-types.js +2 -0
- package/dist/esm/prototyping/clients/dwn-rpc-types.js.map +1 -0
- package/dist/esm/prototyping/clients/dwn-server-info-cache-memory.js +74 -0
- package/dist/esm/prototyping/clients/dwn-server-info-cache-memory.js.map +1 -0
- package/dist/esm/prototyping/clients/http-dwn-rpc-client.js +105 -0
- package/dist/esm/prototyping/clients/http-dwn-rpc-client.js.map +1 -0
- package/dist/esm/prototyping/clients/json-rpc-socket.js +150 -0
- package/dist/esm/prototyping/clients/json-rpc-socket.js.map +1 -0
- package/dist/esm/prototyping/clients/json-rpc.js +58 -0
- package/dist/esm/prototyping/clients/json-rpc.js.map +1 -0
- package/dist/esm/prototyping/clients/server-info-types.js +2 -0
- package/dist/esm/prototyping/clients/server-info-types.js.map +1 -0
- package/dist/esm/prototyping/clients/web-socket-clients.js +90 -0
- package/dist/esm/prototyping/clients/web-socket-clients.js.map +1 -0
- package/dist/esm/prototyping/common/object.js +14 -0
- package/dist/esm/prototyping/common/object.js.map +1 -0
- package/dist/esm/prototyping/common/type-utils.js +2 -0
- package/dist/esm/prototyping/common/type-utils.js.map +1 -0
- package/dist/esm/prototyping/crypto/algorithms/aes-gcm.js +147 -0
- package/dist/esm/prototyping/crypto/algorithms/aes-gcm.js.map +1 -0
- package/dist/esm/prototyping/crypto/algorithms/aes-kw.js +137 -0
- package/dist/esm/prototyping/crypto/algorithms/aes-kw.js.map +1 -0
- package/dist/esm/prototyping/crypto/algorithms/ecdsa.js +307 -0
- package/dist/esm/prototyping/crypto/algorithms/ecdsa.js.map +1 -0
- package/dist/esm/prototyping/crypto/algorithms/eddsa.js +264 -0
- package/dist/esm/prototyping/crypto/algorithms/eddsa.js.map +1 -0
- package/dist/esm/prototyping/crypto/algorithms/hkdf.js +39 -0
- package/dist/esm/prototyping/crypto/algorithms/hkdf.js.map +1 -0
- package/dist/esm/prototyping/crypto/algorithms/pbkdf2.js +41 -0
- package/dist/esm/prototyping/crypto/algorithms/pbkdf2.js.map +1 -0
- package/dist/esm/prototyping/crypto/crypto-error.js +41 -0
- package/dist/esm/prototyping/crypto/crypto-error.js.map +1 -0
- package/dist/esm/prototyping/crypto/dsa.js +236 -0
- package/dist/esm/prototyping/crypto/dsa.js.map +1 -0
- package/dist/esm/prototyping/crypto/jose/jwe-compact.js +130 -0
- package/dist/esm/prototyping/crypto/jose/jwe-compact.js.map +1 -0
- package/dist/esm/prototyping/crypto/jose/jwe-flattened.js +294 -0
- package/dist/esm/prototyping/crypto/jose/jwe-flattened.js.map +1 -0
- package/dist/esm/prototyping/crypto/jose/jwe.js +308 -0
- package/dist/esm/prototyping/crypto/jose/jwe.js.map +1 -0
- package/dist/esm/prototyping/crypto/primitives/aes-gcm.js +352 -0
- package/dist/esm/prototyping/crypto/primitives/aes-gcm.js.map +1 -0
- package/dist/esm/prototyping/crypto/primitives/aes-kw.js +247 -0
- package/dist/esm/prototyping/crypto/primitives/aes-kw.js.map +1 -0
- package/dist/esm/prototyping/crypto/primitives/hkdf.js +80 -0
- package/dist/esm/prototyping/crypto/primitives/hkdf.js.map +1 -0
- package/dist/esm/prototyping/crypto/primitives/pbkdf2.js +85 -0
- package/dist/esm/prototyping/crypto/primitives/pbkdf2.js.map +1 -0
- package/dist/esm/prototyping/crypto/types/cipher.js +2 -0
- package/dist/esm/prototyping/crypto/types/cipher.js.map +1 -0
- package/dist/esm/prototyping/crypto/types/crypto-api.js +2 -0
- package/dist/esm/prototyping/crypto/types/crypto-api.js.map +1 -0
- package/dist/esm/prototyping/crypto/types/key-converter.js +2 -0
- package/dist/esm/prototyping/crypto/types/key-converter.js.map +1 -0
- package/dist/esm/prototyping/crypto/types/key-deriver.js +2 -0
- package/dist/esm/prototyping/crypto/types/key-deriver.js.map +1 -0
- package/dist/esm/prototyping/crypto/types/key-io.js +2 -0
- package/dist/esm/prototyping/crypto/types/key-io.js.map +1 -0
- package/dist/esm/prototyping/crypto/types/key-manager.js +2 -0
- package/dist/esm/prototyping/crypto/types/key-manager.js.map +1 -0
- package/dist/esm/prototyping/crypto/types/key-wrapper.js +2 -0
- package/dist/esm/prototyping/crypto/types/key-wrapper.js.map +1 -0
- package/dist/esm/prototyping/crypto/types/params-direct.js +2 -0
- package/dist/esm/prototyping/crypto/types/params-direct.js.map +1 -0
- package/dist/esm/prototyping/crypto/types/params-kms.js +2 -0
- package/dist/esm/prototyping/crypto/types/params-kms.js.map +1 -0
- package/dist/esm/prototyping/crypto/utils.js +19 -0
- package/dist/esm/prototyping/crypto/utils.js.map +1 -0
- package/dist/esm/prototyping/dids/resolver-cache-memory.js +77 -0
- package/dist/esm/prototyping/dids/resolver-cache-memory.js.map +1 -0
- package/dist/esm/prototyping/dids/utils.js +9 -0
- package/dist/esm/prototyping/dids/utils.js.map +1 -0
- package/dist/esm/rpc-client.js +123 -0
- package/dist/esm/rpc-client.js.map +1 -0
- package/dist/esm/store-data-protocols.js +38 -0
- package/dist/esm/store-data-protocols.js.map +1 -0
- package/dist/esm/store-data.js +320 -0
- package/dist/esm/store-data.js.map +1 -0
- package/dist/esm/store-did.js +136 -0
- package/dist/esm/store-did.js.map +1 -0
- package/dist/esm/store-identity.js +140 -0
- package/dist/esm/store-identity.js.map +1 -0
- package/dist/esm/store-key.js +136 -0
- package/dist/esm/store-key.js.map +1 -0
- package/dist/esm/sync-api.js +61 -0
- package/dist/esm/sync-api.js.map +1 -0
- package/dist/esm/sync-engine-level.js +618 -0
- package/dist/esm/sync-engine-level.js.map +1 -0
- package/dist/esm/test-harness.js +239 -0
- package/dist/esm/test-harness.js.map +1 -0
- package/dist/esm/types/agent.js +2 -0
- package/dist/esm/types/agent.js.map +1 -0
- package/dist/esm/types/dwn.js +31 -0
- package/dist/esm/types/dwn.js.map +1 -0
- package/dist/esm/types/identity-vault.js +2 -0
- package/dist/esm/types/identity-vault.js.map +1 -0
- package/dist/esm/types/identity.js +2 -0
- package/dist/esm/types/identity.js.map +1 -0
- package/dist/esm/types/key-manager.js +2 -0
- package/dist/esm/types/key-manager.js.map +1 -0
- package/dist/esm/types/permissions.js +2 -0
- package/dist/esm/types/permissions.js.map +1 -0
- package/dist/esm/types/sync.js +2 -0
- package/dist/esm/types/sync.js.map +1 -0
- package/dist/esm/types/vc.js +5 -0
- package/dist/esm/types/vc.js.map +1 -0
- package/dist/esm/utils-internal.js +147 -0
- package/dist/esm/utils-internal.js.map +1 -0
- package/dist/esm/utils.js +161 -0
- package/dist/esm/utils.js.map +1 -0
- package/dist/types/agent-did-resolver-cache.d.ts +30 -0
- package/dist/types/agent-did-resolver-cache.d.ts.map +1 -0
- package/dist/types/bearer-identity.d.ts +31 -0
- package/dist/types/bearer-identity.d.ts.map +1 -0
- package/dist/types/connect.d.ts +88 -0
- package/dist/types/connect.d.ts.map +1 -0
- package/dist/types/crypto-api.d.ts +286 -0
- package/dist/types/crypto-api.d.ts.map +1 -0
- package/dist/types/did-api.d.ts +119 -0
- package/dist/types/did-api.d.ts.map +1 -0
- package/dist/types/dwn-api.d.ts +66 -0
- package/dist/types/dwn-api.d.ts.map +1 -0
- package/dist/types/dwn-registrar.d.ts +29 -0
- package/dist/types/dwn-registrar.d.ts.map +1 -0
- package/dist/types/hd-identity-vault.d.ts +306 -0
- package/dist/types/hd-identity-vault.d.ts.map +1 -0
- package/dist/types/identity-api.d.ts +107 -0
- package/dist/types/identity-api.d.ts.map +1 -0
- package/dist/types/index.d.ts +30 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/local-key-manager.d.ts +311 -0
- package/dist/types/local-key-manager.d.ts.map +1 -0
- package/dist/types/oidc.d.ts +247 -0
- package/dist/types/oidc.d.ts.map +1 -0
- package/dist/types/permissions-api.d.ts +35 -0
- package/dist/types/permissions-api.d.ts.map +1 -0
- package/dist/types/prototyping/clients/dwn-rpc-types.d.ts +45 -0
- package/dist/types/prototyping/clients/dwn-rpc-types.d.ts.map +1 -0
- package/dist/types/prototyping/clients/dwn-server-info-cache-memory.d.ts +57 -0
- package/dist/types/prototyping/clients/dwn-server-info-cache-memory.d.ts.map +1 -0
- package/dist/types/prototyping/clients/http-dwn-rpc-client.d.ts +13 -0
- package/dist/types/prototyping/clients/http-dwn-rpc-client.d.ts.map +1 -0
- package/dist/types/prototyping/clients/json-rpc-socket.d.ts +43 -0
- package/dist/types/prototyping/clients/json-rpc-socket.d.ts.map +1 -0
- package/dist/types/prototyping/clients/json-rpc.d.ts +49 -0
- package/dist/types/prototyping/clients/json-rpc.d.ts.map +1 -0
- package/dist/types/prototyping/clients/server-info-types.d.ts +20 -0
- package/dist/types/prototyping/clients/server-info-types.d.ts.map +1 -0
- package/dist/types/prototyping/clients/web-socket-clients.d.ts +10 -0
- package/dist/types/prototyping/clients/web-socket-clients.d.ts.map +1 -0
- package/dist/types/prototyping/common/object.d.ts +2 -0
- package/dist/types/prototyping/common/object.d.ts.map +1 -0
- package/dist/types/prototyping/common/type-utils.d.ts +7 -0
- package/dist/types/prototyping/common/type-utils.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/algorithms/aes-gcm.d.ts +151 -0
- package/dist/types/prototyping/crypto/algorithms/aes-gcm.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/algorithms/aes-kw.d.ts +109 -0
- package/dist/types/prototyping/crypto/algorithms/aes-kw.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/algorithms/ecdsa.d.ts +160 -0
- package/dist/types/prototyping/crypto/algorithms/ecdsa.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/algorithms/eddsa.d.ts +157 -0
- package/dist/types/prototyping/crypto/algorithms/eddsa.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/algorithms/hkdf.d.ts +21 -0
- package/dist/types/prototyping/crypto/algorithms/hkdf.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/algorithms/pbkdf2.d.ts +21 -0
- package/dist/types/prototyping/crypto/algorithms/pbkdf2.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/crypto-error.d.ts +29 -0
- package/dist/types/prototyping/crypto/crypto-error.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/dsa.d.ts +169 -0
- package/dist/types/prototyping/crypto/dsa.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/jose/jwe-compact.d.ts +135 -0
- package/dist/types/prototyping/crypto/jose/jwe-compact.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/jose/jwe-flattened.d.ts +134 -0
- package/dist/types/prototyping/crypto/jose/jwe-flattened.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/jose/jwe.d.ts +378 -0
- package/dist/types/prototyping/crypto/jose/jwe.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/primitives/aes-gcm.d.ts +245 -0
- package/dist/types/prototyping/crypto/primitives/aes-gcm.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/primitives/aes-kw.d.ts +103 -0
- package/dist/types/prototyping/crypto/primitives/aes-kw.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/primitives/hkdf.d.ts +90 -0
- package/dist/types/prototyping/crypto/primitives/hkdf.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/primitives/pbkdf2.d.ts +84 -0
- package/dist/types/prototyping/crypto/primitives/pbkdf2.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/types/cipher.d.ts +14 -0
- package/dist/types/prototyping/crypto/types/cipher.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/types/crypto-api.d.ts +35 -0
- package/dist/types/prototyping/crypto/types/crypto-api.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/types/key-converter.d.ts +49 -0
- package/dist/types/prototyping/crypto/types/key-converter.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/types/key-deriver.d.ts +50 -0
- package/dist/types/prototyping/crypto/types/key-deriver.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/types/key-io.d.ts +49 -0
- package/dist/types/prototyping/crypto/types/key-io.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/types/key-manager.d.ts +69 -0
- package/dist/types/prototyping/crypto/types/key-manager.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/types/key-wrapper.d.ts +14 -0
- package/dist/types/prototyping/crypto/types/key-wrapper.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/types/params-direct.d.ts +75 -0
- package/dist/types/prototyping/crypto/types/params-direct.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/types/params-kms.d.ts +63 -0
- package/dist/types/prototyping/crypto/types/params-kms.d.ts.map +1 -0
- package/dist/types/prototyping/crypto/utils.d.ts +7 -0
- package/dist/types/prototyping/crypto/utils.d.ts.map +1 -0
- package/dist/types/prototyping/dids/resolver-cache-memory.d.ts +57 -0
- package/dist/types/prototyping/dids/resolver-cache-memory.d.ts.map +1 -0
- package/dist/types/prototyping/dids/utils.d.ts +3 -0
- package/dist/types/prototyping/dids/utils.d.ts.map +1 -0
- package/dist/types/rpc-client.d.ts +51 -0
- package/dist/types/rpc-client.d.ts.map +1 -0
- package/dist/types/store-data-protocols.d.ts +4 -0
- package/dist/types/store-data-protocols.d.ts.map +1 -0
- package/dist/types/store-data.d.ts +95 -0
- package/dist/types/store-data.d.ts.map +1 -0
- package/dist/types/store-did.d.ts +33 -0
- package/dist/types/store-did.d.ts.map +1 -0
- package/dist/types/store-identity.d.ts +34 -0
- package/dist/types/store-identity.d.ts.map +1 -0
- package/dist/types/store-key.d.ts +32 -0
- package/dist/types/store-key.d.ts.map +1 -0
- package/dist/types/sync-api.d.ts +41 -0
- package/dist/types/sync-api.d.ts.map +1 -0
- package/dist/types/sync-engine-level.d.ts +85 -0
- package/dist/types/sync-engine-level.d.ts.map +1 -0
- package/dist/types/test-harness.d.ts +69 -0
- package/dist/types/test-harness.d.ts.map +1 -0
- package/dist/types/types/agent.d.ts +172 -0
- package/dist/types/types/agent.d.ts.map +1 -0
- package/dist/types/types/dwn.d.ts +178 -0
- package/dist/types/types/dwn.d.ts.map +1 -0
- package/dist/types/types/identity-vault.d.ts +129 -0
- package/dist/types/types/identity-vault.d.ts.map +1 -0
- package/dist/types/types/identity.d.ts +16 -0
- package/dist/types/types/identity.d.ts.map +1 -0
- package/dist/types/types/key-manager.d.ts +9 -0
- package/dist/types/types/key-manager.d.ts.map +1 -0
- package/dist/types/types/permissions.d.ts +98 -0
- package/dist/types/types/permissions.d.ts.map +1 -0
- package/dist/types/types/sync.d.ts +66 -0
- package/dist/types/types/sync.d.ts.map +1 -0
- package/dist/types/types/vc.d.ts +7 -0
- package/dist/types/types/vc.d.ts.map +1 -0
- package/dist/types/utils-internal.d.ts +50 -0
- package/dist/types/utils-internal.d.ts.map +1 -0
- package/dist/types/utils.d.ts +37 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/package.json +112 -0
- package/src/agent-did-resolver-cache.ts +95 -0
- package/src/bearer-identity.ts +42 -0
- package/src/connect.ts +296 -0
- package/src/crypto-api.ts +593 -0
- package/src/did-api.ts +429 -0
- package/src/dwn-api.ts +462 -0
- package/src/dwn-registrar.ts +127 -0
- package/src/hd-identity-vault.ts +853 -0
- package/src/identity-api.ts +324 -0
- package/src/index.ts +30 -0
- package/src/local-key-manager.ts +672 -0
- package/src/oidc.ts +857 -0
- package/src/permissions-api.ts +408 -0
- package/src/prototyping/clients/dwn-rpc-types.ts +55 -0
- package/src/prototyping/clients/dwn-server-info-cache-memory.ts +79 -0
- package/src/prototyping/clients/http-dwn-rpc-client.ts +110 -0
- package/src/prototyping/clients/json-rpc-socket.ts +169 -0
- package/src/prototyping/clients/json-rpc.ts +113 -0
- package/src/prototyping/clients/server-info-types.ts +21 -0
- package/src/prototyping/clients/web-socket-clients.ts +100 -0
- package/src/prototyping/common/object.ts +15 -0
- package/src/prototyping/common/type-utils.ts +6 -0
- package/src/prototyping/crypto/algorithms/aes-gcm.ts +211 -0
- package/src/prototyping/crypto/algorithms/aes-kw.ts +164 -0
- package/src/prototyping/crypto/algorithms/ecdsa.ts +365 -0
- package/src/prototyping/crypto/algorithms/eddsa.ts +310 -0
- package/src/prototyping/crypto/algorithms/hkdf.ts +40 -0
- package/src/prototyping/crypto/algorithms/pbkdf2.ts +44 -0
- package/src/prototyping/crypto/crypto-error.ts +45 -0
- package/src/prototyping/crypto/dsa.ts +367 -0
- package/src/prototyping/crypto/jose/jwe-compact.ts +225 -0
- package/src/prototyping/crypto/jose/jwe-flattened.ts +459 -0
- package/src/prototyping/crypto/jose/jwe.ts +653 -0
- package/src/prototyping/crypto/primitives/aes-gcm.ts +374 -0
- package/src/prototyping/crypto/primitives/aes-kw.ts +271 -0
- package/src/prototyping/crypto/primitives/hkdf.ts +121 -0
- package/src/prototyping/crypto/primitives/pbkdf2.ts +116 -0
- package/src/prototyping/crypto/types/cipher.ts +17 -0
- package/src/prototyping/crypto/types/crypto-api.ts +78 -0
- package/src/prototyping/crypto/types/key-converter.ts +53 -0
- package/src/prototyping/crypto/types/key-deriver.ts +56 -0
- package/src/prototyping/crypto/types/key-io.ts +51 -0
- package/src/prototyping/crypto/types/key-manager.ts +83 -0
- package/src/prototyping/crypto/types/key-wrapper.ts +17 -0
- package/src/prototyping/crypto/types/params-direct.ts +95 -0
- package/src/prototyping/crypto/types/params-kms.ts +76 -0
- package/src/prototyping/crypto/utils.ts +41 -0
- package/src/prototyping/dids/resolver-cache-memory.ts +83 -0
- package/src/prototyping/dids/utils.ts +10 -0
- package/src/rpc-client.ts +162 -0
- package/src/store-data-protocols.ts +40 -0
- package/src/store-data.ts +400 -0
- package/src/store-did.ts +105 -0
- package/src/store-identity.ts +109 -0
- package/src/store-key.ts +104 -0
- package/src/sync-api.ts +71 -0
- package/src/sync-engine-level.ts +714 -0
- package/src/test-harness.ts +330 -0
- package/src/types/agent.ts +195 -0
- package/src/types/dwn.ts +278 -0
- package/src/types/identity-vault.ts +137 -0
- package/src/types/identity.ts +18 -0
- package/src/types/key-manager.ts +15 -0
- package/src/types/permissions.ts +115 -0
- package/src/types/sync.ts +58 -0
- package/src/types/vc.ts +7 -0
- package/src/utils-internal.ts +157 -0
- package/src/utils.ts +181 -0
package/src/oidc.ts
ADDED
|
@@ -0,0 +1,857 @@
|
|
|
1
|
+
import { Convert, logger, RequireOnly } from '@enbox/common';
|
|
2
|
+
import {
|
|
3
|
+
Ed25519,
|
|
4
|
+
EdDsaAlgorithm,
|
|
5
|
+
JoseHeaderParams,
|
|
6
|
+
Jwk,
|
|
7
|
+
Sha256,
|
|
8
|
+
X25519,
|
|
9
|
+
CryptoUtils,
|
|
10
|
+
} from '@enbox/crypto';
|
|
11
|
+
import { concatenateUrl } from './utils.js';
|
|
12
|
+
import { xchacha20poly1305 } from '@noble/ciphers/chacha';
|
|
13
|
+
import type { ConnectPermissionRequest } from './connect.js';
|
|
14
|
+
import { DidDocument, DidJwk, PortableDid, type BearerDid } from '@enbox/dids';
|
|
15
|
+
import { DwnDataEncodedRecordsWriteMessage, DwnInterface, DwnPermissionScope, DwnProtocolDefinition } from './types/dwn.js';
|
|
16
|
+
import { AgentPermissionsApi } from './permissions-api.js';
|
|
17
|
+
import type { Web5Agent } from './types/agent.js';
|
|
18
|
+
import { isRecordPermissionScope } from './dwn-api.js';
|
|
19
|
+
import { DwnInterfaceName, DwnMethodName } from '@enbox/dwn-sdk-js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Sent to an OIDC server to authorize a client. Allows clients
|
|
23
|
+
* to securely send authorization request parameters directly to
|
|
24
|
+
* the server via POST. This avoids exposing sensitive data in URLs
|
|
25
|
+
* and ensures the server validates the request before user interaction.
|
|
26
|
+
*
|
|
27
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc9126.html | OAuth 2.0 Pushed Authorization Requests}
|
|
28
|
+
*/
|
|
29
|
+
export type PushedAuthRequest = {
|
|
30
|
+
/** The JWT which contains the {@link Web5ConnectAuthRequest} */
|
|
31
|
+
request: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Sent back by OIDC server in response to {@link PushedAuthRequest}
|
|
36
|
+
* The server generates a TTL and a unique request_uri. The request_uri can be shared
|
|
37
|
+
* with the Provider using a link or a QR code along with additional params
|
|
38
|
+
* to access the url and decrypt the payload.
|
|
39
|
+
*/
|
|
40
|
+
export type PushedAuthResponse = {
|
|
41
|
+
request_uri: string;
|
|
42
|
+
expires_in: number;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Used in decentralized apps. The SIOPv2 Auth Request is created by a client relying party (RP)
|
|
47
|
+
* often a web service or an app who wants to obtain information from a provider
|
|
48
|
+
* The contents of this are inserted into a JWT inside of the {@link PushedAuthRequest}.
|
|
49
|
+
* @see {@link https://github.com/TBD54566975/known-customer-credential | TBD OIDC Documentation for SIOPv2 }
|
|
50
|
+
*/
|
|
51
|
+
export type SIOPv2AuthRequest = {
|
|
52
|
+
/** The DID of the client (RP) */
|
|
53
|
+
client_id: string;
|
|
54
|
+
|
|
55
|
+
/** The scope of the access request (e.g., `openid profile`). */
|
|
56
|
+
scope: string;
|
|
57
|
+
|
|
58
|
+
/** The type of response desired (e.g. `id_token`) */
|
|
59
|
+
response_type: string;
|
|
60
|
+
|
|
61
|
+
/** the URL to which the Identity Provider will post the Authorization Response */
|
|
62
|
+
redirect_uri: string;
|
|
63
|
+
|
|
64
|
+
/** The URI to which the SIOPv2 Authorization Response will be sent (Tim's note: not used with encrypted request JWT)*/
|
|
65
|
+
response_uri?: string;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* An opaque value used to maintain state between the request and the callback.
|
|
69
|
+
* Recommended for security to prevent CSRF attacks.
|
|
70
|
+
*/
|
|
71
|
+
state: string;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* A string value used to associate a client session with an ID token to mitigate replay attacks.
|
|
75
|
+
* Recommended when requesting ID tokens.
|
|
76
|
+
*/
|
|
77
|
+
nonce: string;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The PKCE code challenge.
|
|
81
|
+
* Required if `code_challenge_method` is used. Enhances security for public clients (e.g., single-page apps,
|
|
82
|
+
* mobile apps) by requiring an additional verification step during token exchange.
|
|
83
|
+
*/
|
|
84
|
+
code_challenge?: string;
|
|
85
|
+
|
|
86
|
+
/** The method used for the PKCE challenge (typically `S256`). Must be present if `code_challenge` is included. */
|
|
87
|
+
code_challenge_method?: 'S256';
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* An ID token previously issued to the client, passed as a hint about the end-user’s current or past authenticated
|
|
91
|
+
* session with the client. Can streamline user experience if already logged in.
|
|
92
|
+
*/
|
|
93
|
+
id_token_hint?: string;
|
|
94
|
+
|
|
95
|
+
/** A hint to the authorization server about the login identifier the user might use. Useful for pre-filling login information. */
|
|
96
|
+
login_hint?: string;
|
|
97
|
+
|
|
98
|
+
/** Requested Authentication Context Class Reference values. Specifies the authentication context requirements. */
|
|
99
|
+
acr_values?: string;
|
|
100
|
+
|
|
101
|
+
/** When using a PAR for secure cross device flows we use a "form_post" rather than a "direct_post" */
|
|
102
|
+
response_mode: 'direct_post';
|
|
103
|
+
|
|
104
|
+
/** Used by PFI to request VCs as input to IDV process. If present, `response_type: "vp_token""` MUST also be present */
|
|
105
|
+
presentation_definition?: any;
|
|
106
|
+
|
|
107
|
+
/** A JSON object containing the Verifier metadata values (Tim's note: from TBD KCC Repo) */
|
|
108
|
+
client_metadata?: {
|
|
109
|
+
/** Array of strings, each a DID method supported for the subject of ID Token */
|
|
110
|
+
subject_syntax_types_supported: string[];
|
|
111
|
+
/** Human-readable string name of the client to be presented to the end-user during authorization */
|
|
112
|
+
client_name?: string;
|
|
113
|
+
/** URI of a web page providing information about the client */
|
|
114
|
+
client_uri?: string;
|
|
115
|
+
/** URI of an image logo for the client */
|
|
116
|
+
logo_uri?: string;
|
|
117
|
+
/** Array of strings representing ways to contact people responsible for this client, typically email addresses */
|
|
118
|
+
contacts?: string[];
|
|
119
|
+
/** URI that points to a terms of service document for the client */
|
|
120
|
+
tos_uri?: string;
|
|
121
|
+
/** URI that points to a privacy policy document */
|
|
122
|
+
policy_uri?: string;
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* An auth request that is compatible with both Web5 Connect and (hopefully, WIP) OIDC SIOPv2
|
|
128
|
+
* The contents of this are inserted into a JWT inside of the {@link PushedAuthRequest}.
|
|
129
|
+
*/
|
|
130
|
+
export type Web5ConnectAuthRequest = {
|
|
131
|
+
/** The user friendly name of the client/app to be displayed when prompting end-user with permission requests. */
|
|
132
|
+
displayName: string;
|
|
133
|
+
|
|
134
|
+
/** PermissionGrants that are to be sent to the provider */
|
|
135
|
+
permissionRequests: ConnectPermissionRequest[];
|
|
136
|
+
} & SIOPv2AuthRequest;
|
|
137
|
+
|
|
138
|
+
/** The fields for an OIDC SIOPv2 Auth Repsonse */
|
|
139
|
+
export type SIOPv2AuthResponse = {
|
|
140
|
+
/** Issuer MUST match the value of sub (Applicant's DID) */
|
|
141
|
+
iss: string;
|
|
142
|
+
/** Subject Identifier. A locally unique and never reassigned identifier
|
|
143
|
+
* within the Issuer for the End-User, which is intended to be consumed
|
|
144
|
+
* by the Client. */
|
|
145
|
+
sub: string;
|
|
146
|
+
/** Audience(s) that this ID Token is intended for. It MUST contain the
|
|
147
|
+
* OAuth 2.0 client_id of the Relying Party as an audience value. */
|
|
148
|
+
aud: string;
|
|
149
|
+
/** Time at which the JWT was issued. */
|
|
150
|
+
iat: number;
|
|
151
|
+
/** Expiration time on or after which the ID Token MUST NOT be accepted
|
|
152
|
+
* for processing. */
|
|
153
|
+
exp: number;
|
|
154
|
+
/** Time when the End-User authentication occurred. */
|
|
155
|
+
auth_time?: number;
|
|
156
|
+
/** b64url encoded nonce used to associate a Client session with an ID Token, and to
|
|
157
|
+
* mitigate replay attacks. */
|
|
158
|
+
nonce?: string;
|
|
159
|
+
/** Custom claims. */
|
|
160
|
+
[key: string]: any;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
/** An auth response that is compatible with both Web5 Connect and (hopefully, WIP) OIDC SIOPv2 */
|
|
164
|
+
export type Web5ConnectAuthResponse = {
|
|
165
|
+
delegateGrants: DwnDataEncodedRecordsWriteMessage[];
|
|
166
|
+
delegatePortableDid: PortableDid;
|
|
167
|
+
} & SIOPv2AuthResponse;
|
|
168
|
+
|
|
169
|
+
/** Represents the different OIDC endpoint types.
|
|
170
|
+
* 1. `pushedAuthorizationRequest`: client sends {@link PushedAuthRequest} receives {@link PushedAuthResponse}
|
|
171
|
+
* 2. `authorize`: provider gets the {@link Web5ConnectAuthRequest} JWT that was stored by the PAR
|
|
172
|
+
* 3. `callback`: provider sends {@link Web5ConnectAuthResponse} to this endpoint
|
|
173
|
+
* 4. `token`: client gets {@link Web5ConnectAuthResponse} from this endpoint
|
|
174
|
+
*/
|
|
175
|
+
type OidcEndpoint =
|
|
176
|
+
| 'pushedAuthorizationRequest'
|
|
177
|
+
| 'authorize'
|
|
178
|
+
| 'callback'
|
|
179
|
+
| 'token';
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Gets the correct OIDC endpoint out of the {@link OidcEndpoint} options provided.
|
|
183
|
+
* Handles a trailing slash on baseURL
|
|
184
|
+
*
|
|
185
|
+
* @param {Object} options the options object
|
|
186
|
+
* @param {string} options.baseURL for example `http://foo.com/connect/
|
|
187
|
+
* @param {OidcEndpoint} options.endpoint the OIDC endpoint desired
|
|
188
|
+
* @param {string} options.authParam this is the unique id which must be provided when getting the `authorize` endpoint
|
|
189
|
+
* @param {string} options.tokenParam this is the random state as b64url which must be provided with the `token` endpoint
|
|
190
|
+
*/
|
|
191
|
+
function buildOidcUrl({
|
|
192
|
+
baseURL,
|
|
193
|
+
endpoint,
|
|
194
|
+
authParam,
|
|
195
|
+
tokenParam,
|
|
196
|
+
}: {
|
|
197
|
+
baseURL: string;
|
|
198
|
+
endpoint: OidcEndpoint;
|
|
199
|
+
authParam?: string;
|
|
200
|
+
tokenParam?: string;
|
|
201
|
+
}) {
|
|
202
|
+
switch (endpoint) {
|
|
203
|
+
/** 1. client sends {@link PushedAuthRequest} & client receives {@link PushedAuthResponse} */
|
|
204
|
+
case 'pushedAuthorizationRequest':
|
|
205
|
+
return concatenateUrl(baseURL, 'par');
|
|
206
|
+
/** 2. provider gets {@link Web5ConnectAuthRequest} */
|
|
207
|
+
case 'authorize':
|
|
208
|
+
if (!authParam)
|
|
209
|
+
throw new Error(
|
|
210
|
+
`authParam must be providied when building a token URL`
|
|
211
|
+
);
|
|
212
|
+
return concatenateUrl(baseURL, `authorize/${authParam}.jwt`);
|
|
213
|
+
/** 3. provider sends {@link Web5ConnectAuthResponse} */
|
|
214
|
+
case 'callback':
|
|
215
|
+
return concatenateUrl(baseURL, `callback`);
|
|
216
|
+
/** 4. client gets {@link Web5ConnectAuthResponse */
|
|
217
|
+
case 'token':
|
|
218
|
+
if (!tokenParam)
|
|
219
|
+
throw new Error(
|
|
220
|
+
`tokenParam must be providied when building a token URL`
|
|
221
|
+
);
|
|
222
|
+
return concatenateUrl(baseURL, `token/${tokenParam}.jwt`);
|
|
223
|
+
// TODO: metadata endpoints?
|
|
224
|
+
default:
|
|
225
|
+
throw new Error(`No matches for endpoint specified: ${endpoint}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Generates a cryptographically random "code challenge" in
|
|
231
|
+
* accordance with the RFC 7636 PKCE specification.
|
|
232
|
+
*
|
|
233
|
+
* @see {@link https://datatracker.ietf.org/doc/html/rfc7636#section-4.2 | RFC 7636 }
|
|
234
|
+
*/
|
|
235
|
+
async function generateCodeChallenge() {
|
|
236
|
+
const codeVerifierBytes = CryptoUtils.randomBytes(32);
|
|
237
|
+
const codeChallengeBytes = await Sha256.digest({ data: codeVerifierBytes });
|
|
238
|
+
const codeChallengeBase64Url =
|
|
239
|
+
Convert.uint8Array(codeChallengeBytes).toBase64Url();
|
|
240
|
+
|
|
241
|
+
return { codeChallengeBytes, codeChallengeBase64Url };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Client creates the {@link Web5ConnectAuthRequest} */
|
|
245
|
+
async function createAuthRequest(
|
|
246
|
+
options: RequireOnly<
|
|
247
|
+
Web5ConnectAuthRequest,
|
|
248
|
+
'client_id' | 'scope' | 'redirect_uri' | 'permissionRequests' | 'displayName'
|
|
249
|
+
>
|
|
250
|
+
) {
|
|
251
|
+
// Generate a random state value to associate the authorization request with the response.
|
|
252
|
+
const stateBytes = CryptoUtils.randomBytes(16);
|
|
253
|
+
|
|
254
|
+
// Generate a random nonce value to associate the ID Token with the authorization request.
|
|
255
|
+
const nonceBytes = CryptoUtils.randomBytes(16);
|
|
256
|
+
|
|
257
|
+
const requestObject: Web5ConnectAuthRequest = {
|
|
258
|
+
...options,
|
|
259
|
+
nonce : Convert.uint8Array(nonceBytes).toBase64Url(),
|
|
260
|
+
response_type : 'id_token',
|
|
261
|
+
response_mode : 'direct_post',
|
|
262
|
+
state : Convert.uint8Array(stateBytes).toBase64Url(),
|
|
263
|
+
client_metadata : {
|
|
264
|
+
subject_syntax_types_supported: ['did:dht', 'did:jwk'],
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
return requestObject;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** Encrypts the auth request with the key which will be passed through QR code */
|
|
272
|
+
async function encryptAuthRequest({
|
|
273
|
+
jwt,
|
|
274
|
+
encryptionKey,
|
|
275
|
+
}: {
|
|
276
|
+
jwt: string;
|
|
277
|
+
encryptionKey: Uint8Array;
|
|
278
|
+
}) {
|
|
279
|
+
const protectedHeader = {
|
|
280
|
+
alg : 'dir',
|
|
281
|
+
cty : 'JWT',
|
|
282
|
+
enc : 'XC20P',
|
|
283
|
+
typ : 'JWT',
|
|
284
|
+
};
|
|
285
|
+
const nonce = CryptoUtils.randomBytes(24);
|
|
286
|
+
const additionalData = Convert.object(protectedHeader).toUint8Array();
|
|
287
|
+
const jwtBytes = Convert.string(jwt).toUint8Array();
|
|
288
|
+
const chacha = xchacha20poly1305(encryptionKey, nonce, additionalData);
|
|
289
|
+
const ciphertextAndTag = chacha.encrypt(jwtBytes);
|
|
290
|
+
|
|
291
|
+
/** The cipher output concatenates the encrypted data and tag
|
|
292
|
+
* so we need to extract the values for use in the JWE. */
|
|
293
|
+
const ciphertext = ciphertextAndTag.subarray(0, -16);
|
|
294
|
+
const authenticationTag = ciphertextAndTag.subarray(-16);
|
|
295
|
+
|
|
296
|
+
const compactJwe = [
|
|
297
|
+
Convert.object(protectedHeader).toBase64Url(),
|
|
298
|
+
'', // Empty string since there is no wrapped key.
|
|
299
|
+
Convert.uint8Array(nonce).toBase64Url(),
|
|
300
|
+
Convert.uint8Array(ciphertext).toBase64Url(),
|
|
301
|
+
Convert.uint8Array(authenticationTag).toBase64Url(),
|
|
302
|
+
].join('.');
|
|
303
|
+
|
|
304
|
+
return compactJwe;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** Create a response object compatible with Web5 Connect and OIDC SIOPv2 */
|
|
308
|
+
async function createResponseObject(
|
|
309
|
+
options: RequireOnly<
|
|
310
|
+
Web5ConnectAuthResponse,
|
|
311
|
+
'iss' | 'sub' | 'aud' | 'delegateGrants' | 'delegatePortableDid'
|
|
312
|
+
>
|
|
313
|
+
) {
|
|
314
|
+
const currentTimeInSeconds = Math.floor(Date.now() / 1000);
|
|
315
|
+
|
|
316
|
+
const responseObject: Web5ConnectAuthResponse = {
|
|
317
|
+
...options,
|
|
318
|
+
iat : currentTimeInSeconds,
|
|
319
|
+
exp : currentTimeInSeconds + 600, // Expires in 10 minutes.
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
return responseObject;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/** sign an object and transform it into a jwt using a did */
|
|
326
|
+
async function signJwt({
|
|
327
|
+
did,
|
|
328
|
+
data,
|
|
329
|
+
}: {
|
|
330
|
+
did: BearerDid;
|
|
331
|
+
data: Record<string, unknown>;
|
|
332
|
+
}) {
|
|
333
|
+
const header = Convert.object({
|
|
334
|
+
alg : 'EdDSA',
|
|
335
|
+
kid : did.document.verificationMethod![0].id,
|
|
336
|
+
typ : 'JWT',
|
|
337
|
+
}).toBase64Url();
|
|
338
|
+
|
|
339
|
+
const payload = Convert.object(data).toBase64Url();
|
|
340
|
+
|
|
341
|
+
// signs using ed25519 EdDSA
|
|
342
|
+
const signer = await did.getSigner();
|
|
343
|
+
const signature = await signer.sign({
|
|
344
|
+
data: Convert.string(`${header}.${payload}`).toUint8Array(),
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const signatureBase64Url = Convert.uint8Array(signature).toBase64Url();
|
|
348
|
+
|
|
349
|
+
const jwt = `${header}.${payload}.${signatureBase64Url}`;
|
|
350
|
+
|
|
351
|
+
return jwt;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/** Take the decrypted JWT and verify it was signed by its public DID. Return parsed object. */
|
|
355
|
+
async function verifyJwt({ jwt }: { jwt: string }) {
|
|
356
|
+
const [headerB64U, payloadB64U, signatureB64U] = jwt.split('.');
|
|
357
|
+
|
|
358
|
+
// Convert the header back to a JOSE object and verify that the 'kid' header value is present.
|
|
359
|
+
const header: JoseHeaderParams = Convert.base64Url(headerB64U).toObject();
|
|
360
|
+
|
|
361
|
+
if (!header.kid)
|
|
362
|
+
throw new Error(
|
|
363
|
+
`OIDC: Object could not be verified due to missing 'kid' header value.`
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
// Resolve the Client DID document.
|
|
367
|
+
const { didDocument } = await DidJwk.resolve(header.kid.split('#')[0]);
|
|
368
|
+
|
|
369
|
+
if (!didDocument)
|
|
370
|
+
throw new Error(
|
|
371
|
+
'OIDC: Object could not be verified due to Client DID resolution issue.'
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
// Get the public key used to sign the Object from the DID document.
|
|
375
|
+
const { publicKeyJwk } =
|
|
376
|
+
didDocument.verificationMethod?.find((method: any) => {
|
|
377
|
+
return method.id === header.kid;
|
|
378
|
+
}) ?? {};
|
|
379
|
+
|
|
380
|
+
if (!publicKeyJwk)
|
|
381
|
+
throw new Error(
|
|
382
|
+
'OIDC: Object could not be verified due to missing public key in DID document.'
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
const EdDsa = new EdDsaAlgorithm();
|
|
386
|
+
const isValid = await EdDsa.verify({
|
|
387
|
+
key : publicKeyJwk,
|
|
388
|
+
signature : Convert.base64Url(signatureB64U).toUint8Array(),
|
|
389
|
+
data : Convert.string(`${headerB64U}.${payloadB64U}`).toUint8Array(),
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
if (!isValid)
|
|
393
|
+
throw new Error(
|
|
394
|
+
'OIDC: Object failed verification due to invalid signature.'
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
const object = Convert.base64Url(payloadB64U).toObject();
|
|
398
|
+
|
|
399
|
+
return object;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Fetches the {@Web5ConnectAuthRequest} from the authorize endpoint and decrypts it
|
|
404
|
+
* using the encryption key passed via QR code.
|
|
405
|
+
*/
|
|
406
|
+
const getAuthRequest = async (request_uri: string, encryption_key: string) => {
|
|
407
|
+
const authRequest = await fetch(request_uri);
|
|
408
|
+
const jwe = await authRequest.text();
|
|
409
|
+
const jwt = decryptAuthRequest({
|
|
410
|
+
jwe,
|
|
411
|
+
encryption_key,
|
|
412
|
+
});
|
|
413
|
+
const web5ConnectAuthRequest = (await verifyJwt({
|
|
414
|
+
jwt,
|
|
415
|
+
})) as Web5ConnectAuthRequest;
|
|
416
|
+
|
|
417
|
+
return web5ConnectAuthRequest;
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
/** Take the encrypted JWE, decrypt using the code challenge and return a JWT string which will need to be verified */
|
|
421
|
+
function decryptAuthRequest({
|
|
422
|
+
jwe,
|
|
423
|
+
encryption_key,
|
|
424
|
+
}: {
|
|
425
|
+
jwe: string;
|
|
426
|
+
encryption_key: string;
|
|
427
|
+
}) {
|
|
428
|
+
const [
|
|
429
|
+
protectedHeaderB64U,
|
|
430
|
+
,
|
|
431
|
+
nonceB64U,
|
|
432
|
+
ciphertextB64U,
|
|
433
|
+
authenticationTagB64U,
|
|
434
|
+
] = jwe.split('.');
|
|
435
|
+
|
|
436
|
+
const encryptionKeyBytes = Convert.base64Url(encryption_key).toUint8Array();
|
|
437
|
+
const protectedHeader = Convert.base64Url(protectedHeaderB64U).toUint8Array();
|
|
438
|
+
const additionalData = protectedHeader;
|
|
439
|
+
const nonce = Convert.base64Url(nonceB64U).toUint8Array();
|
|
440
|
+
const ciphertext = Convert.base64Url(ciphertextB64U).toUint8Array();
|
|
441
|
+
const authenticationTag = Convert.base64Url(
|
|
442
|
+
authenticationTagB64U
|
|
443
|
+
).toUint8Array();
|
|
444
|
+
|
|
445
|
+
// The cipher expects the encrypted data and tag to be concatenated.
|
|
446
|
+
const ciphertextAndTag = new Uint8Array([
|
|
447
|
+
...ciphertext,
|
|
448
|
+
...authenticationTag,
|
|
449
|
+
]);
|
|
450
|
+
const chacha = xchacha20poly1305(encryptionKeyBytes, nonce, additionalData);
|
|
451
|
+
const decryptedJwtBytes = chacha.decrypt(ciphertextAndTag);
|
|
452
|
+
const jwt = Convert.uint8Array(decryptedJwtBytes).toString();
|
|
453
|
+
|
|
454
|
+
return jwt;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* The client uses to decrypt the jwe obtained from the auth server which contains
|
|
459
|
+
* the {@link Web5ConnectAuthResponse} that was sent by the provider to the auth server.
|
|
460
|
+
*
|
|
461
|
+
* @async
|
|
462
|
+
* @param {BearerDid} clientDid - The did that was initially used by the client for ECDH at connect init.
|
|
463
|
+
* @param {string} jwe - The encrypted data as a jwe.
|
|
464
|
+
* @param {string} pin - The pin that was obtained from the user.
|
|
465
|
+
*/
|
|
466
|
+
async function decryptAuthResponse(
|
|
467
|
+
clientDid: BearerDid,
|
|
468
|
+
jwe: string,
|
|
469
|
+
pin: string
|
|
470
|
+
) {
|
|
471
|
+
const [
|
|
472
|
+
protectedHeaderB64U,
|
|
473
|
+
,
|
|
474
|
+
nonceB64U,
|
|
475
|
+
ciphertextB64U,
|
|
476
|
+
authenticationTagB64U,
|
|
477
|
+
] = jwe.split('.');
|
|
478
|
+
|
|
479
|
+
// get the delegatedid public key from the header
|
|
480
|
+
const header = Convert.base64Url(protectedHeaderB64U).toObject() as Jwk;
|
|
481
|
+
const delegateResolvedDid = await DidJwk.resolve(header.kid!.split('#')[0]);
|
|
482
|
+
|
|
483
|
+
// derive ECDH shared key using the provider's public key and our clientDid private key
|
|
484
|
+
const sharedKey = await Oidc.deriveSharedKey(
|
|
485
|
+
clientDid,
|
|
486
|
+
delegateResolvedDid.didDocument!
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
// add the pin to the AAD
|
|
490
|
+
const additionalData = { ...header, pin: pin };
|
|
491
|
+
const AAD = Convert.object(additionalData).toUint8Array();
|
|
492
|
+
|
|
493
|
+
const nonce = Convert.base64Url(nonceB64U).toUint8Array();
|
|
494
|
+
const ciphertext = Convert.base64Url(ciphertextB64U).toUint8Array();
|
|
495
|
+
const authenticationTag = Convert.base64Url(
|
|
496
|
+
authenticationTagB64U
|
|
497
|
+
).toUint8Array();
|
|
498
|
+
|
|
499
|
+
// The cipher expects the encrypted data and tag to be concatenated.
|
|
500
|
+
const ciphertextAndTag = new Uint8Array([
|
|
501
|
+
...ciphertext,
|
|
502
|
+
...authenticationTag,
|
|
503
|
+
]);
|
|
504
|
+
|
|
505
|
+
// decrypt using the sharedKey
|
|
506
|
+
const chacha = xchacha20poly1305(sharedKey, nonce, AAD);
|
|
507
|
+
const decryptedJwtBytes = chacha.decrypt(ciphertextAndTag);
|
|
508
|
+
const jwt = Convert.uint8Array(decryptedJwtBytes).toString();
|
|
509
|
+
|
|
510
|
+
return jwt;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/** Derives a shared ECDH private key in order to encrypt the {@link Web5ConnectAuthResponse} */
|
|
514
|
+
async function deriveSharedKey(
|
|
515
|
+
privateKeyDid: BearerDid,
|
|
516
|
+
publicKeyDid: DidDocument
|
|
517
|
+
) {
|
|
518
|
+
const privatePortableDid = await privateKeyDid.export();
|
|
519
|
+
|
|
520
|
+
const publicJwk = publicKeyDid.verificationMethod?.[0].publicKeyJwk!;
|
|
521
|
+
const privateJwk = privatePortableDid.privateKeys?.[0]!;
|
|
522
|
+
publicJwk.alg = 'EdDSA';
|
|
523
|
+
|
|
524
|
+
const publicX25519 = await Ed25519.convertPublicKeyToX25519({
|
|
525
|
+
publicKey: publicJwk,
|
|
526
|
+
});
|
|
527
|
+
const privateX25519 = await Ed25519.convertPrivateKeyToX25519({
|
|
528
|
+
privateKey: privateJwk,
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
const sharedKey = await X25519.sharedSecret({
|
|
532
|
+
privateKeyA : privateX25519,
|
|
533
|
+
publicKeyB : publicX25519,
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
const derivedKey = await crypto.subtle.importKey(
|
|
537
|
+
'raw',
|
|
538
|
+
sharedKey,
|
|
539
|
+
{ name: 'HKDF' },
|
|
540
|
+
false,
|
|
541
|
+
['deriveBits']
|
|
542
|
+
);
|
|
543
|
+
const derivedKeyBits = await crypto.subtle.deriveBits(
|
|
544
|
+
{
|
|
545
|
+
name : 'HKDF',
|
|
546
|
+
hash : 'SHA-256',
|
|
547
|
+
info : new Uint8Array(),
|
|
548
|
+
salt : new Uint8Array(),
|
|
549
|
+
},
|
|
550
|
+
derivedKey,
|
|
551
|
+
256
|
|
552
|
+
);
|
|
553
|
+
const sharedEncryptionKey = new Uint8Array(derivedKeyBits);
|
|
554
|
+
return sharedEncryptionKey;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Encrypts the auth response jwt. Requires a randomPin is added to the AAD of the
|
|
559
|
+
* encryption algorithm in order to prevent man in the middle and eavesdropping attacks.
|
|
560
|
+
* The keyid of the delegate did is used to pass the public key to the client in order
|
|
561
|
+
* for the client to derive the shared ECDH private key.
|
|
562
|
+
*/
|
|
563
|
+
function encryptAuthResponse({
|
|
564
|
+
jwt,
|
|
565
|
+
encryptionKey,
|
|
566
|
+
delegateDidKeyId,
|
|
567
|
+
randomPin,
|
|
568
|
+
}: {
|
|
569
|
+
jwt: string;
|
|
570
|
+
encryptionKey: Uint8Array;
|
|
571
|
+
delegateDidKeyId: string;
|
|
572
|
+
randomPin: string;
|
|
573
|
+
}) {
|
|
574
|
+
const protectedHeader = {
|
|
575
|
+
alg : 'dir',
|
|
576
|
+
cty : 'JWT',
|
|
577
|
+
enc : 'XC20P',
|
|
578
|
+
typ : 'JWT',
|
|
579
|
+
kid : delegateDidKeyId,
|
|
580
|
+
};
|
|
581
|
+
const nonce = CryptoUtils.randomBytes(24);
|
|
582
|
+
const additionalData = Convert.object({
|
|
583
|
+
...protectedHeader,
|
|
584
|
+
pin: randomPin,
|
|
585
|
+
}).toUint8Array();
|
|
586
|
+
|
|
587
|
+
const jwtBytes = Convert.string(jwt).toUint8Array();
|
|
588
|
+
const chacha = xchacha20poly1305(encryptionKey, nonce, additionalData);
|
|
589
|
+
const ciphertextAndTag = chacha.encrypt(jwtBytes);
|
|
590
|
+
|
|
591
|
+
/** The cipher output concatenates the encrypted data and tag
|
|
592
|
+
* so we need to extract the values for use in the JWE. */
|
|
593
|
+
const ciphertext = ciphertextAndTag.subarray(0, -16);
|
|
594
|
+
const authenticationTag = ciphertextAndTag.subarray(-16);
|
|
595
|
+
|
|
596
|
+
const compactJwe = [
|
|
597
|
+
Convert.object(protectedHeader).toBase64Url(),
|
|
598
|
+
'', // Empty string since there is no wrapped key.
|
|
599
|
+
Convert.uint8Array(nonce).toBase64Url(),
|
|
600
|
+
Convert.uint8Array(ciphertext).toBase64Url(),
|
|
601
|
+
Convert.uint8Array(authenticationTag).toBase64Url(),
|
|
602
|
+
].join('.');
|
|
603
|
+
|
|
604
|
+
return compactJwe;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
function shouldUseDelegatePermission(scope: DwnPermissionScope): boolean {
|
|
608
|
+
// Currently all record permissions are treated as delegated permissions
|
|
609
|
+
// In the future only methods that modify state will be delegated and the rest will be normal permissions
|
|
610
|
+
if (isRecordPermissionScope(scope)) {
|
|
611
|
+
return true;
|
|
612
|
+
} else if (scope.interface === DwnInterfaceName.Protocols && scope.method === DwnMethodName.Configure) {
|
|
613
|
+
// ProtocolConfigure messages are also delegated, as they modify state
|
|
614
|
+
return true;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// All other permissions are not treated as delegated
|
|
618
|
+
return false;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Creates the permission grants that assign to the selectedDid the level of
|
|
623
|
+
* permissions that the web app requested in the {@link Web5ConnectAuthRequest}
|
|
624
|
+
*/
|
|
625
|
+
async function createPermissionGrants(
|
|
626
|
+
selectedDid: string,
|
|
627
|
+
delegateBearerDid: BearerDid,
|
|
628
|
+
agent: Web5Agent,
|
|
629
|
+
scopes: DwnPermissionScope[],
|
|
630
|
+
) {
|
|
631
|
+
const permissionsApi = new AgentPermissionsApi({ agent });
|
|
632
|
+
|
|
633
|
+
// TODO: cleanup all grants if one fails by deleting them from the DWN: https://github.com/TBD54566975/web5-js/issues/849
|
|
634
|
+
logger.log(`Creating permission grants for ${scopes.length} scopes given...`);
|
|
635
|
+
const permissionGrants = await Promise.all(
|
|
636
|
+
scopes.map((scope) => {
|
|
637
|
+
// check if the scope is a records permission scope, or a protocol configure scope, if so it should use a delegated permission.
|
|
638
|
+
const delegated = shouldUseDelegatePermission(scope);
|
|
639
|
+
return permissionsApi.createGrant({
|
|
640
|
+
delegated,
|
|
641
|
+
store : true,
|
|
642
|
+
grantedTo : delegateBearerDid.uri,
|
|
643
|
+
scope,
|
|
644
|
+
dateExpires : '2040-06-25T16:09:16.693356Z', // TODO: make dateExpires optional
|
|
645
|
+
author : selectedDid,
|
|
646
|
+
});
|
|
647
|
+
})
|
|
648
|
+
);
|
|
649
|
+
|
|
650
|
+
logger.log(`Sending ${permissionGrants.length} permission grants to remote DWN...`);
|
|
651
|
+
const messagePromises = permissionGrants.map(async (grant) => {
|
|
652
|
+
// Quirk: we have to pull out encodedData out of the message the schema validator doesn't want it there
|
|
653
|
+
const { encodedData, ...rawMessage } = grant.message;
|
|
654
|
+
|
|
655
|
+
const data = Convert.base64Url(encodedData).toUint8Array();
|
|
656
|
+
const { reply } = await agent.sendDwnRequest({
|
|
657
|
+
author : selectedDid,
|
|
658
|
+
target : selectedDid,
|
|
659
|
+
messageType : DwnInterface.RecordsWrite,
|
|
660
|
+
dataStream : new Blob([data]),
|
|
661
|
+
rawMessage,
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
// check if the message was sent successfully, if the remote returns 409 the message may have come through already via sync
|
|
665
|
+
if (reply.status.code !== 202 && reply.status.code !== 409) {
|
|
666
|
+
logger.error(`Error sending RecordsWrite: ${reply.status.detail}`);
|
|
667
|
+
logger.error(`RecordsWrite message: ${rawMessage}`);
|
|
668
|
+
throw new Error(
|
|
669
|
+
`Could not send the message. Error details: ${reply.status.detail}`
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
return grant.message;
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
try {
|
|
677
|
+
const messages = await Promise.all(messagePromises);
|
|
678
|
+
return messages;
|
|
679
|
+
} catch (error) {
|
|
680
|
+
logger.error(`Error during batch-send of permission grants: ${error}`);
|
|
681
|
+
throw error;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Installs the protocol required by the Client on the Provider if it doesn't already exist.
|
|
687
|
+
*/
|
|
688
|
+
async function prepareProtocol(
|
|
689
|
+
selectedDid: string,
|
|
690
|
+
agent: Web5Agent,
|
|
691
|
+
protocolDefinition: DwnProtocolDefinition
|
|
692
|
+
): Promise<void> {
|
|
693
|
+
|
|
694
|
+
const queryMessage = await agent.processDwnRequest({
|
|
695
|
+
author : selectedDid,
|
|
696
|
+
messageType : DwnInterface.ProtocolsQuery,
|
|
697
|
+
target : selectedDid,
|
|
698
|
+
messageParams : { filter: { protocol: protocolDefinition.protocol } },
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
if ( queryMessage.reply.status.code !== 200) {
|
|
702
|
+
// if the query failed, throw an error
|
|
703
|
+
throw new Error(
|
|
704
|
+
`Could not fetch protocol: ${queryMessage.reply.status.detail}`
|
|
705
|
+
);
|
|
706
|
+
} else if (queryMessage.reply.entries === undefined || queryMessage.reply.entries.length === 0) {
|
|
707
|
+
logger.log(`Protocol does not exist, creating: ${protocolDefinition.protocol}`);
|
|
708
|
+
|
|
709
|
+
// send the protocol definition to the remote DWN first, if it passes we can process it locally
|
|
710
|
+
const { reply: sendReply, message: configureMessage } = await agent.sendDwnRequest({
|
|
711
|
+
author : selectedDid,
|
|
712
|
+
target : selectedDid,
|
|
713
|
+
messageType : DwnInterface.ProtocolsConfigure,
|
|
714
|
+
messageParams : { definition: protocolDefinition },
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
// check if the message was sent successfully, if the remote returns 409 the message may have come through already via sync
|
|
718
|
+
if (sendReply.status.code !== 202 && sendReply.status.code !== 409) {
|
|
719
|
+
throw new Error(`Could not send protocol: ${sendReply.status.detail}`);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// process the protocol locally, we don't have to check if it exists as this is just a convenience over waiting for sync.
|
|
723
|
+
await agent.processDwnRequest({
|
|
724
|
+
author : selectedDid,
|
|
725
|
+
target : selectedDid,
|
|
726
|
+
messageType : DwnInterface.ProtocolsConfigure,
|
|
727
|
+
rawMessage : configureMessage
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
} else {
|
|
731
|
+
logger.log(`Protocol already exists: ${protocolDefinition.protocol}`);
|
|
732
|
+
|
|
733
|
+
// the protocol already exists, let's make sure it exists on the remote DWN as the requesting app will need it
|
|
734
|
+
const configureMessage = queryMessage.reply.entries![0];
|
|
735
|
+
const { reply: sendReply } = await agent.sendDwnRequest({
|
|
736
|
+
author : selectedDid,
|
|
737
|
+
target : selectedDid,
|
|
738
|
+
messageType : DwnInterface.ProtocolsConfigure,
|
|
739
|
+
rawMessage : configureMessage,
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
if (sendReply.status.code !== 202 && sendReply.status.code !== 409) {
|
|
743
|
+
throw new Error(`Could not send protocol: ${sendReply.status.detail}`);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Creates a delegate did which the web app will use as its future indentity.
|
|
750
|
+
* Assigns to that DID the level of permissions that the web app requested in
|
|
751
|
+
* the {@link Web5ConnectAuthRequest}. Encrypts via ECDH key that the web app
|
|
752
|
+
* will have access to because the web app has the public key which it provided
|
|
753
|
+
* in the {@link Web5ConnectAuthRequest}. Then sends the ciphertext of this
|
|
754
|
+
* {@link Web5ConnectAuthResponse} to the callback endpoint. Which the
|
|
755
|
+
* web app will need to retrieve from the token endpoint and decrypt with the pin to access.
|
|
756
|
+
*/
|
|
757
|
+
async function submitAuthResponse(
|
|
758
|
+
selectedDid: string,
|
|
759
|
+
authRequest: Web5ConnectAuthRequest,
|
|
760
|
+
randomPin: string,
|
|
761
|
+
agent: Web5Agent
|
|
762
|
+
) {
|
|
763
|
+
const delegateBearerDid = await DidJwk.create();
|
|
764
|
+
const delegatePortableDid = await delegateBearerDid.export();
|
|
765
|
+
|
|
766
|
+
// TODO: roll back permissions and protocol configurations if an error occurs. Need a way to delete protocols to achieve this.
|
|
767
|
+
const delegateGrantPromises = authRequest.permissionRequests.map(
|
|
768
|
+
async (permissionRequest) => {
|
|
769
|
+
const { protocolDefinition, permissionScopes } = permissionRequest;
|
|
770
|
+
|
|
771
|
+
// We validate that all permission scopes match the protocol uri of the protocol definition they are provided with.
|
|
772
|
+
const grantsMatchProtocolUri = permissionScopes.every(scope => 'protocol' in scope && scope.protocol === protocolDefinition.protocol);
|
|
773
|
+
if (!grantsMatchProtocolUri) {
|
|
774
|
+
throw new Error('All permission scopes must match the protocol uri they are provided with.');
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
await prepareProtocol(selectedDid, agent, protocolDefinition);
|
|
778
|
+
|
|
779
|
+
const permissionGrants = await Oidc.createPermissionGrants(
|
|
780
|
+
selectedDid,
|
|
781
|
+
delegateBearerDid,
|
|
782
|
+
agent,
|
|
783
|
+
permissionScopes
|
|
784
|
+
);
|
|
785
|
+
|
|
786
|
+
return permissionGrants;
|
|
787
|
+
}
|
|
788
|
+
);
|
|
789
|
+
|
|
790
|
+
const delegateGrants = (await Promise.all(delegateGrantPromises)).flat();
|
|
791
|
+
|
|
792
|
+
logger.log('Generating auth response object...');
|
|
793
|
+
const responseObject = await Oidc.createResponseObject({
|
|
794
|
+
//* the IDP's did that was selected to be connected
|
|
795
|
+
iss : selectedDid,
|
|
796
|
+
//* the client's new identity
|
|
797
|
+
sub : delegateBearerDid.uri,
|
|
798
|
+
//* the client's temporary ephemeral did used for connect
|
|
799
|
+
aud : authRequest.client_id,
|
|
800
|
+
//* the nonce of the original auth request
|
|
801
|
+
nonce : authRequest.nonce,
|
|
802
|
+
delegateGrants,
|
|
803
|
+
delegatePortableDid,
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
// Sign the Response Object using the ephemeral DID's signing key.
|
|
807
|
+
logger.log('Signing auth response object...');
|
|
808
|
+
const responseObjectJwt = await Oidc.signJwt({
|
|
809
|
+
did : delegateBearerDid,
|
|
810
|
+
data : responseObject,
|
|
811
|
+
});
|
|
812
|
+
const clientDid = await DidJwk.resolve(authRequest.client_id);
|
|
813
|
+
|
|
814
|
+
const sharedKey = await Oidc.deriveSharedKey(
|
|
815
|
+
delegateBearerDid,
|
|
816
|
+
clientDid?.didDocument!
|
|
817
|
+
);
|
|
818
|
+
|
|
819
|
+
logger.log('Encrypting auth response object...');
|
|
820
|
+
const encryptedResponse = Oidc.encryptAuthResponse({
|
|
821
|
+
jwt : responseObjectJwt!,
|
|
822
|
+
encryptionKey : sharedKey,
|
|
823
|
+
delegateDidKeyId : delegateBearerDid.document.verificationMethod![0].id,
|
|
824
|
+
randomPin,
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
const formEncodedRequest = new URLSearchParams({
|
|
828
|
+
id_token : encryptedResponse,
|
|
829
|
+
state : authRequest.state,
|
|
830
|
+
}).toString();
|
|
831
|
+
|
|
832
|
+
logger.log(`Sending auth response object to Web5 Connect server: ${authRequest.redirect_uri}`);
|
|
833
|
+
await fetch(authRequest.redirect_uri, {
|
|
834
|
+
body : formEncodedRequest,
|
|
835
|
+
method : 'POST',
|
|
836
|
+
headers : {
|
|
837
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
838
|
+
},
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
export const Oidc = {
|
|
843
|
+
createAuthRequest,
|
|
844
|
+
encryptAuthRequest,
|
|
845
|
+
getAuthRequest,
|
|
846
|
+
decryptAuthRequest,
|
|
847
|
+
createPermissionGrants,
|
|
848
|
+
createResponseObject,
|
|
849
|
+
encryptAuthResponse,
|
|
850
|
+
decryptAuthResponse,
|
|
851
|
+
deriveSharedKey,
|
|
852
|
+
signJwt,
|
|
853
|
+
verifyJwt,
|
|
854
|
+
buildOidcUrl,
|
|
855
|
+
generateCodeChallenge,
|
|
856
|
+
submitAuthResponse,
|
|
857
|
+
};
|