{"id":19179518,"url":"https://github.com/hypermedia-app/web-access-control","last_synced_at":"2026-06-15T02:33:55.185Z","repository":{"id":37960677,"uuid":"355132637","full_name":"hypermedia-app/web-access-control","owner":"hypermedia-app","description":"Using W3C Web Access Control vocabulary to protect Hydra APIs","archived":false,"fork":false,"pushed_at":"2024-04-04T17:41:29.000Z","size":674,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-18T04:01:06.873Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hypermedia-app.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-04-06T09:32:19.000Z","updated_at":"2021-10-29T12:16:12.000Z","dependencies_parsed_at":"2024-11-09T10:43:59.816Z","dependency_job_id":"b9ddbfc4-7403-484e-88f3-7835ef68c5f2","html_url":"https://github.com/hypermedia-app/web-access-control","commit_stats":{"total_commits":57,"total_committers":3,"mean_commits":19.0,"dds":0.4385964912280702,"last_synced_commit":"ce8d8fdfe781b512edc02a61f51e0823e351235d"},"previous_names":[],"tags_count":24,"template":false,"template_full_name":"tpluscode/ts-template","purl":"pkg:github/hypermedia-app/web-access-control","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hypermedia-app%2Fweb-access-control","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hypermedia-app%2Fweb-access-control/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hypermedia-app%2Fweb-access-control/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hypermedia-app%2Fweb-access-control/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hypermedia-app","download_url":"https://codeload.github.com/hypermedia-app/web-access-control/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hypermedia-app%2Fweb-access-control/sbom","scorecard":{"id":475449,"data":{"date":"2025-08-11","repo":{"name":"github.com/hypermedia-app/web-access-control","commit":"d757bf24deaf35d8afa2a4ac1b3268bbd10d221a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.8,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":5,"reason":"dependency not pinned by hash detected -- score normalized to 5","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:9: update your workflow using https://app.stepsecurity.io/secureworkflow/hypermedia-app/web-access-control/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:10: update your workflow using https://app.stepsecurity.io/secureworkflow/hypermedia-app/web-access-control/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/hypermedia-app/web-access-control/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/hypermedia-app/web-access-control/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/hypermedia-app/web-access-control/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:30: update your workflow using https://app.stepsecurity.io/secureworkflow/hypermedia-app/web-access-control/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:36: update your workflow using https://app.stepsecurity.io/secureworkflow/hypermedia-app/web-access-control/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/hypermedia-app/web-access-control/release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/hypermedia-app/web-access-control/release.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/hypermedia-app/web-access-control/release.yml/master?enable=pin","Info:   0 out of   8 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned","Info:   4 out of   4 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Code-Review","score":0,"reason":"Found 0/10 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/build.yml:1","Warn: no topLevel permission defined: .github/workflows/release.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 26 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"22 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-8hc4-vh64-cxmj","Warn: Project is vulnerable to: GHSA-jr5f-v2jv-69x6","Warn: Project is vulnerable to: GHSA-qwcr-r2fm-qrc7","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-pxg6-pf52-xh8x","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-qw6h-vgh9-j6wx","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-75v8-2h7p-7m2m","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-rhx6-c78j-4q9w","Warn: Project is vulnerable to: GHSA-m6fv-jmcg-4jfg","Warn: Project is vulnerable to: GHSA-76p7-773f-r4q5","Warn: Project is vulnerable to: GHSA-cm22-4g7w-348p","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-9qxr-qj54-h672","Warn: Project is vulnerable to: GHSA-m4v8-wqvr-p9f7","Warn: Project is vulnerable to: GHSA-c76h-2ccp-4975","Warn: Project is vulnerable to: GHSA-cxrh-j4jr-qwg3"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-19T15:10:03.027Z","repository_id":37960677,"created_at":"2025-08-19T15:10:03.027Z","updated_at":"2025-08-19T15:10:03.027Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34345577,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-15T02:00:07.085Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T10:43:23.046Z","updated_at":"2026-06-15T02:33:55.168Z","avatar_url":"https://github.com/hypermedia-app.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# web-access-control\n\nQuerying [Web Access Control](https://www.w3.org/wiki/WebAccessControl) entries over a SPARQL endpoint to authorize access to resources.\n\n### What it does?\n\nGiven the accessed resource and an agent, it executes queries to see if that agent should be granted access to said resource.  \n\nCurrently, the library makes some assumptions about the store structure:\n\n1. Protected resources and ACLs have to be in the same store\n2. The default graph is queried. Consult your store so that the [union graph](https://patterns.dataincubator.org/book/union-graph.html) is used as the active dataset\n\nSee below for more details. \n\n### Examples\n\nCheck the [examples](./examples/acls.ru) file for various instances of `acl:Autorization` resources. \n\n## hydra-web-access-control\n\nProtects [Hydra APIs](http://www.hydra-cg.com/spec/latest/core/) running [kopflos](https://github.com/zazuko/kopflos).\n\nInstances of `hydra:Operation` can be annotated with a `acl:mode` property to force this access mode being checked:\n\n```turtle\n@prefix acl: \u003chttp://www.w3.org/ns/auth/acl#\u003e .\n@prefix hydra: \u003chttp://www.w3.org/ns/hydra/core#\u003e .\n\n[\n  a hydra:Operation ;\n  hydra:method \"POST\" ;\n  acl:mode acl:Write ;\n] .\n``` \n\nIf not explicitly stated, the HTTP method will be mapped:\n\n| method | access mode |\n| -- | -- |\n| `GET` | `acl:Read` |\n| `HEAD` | `acl:Read` |\n| `OPTIONS` | `acl:Read` |\n| `POST` | `acl:Write` |\n| `PUT` | `acl:Write` |\n| `PATCH` | `acl:Write` |\n| `DELETE` | `acl:Write` |\n\n### Setup\n\nThe setup requires creating an express middleware by providing a SPARQL client instance ([sparql-http-client](https://npm.im/sparql-http-client)), and a function to get the current user's (agent's) Graph Pointer.\n\n```typescript\nimport express from 'express'\nimport { rdf, acl } from '@tpluscode/rdf-ns-builders'\nimport SparqlClient from 'sparql-http-client'\nimport * as kopflos from '@kopflos-api/core'\nimport accessControl from 'hydra-web-access-control' \n \nconst app = express()\n\nconst client = new SparqlClient({\n  endpoint: 'http://query.example.com/sparql'\n})\n\n// assume that there is an earlier middleware\n// which creates the agent resource\napp.use((req, res, next) =\u003e {\n  // in this example it creates a user by hand, \n  // from info provided by express-basic-auth.\n  // typically, would load from a store\n  req.agent = req.env.clownface()\n    .namedNode(`urn:user:${req.auth.user}`)\n    .addOut(rdf.type, acl.AuthenticatedAgent)\n  \n  next()\n})\n\n// the middleware will access req.agent to get its URI and RDF types\n// it needs to be configured as a hydra-box resource middleware\napp.use(hydraBox.middleware(api, {\n  middleware: {\n    resource: [\n      accessControl({ client })\n    ]\n  }\n}))\n```\n\n### Per-request authorization restrictions\n\nA function of array of functions can be optionally passed to the middleware. They take an RDF/JS variable, and the current request object as parameters and should return additional SPARQL patterns to filter out ACL authorization resources as desired.\n\n```typescript\nimport accessControl from 'hydra-access-control'\nimport { Variable } from '@rdfjs/types'\nimport { Request } from 'express'\n\nconst middleware =  accessControl({ \n    client,\n    additionalPatterns(acl: Variable, req: Request) {\n        // ...\n    }\n })\n```\n\nSee [below](#additional-authorization-restrictions) for a complete example. The only difference is that the `hydra-box-web-access-control` adds the second parameter while the other accepts only one.\n\n## rdf-web-access-control\n\nThe underlying library used by `hydra-web-access-control` middleware.\n\n### Check user access to resource\n\n```typescript\nimport { check } from 'rdf-web-access-control'\n\nconst hasAccess: boolean = await check({\n  accessMode,         // subclass of acl:Access, such as acl:Read or acl:Write\n  agent,              // agent Graph Pointer\n  term,               // resource URI\n  client,             // sparql-http-client\n  additionalPatterns, // function(s) to add more filters\n})\n```\n\nFor the given agent `\u003cA\u003e` and (optionally) resource `\u003cR\u003e`, it will prepare and execute a SPARQL query, looking for any instances of `acl:Authorization` which satisfy one of possible combinations:\n\n1. Direct access grant\n   - `[ acl:agent \u003cA\u003e ; acl:accessTo \u003cR\u003e ]`\n2. Direct grant for class of agents\n   - `[ acl:agent ?typeofAgentA ; acl:accessTo \u003cR\u003e ]`\n3. Access granted to class of resources\n   - `[ acl:agent \u003cA\u003e ; acl:accessTo ?typeofResourceR ]`\n4. Access granted to class of resources for class of agents\n   - `[ acl:agent ?typeofAgentA ; acl:accessTo ?typeofResourceR ]`\n5. Agent owns resource\n   - `\u003cR\u003e ac:owner \u003cA\u003e`\n   \n### Check user access to type of resource\n\nAlternatively, if no specific resource is given, it is possible provide the RDF types instead of an identifier. Such would be the case when creating new resources.\n\n```typescript\nimport { check } from 'rdf-web-access-control'\n\nconst hasAccess: boolean = await check({\n  accessMode, // subclass of acl:Access, such as acl:Read or acl:Write\n  agent,      // agent Graph Pointer\n  types,      // array of RDF types\n  client,     // sparql-http-client\n})\n```\n\nThis will only query for `acl:Authorization` using `acl:accessToClass`.\n\n### Controling access\n\nAll queries will include add `acl:Control` mode so that it grants automatic access to the otherwise specified resource/agent combinations.\n\nAll queries will include checks for `foaf:Agent` and `acl:AuthenticatedUser` to allow creating ACL entries for anonymous users and any authenticated users respectively.\n\nAll queries will implicitly add `rdfs:Resource` to the queries types. Given a store with inferencing capabilities and the use of `rdfs:subClassOf rdfs:Resource`, it would be possible to have an ACL entry such, that it grants access to \"any resource\".\n\n```turtle\n@prefix rdfs: \u003chttp://www.w3.org/2000/01/rdf-schema#\u003e .\n@prefix acl: \u003chttp://www.w3.org/ns/auth/acl#\u003e .\n\n# Any authenticated user has read access to any resource\n\u003c\u003e\n  a acl:Authorization ;\n  acl:mode acl:Read ;\n  acl:agentClass acl:AuthenticatedAgent ;\n  acl:accessToClass rdfs:Resource ;\n.\n```\n\n\n### Custom authorization checks\n\nBy default, the authorization will only occur using the `acl:agent` and `acl:agentClass` properties. Both or those strategies only use information provided by the caller, thus not impacting the query greatly.\n\nTo provide more alternative ways to authorize agents, use the `additionalChecks` argument, by providing an array of functions which must return SPARQL pattern template or a RDF/JS Dataset with pattern triples.\n\nAs an example, below is the implementation of using `acl:agentGroup`. It can be imported from `rdf-web-access-control/checks`\n\n```typescript\nimport { sparql } from '@tpluscode/sparql-builder'\nimport { acl, vcard } from '@tpluscode/rdf-ns-builders'\nimport type { AuthorizationPatterns } from 'rdf-web-access-control'\n\nexport const agentGroup: AuthorizationPatterns = ({ agent, authorization }) =\u003e {\n  return sparql`${authorization} ${acl.agentGroup}/${vcard.hasMember} ${agent} .`\n}\n```\n\n### Additional authorization restrictions\n\nIt is possible to restrict considered instances of `acl:Authorization`, for example to select only ACLs valid for given timeframe or by a custom property.\n\nEvery one of the \"additional patterns\" will be applied to every check, ie. `acl:agent`, `acl:agentClass` and the optional `additionalChecks` described above.\n\nTo do that, pass a function to the `check` call, which will return partial SPARQL patterns. It takes an RDF/JS Variable object as input which will match the ACL resources in the query.\n\n```typescript\nimport { Variable } from '@rdfjs/types'\nimport { schema } from '@tpluscode/rdf-ns-builders'\nimport { sparql } from '@tpluscode/sparql-builder'\nimport { toRdf } from 'rdf-literal'\nimport { check } from 'rdf-web-access-control' \n\nconst hasAccess: boolean = check({\n  additionalPatterns(acl: Variable) {\n    return sparql`\n      ${acl} ${schema.validThrough} ?validThrough .\n      FILTER( ?validThrough \u003e= ${toRdf(new Date())})\n    `\n  }\n})\n```\n\nIt is also possible to pass an array of multiple such SPARQL-building functions.\n\nSimilar applies to configuring the `hydra-web-access-control` middleware.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhypermedia-app%2Fweb-access-control","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhypermedia-app%2Fweb-access-control","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhypermedia-app%2Fweb-access-control/lists"}