{"id":22489754,"url":"https://github.com/ucan-wg/ts-ucan","last_synced_at":"2025-12-24T04:19:24.358Z","repository":{"id":37930018,"uuid":"406480703","full_name":"ucan-wg/ts-ucan","owner":"ucan-wg","description":"Auth tokens for a distributed, user-controlled world","archived":false,"fork":false,"pushed_at":"2024-03-15T19:04:14.000Z","size":435,"stargazers_count":102,"open_issues_count":15,"forks_count":12,"subscribers_count":9,"default_branch":"main","last_synced_at":"2024-11-29T09:56:48.544Z","etag":null,"topics":["authorization","jwt","ocap","security","spki"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/ucans","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ucan-wg.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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}},"created_at":"2021-09-14T18:33:00.000Z","updated_at":"2024-11-14T23:09:42.000Z","dependencies_parsed_at":"2024-03-15T20:25:02.848Z","dependency_job_id":"d0f4bf8d-4e54-4ff1-83ce-6783b28110f3","html_url":"https://github.com/ucan-wg/ts-ucan","commit_stats":{"total_commits":131,"total_committers":8,"mean_commits":16.375,"dds":"0.43511450381679384","last_synced_commit":"b9188fcf5a4dc505aef46fd60ef24546f73be1a7"},"previous_names":["fission-suite/ucan"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ucan-wg%2Fts-ucan","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ucan-wg%2Fts-ucan/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ucan-wg%2Fts-ucan/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ucan-wg%2Fts-ucan/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ucan-wg","download_url":"https://codeload.github.com/ucan-wg/ts-ucan/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228500456,"owners_count":17930073,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["authorization","jwt","ocap","security","spki"],"created_at":"2024-12-06T17:20:37.989Z","updated_at":"2025-12-24T04:19:24.349Z","avatar_url":"https://github.com/ucan-wg.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# ts-ucan\n![UCAN](https://img.shields.io/badge/UCAN-v0.8.1-blue)\n[![NPM](https://img.shields.io/npm/v/ucans)](https://www.npmjs.com/package/ucans)\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/fission-suite/blob/master/LICENSE)\n[![Discussions](https://img.shields.io/github/discussions/ucan-wg/ts-ucan)](https://github.com/ucan-wg/ts-ucan/discussions)\n\nUCANs are JWTs that contain special keys.\n\nAt a high level, UCANs (“User Controlled Authorization Network”) are an authorization scheme (\"what you can do\") where users are fully in control. UCANs use DIDs (\"Decentralized Identifiers\") to identify users and services (\"who you are\").\n\nNo all-powerful authorization server or server of any kind is required for UCANs. Instead, everything a user can do is captured directly in a key or token, which can be sent to anyone who knows how to interpret the UCAN format. Because UCANs are self-contained, they are easy to consume permissionlessly, and they work well offline and in distributed systems.\n\nUCANs work\n- Server → Server\n- Client → Server\n- Peer-to-peer\n\n**OAuth is designed for a centralized world, UCAN is the distributed user-controlled version.**\n\nRead more in the whitepaper: https://whitepaper.fission.codes/access-control/ucan\n\n\n\n## Structure\n\n### Header\n\n `alg`, Algorithm, the type of signature.\n\n `typ`, Type, the type of this data structure, JWT.\n\n `uav`, UCAN version.\n\n### Payload\n\n `att`, Attenuation, a list of resources and capabilities that the ucan grants.\n\n `aud`, Audience, the DID of who it's intended for.\n\n `exp`, Expiry, unix timestamp of when the jwt is no longer valid.\n\n `fct`, Facts, an array of extra facts or information to attach to the jwt.\n\n `iss`, Issuer, the DID of who sent this.\n\n `nbf`, Not Before, unix timestamp of when the jwt becomes valid.\n\n `prf`, Proof, an optional nested token with equal or greater privileges.\n\n ### Signature\n\n A signature (using `alg`) of the base64 encoded header and payload concatenated together and delimited by `.`\n\n\n\n## Build\n\n`ucan.build` can be used to help in formatting and signing a UCAN. It takes the following parameters:\n```ts\ntype BuildParams = {\n  // from/to\n  issuer: Keypair\n  audience: string\n\n  // capabilities\n  capabilities?: Array\u003cCapability\u003e\n\n  // time bounds\n  lifetimeInSeconds?: number // expiration overrides lifetimeInSeconds\n  expiration?: number\n  notBefore?: number\n\n  // proofs / other info\n  facts?: Array\u003cFact\u003e\n  proofs?: Array\u003cstring\u003e\n  addNonce?: boolean\n}\n```\n\n### Capabilities\n\n`capabilities` is an array of resource pointers and abilities:\n```ts\n{\n  // `with` is a resource pointer in the form of a URI, which has a `scheme` and `hierPart`.\n  // → \"mailto:boris@fission.codes\"\n  with: { scheme: \"mailto\", hierPart: \"boris@fission.codes\" },\n\n  // `can` is an ability, which always has a namespace and optional segments.\n  // → \"msg/SEND\"\n  can: { namespace: \"msg\", segments: [ \"SEND\" ] }\n}\n```\n\n\n\n## Installation\n\n### NPM:\n\n```\nnpm install --save @ucans/ucans\n```\n\n### yarn:\n\n```\nyarn add @ucans/ucans\n```\n\n## Example\n```ts\nimport * as ucans from \"@ucans/ucans\"\n\n// in-memory keypair\nconst keypair = await ucans.EdKeypair.create()\nconst ucan = await ucans.build({\n  audience: \"did:key:zabcde...\", // recipient DID\n  issuer: keypair, // signing key\n  capabilities: [ // permissions for ucan\n    {\n      with: { scheme: \"wnfs\", hierPart: \"//boris.fission.name/public/photos/\" },\n      can: { namespace: \"wnfs\", segments: [ \"OVERWRITE\" ] }\n    },\n    {\n      with: { scheme: \"wnfs\", hierPart: \"//boris.fission.name/private/6m-mLXYuXi5m6vxgRTfJ7k_xzbmpk7LeD3qYt0TM1M0\" },\n      can: { namespace: \"wnfs\", segments: [ \"APPEND\" ] }\n    },\n    {\n      with: { scheme: \"mailto\", hierPart: \"boris@fission.codes\" },\n      can: { namespace: \"msg\", segments: [ \"SEND\" ] }\n    }\n  ]\n})\nconst token = ucans.encode(ucan) // base64 jwt-formatted auth token\n\n// You can also use your own signing function if you're bringing your own key management solution\nconst payload = await ucans.buildPayload(...)\nconst ucan = await ucans.sign(payload, keyType, signingFn)\n```\n\n\n\n## Verifying UCAN Invocations\n\nUsing a UCAN to authorize an action is called \"invocation\".\n\nTo verify invocations, you need to use the `verify` function.\n\n```ts\nimport * as ucans from \"@ucans/ucans\"\n\nconst serviceDID = \"did:key:zabcde...\"\n\n// Generate a UCAN on one machine\nconst ucan = ucans.build({ ... })\n\n// encode the UCAN to send it over to another machine\nconst encoded = ucans.encode(ucan)\n\n// verify an invocation of a UCAN on another machine (in this example a service)\nconst result = await ucans.verify(encoded, {\n  // to make sure we're the intended recipient of this UCAN\n  audience: serviceDID,\n  // A callback for figuring out whether a UCAN is known to be revoked\n  isRevoked: async ucan =\u003e false // as a stub. Should look up the UCAN CID in a DB.\n  // capabilities required for this invocation \u0026 which owner we expect for each capability\n  requiredCapabilities: [\n    {\n      capability: {\n        with: { scheme: \"mailto\", hierPart: \"boris@fission.codes\" },\n        can: { namespace: \"msg\", segments: [ \"SEND\" ] }\n      },\n      rootIssuer: borisDID, // check against a known owner of the boris@fission.codes email address\n    }\n  ],\n)\n\nif (result.ok) {\n  // The UCAN authorized the user\n} else {\n  // Unauthorized\n}\n```\n\n\n## Delegation Semantics\n\nUCAN capabilities can have arbitrary semantics for delegation.\nThese semantics can be configured via a record of two functions:\n- `canDelegateResource(parent: ResourcePointer, child: ResourcePointer): boolean` and\n- `canDelegateAbility(parent: Ability, child: Ability): boolean`.\nWhich specify exactly which delegations are valid.\n\n(This doesn't support rights amplification yet, where multiple capabilities\nin combination may result in a delegation being possible. Please talk to us\nwith your use-case and ideas for how a good API for that may work.)\n\n```ts\nimport * as ucans from \"@ucans/ucans\"\n\n// Delegation semantics for path-like capabilities (e.g. \"path:/home/abc/\")\nconst PATH_SEMANTICS = {\n  canDelegateResource: (parentRes, childRes) =\u003e {\n    if (parentRes.with.scheme !== \"path\" || childRes.with.scheme !== \"path\") {\n      // If this is not about the \"path\" capability, then\n      // just use the normal equality delegation\n      return ucans.equalCanDelegate.canDelegateResource(parentRes, childRes)\n    }\n\n    // we've got access to everything\n    if (parentRes.hierPart === ucans.capability.superUser.SUPERUSER) {\n      return true\n    }\n\n    // path must be the same or a path below\n    if (`${childRes.hierPart}/`.startsWith(`${parentRes.hierPart}/`)) {\n      return true\n    }\n\n    // 🚨 cannot delegate\n    return false\n  },\n\n  // we're reusing equalCanDelegate's semantics for ability delegation\n  canDelegateAbility: equalCanDelegate.canDelegateAbility\n}\n```\n\n## Contributing\n\nTo get started working with this repository:\n\n - `git clone git@github.com:ucan-wg/ts-ucan.git`\n - `cd ts-ucan`\n - `yarn`\n\nNote that using npm with this repository will likely fail, please use yarn instead.\n\n\n## Sponsors\n\nSponsors that contribute developer time or resources to this implementation of UCANs:\n\n- [Fission](https://fission.codes/)\n\n\n\n## UCAN Toucan\n\n![](https://ipfs.runfission.com/ipfs/QmcyAwK7AjvLXbGuL4cqG5nufEKJquFmFGo2SDsaAe939Z)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fucan-wg%2Fts-ucan","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fucan-wg%2Fts-ucan","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fucan-wg%2Fts-ucan/lists"}