Skip to content

Commit

Permalink
fix: docs, js comments and minor improvements (#5)
Browse files Browse the repository at this point in the history
Co-authored-by: Hugo Dias <hugomrdias@gmail.com>
  • Loading branch information
flea89 and hugomrdias authored Mar 14, 2022
1 parent eb57331 commit 62672b3
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 17 deletions.
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ We use `pnpm` in this project and commit the `pnpm-lock.yaml` file.

```bash
# install all dependencies in the mono-repo
pnpm
pnpm i
# setup git hooks
npx simple-git-hooks
```
Expand Down
1 change: 1 addition & 0 deletions spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ When a [Service](#21-Service) issues a UCAN for `did:user1` the resource will be

When restricting, the issuer can OPTIONALLY add another path segment to the resource URI. Using audience DID will guarantee uniqueness, although it is not REQUIRED to be unique and could be anything i.e. `storage://did:user-1/public`.


<!--
> We avoid name collisions simply by treating `/` terminated paths as directories and non `/` terminated as files.
-->
Expand Down
4 changes: 4 additions & 0 deletions src/keypair.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export class KeyPair {
}

/**
* Return the Base64 encoded public key
*
* @returns {string} the public key, as a base64 encoded string (padded).
*/
publicKeyStr() {
Expand All @@ -54,6 +56,8 @@ export class KeyPair {
}

/**
* Create did from public key
*
* @returns {string} the public key, encoded into a `did:key:` DID string.
*/
did() {
Expand Down
2 changes: 2 additions & 0 deletions src/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ export class Service {
}

/**
* It checks the full validity of the chain, that root issuer and target audience is the service did.
*
* @param {string} encodedUcan
*/
async validateFromCaps(encodedUcan) {
Expand Down
40 changes: 28 additions & 12 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,38 @@ export interface UcanHeader {
ucv: string
}

/**
* ### Payload
*
* `aud`, Audience, the ID 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 ID of who sent this.
* `nbf`, Not Before, unix timestamp of when the jwt becomes valid.
* `nnc`, Nonce, a randomly generated string, used to ensure the uniqueness of the jwt.
* `prf`, Proofs, nested tokens with equal or greater privileges.
* `att`, Attenuation, a list of resources and capabilities that the ucan grants.
*/
export interface UcanPayload<Prf = string> {
/**
* DID of the issuer
*/
iss: string
/**
* Audience, the ID of who it's intended for
*/
aud: string
/**
* Expiry, unix timestamp of when the jwt is no longer valid.
*/
exp: number
/**
* Not Before, unix timestamp of when the jwt becomes valid.
*/
nbf?: number
/**
* Nonce, a randomly generated string, used to ensure the uniqueness of the jwt.
*/
nnc?: string
/**
* Attenuation, a list of resources and capabilities that the ucan grants.
*/
att: Capability[]
/**
* Facts, an array of extra facts or information to attach to the jwt.
*/
fct?: Fact[]
/**
* Proofs, nested tokens with equal or greater privileges.
*/
prf: Prf[]
}

Expand All @@ -71,6 +83,10 @@ export interface Ucan<Prf = string> {
signature: Uint8Array
}

export interface UcanWithJWT extends Ucan {
jwt: string
}

/**
* Parameters for the internal `build` function, which creates new UCAN tokens.
*/
Expand Down
4 changes: 3 additions & 1 deletion src/ucan-chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,10 @@ export class UcanChain {
}

/**
* Create an instance of UcanChain from a ucan jwt token
*
* @param {string} encodedUcan
* @param {import('./types').ValidateOptions} options
* @param {import('./types').ValidateOptions} [options]
* @returns {Promise<UcanChain>}
*/
static async fromToken(encodedUcan, options) {
Expand Down
6 changes: 5 additions & 1 deletion src/ucan-storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { storageSemantics } from './semantics.js'

/**
* Construct and sign a new UCAN token with the given {@link UcanStorageOptions}.
* The UCAN is specific to the Storage services and semantics.
*
* @param {import('./types').UcanStorageOptions} params
* @returns {Promise<string>} a Promise that resolves to the encoded JWT representation of the signed UCAN token.
Expand All @@ -22,7 +23,10 @@ export async function build(params) {
}

/**
* Validate the given encoded UCAN token.
* Validate the given encoded UCAN token and return the parsed instance.
*
* **Note** This doesn't validate it's a valid UCAN chain but rather a single ucan is,
* disregarding the proof chain.
*
* You may optionally skip parts of the validation process by passing in {@link ValidateOptions}. By default,
* all validation checks are performed.
Expand Down
11 changes: 9 additions & 2 deletions src/ucan.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ const VERSION = '0.8.0'
export { KeyPair } from './keypair.js'

/**
* Build a Ucan from the given parameters.
*
* @param {import("./types").BuildParams} params
* @returns {Promise<import('./types').UcanWithJWT>}
*/
export async function build(params) {
const keypair = params.issuer
Expand Down Expand Up @@ -68,6 +71,8 @@ function buildPayload(params) {
*
* @param {import("./types").UcanPayload<string>} payload
* @param {import("./keypair.js").KeyPair} keypair
*
* @returns {Promise<import('./types').UcanWithJWT>}
*/
export async function sign(payload, keypair) {
/** @type {import('./types').UcanHeader} */
Expand All @@ -88,16 +93,18 @@ export async function sign(payload, keypair) {
return {
header,
payload,
signature: encodedSig,
signature: sig,
jwt: encodedHeader + '.' + encodedPayload + '.' + encodedSig,
}
}

/**
* @param {string} encodedUcan
* @param {import('./types').ValidateOptions} [options]
*
* @returns {Promise<import('./types').Ucan>}
*/
export async function validate(encodedUcan, options) {
export async function validate(encodedUcan, options = {}) {
/** @type {import('./types').ValidateOptions} */
const opts = {
checkIssuer: true,
Expand Down
54 changes: 54 additions & 0 deletions tests/ucan-chain.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,60 @@ test('should fail with unmatched from/to', async () => {
}
})

test('should fail with unmatched from/to deep in the chain', async () => {
const kp1 = await Ucan.KeyPair.create()
const kp2 = await Ucan.KeyPair.create()
const kp3 = await Ucan.KeyPair.create()
const kp4 = await Ucan.KeyPair.create()
const kp5 = await Ucan.KeyPair.create()

const token1 = await Ucan.build({
issuer: kp1,
audience: kp2.did(),
lifetimeInSeconds: 1000,
capabilities: [
{
with: `storage://${kp2.did()}`,
can: 'upload/*',
},
],
})

const token2 = await Ucan.build({
issuer: kp3,
audience: kp4.did(),
lifetimeInSeconds: 1000,
capabilities: [
{
with: `storage://${kp4.did()}`,
can: 'upload/*',
},
],
proofs: [token1.jwt],
})

const token3 = await Ucan.build({
issuer: kp4,
audience: kp5.did(),
lifetimeInSeconds: 1000,
capabilities: [
{
with: `storage://${kp5.did()}`,
can: 'upload/*',
},
],
proofs: [token2.jwt],
})

try {
await UcanChain.fromToken(token3.jwt)
assert.unreachable('should have thrown')
} catch (error) {
assert.instance(error, Error)
assert.match(error.message, 'Invalid UCAN: Audience')
}
})

test('should fail claim with bad capability', async () => {
const kp1 = await Ucan.KeyPair.create()
const kp2 = await Ucan.KeyPair.create()
Expand Down

0 comments on commit 62672b3

Please sign in to comment.