Repository: solid/community-server
Pull request #1160 (2022-02-15 09:59:16)

test: Add IDP integration test for clients with a WebID
#### 📁 Related issues

#838

#### ✍️ Description

Adds an integration test for clients that have a valid WebID when authenticating with the IDP.



Patch:
diff --git a/config/identity/ownership/token.json b/config/identity/ownership/token.json
index fb3275c70..e5d9fdace 100644
--- a/config/identity/ownership/token.json
+++ b/config/identity/ownership/token.json
@@ -5,7 +5,6 @@
       "comment": "Determines WebID ownership by requesting a specific value to be added to the WebID document",
       "@id": "urn:solid-server:auth:password:OwnershipValidator",
       "@type": "TokenOwnershipValidator",
-      "converter": { "@id": "urn:solid-server:default:RepresentationConverter" },
       "storage": { "@id": "urn:solid-server:default:ExpiringTokenStorage" }
     },
 
diff --git a/config/ldp/authorization/readers/access-checkers/agent-group.json b/config/ldp/authorization/readers/access-checkers/agent-group.json
index fca974b8f..9cf614b1c 100644
--- a/config/ldp/authorization/readers/access-checkers/agent-group.json
+++ b/config/ldp/authorization/readers/access-checkers/agent-group.json
@@ -5,7 +5,6 @@
       "comment": "Checks if the agent belongs to a group that has access.",
       "@id": "urn:solid-server:default:AgentGroupAccessChecker",
       "@type": "AgentGroupAccessChecker",
-      "converter": { "@id": "urn:solid-server:default:RepresentationConverter" },
       "cache": {
         "@id": "urn:solid-server:default:ExpiringAclCache",
         "@type": "WrappedExpiringStorage",
diff --git a/package-lock.json b/package-lock.json
index 6d9cc1450..b1e825606 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -51,6 +51,7 @@
         "oidc-provider": "^6.31.1",
         "pump": "^3.0.0",
         "punycode": "^2.1.1",
+        "rdf-dereference": "^1.9.0",
         "rdf-parse": "^1.9.1",
         "rdf-serialize": "^1.2.0",
         "rdf-terms": "^1.7.1",
@@ -69,7 +70,7 @@
         "community-solid-server": "bin/server.js"
       },
       "devDependencies": {
-        "@inrupt/solid-client-authn-node": "^1.11.3",
+        "@inrupt/solid-client-authn-node": "^1.11.5",
         "@microsoft/tsdoc-config": "^0.15.2",
         "@tsconfig/node12": "^1.0.9",
         "@types/cheerio": "^0.22.30",
@@ -3727,9 +3728,9 @@
       }
     },
     "node_modules/@inrupt/solid-client-authn-core": {
-      "version": "1.11.3",
-      "resolved": "https://registry.npmjs.org/@inrupt/solid-client-authn-core/-/solid-client-authn-core-1.11.3.tgz",
-      "integrity": "sha512-XhxlH+mmCbDQxRQVCQWR5tS/jM0S+lHyFkxDhT9Ts3gAokv4YfwgYsIe2jHgiY8T4Qp3keYFy4RVhZdSRgKGIQ==",
+      "version": "1.11.5",
+      "resolved": "https://registry.npmjs.org/@inrupt/solid-client-authn-core/-/solid-client-authn-core-1.11.5.tgz",
+      "integrity": "sha512-hsGKgv2SsTEo33V5t9crRs+RdKgdtxuIb3VoiGmaDXqLkd9rI/CZZkqnpwUskf4VuBN7Z3h9TKAFohRJMcvF7Q==",
       "dev": true,
       "dependencies": {
         "@inrupt/solid-common-vocab": "^1.0.0",
@@ -3743,24 +3744,24 @@
       }
     },
     "node_modules/@inrupt/solid-client-authn-node": {
-      "version": "1.11.3",
-      "resolved": "https://registry.npmjs.org/@inrupt/solid-client-authn-node/-/solid-client-authn-node-1.11.3.tgz",
-      "integrity": "sha512-/4z6TcFOLbFwXPFK9P2TGeCVY19vMTDn1UBRiOJ4PA6arsTefWdtd1JMwSTkdB3BKg8bJJPEaYuA87odtixPCQ==",
+      "version": "1.11.5",
+      "resolved": "https://registry.npmjs.org/@inrupt/solid-client-authn-node/-/solid-client-authn-node-1.11.5.tgz",
+      "integrity": "sha512-ToPEcnCL5BeQdfeB3MzhR9I+ZXQyuoGnumt60R57QarUi7de/BU2Ag+IAEvcbm+HvEtfF5LaabWbapryTRPGYA==",
       "dev": true,
       "dependencies": {
-        "@inrupt/solid-client-authn-core": "^1.11.3",
-        "@types/node": "^16.11.12",
+        "@inrupt/solid-client-authn-core": "^1.11.5",
+        "@types/node": "^17.0.2",
         "@types/uuid": "^8.3.0",
         "cross-fetch": "^3.0.6",
         "jose": "^4.3.7",
-        "openid-client": "^4.2.2",
+        "openid-client": "^5.1.0",
         "uuid": "^8.3.2"
       }
     },
     "node_modules/@inrupt/solid-client-authn-node/node_modules/@types/node": {
-      "version": "16.11.22",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.22.tgz",
-      "integrity": "sha512-DYNtJWauMQ9RNpesl4aVothr97/tIJM8HbyOXJ0AYT1Z2bEjLHyfjOBPAQQVMLf8h3kSShYfNk8Wnto8B2zHUA==",
+      "version": "17.0.18",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.18.tgz",
+      "integrity": "sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA==",
       "dev": true
     },
     "node_modules/@inrupt/solid-common-vocab": {
@@ -4340,18 +4341,6 @@
         "@types/node": "*"
       }
     },
-    "node_modules/@types/cacheable-request": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz",
-      "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==",
-      "dev": true,
-      "dependencies": {
-        "@types/http-cache-semantics": "*",
-        "@types/keyv": "*",
-        "@types/node": "*",
-        "@types/responselike": "*"
-      }
-    },
     "node_modules/@types/cheerio": {
       "version": "0.22.30",
       "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.30.tgz",
@@ -4454,12 +4443,6 @@
       "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz",
       "integrity": "sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ=="
     },
-    "node_modules/@types/http-cache-semantics": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz",
-      "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==",
-      "dev": true
-    },
     "node_modules/@types/http-errors": {
       "version": "1.8.0",
       "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz",
@@ -4524,15 +4507,6 @@
       "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz",
       "integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw=="
     },
-    "node_modules/@types/keyv": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz",
-      "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==",
-      "dev": true,
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
     "node_modules/@types/koa": {
       "version": "2.13.1",
       "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.13.1.tgz",
@@ -4705,15 +4679,6 @@
         "@types/bluebird": "*"
       }
     },
-    "node_modules/@types/responselike": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
-      "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
-      "dev": true,
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
     "node_modules/@types/semver": {
       "version": "7.3.6",
       "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.6.tgz",
@@ -5368,19 +5333,6 @@
         "node": ">= 6.0.0"
       }
     },
-    "node_modules/aggregate-error": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
-      "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
-      "dev": true,
-      "dependencies": {
-        "clean-stack": "^2.0.0",
-        "indent-string": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
     "node_modules/ajv": {
       "version": "6.12.6",
       "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -5946,15 +5898,6 @@
         "node": ">= 6.0.0"
       }
     },
-    "node_modules/cacheable-lookup": {
-      "version": "5.0.4",
-      "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
-      "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
-      "dev": true,
-      "engines": {
-        "node": ">=10.6.0"
-      }
-    },
     "node_modules/cacheable-request": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz",
@@ -6233,15 +6176,6 @@
         "node": ">=0.8.0"
       }
     },
-    "node_modules/clean-stack": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
-      "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      }
-    },
     "node_modules/cli-boxes": {
       "version": "2.2.1",
       "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz",
@@ -9269,31 +9203,6 @@
         "node": ">= 6"
       }
     },
-    "node_modules/http2-wrapper": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
-      "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
-      "dev": true,
-      "dependencies": {
-        "quick-lru": "^5.1.1",
-        "resolve-alpn": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=10.19.0"
-      }
-    },
-    "node_modules/http2-wrapper/node_modules/quick-lru": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
-      "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/https-proxy-agent": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
@@ -12183,213 +12092,23 @@
       }
     },
     "node_modules/openid-client": {
-      "version": "4.7.4",
-      "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-4.7.4.tgz",
-      "integrity": "sha512-n+RURXYuR0bBZo9i0pn+CXZSyg5JYQ1nbwEwPQvLE7EcJt/vMZ2iIMjLehl5DvCN53XUoPVZs9KAE5r6d9fxsw==",
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.1.3.tgz",
+      "integrity": "sha512-i5quCXurPkN50ndRLE2D3Q6khz6AieJ0gTKOmsl3G4ZIP/Udf5Qw5CMRdhMvbFvfKRrkcCWPFXmduFUFYTC0xw==",
       "dev": true,
       "dependencies": {
-        "aggregate-error": "^3.1.0",
-        "got": "^11.8.0",
-        "jose": "^2.0.5",
+        "jose": "^4.1.4",
         "lru-cache": "^6.0.0",
-        "make-error": "^1.3.6",
         "object-hash": "^2.0.1",
         "oidc-token-hash": "^5.0.1"
       },
       "engines": {
-        "node": "^10.19.0 || >=12.0.0 < 13 || >=13.7.0 < 14 || >= 14.2.0"
+        "node": "^12.19.0 || ^14.15.0 || ^16.13.0"
       },
       "funding": {
         "url": "https://github.com/sponsors/panva"
       }
     },
-    "node_modules/openid-client/node_modules/@sindresorhus/is": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz",
-      "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sindresorhus/is?sponsor=1"
-      }
-    },
-    "node_modules/openid-client/node_modules/@szmarczak/http-timer": {
-      "version": "4.0.5",
-      "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz",
-      "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==",
-      "dev": true,
-      "dependencies": {
-        "defer-to-connect": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/openid-client/node_modules/cacheable-request": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz",
-      "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==",
-      "dev": true,
-      "dependencies": {
-        "clone-response": "^1.0.2",
-        "get-stream": "^5.1.0",
-        "http-cache-semantics": "^4.0.0",
-        "keyv": "^4.0.0",
-        "lowercase-keys": "^2.0.0",
-        "normalize-url": "^6.0.1",
-        "responselike": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/openid-client/node_modules/decompress-response": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
-      "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
-      "dev": true,
-      "dependencies": {
-        "mimic-response": "^3.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/openid-client/node_modules/defer-to-connect": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
-      "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/openid-client/node_modules/get-stream": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
-      "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
-      "dev": true,
-      "dependencies": {
-        "pump": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/openid-client/node_modules/got": {
-      "version": "11.8.2",
-      "resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz",
-      "integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==",
-      "dev": true,
-      "dependencies": {
-        "@sindresorhus/is": "^4.0.0",
-        "@szmarczak/http-timer": "^4.0.5",
-        "@types/cacheable-request": "^6.0.1",
-        "@types/responselike": "^1.0.0",
-        "cacheable-lookup": "^5.0.3",
-        "cacheable-request": "^7.0.1",
-        "decompress-response": "^6.0.0",
-        "http2-wrapper": "^1.0.0-beta.5.2",
-        "lowercase-keys": "^2.0.0",
-        "p-cancelable": "^2.0.0",
-        "responselike": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=10.19.0"
-      },
-      "funding": {
-        "url": "https://github.com/sindresorhus/got?sponsor=1"
-      }
-    },
-    "node_modules/openid-client/node_modules/jose": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.5.tgz",
-      "integrity": "sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA==",
-      "dev": true,
-      "dependencies": {
-        "@panva/asn1.js": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=10.13.0 < 13 || >=13.7.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/panva"
-      }
-    },
-    "node_modules/openid-client/node_modules/json-buffer": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
-      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
-      "dev": true
-    },
-    "node_modules/openid-client/node_modules/keyv": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz",
-      "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==",
-      "dev": true,
-      "dependencies": {
-        "json-buffer": "3.0.1"
-      }
-    },
-    "node_modules/openid-client/node_modules/lowercase-keys": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
-      "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/openid-client/node_modules/mimic-response": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
-      "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/openid-client/node_modules/normalize-url": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
-      "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/openid-client/node_modules/p-cancelable": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
-      "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/openid-client/node_modules/responselike": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
-      "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
-      "dev": true,
-      "dependencies": {
-        "lowercase-keys": "^2.0.0"
-      }
-    },
     "node_modules/optionator": {
       "version": "0.9.1",
       "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
@@ -13328,12 +13047,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/resolve-alpn": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.1.2.tgz",
-      "integrity": "sha512-8OyfzhAtA32LVUsJSke3auIyINcwdh5l3cvYKdKO0nvsYSKuiLfTM5i78PJswFPT8y6cPW+L1v6/hE95chcpDA==",
-      "dev": true
-    },
     "node_modules/resolve-cwd": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
@@ -17992,9 +17705,9 @@
       "dev": true
     },
     "@inrupt/solid-client-authn-core": {
-      "version": "1.11.3",
-      "resolved": "https://registry.npmjs.org/@inrupt/solid-client-authn-core/-/solid-client-authn-core-1.11.3.tgz",
-      "integrity": "sha512-XhxlH+mmCbDQxRQVCQWR5tS/jM0S+lHyFkxDhT9Ts3gAokv4YfwgYsIe2jHgiY8T4Qp3keYFy4RVhZdSRgKGIQ==",
+      "version": "1.11.5",
+      "resolved": "https://registry.npmjs.org/@inrupt/solid-client-authn-core/-/solid-client-authn-core-1.11.5.tgz",
+      "integrity": "sha512-hsGKgv2SsTEo33V5t9crRs+RdKgdtxuIb3VoiGmaDXqLkd9rI/CZZkqnpwUskf4VuBN7Z3h9TKAFohRJMcvF7Q==",
       "dev": true,
       "requires": {
         "@inrupt/solid-common-vocab": "^1.0.0",
@@ -18008,24 +17721,24 @@
       }
     },
     "@inrupt/solid-client-authn-node": {
-      "version": "1.11.3",
-      "resolved": "https://registry.npmjs.org/@inrupt/solid-client-authn-node/-/solid-client-authn-node-1.11.3.tgz",
-      "integrity": "sha512-/4z6TcFOLbFwXPFK9P2TGeCVY19vMTDn1UBRiOJ4PA6arsTefWdtd1JMwSTkdB3BKg8bJJPEaYuA87odtixPCQ==",
+      "version": "1.11.5",
+      "resolved": "https://registry.npmjs.org/@inrupt/solid-client-authn-node/-/solid-client-authn-node-1.11.5.tgz",
+      "integrity": "sha512-ToPEcnCL5BeQdfeB3MzhR9I+ZXQyuoGnumt60R57QarUi7de/BU2Ag+IAEvcbm+HvEtfF5LaabWbapryTRPGYA==",
       "dev": true,
       "requires": {
-        "@inrupt/solid-client-authn-core": "^1.11.3",
-        "@types/node": "^16.11.12",
+        "@inrupt/solid-client-authn-core": "^1.11.5",
+        "@types/node": "^17.0.2",
         "@types/uuid": "^8.3.0",
         "cross-fetch": "^3.0.6",
         "jose": "^4.3.7",
-        "openid-client": "^4.2.2",
+        "openid-client": "^5.1.0",
         "uuid": "^8.3.2"
       },
       "dependencies": {
         "@types/node": {
-          "version": "16.11.22",
-          "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.22.tgz",
-          "integrity": "sha512-DYNtJWauMQ9RNpesl4aVothr97/tIJM8HbyOXJ0AYT1Z2bEjLHyfjOBPAQQVMLf8h3kSShYfNk8Wnto8B2zHUA==",
+          "version": "17.0.18",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.18.tgz",
+          "integrity": "sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA==",
           "dev": true
         }
       }
@@ -18519,18 +18232,6 @@
         "@types/node": "*"
       }
     },
-    "@types/cacheable-request": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz",
-      "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==",
-      "dev": true,
-      "requires": {
-        "@types/http-cache-semantics": "*",
-        "@types/keyv": "*",
-        "@types/node": "*",
-        "@types/responselike": "*"
-      }
-    },
     "@types/cheerio": {
       "version": "0.22.30",
       "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.30.tgz",
@@ -18633,12 +18334,6 @@
       "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz",
       "integrity": "sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ=="
     },
-    "@types/http-cache-semantics": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz",
-      "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==",
-      "dev": true
-    },
     "@types/http-errors": {
       "version": "1.8.0",
       "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz",
@@ -18703,15 +18398,6 @@
       "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz",
       "integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw=="
     },
-    "@types/keyv": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz",
-      "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==",
-      "dev": true,
-      "requires": {
-        "@types/node": "*"
-      }
-    },
     "@types/koa": {
       "version": "2.13.1",
       "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.13.1.tgz",
@@ -18883,15 +18569,6 @@
         "@types/bluebird": "*"
       }
     },
-    "@types/responselike": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
-      "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
-      "dev": true,
-      "requires": {
-        "@types/node": "*"
-      }
-    },
     "@types/semver": {
       "version": "7.3.6",
       "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.6.tgz",
@@ -19328,16 +19005,6 @@
         "debug": "4"
       }
     },
-    "aggregate-error": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
-      "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
-      "dev": true,
-      "requires": {
-        "clean-stack": "^2.0.0",
-        "indent-string": "^4.0.0"
-      }
-    },
     "ajv": {
       "version": "6.12.6",
       "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -19779,12 +19446,6 @@
         "ylru": "^1.2.0"
       }
     },
-    "cacheable-lookup": {
-      "version": "5.0.4",
-      "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
-      "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
-      "dev": true
-    },
     "cacheable-request": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz",
@@ -19993,12 +19654,6 @@
         }
       }
     },
-    "clean-stack": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
-      "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
-      "dev": true
-    },
     "cli-boxes": {
       "version": "2.2.1",
       "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz",
@@ -22324,24 +21979,6 @@
         "debug": "4"
       }
     },
-    "http2-wrapper": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
-      "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
-      "dev": true,
-      "requires": {
-        "quick-lru": "^5.1.1",
-        "resolve-alpn": "^1.0.0"
-      },
-      "dependencies": {
-        "quick-lru": {
-          "version": "5.1.1",
-          "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
-          "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
-          "dev": true
-        }
-      }
-    },
     "https-proxy-agent": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
@@ -24547,150 +24184,15 @@
       "dev": true
     },
     "openid-client": {
-      "version": "4.7.4",
-      "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-4.7.4.tgz",
-      "integrity": "sha512-n+RURXYuR0bBZo9i0pn+CXZSyg5JYQ1nbwEwPQvLE7EcJt/vMZ2iIMjLehl5DvCN53XUoPVZs9KAE5r6d9fxsw==",
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.1.3.tgz",
+      "integrity": "sha512-i5quCXurPkN50ndRLE2D3Q6khz6AieJ0gTKOmsl3G4ZIP/Udf5Qw5CMRdhMvbFvfKRrkcCWPFXmduFUFYTC0xw==",
       "dev": true,
       "requires": {
-        "aggregate-error": "^3.1.0",
-        "got": "^11.8.0",
-        "jose": "^2.0.5",
+        "jose": "^4.1.4",
         "lru-cache": "^6.0.0",
-        "make-error": "^1.3.6",
         "object-hash": "^2.0.1",
         "oidc-token-hash": "^5.0.1"
-      },
-      "dependencies": {
-        "@sindresorhus/is": {
-          "version": "4.0.1",
-          "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz",
-          "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==",
-          "dev": true
-        },
-        "@szmarczak/http-timer": {
-          "version": "4.0.5",
-          "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz",
-          "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==",
-          "dev": true,
-          "requires": {
-            "defer-to-connect": "^2.0.0"
-          }
-        },
-        "cacheable-request": {
-          "version": "7.0.2",
-          "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz",
-          "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==",
-          "dev": true,
-          "requires": {
-            "clone-response": "^1.0.2",
-            "get-stream": "^5.1.0",
-            "http-cache-semantics": "^4.0.0",
-            "keyv": "^4.0.0",
-            "lowercase-keys": "^2.0.0",
-            "normalize-url": "^6.0.1",
-            "responselike": "^2.0.0"
-          }
-        },
-        "decompress-response": {
-          "version": "6.0.0",
-          "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
-          "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
-          "dev": true,
-          "requires": {
-            "mimic-response": "^3.1.0"
-          }
-        },
-        "defer-to-connect": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
-          "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
-          "dev": true
-        },
-        "get-stream": {
-          "version": "5.2.0",
-          "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
-          "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
-          "dev": true,
-          "requires": {
-            "pump": "^3.0.0"
-          }
-        },
-        "got": {
-          "version": "11.8.2",
-          "resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz",
-          "integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==",
-          "dev": true,
-          "requires": {
-            "@sindresorhus/is": "^4.0.0",
-            "@szmarczak/http-timer": "^4.0.5",
-            "@types/cacheable-request": "^6.0.1",
-            "@types/responselike": "^1.0.0",
-            "cacheable-lookup": "^5.0.3",
-            "cacheable-request": "^7.0.1",
-            "decompress-response": "^6.0.0",
-            "http2-wrapper": "^1.0.0-beta.5.2",
-            "lowercase-keys": "^2.0.0",
-            "p-cancelable": "^2.0.0",
-            "responselike": "^2.0.0"
-          }
-        },
-        "jose": {
-          "version": "2.0.5",
-          "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.5.tgz",
-          "integrity": "sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA==",
-          "dev": true,
-          "requires": {
-            "@panva/asn1.js": "^1.0.0"
-          }
-        },
-        "json-buffer": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
-          "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
-          "dev": true
-        },
-        "keyv": {
-          "version": "4.0.3",
-          "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz",
-          "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==",
-          "dev": true,
-          "requires": {
-            "json-buffer": "3.0.1"
-          }
-        },
-        "lowercase-keys": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
-          "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
-          "dev": true
-        },
-        "mimic-response": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
-          "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
-          "dev": true
-        },
-        "normalize-url": {
-          "version": "6.1.0",
-          "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
-          "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
-          "dev": true
-        },
-        "p-cancelable": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
-          "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
-          "dev": true
-        },
-        "responselike": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
-          "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
-          "dev": true,
-          "requires": {
-            "lowercase-keys": "^2.0.0"
-          }
-        }
       }
     },
     "optionator": {
@@ -25441,12 +24943,6 @@
         "path-parse": "^1.0.6"
       }
     },
-    "resolve-alpn": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.1.2.tgz",
-      "integrity": "sha512-8OyfzhAtA32LVUsJSke3auIyINcwdh5l3cvYKdKO0nvsYSKuiLfTM5i78PJswFPT8y6cPW+L1v6/hE95chcpDA==",
-      "dev": true
-    },
     "resolve-cwd": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
diff --git a/package.json b/package.json
index a00ff2b86..1ddbc435d 100644
--- a/package.json
+++ b/package.json
@@ -117,6 +117,7 @@
     "oidc-provider": "^6.31.1",
     "pump": "^3.0.0",
     "punycode": "^2.1.1",
+    "rdf-dereference": "^1.9.0",
     "rdf-parse": "^1.9.1",
     "rdf-serialize": "^1.2.0",
     "rdf-terms": "^1.7.1",
@@ -132,7 +133,7 @@
     "yargs": "^17.3.1"
   },
   "devDependencies": {
-    "@inrupt/solid-client-authn-node": "^1.11.3",
+    "@inrupt/solid-client-authn-node": "^1.11.5",
     "@microsoft/tsdoc-config": "^0.15.2",
     "@tsconfig/node12": "^1.0.9",
     "@types/cheerio": "^0.22.30",
diff --git a/src/authorization/access/AgentGroupAccessChecker.ts b/src/authorization/access/AgentGroupAccessChecker.ts
index b56cc537d..64b458efc 100644
--- a/src/authorization/access/AgentGroupAccessChecker.ts
+++ b/src/authorization/access/AgentGroupAccessChecker.ts
@@ -1,6 +1,5 @@
 import type { Store, Term } from 'n3';
 import type { ResourceIdentifier } from '../../http/representation/ResourceIdentifier';
-import type { RepresentationConverter } from '../../storage/conversion/RepresentationConverter';
 import type { ExpiringStorage } from '../../storage/keyvalue/ExpiringStorage';
 import { fetchDataset } from '../../util/FetchUtil';
 import { promiseSome } from '../../util/PromiseUtil';
@@ -19,14 +18,11 @@ import { AccessChecker } from './AccessChecker';
  * `expiration` parameter is how long entries in the cache should be stored in seconds, defaults to 3600.
  */
 export class AgentGroupAccessChecker extends AccessChecker {
-  private readonly converter: RepresentationConverter;
   private readonly cache: ExpiringStorage<string, Promise<Store>>;
   private readonly expiration: number;
 
-  public constructor(converter: RepresentationConverter, cache: ExpiringStorage<string, Promise<Store>>,
-    expiration = 3600) {
+  public constructor(cache: ExpiringStorage<string, Promise<Store>>, expiration = 3600) {
     super();
-    this.converter = converter;
     this.cache = cache;
     this.expiration = expiration * 1000;
   }
@@ -65,7 +61,7 @@ export class AgentGroupAccessChecker extends AccessChecker {
     let result = await this.cache.get(url);
     if (!result) {
       const prom = (async(): Promise<Store> => {
-        const representation = await fetchDataset(url, this.converter);
+        const representation = await fetchDataset(url);
         return readableToQuads(representation.data);
       })();
       await this.cache.set(url, prom, this.expiration);
diff --git a/src/identity/ownership/TokenOwnershipValidator.ts b/src/identity/ownership/TokenOwnershipValidator.ts
index 4ba0ca501..3f0504d83 100644
--- a/src/identity/ownership/TokenOwnershipValidator.ts
+++ b/src/identity/ownership/TokenOwnershipValidator.ts
@@ -2,7 +2,6 @@ import type { Quad } from 'n3';
 import { DataFactory } from 'n3';
 import { v4 } from 'uuid';
 import { getLoggerFor } from '../../logging/LogUtil';
-import type { RepresentationConverter } from '../../storage/conversion/RepresentationConverter';
 import type { ExpiringStorage } from '../../storage/keyvalue/ExpiringStorage';
 import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
 import { fetchDataset } from '../../util/FetchUtil';
@@ -17,13 +16,11 @@ const { literal, namedNode, quad } = DataFactory;
 export class TokenOwnershipValidator extends OwnershipValidator {
   protected readonly logger = getLoggerFor(this);
 
-  private readonly converter: RepresentationConverter;
   private readonly storage: ExpiringStorage<string, string>;
   private readonly expiration: number;
 
-  public constructor(converter: RepresentationConverter, storage: ExpiringStorage<string, string>, expiration = 30) {
+  public constructor(storage: ExpiringStorage<string, string>, expiration = 30) {
     super();
-    this.converter = converter;
     this.storage = storage;
     // Convert minutes to milliseconds
     this.expiration = expiration * 60 * 1000;
@@ -66,7 +63,7 @@ export class TokenOwnershipValidator extends OwnershipValidator {
    * Fetches data from the WebID to determine if the token is present.
    */
   private async hasToken(webId: string, token: string): Promise<boolean> {
-    const representation = await fetchDataset(webId, this.converter);
+    const representation = await fetchDataset(webId);
     const expectedQuad = quad(namedNode(webId), SOLID.terms.oidcIssuerRegistrationToken, literal(token));
     for await (const data of representation.data) {
       const triple = data as Quad;
diff --git a/src/identity/storage/WebIdAdapterFactory.ts b/src/identity/storage/WebIdAdapterFactory.ts
index c29c334aa..734d0126e 100644
--- a/src/identity/storage/WebIdAdapterFactory.ts
+++ b/src/identity/storage/WebIdAdapterFactory.ts
@@ -5,7 +5,7 @@ import type { Adapter, AdapterPayload } from 'oidc-provider';
 import { getLoggerFor } from '../../logging/LogUtil';
 import type { RepresentationConverter } from '../../storage/conversion/RepresentationConverter';
 import { createErrorMessage } from '../../util/errors/ErrorUtil';
-import { fetchDataset } from '../../util/FetchUtil';
+import { responseToDataset } from '../../util/FetchUtil';
 import { OIDC } from '../../util/Vocabularies';
 import type { AdapterFactory } from './AdapterFactory';
 
@@ -91,7 +91,7 @@ export class WebIdAdapter implements Adapter {
    * @param response - Response object from the request.
    */
   private async parseRdfWebId(data: string, id: string, response: Response): Promise<AdapterPayload> {
-    const representation = await fetchDataset(response, this.converter, data);
+    const representation = await responseToDataset(response, this.converter, data);
 
     // Find the valid redirect URIs
     const redirectUris: string[] = [];
diff --git a/src/util/FetchUtil.ts b/src/util/FetchUtil.ts
index 9d1c317a3..5398dede5 100644
--- a/src/util/FetchUtil.ts
+++ b/src/util/FetchUtil.ts
@@ -1,5 +1,8 @@
+import type { Readable } from 'stream';
+import type { Quad } from '@rdfjs/types';
+import arrayifyStream from 'arrayify-stream';
 import type { Response } from 'cross-fetch';
-import { fetch } from 'cross-fetch';
+import rdfDereferencer from 'rdf-dereference';
 import { BasicRepresentation } from '../http/representation/BasicRepresentation';
 import type { Representation } from '../http/representation/Representation';
 import { getLoggerFor } from '../logging/LogUtil';
@@ -12,24 +15,32 @@ const logger = getLoggerFor('FetchUtil');
 
 /**
  * Fetches an RDF dataset from the given URL.
- * Input can also be a Response if the request was already made.
+ *
+ * Response will be a Representation with content-type internal/quads.
+ */
+export async function fetchDataset(url: string): Promise<Representation> {
+  // Try content negotiation to parse quads from the URL
+  return (async(): Promise<Representation> => {
+    try {
+      const quadStream = (await rdfDereferencer.dereference(url)).quads as Readable;
+      const quadArray = await arrayifyStream(quadStream) as Quad[];
+      return new BasicRepresentation(quadArray, { path: url }, INTERNAL_QUADS, false);
+    } catch {
+      throw new BadRequestHttpError(`Could not parse resource at URL (${url})!`);
+    }
+  })();
+}
+
+/**
+ * Converts a given Response (from a request that was already made) to  an RDF dataset.
  * In case the given Response object was already parsed its body can be passed along as a string.
  *
  * The converter will be used to convert the response body to RDF.
  *
  * Response will be a Representation with content-type internal/quads.
  */
-export async function fetchDataset(url: string, converter: RepresentationConverter): Promise<Representation>;
-export async function fetchDataset(response: Response, converter: RepresentationConverter, body?: string):
-Promise<Representation>;
-export async function fetchDataset(input: string | Response, converter: RepresentationConverter, body?: string):
+export async function responseToDataset(response: Response, converter: RepresentationConverter, body?: string):
 Promise<Representation> {
-  let response: Response;
-  if (typeof input === 'string') {
-    response = await fetch(input);
-  } else {
-    response = input;
-  }
   if (!body) {
     body = await response.text();
   }


Test patch:
diff --git a/test/integration/Identity.test.ts b/test/integration/Identity.test.ts
index a7c8ee2b0..3be14c145 100644
--- a/test/integration/Identity.test.ts
+++ b/test/integration/Identity.test.ts
@@ -176,6 +176,83 @@ describe('A Solid server with IDP', (): void => {
     });
   });
 
+  describe('authenticating a client with a WebID', (): void => {
+    const clientId = joinUrl(baseUrl, 'client-id');
+    const badClientId = joinUrl(baseUrl, 'bad-client-id');
+    /* eslint-disable @typescript-eslint/naming-convention */
+    const clientJson = {
+      '@context': 'https://www.w3.org/ns/solid/oidc-context.jsonld',
+
+      client_id: clientId,
+      client_name: 'Solid Application Name',
+      redirect_uris: [ redirectUrl ],
+      post_logout_redirect_uris: [ 'https://app.example/logout' ],
+      client_uri: 'https://app.example/',
+      logo_uri: 'https://app.example/logo.png',
+      tos_uri: 'https://app.example/tos.html',
+      scope: 'openid profile offline_access webid',
+      grant_types: [ 'refresh_token', 'authorization_code' ],
+      response_types: [ 'code' ],
+      default_max_age: 3600,
+      require_auth_time: true,
+    };
+    /* eslint-enable @typescript-eslint/naming-convention */
+    let state: IdentityTestState;
+
+    beforeAll(async(): Promise<void> => {
+      state = new IdentityTestState(baseUrl, redirectUrl, oidcIssuer);
+
+      await fetch(clientId, {
+        method: 'PUT',
+        headers: { 'content-type': 'application/ld+json' },
+        body: JSON.stringify(clientJson),
+      });
+
+      // This client will always reject requests since there is no valid redirect
+      clientJson.client_id = badClientId;
+      clientJson.redirect_uris = [];
+      await fetch(badClientId, {
+        method: 'PUT',
+        headers: { 'content-type': 'application/ld+json' },
+        body: JSON.stringify(clientJson),
+      });
+    });
+
+    afterAll(async(): Promise<void> => {
+      await state.session.logout();
+    });
+
+    it('initializes the session and logs in.', async(): Promise<void> => {
+      const url = await state.startSession(clientId);
+      const res = await state.fetchIdp(url);
+      expect(res.status).toBe(200);
+      await state.login(url, email, password);
+      expect(state.session.info?.webId).toBe(webId);
+    });
+
+    it('rejects requests in case the redirect URL is not accepted.', async(): Promise<void> => {
+      // This test allows us to make sure the server actually uses the client WebID.
+      // If it did not, it would not see the invalid redirect_url array.
+
+      let nextUrl = '';
+      await state.session.login({
+        redirectUrl,
+        oidcIssuer,
+        clientId: badClientId,
+        handleRedirect(data): void {
+          nextUrl = data;
+        },
+      });
+      expect(nextUrl.length > 0).toBeTruthy();
+      expect(nextUrl.startsWith(oidcIssuer)).toBeTruthy();
+
+      // Redirect will error due to invalid client WebID
+      const res = await state.fetchIdp(nextUrl);
+      expect(res.status).toBe(400);
+      await expect(res.text()).resolves.toContain('invalid_redirect_uri');
+    });
+  });
+
   describe('resetting password', (): void => {
     let nextUrl: string;
 
diff --git a/test/integration/IdentityTestState.ts b/test/integration/IdentityTestState.ts
index 59816a44a..17f4364bb 100644
--- a/test/integration/IdentityTestState.ts
+++ b/test/integration/IdentityTestState.ts
@@ -74,11 +74,12 @@ export class IdentityTestState {
    * Initializes an authentication session and stores the relevant cookies for later re-use.
    * All te relevant links from the login page get extracted.
    */
-  public async startSession(): Promise<string> {
+  public async startSession(clientId?: string): Promise<string> {
     let nextUrl = '';
     await this.session.login({
       redirectUrl: this.redirectUrl,
       oidcIssuer: this.oidcIssuer,
+      clientId,
       handleRedirect(data): void {
         nextUrl = data;
       },
diff --git a/test/unit/authorization/access/AgentGroupAccessChecker.test.ts b/test/unit/authorization/access/AgentGroupAccessChecker.test.ts
index ae86a9356..adc44727c 100644
--- a/test/unit/authorization/access/AgentGroupAccessChecker.test.ts
+++ b/test/unit/authorization/access/AgentGroupAccessChecker.test.ts
@@ -3,7 +3,6 @@ import type { AccessCheckerArgs } from '../../../../src/authorization/access/Acc
 import { AgentGroupAccessChecker } from '../../../../src/authorization/access/AgentGroupAccessChecker';
 import { BasicRepresentation } from '../../../../src/http/representation/BasicRepresentation';
 import type { Representation } from '../../../../src/http/representation/Representation';
-import type { RepresentationConverter } from '../../../../src/storage/conversion/RepresentationConverter';
 import type { ExpiringStorage } from '../../../../src/storage/keyvalue/ExpiringStorage';
 import { INTERNAL_QUADS } from '../../../../src/util/ContentTypes';
 import * as fetchUtil from '../../../../src/util/FetchUtil';
@@ -18,7 +17,6 @@ describe('An AgentGroupAccessChecker', (): void => {
   acl.addQuad(namedNode('noMatch'), ACL.terms.agentGroup, namedNode('badGroup'));
   let fetchMock: jest.SpyInstance;
   let representation: Representation;
-  const converter: RepresentationConverter = {} as any;
   let cache: ExpiringStorage<string, Promise<Store>>;
   let checker: AgentGroupAccessChecker;
 
@@ -31,7 +29,7 @@ describe('An AgentGroupAccessChecker', (): void => {
 
     cache = new Map() as any;
 
-    checker = new AgentGroupAccessChecker(converter, cache);
+    checker = new AgentGroupAccessChecker(cache);
   });
 
   it('can handle all requests.', async(): Promise<void> => {
diff --git a/test/unit/identity/ownership/TokenOwnershipValidator.test.ts b/test/unit/identity/ownership/TokenOwnershipValidator.test.ts
index 78b327e15..46c58f0c3 100644
--- a/test/unit/identity/ownership/TokenOwnershipValidator.test.ts
+++ b/test/unit/identity/ownership/TokenOwnershipValidator.test.ts
@@ -1,15 +1,17 @@
-import { fetch } from 'cross-fetch';
+import { Readable } from 'stream';
 import { DataFactory } from 'n3';
 import type { Quad } from 'n3';
+import rdfDereferencer from 'rdf-dereference';
 import { v4 } from 'uuid';
 import { TokenOwnershipValidator } from '../../../../src/identity/ownership/TokenOwnershipValidator';
-import { RdfToQuadConverter } from '../../../../src/storage/conversion/RdfToQuadConverter';
 import type { ExpiringStorage } from '../../../../src/storage/keyvalue/ExpiringStorage';
 import { SOLID } from '../../../../src/util/Vocabularies';
 const { literal, namedNode, quad } = DataFactory;
 
-jest.mock('cross-fetch');
 jest.mock('uuid');
+jest.mock('rdf-dereference', (): any => ({
+  dereference: jest.fn(),
+}));
 
 function quadToString(qq: Quad): string {
   const subPred = `<${qq.subject.value}> <${qq.predicate.value}>`;
@@ -20,21 +22,19 @@ function quadToString(qq: Quad): string {
 }
 
 describe('A TokenOwnershipValidator', (): void => {
-  const fetchMock: jest.Mock = fetch as any;
+  const rdfDereferenceMock: jest.Mocked<typeof rdfDereferencer> = rdfDereferencer as any;
   const webId = 'http://alice.test.com/#me';
   const token = 'randomlyGeneratedToken';
   const tokenTriple = quad(namedNode(webId), SOLID.terms.oidcIssuerRegistrationToken, literal(token));
   const tokenString = `${quadToString(tokenTriple)}.`;
-  const converter = new RdfToQuadConverter();
   let storage: ExpiringStorage<string, string>;
   let validator: TokenOwnershipValidator;
 
-  function mockFetch(body: string): void {
-    fetchMock.mockImplementation((url: string): any => ({
-      text: (): any => body,
-      url,
-      status: 200,
-      headers: { get: (): any => 'text/turtle' },
+  function mockDereference(qq?: Quad): any {
+    rdfDereferenceMock.dereference.mockImplementation((uri: string): any => ({
+      uri,
+      quads: Readable.from(qq ? [ qq ] : []),
+      exists: true,
     }));
   }
 
@@ -50,32 +50,32 @@ describe('A TokenOwnershipValidator', (): void => {
       delete: jest.fn().mockImplementation((key: string): any => map.delete(key)),
     } as any;
 
-    mockFetch('');
+    mockDereference();
 
-    validator = new TokenOwnershipValidator(converter, storage);
+    validator = new TokenOwnershipValidator(storage);
   });
 
   it('errors if no token is stored in the storage.', async(): Promise<void> => {
     // Even if the token is in the WebId, it will error since it's not in the storage
-    mockFetch(tokenString);
+    mockDereference(tokenTriple);
     await expect(validator.handle({ webId })).rejects.toThrow(expect.objectContaining({
       message: expect.stringContaining(tokenString),
       details: { quad: tokenString },
     }));
-    expect(fetch).toHaveBeenCalledTimes(0);
+    expect(rdfDereferenceMock.dereference).toHaveBeenCalledTimes(0);
   });
 
   it('errors if the expected triple is missing.', async(): Promise<void> => {
     // First call will add the token to the storage
     await expect(validator.handle({ webId })).rejects.toThrow(tokenString);
-    expect(fetch).toHaveBeenCalledTimes(0);
+    expect(rdfDereferenceMock.dereference).toHaveBeenCalledTimes(0);
     // Second call will fetch the WebId
     await expect(validator.handle({ webId })).rejects.toThrow(tokenString);
-    expect(fetch).toHaveBeenCalledTimes(1);
+    expect(rdfDereferenceMock.dereference).toHaveBeenCalledTimes(1);
   });
 
   it('resolves if the WebId contains the verification triple.', async(): Promise<void> => {
-    mockFetch(tokenString);
+    mockDereference(tokenTriple);
     // First call will add the token to the storage
     await expect(validator.handle({ webId })).rejects.toThrow(tokenString);
     // Second call will succeed since it has the verification triple
@@ -84,7 +84,7 @@ describe('A TokenOwnershipValidator', (): void => {
 
   it('fails if the WebId contains the wrong verification triple.', async(): Promise<void> => {
     const wrongQuad = quad(namedNode(webId), SOLID.terms.oidcIssuerRegistrationToken, literal('wrongToken'));
-    mockFetch(`${quadToString(wrongQuad)} .`);
+    mockDereference(wrongQuad);
     // First call will add the token to the storage
     await expect(validator.handle({ webId })).rejects.toThrow(tokenString);
     // Second call will fail since it has the wrong verification triple
diff --git a/test/unit/util/FetchUtil.test.ts b/test/unit/util/FetchUtil.test.ts
index d08fa4c22..16e0d9782 100644
--- a/test/unit/util/FetchUtil.test.ts
+++ b/test/unit/util/FetchUtil.test.ts
@@ -1,55 +1,81 @@
+import { Readable } from 'stream';
+import type { Quad } from '@rdfjs/types';
 import arrayifyStream from 'arrayify-stream';
-import { fetch } from 'cross-fetch';
+import type { Response } from 'cross-fetch';
 import { DataFactory } from 'n3';
+import rdfDereferencer from 'rdf-dereference';
 import { RdfToQuadConverter } from '../../../src/storage/conversion/RdfToQuadConverter';
-import { fetchDataset } from '../../../src/util/FetchUtil';
+import { fetchDataset, responseToDataset } from '../../../src/util/FetchUtil';
 const { namedNode, quad } = DataFactory;
 
-jest.mock('cross-fetch');
+jest.mock('rdf-dereference', (): any => ({
+  dereference: jest.fn<string, any>(),
+}));
 
 describe('FetchUtil', (): void => {
+  const url = 'http://test.com/foo';
+
+  function mockResponse(body: string, contentType: string | null, status = 200): Response {
+    return ({
+      text: (): any => body,
+      url,
+      status,
+      headers: { get: (): any => contentType },
+    }) as any;
+  }
+
   describe('#fetchDataset', (): void => {
-    const fetchMock: jest.Mock = fetch as any;
-    const url = 'http://test.com/foo';
-    const converter = new RdfToQuadConverter();
+    const rdfDereferenceMock: jest.Mocked<typeof rdfDereferencer> = rdfDereferencer as any;
 
-    function mockFetch(body: string, status = 200): void {
-      fetchMock.mockImplementation((input: string): any => ({
-        text: (): any => body,
-        url: input,
-        status,
-        headers: { get: (): any => 'text/turtle' },
-      }));
+    function mockDereference(quads?: Quad[]): any {
+      rdfDereferenceMock.dereference.mockImplementation((uri: string): any => {
+        if (!quads) {
+          throw new Error('Throws error because url does not exist');
+        }
+        return {
+          uri,
+          quads: Readable.from(quads),
+          exists: true,
+        };
+      });
     }
 
-    it('errors if the status code is not 200.', async(): Promise<void> => {
-      mockFetch('Invalid URL!', 404);
-      await expect(fetchDataset(url, converter)).rejects.toThrow(`Unable to access data at ${url}`);
-      expect(fetchMock).toHaveBeenCalledWith(url);
-    });
-
-    it('errors if there is no content-type.', async(): Promise<void> => {
-      fetchMock.mockResolvedValueOnce({ url, text: (): any => '', status: 200, headers: { get: jest.fn() }});
-      await expect(fetchDataset(url, converter)).rejects.toThrow(`Unable to access data at ${url}`);
-      expect(fetchMock).toHaveBeenCalledWith(url);
+    it('errors if the URL does not exist.', async(): Promise<void> => {
+      mockDereference();
+      await expect(fetchDataset(url)).rejects.toThrow(`Could not parse resource at URL (${url})!`);
+      expect(rdfDereferenceMock.dereference).toHaveBeenCalledWith(url);
     });
 
     it('returns a Representation with quads.', async(): Promise<void> => {
-      mockFetch('<http://test.com/s> <http://test.com/p> <http://test.com/o>.');
-      const representation = await fetchDataset(url, converter);
+      const quads = [ quad(namedNode('http://test.com/s'), namedNode('http://test.com/p'), namedNode('http://test.com/o')) ];
+      mockDereference(quads);
+      const representation = await fetchDataset(url);
       await expect(arrayifyStream(representation.data)).resolves.toEqual([
         quad(namedNode('http://test.com/s'), namedNode('http://test.com/p'), namedNode('http://test.com/o')),
       ]);
     });
+  });
+
+  describe('#responseToDataset', (): void => {
+    const converter = new RdfToQuadConverter();
 
     it('accepts Response objects as input.', async(): Promise<void> => {
-      mockFetch('<http://test.com/s> <http://test.com/p> <http://test.com/o>.');
-      const response = await fetch(url);
+      const response = mockResponse('<http://test.com/s> <http://test.com/p> <http://test.com/o>.', 'text/turtle');
       const body = await response.text();
-      const representation = await fetchDataset(response, converter, body);
+      const representation = await responseToDataset(response, converter, body);
       await expect(arrayifyStream(representation.data)).resolves.toEqual([
         quad(namedNode('http://test.com/s'), namedNode('http://test.com/p'), namedNode('http://test.com/o')),
       ]);
     });
+
+    it('errors if the status code is not 200.', async(): Promise<void> => {
+      const response = mockResponse('Incorrect status!', null, 400);
+      await expect(responseToDataset(response, converter)).rejects.toThrow(`Unable to access data at ${url}`);
+    });
+
+    it('errors if there is no content-type.', async(): Promise<void> => {
+      const response = mockResponse('No content-type!', null);
+      await expect(responseToDataset(response, converter)).rejects.toThrow(`Unable to access data at ${url}`);
+    });
   });
 });