feat: Add DPoPWebIdExtractor.

This commit is contained in:
Ruben Verborgh 2020-11-26 20:09:23 +01:00 committed by Joachim Van Herwegen
parent af8f1976cd
commit 0407a36490
10 changed files with 463 additions and 3 deletions

View File

@ -3,7 +3,10 @@
"@graph": [
{
"@id": "urn:solid-server:default:CredentialsExtractor",
"@type": "UnsecureWebIdExtractor"
"@type": "DPoPWebIdExtractor",
"DPoPWebIdExtractor:_targetExtractor": {
"@id": "urn:solid-server:default:TargetExtractor"
}
}
]
}

View File

@ -5,7 +5,7 @@
"@id": "urn:solid-server:default:RequestParser",
"@type": "BasicRequestParser",
"BasicRequestParser:_args_targetExtractor": {
"@type": "BasicTargetExtractor"
"@id": "urn:solid-server:default:TargetExtractor"
},
"BasicRequestParser:_args_preferenceParser": {
"@type": "AcceptPreferenceParser"
@ -24,6 +24,10 @@
}
]
}
},
{
"@id": "urn:solid-server:default:TargetExtractor",
"@type": "BasicTargetExtractor"
}
]
}

View File

@ -1,6 +1,7 @@
// Authentication
export * from './src/authentication/Credentials';
export * from './src/authentication/CredentialsExtractor';
export * from './src/authentication/DPoPWebIdExtractor';
export * from './src/authentication/UnsecureWebIdExtractor';
// Authorization

316
package-lock.json generated
View File

@ -444,6 +444,25 @@
"parse-link-header": "^1.0.1"
}
},
"@comunica/actor-http-proxy": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/@comunica/actor-http-proxy/-/actor-http-proxy-1.16.0.tgz",
"integrity": "sha512-5aD5y9b6DvcCX2/ud/aVvUjvipppMmzKRV3C0evbA3NoAZ4VF8ejD+JpIjDsrvrbWeXVPueXvL/yfssUz17iIg=="
},
"@comunica/actor-rdf-dereference-file": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/@comunica/actor-rdf-dereference-file/-/actor-rdf-dereference-file-1.15.0.tgz",
"integrity": "sha512-hTG+JhPcdhlkEj96xI+0cTIC5uHBev3R1BiLXuOP7daVLLTfUHvBI09xgcVvKMqKJMuQNDVg3+aBRfMcR2bYVQ=="
},
"@comunica/actor-rdf-dereference-http-parse": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/@comunica/actor-rdf-dereference-http-parse/-/actor-rdf-dereference-http-parse-1.16.0.tgz",
"integrity": "sha512-IKSw5jjQLIDkbd7eX3Sw0oMuPBujQ4JX6UMpDA846T4lOZcUBTovp0PPKL4zyUd88Ea9a84Qn6uREOWed4nKvg==",
"requires": {
"cross-fetch": "^3.0.5",
"relative-to-absolute-iri": "^1.0.5"
}
},
"@comunica/actor-rdf-parse-html": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/@comunica/actor-rdf-parse-html/-/actor-rdf-parse-html-1.15.0.tgz",
@ -463,6 +482,14 @@
}
}
},
"@comunica/actor-rdf-parse-html-microdata": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/actor-rdf-parse-html-microdata/-/actor-rdf-parse-html-microdata-1.18.0.tgz",
"integrity": "sha512-2IjfPDXqS6IW+9k6RM2mcd8dx9gW4NeBL1teNfSpAIH3mBdRdum+FAfbeWirOOIX+lpKJ3pKo57mXf5eUFNkXw==",
"requires": {
"microdata-rdf-streaming-parser": "^1.1.0"
}
},
"@comunica/actor-rdf-parse-html-rdfa": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/@comunica/actor-rdf-parse-html-rdfa/-/actor-rdf-parse-html-rdfa-1.15.0.tgz",
@ -578,6 +605,24 @@
"resolved": "https://registry.npmjs.org/@comunica/bus-init/-/bus-init-1.15.0.tgz",
"integrity": "sha512-HwVTEznx2H7GvcfQAREz52QF8xXT1AqxkJDnI9vTeGvLpWrM+LVOAJZxBcYFFK3X3bEhTsPfDVzikziMGqZuZw=="
},
"@comunica/bus-rdf-dereference": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/@comunica/bus-rdf-dereference/-/bus-rdf-dereference-1.15.0.tgz",
"integrity": "sha512-4vXq8e51zK1gkw5lxL2HkqYx/Wig215w8Bi3jguUTM/10EQW7KgebhRxeFU/EQOamFTMeS5anOu5BVfNps8rJQ==",
"requires": {
"@types/rdf-js": "^3.0.0"
},
"dependencies": {
"@types/rdf-js": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/rdf-js/-/rdf-js-3.0.3.tgz",
"integrity": "sha512-1dvodNHh/YpLovHA/b046Nu+xERVt0KcYJFQH1A8S4ZcMj+stYtx4ypXw0X2/oatbREUo4JW+cjoBll3CVtwSQ==",
"requires": {
"@types/node": "*"
}
}
}
},
"@comunica/bus-rdf-parse": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/@comunica/bus-rdf-parse/-/bus-rdf-parse-1.15.0.tgz",
@ -6005,6 +6050,11 @@
"integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=",
"dev": true
},
"jose": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/jose/-/jose-3.1.3.tgz",
"integrity": "sha512-ppasqfGfKZ8hmpJVAuXfQR8S+AiDCcUa2Cj1WDw+I4Ui7J9iIfsg1CIjMSlwxN1PtK6yiSKqIXfi9mTDrXQm/w=="
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -6538,6 +6588,30 @@
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"microdata-rdf-streaming-parser": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/microdata-rdf-streaming-parser/-/microdata-rdf-streaming-parser-1.1.0.tgz",
"integrity": "sha512-nvPEFzG4vZWzWJP2x8Ax7mJmdrFkSYrfhdTUDHLtXYZJVl8Ip7ScHUPLkUfX+Ci4g7sOdgHsotkxuccnlxtCAg==",
"requires": {
"@types/rdf-js": "*",
"htmlparser2": "^5.0.0",
"rdf-data-factory": "^1.0.2",
"relative-to-absolute-iri": "^1.0.2"
},
"dependencies": {
"htmlparser2": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz",
"integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==",
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^3.3.0",
"domutils": "^2.4.2",
"entities": "^2.0.0"
}
}
}
},
"micromatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
@ -7492,6 +7566,46 @@
"@types/rdf-js": "*"
}
},
"rdf-dereference": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/rdf-dereference/-/rdf-dereference-1.6.0.tgz",
"integrity": "sha512-anUzTvHwRfStCQhgEmO5EOnLPaGDgTlb4o84ZTqqXvbI+bhLAKYoCWVVFd9uRuTk7ZPxezHZlDgN5uqAuFaEmQ==",
"requires": {
"@comunica/actor-http-native": "~1.16.2",
"@comunica/actor-http-proxy": "~1.16.0",
"@comunica/actor-rdf-dereference-file": "~1.15.0",
"@comunica/actor-rdf-dereference-http-parse": "~1.16.0",
"@comunica/actor-rdf-parse-html": "~1.15.0",
"@comunica/actor-rdf-parse-html-rdfa": "~1.15.0",
"@comunica/actor-rdf-parse-html-script": "~1.16.1",
"@comunica/actor-rdf-parse-jsonld": "~1.16.0",
"@comunica/actor-rdf-parse-n3": "~1.15.0",
"@comunica/actor-rdf-parse-rdfxml": "~1.15.0",
"@comunica/actor-rdf-parse-xml-rdfa": "~1.15.0",
"@comunica/bus-http": "~1.16.0",
"@comunica/bus-init": "~1.15.0",
"@comunica/bus-rdf-dereference": "~1.15.0",
"@comunica/bus-rdf-parse": "~1.15.0",
"@comunica/bus-rdf-parse-html": "~1.15.0",
"@comunica/core": "~1.15.0",
"@comunica/mediator-combine-union": "~1.15.0",
"@comunica/mediator-number": "~1.15.0",
"@comunica/mediator-race": "~1.15.0",
"@types/rdf-js": "^3.0.0",
"rdf-string": "^1.3.1",
"stream-to-string": "^1.2.0"
},
"dependencies": {
"@types/rdf-js": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/rdf-js/-/rdf-js-3.0.3.tgz",
"integrity": "sha512-1dvodNHh/YpLovHA/b046Nu+xERVt0KcYJFQH1A8S4ZcMj+stYtx4ypXw0X2/oatbREUo4JW+cjoBll3CVtwSQ==",
"requires": {
"@types/node": "*"
}
}
}
},
"rdf-isomorphic": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/rdf-isomorphic/-/rdf-isomorphic-1.2.0.tgz",
@ -7554,6 +7668,15 @@
"stream-to-string": "^1.2.0"
}
},
"rdf-store-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/rdf-store-stream/-/rdf-store-stream-1.1.0.tgz",
"integrity": "sha512-JWvQUv/1yja1TiEzhS1PTactSER9ORjM/6TV8z3KdGWpeQOs9TeUgLzx5PLXSRePFZ8GKNTkG5dD+wC6Yh3sbQ==",
"requires": {
"@types/rdf-js": "*",
"n3": "^1.6.3"
}
},
"rdf-string": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/rdf-string/-/rdf-string-1.5.0.tgz",
@ -9175,6 +9298,199 @@
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
"integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
},
"ts-dpop": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/ts-dpop/-/ts-dpop-0.1.1.tgz",
"integrity": "sha512-KrIafLRqG9XLiRghAe7TKy9H0fQ1VNxdG+rOc5k/DoN9dJCQKFWKSyggwPoi5y5sQX2OZKaJCgbQO5c3aH1e5g==",
"requires": {
"cross-fetch": "^3.0.6",
"jose": "^3.1.0",
"n3": "^1.6.4",
"rdf-dereference": "^1.6.0",
"rdf-parse": "^1.6.1",
"rdf-store-stream": "^1.1.0",
"ts-guards": "^0.5.1",
"uuid": "^8.3.1"
},
"dependencies": {
"@comunica/actor-abstract-mediatyped": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/actor-abstract-mediatyped/-/actor-abstract-mediatyped-1.18.0.tgz",
"integrity": "sha512-bu6QorV2OPdHHWuw6lbI87mTTAZKS6TbQA6fNBFBw7LfwMfLugMrjUTxJcJ/UKXV4uPkpJRhhKEG6hns+k0S/g=="
},
"@comunica/actor-http-native": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/actor-http-native/-/actor-http-native-1.18.0.tgz",
"integrity": "sha512-wooUx1R0VKfDxtiui5dS5JSstQmRxl1fQY9q6iwYlEMcMXST6RsIhcRi5MWqEaJE5cVsTEBMXO4Fqdl51kvgeg==",
"requires": {
"@types/parse-link-header": "^1.0.0",
"cross-fetch": "^3.0.5",
"follow-redirects": "^1.5.1",
"parse-link-header": "^1.0.1"
}
},
"@comunica/actor-rdf-parse-html": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/actor-rdf-parse-html/-/actor-rdf-parse-html-1.18.0.tgz",
"integrity": "sha512-0mdU7msQJwdSVtSSAOGE3xykaW22Zg/bytjBe0sG+/S82wGnZdTYEmchzR4prH/R+IyRhZHVgNzGzmcaasX66A==",
"requires": {
"@comunica/bus-rdf-parse-html": "^1.18.0",
"@types/rdf-js": "*",
"htmlparser2": "^5.0.0"
}
},
"@comunica/actor-rdf-parse-html-rdfa": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/actor-rdf-parse-html-rdfa/-/actor-rdf-parse-html-rdfa-1.18.0.tgz",
"integrity": "sha512-tvfQP01uxcgZIDF6aXO83ckzNf/lrkLavLI2QQciuLXToCDFgTwXqyEPeZs4/nf1L3uuXJOIzP0Bq0ZnVAgLBw==",
"requires": {
"rdfa-streaming-parser": "^1.4.0"
}
},
"@comunica/actor-rdf-parse-html-script": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/actor-rdf-parse-html-script/-/actor-rdf-parse-html-script-1.18.0.tgz",
"integrity": "sha512-j/Pcj3oXPDXjnhshKdtk355HhZDNIUItzkDxH1EBdL9S0Ll6HbEt0DJwXy51JRFxiK/ZpfXWGCHuOC1rOtP1oQ==",
"requires": {
"@comunica/bus-rdf-parse-html": "^1.18.0",
"@types/rdf-js": "*",
"relative-to-absolute-iri": "^1.0.5"
}
},
"@comunica/actor-rdf-parse-jsonld": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/actor-rdf-parse-jsonld/-/actor-rdf-parse-jsonld-1.18.0.tgz",
"integrity": "sha512-SbILO+HnBi88+M3XQQ7ZV3OKy3eqH0VHn6QuKYTcucOzcYtz60CeRRC0IVhEIKIbEHOpupZ3ZWOQzGudKZ71Rw==",
"requires": {
"@types/rdf-js": "*",
"jsonld-context-parser": "^2.1.1",
"jsonld-streaming-parser": "^2.1.1",
"stream-to-string": "^1.2.0"
}
},
"@comunica/actor-rdf-parse-n3": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/actor-rdf-parse-n3/-/actor-rdf-parse-n3-1.18.0.tgz",
"integrity": "sha512-4aud6izDZCzgNuo0ggCCQf4TdW9vyN0BU29kEXvWfX0kX6xIeTENOVuCddRSXEl9ndaD6WrJtnpG2GDu/tlqUA==",
"requires": {
"@types/n3": "^1.4.4",
"n3": "^1.6.3"
}
},
"@comunica/actor-rdf-parse-rdfxml": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/actor-rdf-parse-rdfxml/-/actor-rdf-parse-rdfxml-1.18.0.tgz",
"integrity": "sha512-2bqH5JmCISZbE+9kdE6H42TVYEGArIzosWJrtPrAzhWhAi2hN7c+wpo8arqRe8UB8MxO58JbnuxxGaNJcSYonw==",
"requires": {
"rdfxml-streaming-parser": "^1.4.0"
}
},
"@comunica/actor-rdf-parse-xml-rdfa": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/actor-rdf-parse-xml-rdfa/-/actor-rdf-parse-xml-rdfa-1.18.0.tgz",
"integrity": "sha512-Sp9VRNL4dVv6IQTSwjAY+4o46WV/SZq4PRebU/enWF82w5Pbj1Q4s1iRNXb+BZFUO7s3mihBcQ62+sJ4Ds7Big==",
"requires": {
"rdfa-streaming-parser": "^1.3.0"
}
},
"@comunica/bus-http": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/bus-http/-/bus-http-1.18.0.tgz",
"integrity": "sha512-SAfI+1Rk6caHlpRBpxiLpxmBWOA+dho4IurU+++LsmwC25353W8hjOSSg0GPnuLBebFO7YVcB1rTOyJfp53kcg==",
"requires": {
"is-stream": "^2.0.0",
"web-streams-node": "^0.4.0"
}
},
"@comunica/bus-init": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/bus-init/-/bus-init-1.18.0.tgz",
"integrity": "sha512-BjDX31UWf2iTCVhhe3NllfizYGoE4Vh0YaY4s3RLOKpMCN6UlplmIprUELRSju0E8CRR2KHddNC1j5wt3a4cTw=="
},
"@comunica/bus-rdf-parse": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/bus-rdf-parse/-/bus-rdf-parse-1.18.0.tgz",
"integrity": "sha512-zXH7AwnkF10agvNEXY08h8HAevjmcILl/S3CovMTtQLMaU1bGSQCjcYJl6QOIqYehYMntsvjeSKTSZ+VhESgOg==",
"requires": {
"@comunica/actor-abstract-mediatyped": "^1.18.0",
"@types/rdf-js": "*"
}
},
"@comunica/bus-rdf-parse-html": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/bus-rdf-parse-html/-/bus-rdf-parse-html-1.18.0.tgz",
"integrity": "sha512-QGjV3p7O2c1mJOURrXHe+9uifLMPLwfW/jAhBpVt4UVzIoZl5onK9e8ukJBjxsCl1pnH07KOPO1i91VyBOWjWA==",
"requires": {
"@types/rdf-js": "*"
}
},
"@comunica/core": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/core/-/core-1.18.0.tgz",
"integrity": "sha512-7JCzSJkilbemhIiSSpFuylbEqMntWp+JSs3gNgvb+r7NzxDXlB9l1Ng3UVJg6QvoO8EuoprwRGyeiG2824TarQ==",
"requires": {
"immutable": "^3.8.2"
}
},
"@comunica/mediator-combine-union": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/mediator-combine-union/-/mediator-combine-union-1.18.0.tgz",
"integrity": "sha512-AO6DxwSQubD4p6dWllom3jXLP7zEYWVsI3pmXsnJYP8TqUtcJ0Mah5JaCnzbHIcRToZIy/WUDUN9Kcxp0ZwCXA=="
},
"@comunica/mediator-number": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/mediator-number/-/mediator-number-1.18.0.tgz",
"integrity": "sha512-JGoUQRJQSLiAkBPAE2591DhMyv0mT24l91vyfo5TPKNtHXa0ull12fYRpPaXbRTGGAENKvxoEFR491vi1XIb8A=="
},
"@comunica/mediator-race": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@comunica/mediator-race/-/mediator-race-1.18.0.tgz",
"integrity": "sha512-8iEFxjQdi4wCSz+4yVywhF/DpcWWDxZ0GZZwc2Qp6ShyX79Zn/I2LEDuj50Uuh+0fHDt91H5Em9UhRMI7IRtLQ=="
},
"htmlparser2": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz",
"integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==",
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^3.3.0",
"domutils": "^2.4.2",
"entities": "^2.0.0"
}
},
"rdf-parse": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/rdf-parse/-/rdf-parse-1.6.1.tgz",
"integrity": "sha512-WBylJN11cuvo2GAvS/drqus+94tb9evl1QvUSbkNAVxYWUc5chnyV9ZpWPZW/F2pXJlPLqet9kE8U52eXKMo3Q==",
"requires": {
"@comunica/actor-http-native": "~1.18.0",
"@comunica/actor-rdf-parse-html": "~1.18.0",
"@comunica/actor-rdf-parse-html-microdata": "~1.18.0",
"@comunica/actor-rdf-parse-html-rdfa": "~1.18.0",
"@comunica/actor-rdf-parse-html-script": "~1.18.0",
"@comunica/actor-rdf-parse-jsonld": "~1.18.0",
"@comunica/actor-rdf-parse-n3": "~1.18.0",
"@comunica/actor-rdf-parse-rdfxml": "~1.18.0",
"@comunica/actor-rdf-parse-xml-rdfa": "~1.18.0",
"@comunica/bus-http": "~1.18.0",
"@comunica/bus-init": "~1.18.0",
"@comunica/bus-rdf-parse": "~1.18.0",
"@comunica/bus-rdf-parse-html": "~1.18.0",
"@comunica/core": "~1.18.0",
"@comunica/mediator-combine-union": "~1.18.0",
"@comunica/mediator-number": "~1.18.0",
"@comunica/mediator-race": "~1.18.0",
"@types/rdf-js": "*",
"stream-to-string": "^1.2.0"
}
}
}
},
"ts-guards": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/ts-guards/-/ts-guards-0.5.1.tgz",
"integrity": "sha512-Y6P/VJnwARiPMfxO7rvaYaz5tGQ5TQ0Wnb2cWIxMpFOioYkhsT8XaCrJX6wYPNFACa4UOrN5SPqhwpM8NolAhQ=="
},
"ts-jest": {
"version": "26.4.3",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.4.3.tgz",

View File

@ -99,6 +99,7 @@
"sparqlalgebrajs": "^2.3.1",
"sparqljs": "^3.1.2",
"streamify-array": "^1.0.1",
"ts-dpop": "^0.1.1",
"uuid": "^8.3.0",
"winston": "^3.3.3",
"winston-transport": "^4.4.0",

View File

@ -4,6 +4,5 @@ import type { Credentials } from './Credentials';
/**
* Responsible for extracting credentials from an incoming request.
* Will return `null` if no credentials were found.
*/
export abstract class CredentialsExtractor extends AsyncHandler<HttpRequest, Credentials> {}

View File

@ -0,0 +1,36 @@
import { verify } from 'ts-dpop';
import type { TargetExtractor } from '../ldp/http/TargetExtractor';
import { getLoggerFor } from '../logging/LogUtil';
import type { HttpRequest } from '../server/HttpRequest';
import type { Credentials } from './Credentials';
import { CredentialsExtractor } from './CredentialsExtractor';
/**
* Credentials extractor which extracts a WebID from a DPoP token.
*/
export class DPoPWebIdExtractor extends CredentialsExtractor {
protected readonly logger = getLoggerFor(this);
private readonly targetExtractor: TargetExtractor;
public constructor(targetExtractor: TargetExtractor) {
super();
this.targetExtractor = targetExtractor;
}
public async handle(request: HttpRequest): Promise<Credentials> {
let webID: string | undefined;
const authorizationHeader = request.headers.authorization;
const dpopHeader = request.headers.dpop as string;
if (authorizationHeader && dpopHeader) {
const method = request.method as any;
const resource = await this.targetExtractor.handleSafe(request);
try {
webID = await verify(authorizationHeader, dpopHeader, method, resource.path);
this.logger.info(`Extracted WebID via DPoP token: ${webID}`);
} catch (error: unknown) {
this.logger.warn(`Error verifying WebID via DPoP token: ${(error as Error).message}`);
}
}
return { webID };
}
}

View File

@ -0,0 +1,5 @@
module.exports = {
rules: {
'unicorn/filename-case': 'off',
},
};

View File

@ -0,0 +1 @@
export const verify = jest.fn((): string => 'http://alice.example/card#me');

View File

@ -0,0 +1,94 @@
import { verify } from 'ts-dpop';
import { DPoPWebIdExtractor } from '../../../src/authentication/DPoPWebIdExtractor';
import { TargetExtractor } from '../../../src/ldp/http/TargetExtractor';
import type { ResourceIdentifier } from '../../../src/ldp/representation/ResourceIdentifier';
import type { HttpRequest } from '../../../src/server/HttpRequest';
class DummyTargetExtractor extends TargetExtractor {
public async handle(): Promise<ResourceIdentifier> {
return { path: 'http://example.org/foo/bar' };
}
}
describe('A DPoPWebIdExtractor', (): void => {
const targetExtractor = new DummyTargetExtractor();
const webIdExtractor = new DPoPWebIdExtractor(targetExtractor);
beforeEach((): void => {
jest.clearAllMocks();
jest.spyOn(targetExtractor, 'handle');
});
describe('on a request without Authorization header', (): void => {
const request = {
method: 'GET',
headers: {
dpop: 'token-5678',
},
} as any as HttpRequest;
it('returns empty credentials.', async(): Promise<void> => {
await expect(webIdExtractor.handle(request)).resolves.toEqual({});
});
});
describe('on a request without DPoP header', (): void => {
const request = {
method: 'GET',
headers: {
authorization: 'DPoP token-1234',
},
} as any as HttpRequest;
it('returns empty credentials.', async(): Promise<void> => {
await expect(webIdExtractor.handle(request)).resolves.toEqual({});
});
});
describe('on a request with Authorization and DPop headers', (): void => {
const request = {
method: 'GET',
headers: {
authorization: 'DPoP token-1234',
dpop: 'token-5678',
},
} as any as HttpRequest;
it('calls the target extractor with the correct parameters.', async(): Promise<void> => {
await webIdExtractor.handle(request);
expect(targetExtractor.handle).toHaveBeenCalledTimes(1);
expect(targetExtractor.handle).toHaveBeenCalledWith(request);
});
it('calls the DPoP verifier with the correct parameters.', async(): Promise<void> => {
await webIdExtractor.handle(request);
expect(verify).toHaveBeenCalledTimes(1);
expect(verify).toHaveBeenCalledWith('DPoP token-1234', 'token-5678', 'GET', 'http://example.org/foo/bar');
});
it('returns the extracted WebID.', async(): Promise<void> => {
await expect(webIdExtractor.handle(request)).resolves
.toEqual({ webID: 'http://alice.example/card#me' });
});
});
describe('when verification throws an error', (): void => {
const request = {
method: 'GET',
headers: {
authorization: 'DPoP token-1234',
dpop: 'token-5678',
},
} as any as HttpRequest;
beforeEach((): void => {
(verify as jest.MockedFunction<any>).mockImplementationOnce((): void => {
throw new Error('invalid');
});
});
it('returns empty credentials.', async(): Promise<void> => {
await expect(webIdExtractor.handle(request)).resolves.toEqual({});
});
});
});