Network Pipe Manufacturer

    ucans
    TypeScript icon, indicating that this package has built-in type declarations

    0.9.1 • Public • Published

    ts-ucan

    NPM License Discussions

    UCANs are JWTs that contain special keys.

    At 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").

    No 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.

    UCANs work

    • Server → Server
    • Client → Server
    • Peer-to-peer

    OAuth is designed for a centralized world, UCAN is the distributed user-controlled version.

    Read more in the whitepaper: https://whitepaper.fission.codes/access-control/ucan

    Structure

    Header

    alg, Algorithm, the type of signature.

    typ, Type, the type of this data structure, JWT.

    uav, UCAN version.

    Payload

    att, Attenuation, a list of resources and capabilities that the ucan grants.

    aud, Audience, the DID of who it's intended for.

    exp, Expiry, unix timestamp of when the jwt is no longer valid.

    fct, Facts, an array of extra facts or information to attach to the jwt.

    iss, Issuer, the DID of who sent this.

    nbf, Not Before, unix timestamp of when the jwt becomes valid.

    prf, Proof, an optional nested token with equal or greater privileges.

    Signature

    A signature (using alg) of the base64 encoded header and payload concatenated together and delimited by .

    Build

    ucan.build can be used to help in formatting and signing a UCAN. It takes the following parameters:

    type BuildParams = {
      // from/to
      issuer: Keypair
      audience: string
    
      // capabilities
      capabilities?: Array<Capability>
    
      // time bounds
      lifetimeInSeconds?: number // expiration overrides lifetimeInSeconds
      expiration?: number
      notBefore?: number
    
      // proofs / other info
      facts?: Array<Fact>
      proofs?: Array<string>
      addNonce?: boolean
    }

    Capabilities

    capabilities is an array of resource pointers and abilities:

    {
      // `with` is a resource pointer in the form of a URI, which has a `scheme` and `hierPart`.
      // → "mailto:boris@fission.codes"
      with: { scheme: "mailto", hierPart: "boris@fission.codes" },
    
      // `can` is an ability, which always has a namespace and optional segments.
      // → "msg/SEND"
      can: { namespace: "msg", segments: [ "SEND" ] }
    }

    Installation

    NPM:

    npm install --save ucans
    

    yarn:

    yarn add ucans
    

    Example

    import * as ucans from "ucans"
    
    // in-memory keypair
    const keypair = await ucans.EdKeypair.create()
    const ucan = await ucans.build({
      audience: "did:key:zabcde...", // recipient DID
      issuer: keypair, // signing key
      capabilities: [ // permissions for ucan
        {
          with: { scheme: "wnfs", hierPart: "//boris.fission.name/public/photos/" },
          can: { namespace: "wnfs", segments: [ "OVERWRITE" ] }
        },
        {
          with: { scheme: "wnfs", hierPart: "//boris.fission.name/private/4tZA6S61BSXygmJGGW885odfQwpnR2UgmCaS5CfCuWtEKQdtkRnvKVdZ4q6wBXYTjhewomJWPL2ui3hJqaSodFnKyWiPZWLwzp1h7wLtaVBQqSW4ZFgyYaJScVkBs32BThn6BZBJTmayeoA9hm8XrhTX4CGX5CVCwqvEUvHTSzAwdaR" },
          can: { namespace: "wnfs", segments: [ "APPEND" ] }
        },
        {
          with: { scheme: "mailto", hierPart: "boris@fission.codes" },
          can: { namespace: "wnfs", segments: [ "SEND" ] }
        }
      ]
    })
    const token = ucans.encode(ucan) // base64 jwt-formatted auth token
    
    // You can also use your own signing function if you're bringing your own key management solution
    const payload = await ucans.buildPayload(...)
    const ucan = await ucans.sign(payload, keyType, signingFn)

    Validating

    import * as ucans from "ucans"
    
    const ucan = ucans.build({ ... })
    
    ucans.isExpired(ucan)
    ucans.isTooEarly(ucan)
    ucans.validate(ucans.encode(ucan)) // checks signature, issuer key type, and the above.

    Capabilities

    import * as ucans from "ucans"
    
    // Utility functions to create capabilities
    const ucan = ucans.build({
      audience: "did:key:zabcde...",
      issuer: keypair,
      capabilities: [
        ucans.capability.my("resource"),
        ucans.capability.parse({
          with: "wnfs://boris.fission.name/public/photos/",
          can: "wnfs/OVERWRITE"
        }),
        {
          with: ucans.capability.resourcePointer.parse("wnfs://boris.fission.name/public/photos/vacation/"),
          can: ucans.capability.ability.parse("wnfs/REVISE")
        },
        {
          with: { scheme: "wnfs", hierPart: "//boris.fission.name/public/photos/vacation/" },
          can: { namespace: "wnfs", segments: [ "REVISE" ] }
        }
      ]
    })
    
    // Capability semantics
    const SEMANTICS = {
      // wether or not to use the default capability structure
      // (this would parse a regular capability into a custom one)
      tryParsing: a => a,
    
      // can a given child capability be delegated from a parent capability?
      tryDelegating: (parentCap, childCap) => {
        if (childCap.with.scheme !== "wnfs") return null
    
        // we've got access to everything
        if (parentCap.with.hierPart === ucans.capability.superUser.SUPERUSER) return childCap
    
        // path must be the same or a path below
        if (childCap.with.hierPart.startsWith(parentCap.with.hierPart)) return childCap
    
        // 🚨 cannot delegate
        return null
      }
    }
    
    // Capability checking
    const nowInSeconds = Math.floor(Date.now() / 1000)
    const result = ucans.hasCapability(
      SEMANTICS,
      {
        info: {
          originator: keypair.did(),  // capability must have been originated from this issuer
          expiresAt: nowInSeconds,    // ucan must not have been expired before this timestamp
          notBefore: nowInSeconds     // optional
        },
        capability: {
          with: ucans.capability.resourcePointer.parse("wnfs://boris.fission.name/public/photos/vacation/"),
          can: ucans.capability.ability.parse("wnfs/REVISE")
        }
      },
      ucans.Chained.fromToken(ucans.encode(ucan))
    )
    
    if (result === false) log("UCAN does not have this capability 🚨")
    else log("UCAN has the capability ✅ Info:", result.info, "Capability:", result.capability)
    
    // Comparing capabilities
    const a = {
      with: { scheme: "scheme", hierPart: "hierPart" },
      can: { namespace: "namespace", segments: [ "a", "B" ] }
    }
    
    const b = {
      with: { scheme: "SCHEME", hierPart: "hierPart" },
      can: { namespace: "NAMESPACE", segments: [ "A", "b" ] }
    }
    
    ucans.capability.isEqual(a, b)
    ucans.capability.resourcePointer.isEqual(a.with, b.with)
    ucans.capability.ability.isEqual(a.can, b.can)

    Sponsors

    Sponsors that contribute developer time or resources to this implementation of UCANs:

    UCAN Toucan

    Keywords

    none

    Install

    npm i ucans

    DownloadsWeekly Downloads

    79

    Version

    0.9.1

    License

    Apache-2.0

    Unpacked Size

    403 kB

    Total Files

    179

    Last publish

    Collaborators

    • appcypher
    • icidasset
    • quinnwilton
    • bgins-fission
    • expede
    • walkah
    • philipp-fission
    • dholms