@ipld/dag-ucan
Advanced tools
Comparing version 1.1.1-beta to 1.2.0-beta
@@ -9,3 +9,3 @@ export interface AsyncVerifier<A extends number> { | ||
} | ||
export interface Verifier<A extends number> { | ||
export interface Verifier<A extends number = number> { | ||
readonly algorithm: A; | ||
@@ -12,0 +12,0 @@ verify<S extends Signer<A>, T>(payload: ByteView<T>, signature: Signature<T, S>): T; |
@@ -1,10 +0,7 @@ | ||
export function format<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>({ header, body, signature }: UCAN.Data<C>): UCAN.JWT<UCAN.CBOR<C>>; | ||
export function formatPayload<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>({ header, body }: { | ||
header: UCAN.Header; | ||
body: UCAN.Body<C>; | ||
}): `${UCAN.Header}.${UCAN.Body}`; | ||
export function formatHeader(header: UCAN.Header): `${UCAN.Header}`; | ||
export function formatBody(body: UCAN.Body): `${UCAN.Body}`; | ||
export function formatSignature<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(signature: UCAN.Signature<[UCAN.Header, UCAN.Body<C>], UCAN.Signer<number>>): string; | ||
export function encodeHeader(header: UCAN.Header): import("multiformats/codecs/interface").ByteView<{ | ||
export function format<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(model: UCAN.Model<C>): UCAN.JWT<C>; | ||
export function formatPayload<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(model: UCAN.Input<C>): `${UCAN.Header}.${UCAN.Body}`; | ||
export function formatHeader(model: UCAN.Input): `${UCAN.Header}`; | ||
export function formatBody(model: UCAN.Input): `${UCAN.Body}`; | ||
export function formatSignature<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(signature: UCAN.Signature<C>): UCAN.ToString<UCAN.Signature<C>, string>; | ||
export function encodeHeader(model: UCAN.Input): import("multiformats/codecs/interface").ByteView<{ | ||
alg: string; | ||
@@ -14,3 +11,3 @@ ucv: `${number}.${number}.${number}`; | ||
}>; | ||
export function encodeBody(body: UCAN.Body): import("multiformats/codecs/interface").ByteView<{ | ||
export function encodeBody(body: UCAN.Input): import("multiformats/codecs/interface").ByteView<{ | ||
nbf?: number | undefined; | ||
@@ -26,4 +23,4 @@ nnc?: string | undefined; | ||
export function encodeProof(proof: UCAN.Proof): string; | ||
export function encodeAgorithm<Code extends number>(code: Code): "EdDSA" | "RS256"; | ||
export function encodeAgorithm(model: UCAN.Input): "EdDSA" | "RS256"; | ||
import * as UCAN from "./ucan.js"; | ||
//# sourceMappingURL=formatter.d.ts.map |
export * from "./ucan.js"; | ||
export * as DID from "./did.js"; | ||
/** @type {UCAN.Version} */ | ||
export const VERSION: UCAN.Version; | ||
export const name: "dag-ucan"; | ||
export const raw: 85; | ||
export function encode<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(ucan: UCAN.UCAN<C>): UCAN.ByteView<UCAN.UCAN<C>>; | ||
export function decode<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(bytes: UCAN.ByteView<UCAN.UCAN<C>>): UCAN.View<C>; | ||
/** @type {typeof CBOR.code|typeof RAW.code} */ | ||
export const code: typeof CBOR.code | typeof RAW.code; | ||
export function encode<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(ucan: UCAN.UCAN<C>): UCAN.ByteView<UCAN.Model<C>> | UCAN.ByteView<UCAN.JWT<C>>; | ||
export function decode<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(bytes: UCAN.ByteView<UCAN.Model<C>> | UCAN.ByteView<UCAN.JWT<C>>): UCAN.View<C>; | ||
export function link<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(ucan: UCAN.UCAN<C>, options?: { | ||
hasher?: UCAN.MultihashHasher<number> | undefined; | ||
} | undefined): Promise<CID & UCAN.Proof<C, number>>; | ||
export function write<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>, A extends number = 18>(ucan: UCAN.UCAN<C>, { hasher }?: { | ||
export function write<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>, A extends number = 18>(data: UCAN.UCAN<C>, { hasher }?: { | ||
hasher?: UCAN.MultihashHasher<A> | undefined; | ||
} | undefined): Promise<{ | ||
cid: CID & UCAN.Proof<C, A>; | ||
bytes: UCAN.ByteView<UCAN.UCAN<C>>; | ||
bytes: UCAN.ByteView<UCAN.JWT<C>> | UCAN.ByteView<UCAN.Model<C>>; | ||
data: UCAN.UCAN<C>; | ||
}>; | ||
export function parse<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(input: UCAN.JWT<UCAN.UCAN<C>>): UCAN.View<C>; | ||
export function format<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(ucan: UCAN.UCAN<C>): UCAN.JWT<UCAN.UCAN<C>>; | ||
export function parse<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(jwt: UCAN.JWT<C>): UCAN.View<C>; | ||
export function format<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(ucan: UCAN.UCAN<C>): UCAN.JWT<C>; | ||
export function issue<A extends number, C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>({ issuer, audience, capabilities, lifetimeInSeconds, expiration, notBefore, facts, proofs, nonce, }: UCAN.UCANOptions<C, A>): Promise<UCAN.View<C>>; | ||
import * as UCAN from "./ucan.js"; | ||
import * as CBOR from "./codec/cbor.js"; | ||
import * as RAW from "./codec/raw.js"; | ||
import * as View from "./view.js"; | ||
import { CID } from "multiformats/cid"; | ||
//# sourceMappingURL=lib.d.ts.map |
@@ -1,4 +0,18 @@ | ||
export function parse<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(input: UCAN.JWT<UCAN.UCAN<C>>): UCAN.Data<C>; | ||
export function parseHeader(header: string): UCAN.Header; | ||
export function parseBody<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(input: string): UCAN.Body<C>; | ||
export function parse<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(input: UCAN.JWT<C>): UCAN.Model<C>; | ||
export function parseHeader(header: string): { | ||
version: `${number}.${number}.${number}`; | ||
}; | ||
export function parseBody<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(input: string): { | ||
issuer: UCAN.DIDView; | ||
audience: UCAN.DIDView; | ||
expiration: number; | ||
nonce: string | undefined; | ||
notBefore: number | undefined; | ||
facts: UCAN.Fact[]; | ||
proofs: UCAN.Proof<UCAN.Capability<UCAN.Ability, `${string}:${string}`>, number>[]; | ||
capabilities: C[]; | ||
}; | ||
export function parseInt(input: unknown, name: string): number; | ||
export function parseCapability(input: unknown, context: string): UCAN.Capability<UCAN.Ability, `${string}:${string}`>; | ||
export function parseCapabilities(input: unknown, context: string): UCAN.Capability<UCAN.Ability, `${string}:${string}`>[]; | ||
export function asCapability<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(input: C | (object & { | ||
@@ -8,3 +22,20 @@ can?: unknown; | ||
})): C; | ||
export function parseArray<T>(input: unknown, parser: (input: unknown, context: string) => T, context: string): T[]; | ||
export function parseOptionalArray<T>(input: unknown, parser: (input: unknown, context: string) => T, context: string): T[] | undefined; | ||
export function parseStruct<T>(input: unknown, parser: (input: object) => T, context: string): T; | ||
export function parseFact(input: unknown, context: string): UCAN.Fact; | ||
export function parseDID(input: unknown, context: string): UCAN.DIDView; | ||
export function parseOptionalString(input: unknown, context?: string | undefined): string | undefined; | ||
export function parseOptionalInt(input: unknown, context: string): number | undefined; | ||
export function parseVersion(input: unknown, context: string): UCAN.Version; | ||
export function parseBytes(input: unknown, context: string): Uint8Array; | ||
export class ParseError extends TypeError { | ||
/** | ||
* @param {string} message | ||
* @returns {never} | ||
*/ | ||
static throw(message: string): never; | ||
get name(): string; | ||
} | ||
import * as UCAN from "./ucan.js"; | ||
//# sourceMappingURL=parser.d.ts.map |
import type { MultihashDigest, MultihashHasher } from "multiformats/hashes/interface"; | ||
import type { MultibaseEncoder } from "multiformats/bases/interface"; | ||
import type { code as RAW_CODE } from "multiformats/codecs/raw"; | ||
import type { Signer, Signature } from "./crypto.js"; | ||
import type { code as CBOR_CODE } from "@ipld/dag-cbor"; | ||
import type { Signer } from "./crypto.js"; | ||
import * as Crypto from "./crypto.js"; | ||
export * from "./crypto.js"; | ||
export type { MultihashDigest, MultibaseEncoder, MultihashHasher }; | ||
export declare const code = 30912; | ||
export declare type Fact = Record<string, unknown>; | ||
@@ -30,20 +31,46 @@ export interface Agent { | ||
} | ||
export declare type JWT<T> = ToString<T>; | ||
export declare type UCAN<C extends Capability = Capability> = CBOR<C> | RAW<C>; | ||
export interface Data<C extends Capability = Capability> { | ||
readonly header: Header; | ||
readonly body: Body<C>; | ||
readonly signature: Signature<[Header, Body<C>]>; | ||
export declare type JWT<C extends Capability = Capability> = ToString<[ | ||
Head, | ||
Payload<C>, | ||
Signature<C> | ||
], `${ToString<Head>}.${ToString<Payload<C>>}.${ToString<Signature<C>>}`>; | ||
export declare type Signature<C extends Capability = Capability> = Crypto.Signature<`${ToString<Head>}.${ToString<Payload<C>>}>`>; | ||
interface Head { | ||
ucv: Version; | ||
alg: "EdDSA" | "RS256"; | ||
typ: "JWT"; | ||
} | ||
export interface CBOR<C extends Capability = Capability> extends Data<C> { | ||
readonly code: typeof code; | ||
export interface Payload<C extends Capability> { | ||
iss: DID; | ||
aud: DID; | ||
exp: number; | ||
att: C[]; | ||
nnc?: string; | ||
nbf?: number; | ||
fct?: Fact[]; | ||
prf?: ToString<Proof<C>>; | ||
} | ||
export interface RAW<C extends Capability = Capability> { | ||
readonly code: typeof RAW_CODE; | ||
readonly jwt: JWT<RAW<C>>; | ||
export declare type UCAN<C extends Capability = Capability> = Model<C> | RAW<C>; | ||
export interface Input<C extends Capability = Capability> { | ||
version: Version; | ||
issuer: ByteView<DID>; | ||
audience: ByteView<DID>; | ||
capabilities: C[]; | ||
expiration: number; | ||
notBefore?: number; | ||
nonce?: string; | ||
facts: Fact[]; | ||
proofs: Proof[]; | ||
} | ||
export declare type View<C extends Capability = Capability> = UCAN<C> & Data<C> & Header & Body<C>; | ||
export interface Model<C extends Capability = Capability> extends Input<C> { | ||
signature: Signature<C>; | ||
} | ||
export interface RAW<C extends Capability = Capability> extends Model<C>, ByteView<JWT<C>> { | ||
} | ||
export interface View<C extends Capability = Capability> extends Model<C> { | ||
readonly model: Model<C>; | ||
} | ||
export interface UCANOptions<C extends Capability = Capability, A extends number = number> { | ||
issuer: Issuer<A>; | ||
audience: DID; | ||
audience: Agent; | ||
capabilities: C[]; | ||
@@ -57,3 +84,3 @@ lifetimeInSeconds?: number; | ||
} | ||
export declare type Proof<C extends Capability = Capability, A extends number = number> = Link<Data<C>, 1, typeof code, A> | Link<JWT<Data<C>>, 1, typeof RAW_CODE, A>; | ||
export declare type Proof<C extends Capability = Capability, A extends number = number> = Link<Model<C>, 1, typeof CBOR_CODE, A> | Link<JWT<C>, 1, typeof RAW_CODE, A>; | ||
export interface Block<T extends unknown = unknown, C extends number = number, A extends number = number> { | ||
@@ -70,2 +97,4 @@ bytes: ByteView<T>; | ||
export declare type DID<T = unknown> = ToString<T, `did:${string}`>; | ||
export interface DIDView extends ByteView<DID>, Agent { | ||
} | ||
/** | ||
@@ -72,0 +101,0 @@ * Represents an IPLD link to a specific data of type `T`. |
@@ -1,31 +0,19 @@ | ||
export function cbor<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(data: UCAN.Data<C>): UCAN.View<C>; | ||
export function jwt<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(data: UCAN.Data<C>, jwt: UCAN.JWT<UCAN.RAW<C>>): UCAN.View<C>; | ||
export function cbor<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(data: UCAN.Model<C>): UCAN.View<C>; | ||
export function jwt<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>>(model: UCAN.Model<C>, bytes: UCAN.ByteView<UCAN.JWT<C>>): UCAN.View<C>; | ||
import * as UCAN from "./ucan.js"; | ||
/** | ||
* @template {UCAN.Capability} C | ||
* @template {typeof UCAN.code|typeof RAW.code} Code | ||
* @extends {View<C>} | ||
* @implements {UCAN.View<C>} | ||
*/ | ||
declare class View<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>, Code extends 30912 | 85> { | ||
declare class View<C extends UCAN.Capability<UCAN.Ability, `${string}:${string}`>> implements UCAN.View<C> { | ||
/** | ||
* @param {Code} code | ||
* @param {UCAN.Data<C>} data | ||
* @param {UCAN.Model<C>} model | ||
*/ | ||
constructor(code: Code, { header, body, signature }: UCAN.Data<C>); | ||
constructor(model: UCAN.Model<C>); | ||
/** @readonly */ | ||
readonly code: Code; | ||
/** @readonly */ | ||
readonly header: UCAN.Header; | ||
/** @readonly */ | ||
readonly body: UCAN.Body<C>; | ||
/** @readonly */ | ||
readonly signature: UCAN.Signature<[UCAN.Header, UCAN.Body<C>], UCAN.Signer<number>>; | ||
readonly model: UCAN.Model<C>; | ||
get version(): `${number}.${number}.${number}`; | ||
get algorithm(): number; | ||
get issuer(): UCAN.DID<unknown>; | ||
get issuer(): UCAN.ByteView<UCAN.DID<unknown>>; | ||
get audience(): UCAN.ByteView<UCAN.DID<unknown>>; | ||
/** | ||
* @returns {UCAN.DID} | ||
*/ | ||
get audience(): UCAN.DID<unknown>; | ||
/** | ||
* @returns {C[]} | ||
@@ -54,5 +42,5 @@ */ | ||
get proofs(): UCAN.Proof<UCAN.Capability<UCAN.Ability, `${string}:${string}`>, number>[]; | ||
get signature(): UCAN.Signature<C>; | ||
} | ||
import * as RAW from "multiformats/codecs/raw"; | ||
export {}; | ||
//# sourceMappingURL=view.d.ts.map |
{ | ||
"name": "@ipld/dag-ucan", | ||
"description": "UCAN codec for IPLD", | ||
"version": "1.1.1-beta", | ||
"version": "1.2.0-beta", | ||
"keywords": [ | ||
@@ -56,2 +56,10 @@ "UCAN", | ||
"import": "./src/lib.js" | ||
}, | ||
"./src/codec/cbor.js": { | ||
"types": "./dist/src/codec/cbor.d.ts", | ||
"import": "./src/codec/cbor.js" | ||
}, | ||
"./src/codec/raw.js": { | ||
"types": "./dist/src/codec/raw.d.ts", | ||
"import": "./src/codec/raw.js" | ||
} | ||
@@ -58,0 +66,0 @@ }, |
# @ipld/dag-ucan | ||
An implementation of [UCAN][]s representation in [IPLD][], designed for use with [multiformats][]. | ||
An implementation of [UCAN][]s in [IPLD][] via [Advanced Data Layout (ADL)](ADL), designed for use with [multiformats][]. | ||
## Overview | ||
This library implements multicodec for represenating [UCAN]s natively in [IPLD][]. It uses [DAG-CBOR][] as a primary encoding, which is more compact and has a better hash consitency than a secondary RAW JWT encoding. Every UCAN in primary encoding can be formatted into a JWT string and consumed by spec compliant [UCAN][] implementations. However not every [UCAN][] can be encoded in a primary CBOR representation, as loss of whitespaces and key order would lead to mismatched signature. Library issues UCANs only in primary CBOR representation. When parsing UCANs that can not have valid CBOR representation, secondary RAW representation is used, which allows interop with all existing tokens in the wild. | ||
This library implements [ADL][] for representing [UCAN]s natively in [IPLD][]. It uses [DAG-CBOR][] as a primary encoding, which is hash consistent and more compact than a secondary RAW JWT encoding. Every UCAN in either encoding can be formatted into a valid JWT string and consumed by other spec compliant [UCAN][] implementations. However [UCAN][]s issued by other libraries may end up in represented in secondory RAW JWT encoding, that is because whitespaces and key order in JWT affects signatures and there for can't be represented accurately in CBOR. When parsing UCANs library will use CBOR representation and fallback to RAW JWT, which allows interop with all existing tokens in the wild. | ||
@@ -16,29 +16,25 @@ ### Primary Representation | ||
type UCAN struct { | ||
header Heder | ||
body Body | ||
version String | ||
issuer SigningKey | ||
audience SigningKey | ||
signature Signature | ||
} | ||
type Header struct { | ||
version String | ||
algorithm Algorithm | ||
} | ||
type Body struct { | ||
issuer String | ||
audience String, | ||
capabilities [Capability] | ||
proofs [&UCAN] | ||
expiration Int | ||
proofs [&UCAN] | ||
-- If empty omitted | ||
facts optional [Fact] | ||
facts [Fact] | ||
nonce optional String | ||
notBefore optional Int | ||
} representation map { | ||
field facts default [] | ||
field proofs default [] | ||
} | ||
type Capability struct { | ||
with String | ||
-- Must be all lowercase | ||
can String | ||
-- can have other fields | ||
with Resource | ||
can Ability | ||
-- can have arbitrary other fields | ||
} | ||
@@ -48,7 +44,9 @@ | ||
enum Algorithm { | ||
EdDSA (237) -- 0xed Ed25519 multicodec | ||
RS256 (4613) -- 0x1205 RSA multicodec | ||
} representation int | ||
-- The resource pointer in URI format | ||
type Resource = String | ||
-- Must be all lower-case `/` delimeted with at least one path segment | ||
type Ability = String | ||
-- Signature is computed by seralizing header & body | ||
@@ -59,2 +57,7 @@ -- into corresponding JSON with DAG-JSON (to achieve | ||
type Signature = Bytes | ||
-- multicodec tagged public key | ||
-- 0xed Ed25519 | ||
-- 0x1205 RSA | ||
type SigningKey = Bytes | ||
``` | ||
@@ -68,3 +71,3 @@ | ||
#### `UCAN.parse(jwt: string): UCAN.UCAN` | ||
#### `UCAN.parse(jwt: string): UCAN.View` | ||
@@ -75,3 +78,3 @@ Parses UCAN formatted as JWT string into a representatino that can be encoded, formatted and queried. | ||
const ucan = UCAN.parse(jwt) | ||
ucan.issuer // did:key:zAlice | ||
ucan.issuer.did() // did:key:z6Mkk89bC3JrVqKie71YEcc5M1SMVxuCgNx6zLZ8SYJsxALi | ||
``` | ||
@@ -91,25 +94,33 @@ | ||
```ts | ||
UCAN.encode(UCAN.parse(jwt)) // Uint8Array(679) | ||
``` | ||
#### `UCAN.decode(bytes: Uint8Array): UCAN.UCAN` | ||
Decodes byte encoded UCAN. | ||
Decodes UCAN from binary representation into object representation. | ||
```ts | ||
UCAN.decode(UCAN.encode(ucan)) | ||
``` | ||
#### `UCAN.issue(options: UCAN.UCANOptions): Promise<UCAN.UCAN>` | ||
Issues or derives a UCAN. | ||
Issues a signed UCAN. | ||
> Please note that no capability or time bound validation takes place | ||
> Please note that no capability or time bound validation takes place. | ||
```ts | ||
const ucan = await UCAN.issue({ | ||
issuer: boris, | ||
audience: 'did:key:z6MkffDZCkCTWreg8868fG1FGFogcJj5X6PY93pPcWDn9bob' | ||
issuer: alice, | ||
audience: bob, | ||
capabilities: [ | ||
{ | ||
with: "wnfs://boris.fission.name/public/photos/", | ||
can: "wnfs/append", | ||
can: "fs/read", | ||
with: `storage://${alice.did()}/public/photos/`, | ||
}, | ||
{ | ||
with: "mailto:boris@fission.codes", | ||
can: "msg/send" | ||
} | ||
can: "pin/add", | ||
with: alice.did(), | ||
}, | ||
], | ||
@@ -124,1 +135,2 @@ }) | ||
[multiformats]: https://github.com/multiformats/js-multiformats | ||
[adl]: https://ipld.io/docs/advanced-data-layouts/ |
@@ -17,3 +17,3 @@ export interface AsyncVerifier<A extends number> { | ||
export interface Verifier<A extends number> { | ||
export interface Verifier<A extends number = number> { | ||
readonly algorithm: A | ||
@@ -20,0 +20,0 @@ verify<S extends Signer<A>, T>( |
import * as UCAN from "./ucan.js" | ||
import * as DID from "./did.js" | ||
import * as json from "@ipld/dag-json" | ||
import { base64urlpad } from "multiformats/bases/base64" | ||
import { algorithm, ED25519, RSA } from "./did.js" | ||
/** | ||
* @template {UCAN.Capability} C | ||
* @param {UCAN.Data<C>} data | ||
* @returns {UCAN.JWT<UCAN.CBOR<C>>} | ||
* @param {UCAN.Model<C>} model | ||
* @returns {UCAN.JWT<C>} | ||
*/ | ||
export const format = ({ header, body, signature }) => | ||
`${formatHeader(header)}.${formatBody(body)}.${formatSignature(signature)}` | ||
export const format = model => | ||
`${formatHeader(model)}.${formatBody(model)}.${formatSignature( | ||
model.signature | ||
)}` | ||
/** | ||
* @template {UCAN.Capability} C | ||
* @param {object} payload | ||
* @param {UCAN.Header} payload.header | ||
* @param {UCAN.Body<C>} payload.body | ||
* @param {UCAN.Input<C>} model | ||
* @returns {`${UCAN.Header}.${UCAN.Body}`} | ||
*/ | ||
export const formatPayload = ({ header, body }) => | ||
`${formatHeader(header)}.${formatBody(body)}` | ||
export const formatPayload = model => | ||
`${formatHeader(model)}.${formatBody(model)}` | ||
/** | ||
* @param {UCAN.Header} header | ||
* @param {UCAN.Input} model | ||
* @returns {`${UCAN.Header}`} | ||
*/ | ||
export const formatHeader = header => | ||
base64urlpad.baseEncode(encodeHeader(header)) | ||
export const formatHeader = model => | ||
base64urlpad.baseEncode(encodeHeader(model)) | ||
/** | ||
* @param {UCAN.Body} body | ||
* @param {UCAN.Input} model | ||
* @returns {`${UCAN.Body}`} | ||
*/ | ||
export const formatBody = body => base64urlpad.baseEncode(encodeBody(body)) | ||
export const formatBody = model => base64urlpad.baseEncode(encodeBody(model)) | ||
/** | ||
* @template {UCAN.Capability} C | ||
* @param {UCAN.Signature<[UCAN.Header, UCAN.Body<C>]>} signature | ||
* @param {UCAN.Signature<C>} signature | ||
* @returns {UCAN.ToString<UCAN.Signature<C>>} | ||
*/ | ||
@@ -43,8 +46,8 @@ export const formatSignature = signature => base64urlpad.baseEncode(signature) | ||
/** | ||
* @param {UCAN.Header} header | ||
* @param {UCAN.Input} model | ||
*/ | ||
export const encodeHeader = header => | ||
export const encodeHeader = model => | ||
json.encode({ | ||
alg: encodeAgorithm(header.algorithm), | ||
ucv: header.version, | ||
alg: encodeAgorithm(model), | ||
ucv: model.version, | ||
typ: "JWT", | ||
@@ -54,8 +57,8 @@ }) | ||
/** | ||
* @param {UCAN.Body} body | ||
* @param {UCAN.Input} body | ||
*/ | ||
export const encodeBody = body => | ||
json.encode({ | ||
iss: body.issuer, | ||
aud: body.audience, | ||
iss: DID.format(body.issuer), | ||
aud: DID.format(body.audience), | ||
att: body.capabilities, | ||
@@ -76,15 +79,14 @@ exp: body.expiration, | ||
/** | ||
* @template {number} Code | ||
* @param {Code} code | ||
* @param {UCAN.Input} model | ||
*/ | ||
export const encodeAgorithm = code => { | ||
switch (code) { | ||
case 0xed: | ||
export const encodeAgorithm = model => { | ||
switch (algorithm(model.issuer)) { | ||
case ED25519: | ||
return "EdDSA" | ||
case 0x1205: | ||
case RSA: | ||
return "RS256" | ||
/* c8 ignore next 2 */ | ||
default: | ||
throw new RangeError(`Unknown KeyType "${code}"`) | ||
throw new RangeError(`Unknown KeyType "${algorithm(model.issuer)}"`) | ||
} | ||
} |
145
src/lib.js
import * as UCAN from "./ucan.js" | ||
import * as CBOR from "@ipld/dag-cbor" | ||
import * as RAW from "multiformats/codecs/raw" | ||
import * as CBOR from "./codec/cbor.js" | ||
import * as RAW from "./codec/raw.js" | ||
import * as UTF8 from "./utf8.js" | ||
@@ -12,4 +12,5 @@ import * as View from "./view.js" | ||
export * from "./ucan.js" | ||
import { code } from "./ucan.js" | ||
export * as DID from "./did.js" | ||
/** @type {UCAN.Version} */ | ||
@@ -19,3 +20,4 @@ export const VERSION = "0.8.1" | ||
export const raw = RAW.code | ||
/** @type {typeof CBOR.code|typeof RAW.code} */ | ||
export const code = CBOR.code | ||
@@ -29,42 +31,8 @@ /** | ||
* @param {UCAN.UCAN<C>} ucan | ||
* @returns {UCAN.ByteView<UCAN.UCAN<C>>} | ||
* @returns {UCAN.ByteView<UCAN.Model<C>>|UCAN.ByteView<UCAN.JWT<C>>} | ||
*/ | ||
export const encode = ucan => { | ||
switch (ucan.code) { | ||
case code: | ||
return CBOR.encode({ | ||
header: { | ||
version: ucan.header.version, | ||
algorithm: ucan.header.algorithm, | ||
}, | ||
body: { | ||
issuer: ucan.body.issuer, | ||
audience: ucan.body.audience, | ||
capabilities: ucan.body.capabilities.map(Parser.asCapability), | ||
expiration: ucan.body.expiration, | ||
proofs: ucan.body.proofs, | ||
// leave out optionals unless they are set | ||
...(ucan.body.facts.length > 0 && { facts: ucan.body.facts }), | ||
...(ucan.body.nonce && { nonce: ucan.body.nonce }), | ||
...(ucan.body.notBefore && { notBefore: ucan.body.notBefore }), | ||
}, | ||
signature: ucan.signature, | ||
}) | ||
case raw: | ||
return /** @type {Uint8Array} */ (UTF8.encode(ucan.jwt)) | ||
default: | ||
return invalidCode(ucan) | ||
} | ||
} | ||
export const encode = ucan => | ||
ucan instanceof Uint8Array ? RAW.encode(ucan) : CBOR.encode(ucan) | ||
/** | ||
* @param {never} ucan | ||
*/ | ||
const invalidCode = ({ code: unknown }) => { | ||
throw new TypeError( | ||
`Provided UCAN has unsupported code: ${unknown}, it must be ${code} for CBOR representation or ${raw} for JWT representation` | ||
) | ||
} | ||
/** | ||
* Decodes binary encoded UCAN. It assumes UCAN is in primary IPLD | ||
@@ -76,3 +44,3 @@ * representation and attempts to decode it with DAG-CBOR, if that | ||
* @template {UCAN.Capability} C | ||
* @param {UCAN.ByteView<UCAN.UCAN<C>>} bytes | ||
* @param {UCAN.ByteView<UCAN.Model<C>>|UCAN.ByteView<UCAN.JWT<C>>} bytes | ||
* @returns {UCAN.View<C>} | ||
@@ -82,9 +50,6 @@ */ | ||
try { | ||
const data = CBOR.decode(bytes) | ||
data.body.facts = data.body.facts || [] | ||
data.body.nonce = data.body.nonce || undefined | ||
data.body.notBefore = data.body.notBefore || undefined | ||
return View.cbor(data) | ||
return CBOR.decode(/** @type {UCAN.ByteView<UCAN.Model<C>>} */ (bytes)) | ||
} catch (error) { | ||
return parse(UTF8.decode(/** @type {Uint8Array} */ (bytes))) | ||
const jwt = UTF8.decode(/** @type {UCAN.RAW<C>} */ (bytes)) | ||
return parse(jwt) | ||
} | ||
@@ -110,14 +75,18 @@ } | ||
* @template {number} [A=typeof sha256.code] | ||
* @param {UCAN.UCAN<C>} ucan | ||
* @param {UCAN.UCAN<C>} data | ||
* @param {{hasher?: UCAN.MultihashHasher<A>}} [options] | ||
*/ | ||
export const write = async ( | ||
ucan, | ||
data, | ||
{ hasher = /** @type {UCAN.MultihashHasher<any> } */ (sha256) } = {} | ||
) => { | ||
const bytes = encode(ucan) | ||
const [code, bytes] = | ||
data instanceof Uint8Array | ||
? [RAW.code, RAW.encode(data)] | ||
: [CBOR.code, CBOR.encode(data)] | ||
const cid = /** @type {CID & UCAN.Proof<C, A>} */ ( | ||
CID.createV1(ucan.code, await hasher.digest(bytes)) | ||
CID.createV1(code, await hasher.digest(bytes)) | ||
) | ||
return { cid, bytes } | ||
return { cid, bytes, data } | ||
} | ||
@@ -138,7 +107,8 @@ | ||
* @template {UCAN.Capability} C | ||
* @param {UCAN.JWT<UCAN.UCAN<C>>} input | ||
* @param {UCAN.JWT<C>} jwt | ||
* @returns {UCAN.View<C>} | ||
*/ | ||
export const parse = input => { | ||
const ucan = Parser.parse(input) | ||
export const parse = jwt => { | ||
const model = Parser.parse(jwt) | ||
@@ -148,5 +118,5 @@ // If formatting UCAN produces same jwt string we can use IPLD representation | ||
// affect how we `encode` the UCAN. | ||
return Formatter.format(ucan) === input | ||
? View.cbor(ucan) | ||
: View.jwt(ucan, /** @type {UCAN.JWT<UCAN.RAW<C>>} */ (input)) | ||
return Formatter.format(model) === jwt | ||
? View.cbor(model) | ||
: View.jwt(model, UTF8.encode(jwt)) | ||
} | ||
@@ -159,14 +129,6 @@ | ||
* @param {UCAN.UCAN<C>} ucan | ||
* @returns {UCAN.JWT<UCAN.UCAN<C>>} | ||
* @returns {UCAN.JWT<C>} | ||
*/ | ||
export const format = ucan => { | ||
switch (ucan.code) { | ||
case code: | ||
return Formatter.format(ucan) | ||
case raw: | ||
return ucan.jwt | ||
default: | ||
return invalidCode(ucan) | ||
} | ||
} | ||
export const format = ucan => | ||
ucan instanceof Uint8Array ? UTF8.decode(ucan) : Formatter.format(ucan) | ||
@@ -194,17 +156,7 @@ /** | ||
}) => { | ||
const header = { | ||
const data = CBOR.match({ | ||
version: VERSION, | ||
algorithm: issuer.algorithm, | ||
} | ||
// Validate | ||
if (!audience.startsWith("did:")) { | ||
throw new TypeError("The audience must be a DID") | ||
} | ||
/** @type {UCAN.Body<C>} */ | ||
const body = { | ||
issuer: issuer.did(), | ||
audience, | ||
capabilities: capabilities.map(Parser.asCapability), | ||
issuer: parseDID(issuer, "issuer"), | ||
audience: parseDID(audience, "audience"), | ||
capabilities, | ||
facts, | ||
@@ -215,9 +167,26 @@ expiration, | ||
nonce, | ||
} | ||
// Provide fake signature to pass validation | ||
// we'll replace this with actual signature | ||
signature: EMPTY, | ||
}) | ||
const payload = UTF8.encode(Formatter.formatPayload({ header, body })) | ||
/** @type {UCAN.Signature<[UCAN.Header, UCAN.Body<C>]>} */ | ||
const payload = UTF8.encode(Formatter.formatPayload(data)) | ||
/** @type {UCAN.Signature<C>} */ | ||
const signature = await issuer.sign(payload) | ||
return View.cbor({ header, body, signature }) | ||
return View.cbor({ ...data, signature }) | ||
} | ||
/** | ||
* | ||
* @param {unknown & {did?:unknown}} value | ||
* @param {string} context | ||
*/ | ||
const parseDID = (value, context) => | ||
value && typeof value.did === "function" | ||
? Parser.parseDID(value.did(), `${context}.did()`) | ||
: Parser.ParseError.throw( | ||
`The ${context}.did() must be a function that returns DID` | ||
) | ||
const EMPTY = new Uint8Array() |
@@ -7,2 +7,3 @@ import * as UCAN from "./ucan.js" | ||
import { identity } from "multiformats/hashes/identity" | ||
import * as DID from "./did.js" | ||
import * as raw from "multiformats/codecs/raw" | ||
@@ -14,4 +15,4 @@ | ||
* @template {UCAN.Capability} C | ||
* @param {UCAN.JWT<UCAN.UCAN<C>>} input | ||
* @returns {UCAN.Data<C>} | ||
* @param {UCAN.JWT<C>} input | ||
* @returns {UCAN.Model<C>} | ||
*/ | ||
@@ -28,4 +29,4 @@ export const parse = input => { | ||
return { | ||
header: parseHeader(header), | ||
body: parseBody(body), | ||
...parseHeader(header), | ||
...parseBody(body), | ||
signature: base64urlpad.baseDecode(signature), | ||
@@ -37,3 +38,2 @@ } | ||
* @param {string} header | ||
* @returns {UCAN.Header} | ||
*/ | ||
@@ -44,6 +44,6 @@ export const parseHeader = header => { | ||
const _type = parseJWT(typ) | ||
const _algorithm = parseAlgorithm(alg) | ||
return { | ||
version: parseUCV(ucv), | ||
algorithm: parseAlgorithm(alg), | ||
version: parseVersion(ucv, "ucv"), | ||
} | ||
@@ -55,18 +55,16 @@ } | ||
* @param {string} input | ||
* @returns {UCAN.Body<C>} | ||
*/ | ||
export const parseBody = input => { | ||
/** @type {UCAN.Payload<C>} */ | ||
const body = json.decode(base64urlpad.baseDecode(input)) | ||
return { | ||
issuer: parseDID(body.iss), | ||
audience: parseDID(body.aud), | ||
expiration: parseInt(body.exp, 10), | ||
issuer: parseDID(body.iss, "iss"), | ||
audience: parseDID(body.aud, "aud"), | ||
expiration: parseInt(body.exp, "exp"), | ||
nonce: parseOptionalString(body.nnc, "nnc"), | ||
notBefore: parseMaybeInt(body.nbf, "nbf"), | ||
notBefore: parseOptionalInt(body.nbf, "nbf"), | ||
facts: parseOptionalArray(body.fct, parseFact, "fct") || [], | ||
proofs: parseProofs(body.prf), | ||
capabilities: /** @type {C[]} */ ( | ||
parseArray(body.att, parseCapability, "att") | ||
), | ||
proofs: parseProofs(body.prf, "prf"), | ||
capabilities: /** @type {C[]} */ (parseCapabilities(body.att, "att")), | ||
} | ||
@@ -77,7 +75,27 @@ } | ||
* @param {unknown} input | ||
* @param {string} name | ||
* @returns {number} | ||
*/ | ||
export const parseInt = (input, name) => | ||
Number.isInteger(input) | ||
? /** @type {number} */ (input) | ||
: ParseError.throw( | ||
`Expected integer but instead got '${name}: ${JSON.stringify(input)}'` | ||
) | ||
const parseCapability = input => parseStruct(input, asCapability, "att") | ||
/** | ||
* @param {unknown} input | ||
* @param {string} context | ||
*/ | ||
export const parseCapability = (input, context) => | ||
parseStruct(input, asCapability, context) | ||
/** | ||
* @param {unknown} input | ||
* @param {string} context | ||
*/ | ||
export const parseCapabilities = (input, context) => | ||
parseArray(input, parseCapability, context) | ||
/** | ||
* @template {UCAN.Capability} C | ||
@@ -158,9 +176,9 @@ * @param {object & {can?:unknown, with?:unknown}|C} input | ||
* @param {unknown} input | ||
* @param {(input:unknown) => T} parser | ||
* @param {(input:unknown, context:string) => T} parser | ||
* @param {string} context | ||
* @returns {T[]} | ||
*/ | ||
const parseArray = (input, parser, context) => | ||
export const parseArray = (input, parser, context) => | ||
Array.isArray(input) | ||
? input.map(parser) | ||
? input.map((element, n) => parser(element, `${context}[${n}]`)) | ||
: ParseError.throw(`${context} must be an array`) | ||
@@ -171,7 +189,7 @@ | ||
* @param {unknown} input | ||
* @param {(input:unknown) => T} parser | ||
* @param {(input:unknown, context: string) => T} parser | ||
* @param {string} context | ||
* @returns {T[]|undefined} | ||
*/ | ||
const parseOptionalArray = (input, parser, context) => | ||
export const parseOptionalArray = (input, parser, context) => | ||
input === undefined ? input : parseArray(input, parser, context) | ||
@@ -186,26 +204,31 @@ | ||
*/ | ||
const parseStruct = (input, parser, context) => | ||
export const parseStruct = (input, parser, context) => | ||
input != null && typeof input === "object" | ||
? parser(input) | ||
: ParseError.throw(`${context} must be of type object`) | ||
: ParseError.throw( | ||
`${context} must be of type object, instead got ${input}` | ||
) | ||
/** | ||
* @param {unknown} input | ||
* @param {string} context | ||
* @returns {UCAN.Fact} | ||
*/ | ||
const parseFact = input => parseStruct(input, Object, "fct elements") | ||
export const parseFact = (input, context) => parseStruct(input, Object, context) | ||
/** | ||
* @param {unknown} input | ||
* @param {string} context | ||
*/ | ||
const parseProofs = input => | ||
const parseProofs = (input, context) => | ||
Array.isArray(input) | ||
? parseArray(input, parseProof, "prf") | ||
: [parseProof(input)] | ||
? parseArray(input, parseProof, context) | ||
: [parseProof(input, context)] | ||
/** | ||
* @param {unknown} input | ||
* @param {string} context | ||
* @returns {UCAN.Proof} | ||
*/ | ||
const parseProof = input => { | ||
const parseProof = (input, context) => { | ||
const proof = | ||
@@ -215,3 +238,5 @@ typeof input === "string" | ||
: ParseError.throw( | ||
`prf has invalid value ${JSON.stringify(input)}, must be a string` | ||
`${context} has invalid value ${JSON.stringify( | ||
input | ||
)}, must be a string` | ||
) | ||
@@ -229,8 +254,10 @@ try { | ||
* @param {unknown} input | ||
* @returns {UCAN.DID} | ||
* @param {string} context | ||
*/ | ||
const parseDID = input => | ||
export const parseDID = (input, context) => | ||
typeof input === "string" && input.startsWith("did:") | ||
? /** @type {UCAN.DID} */ (input) | ||
: ParseError.throw(`DID has invalid representation '${input}'`) | ||
? DID.parse(/** @type {UCAN.DID} */ (input)) | ||
: ParseError.throw( | ||
`DID has invalid representation '${context}: ${JSON.stringify(input)}'` | ||
) | ||
@@ -241,3 +268,3 @@ /** | ||
*/ | ||
const parseOptionalString = (input, context = "Field") => { | ||
export const parseOptionalString = (input, context = "Field") => { | ||
switch (typeof input) { | ||
@@ -254,5 +281,5 @@ case "string": | ||
* @param {unknown} input | ||
* @param {string} [context] | ||
* @param {string} context | ||
*/ | ||
const parseMaybeInt = (input, context = "Field") => { | ||
export const parseOptionalInt = (input, context) => { | ||
switch (typeof input) { | ||
@@ -262,3 +289,3 @@ case "undefined": | ||
case "number": | ||
return parseInt(/** @type {any} */ (input), 10) | ||
return parseInt(/** @type {any} */ (input), context) | ||
default: | ||
@@ -273,11 +300,24 @@ return ParseError.throw( | ||
* @param {unknown} input | ||
* @param {string} context | ||
* @returns {UCAN.Version} | ||
*/ | ||
const parseUCV = input => | ||
export const parseVersion = (input, context) => | ||
/\d+\.\d+\.\d+/.test(/** @type {string} */ (input)) | ||
? /** @type {UCAN.Version} */ (input) | ||
: ParseError.throw(`Header has invalid version 'ucv: "${input}"'`) | ||
: ParseError.throw(`Invalid version '${context}: ${JSON.stringify(input)}'`) | ||
/** | ||
* | ||
* @param {unknown} input | ||
* @param {string} context | ||
* @returns {Uint8Array} | ||
*/ | ||
export const parseBytes = (input, context) => | ||
input instanceof Uint8Array | ||
? input | ||
: ParseError.throw( | ||
`${context} must be Uint8Array, instead got ${JSON.stringify(input)}` | ||
) | ||
/** | ||
* @param {unknown} input | ||
* @returns {"JWT"} | ||
@@ -306,3 +346,6 @@ */ | ||
class ParseError extends TypeError { | ||
export class ParseError extends TypeError { | ||
get name() { | ||
return "ParseError" | ||
} | ||
/** | ||
@@ -309,0 +352,0 @@ * @param {string} message |
@@ -7,3 +7,5 @@ import type { | ||
import type { code as RAW_CODE } from "multiformats/codecs/raw" | ||
import type { Signer, Signature } from "./crypto.js" | ||
import type { code as CBOR_CODE } from "@ipld/dag-cbor" | ||
import type { Signer } from "./crypto.js" | ||
import * as Crypto from "./crypto.js" | ||
@@ -14,3 +16,2 @@ export * from "./crypto.js" | ||
export const code = 0x78c0 | ||
export type Fact = Record<string, unknown> | ||
@@ -43,25 +44,62 @@ | ||
} | ||
export type JWT<T> = ToString<T> | ||
export type JWT<C extends Capability = Capability> = ToString< | ||
[Head, Payload<C>, Signature<C>], | ||
`${ToString<Head>}.${ToString<Payload<C>>}.${ToString<Signature<C>>}` | ||
> | ||
export type UCAN<C extends Capability = Capability> = CBOR<C> | RAW<C> | ||
export type Signature<C extends Capability = Capability> = | ||
Crypto.Signature<`${ToString<Head>}.${ToString<Payload<C>>}>`> | ||
export interface Data<C extends Capability = Capability> { | ||
readonly header: Header | ||
readonly body: Body<C> | ||
readonly signature: Signature<[Header, Body<C>]> | ||
interface Head { | ||
ucv: Version | ||
alg: "EdDSA" | "RS256" | ||
typ: "JWT" | ||
} | ||
export interface CBOR<C extends Capability = Capability> extends Data<C> { | ||
readonly code: typeof code | ||
export interface Payload<C extends Capability> { | ||
iss: DID | ||
aud: DID | ||
exp: number | ||
att: C[] | ||
nnc?: string | ||
nbf?: number | ||
fct?: Fact[] | ||
prf?: ToString<Proof<C>> | ||
} | ||
export interface RAW<C extends Capability = Capability> { | ||
readonly code: typeof RAW_CODE | ||
readonly jwt: JWT<RAW<C>> | ||
export type UCAN<C extends Capability = Capability> = Model<C> | RAW<C> | ||
// export interface Data<C extends Capability = Capability> { | ||
// readonly header: Header | ||
// readonly body: Body<C> | ||
// readonly signature: Signature<[Header, Body<C>]> | ||
// } | ||
// export interface CBOR<C extends Capability = Capability> extends Data<C> { | ||
// readonly code: typeof code | ||
// } | ||
export interface Input<C extends Capability = Capability> { | ||
version: Version | ||
issuer: ByteView<DID> | ||
audience: ByteView<DID> | ||
capabilities: C[] | ||
expiration: number | ||
notBefore?: number | ||
nonce?: string | ||
facts: Fact[] | ||
proofs: Proof[] | ||
} | ||
export type View<C extends Capability = Capability> = UCAN<C> & | ||
Data<C> & | ||
Header & | ||
Body<C> | ||
export interface Model<C extends Capability = Capability> extends Input<C> { | ||
signature: Signature<C> | ||
} | ||
export interface RAW<C extends Capability = Capability> | ||
extends Model<C>, | ||
ByteView<JWT<C>> {} | ||
export interface View<C extends Capability = Capability> extends Model<C> { | ||
readonly model: Model<C> | ||
} | ||
export interface UCANOptions< | ||
@@ -72,3 +110,3 @@ C extends Capability = Capability, | ||
issuer: Issuer<A> | ||
audience: DID | ||
audience: Agent | ||
capabilities: C[] | ||
@@ -88,3 +126,3 @@ lifetimeInSeconds?: number | ||
A extends number = number | ||
> = Link<Data<C>, 1, typeof code, A> | Link<JWT<Data<C>>, 1, typeof RAW_CODE, A> | ||
> = Link<Model<C>, 1, typeof CBOR_CODE, A> | Link<JWT<C>, 1, typeof RAW_CODE, A> | ||
@@ -112,2 +150,3 @@ export interface Block< | ||
export type DID<T = unknown> = ToString<T, `did:${string}`> | ||
export interface DIDView extends ByteView<DID>, Agent {} | ||
@@ -114,0 +153,0 @@ /** |
136
src/view.js
import * as UCAN from "./ucan.js" | ||
import * as RAW from "multiformats/codecs/raw" | ||
import * as DID from "./did.js" | ||
/** | ||
* @template {UCAN.Capability} C | ||
* @template {typeof UCAN.code|typeof RAW.code} Code | ||
* @extends {View<C>} | ||
* @implements {UCAN.View<C>} | ||
*/ | ||
class View { | ||
/** | ||
* @param {Code} code | ||
* @param {UCAN.Data<C>} data | ||
* @param {UCAN.Model<C>} model | ||
*/ | ||
constructor(code, { header, body, signature }) { | ||
constructor(model) { | ||
/** @readonly */ | ||
this.code = code | ||
/** @readonly */ | ||
this.header = header | ||
/** @readonly */ | ||
this.body = body | ||
/** @readonly */ | ||
this.signature = signature | ||
this.model = model | ||
} | ||
get version() { | ||
return this.header.version | ||
return this.model.version | ||
} | ||
get algorithm() { | ||
return this.header.algorithm | ||
} | ||
get issuer() { | ||
return this.body.issuer | ||
return this.model.issuer | ||
} | ||
/** | ||
* @returns {UCAN.DID} | ||
*/ | ||
get audience() { | ||
return this.body.audience | ||
return this.model.audience | ||
} | ||
@@ -47,3 +34,3 @@ | ||
get capabilities() { | ||
return this.body.capabilities | ||
return this.model.capabilities | ||
} | ||
@@ -55,3 +42,3 @@ | ||
get expiration() { | ||
return this.body.expiration | ||
return this.model.expiration | ||
} | ||
@@ -63,3 +50,3 @@ | ||
get notBefore() { | ||
return this.body.notBefore | ||
return this.model.notBefore | ||
} | ||
@@ -72,3 +59,3 @@ | ||
get nonce() { | ||
return this.body.nonce | ||
return this.model.nonce | ||
} | ||
@@ -80,3 +67,3 @@ | ||
get facts() { | ||
return this.body.facts | ||
return this.model.facts | ||
} | ||
@@ -89,4 +76,8 @@ | ||
get proofs() { | ||
return this.body.proofs | ||
return this.model.proofs | ||
} | ||
get signature() { | ||
return this.model.signature | ||
} | ||
} | ||
@@ -96,14 +87,79 @@ | ||
* @template {UCAN.Capability} C | ||
* @extends {View<C, typeof RAW.code>} | ||
* @implements {UCAN.View<C>} | ||
*/ | ||
class RAWView extends View { | ||
class JWTView extends Uint8Array { | ||
/** | ||
* | ||
* @param {UCAN.Data<C>} data | ||
* @param {UCAN.JWT<UCAN.RAW<C>>} jwt | ||
* @param {UCAN.Model<C>} model | ||
* @param {object} bytes | ||
* @param {ArrayBuffer} bytes.buffer | ||
* @param {number} [bytes.byteOffset] | ||
* @param {number} [bytes.byteLength] | ||
*/ | ||
constructor(data, jwt) { | ||
super(RAW.code, data) | ||
this.jwt = jwt | ||
constructor( | ||
model, | ||
{ buffer, byteOffset = 0, byteLength = buffer.byteLength } | ||
) { | ||
super(buffer, byteOffset, byteLength) | ||
this.model = model | ||
} | ||
get version() { | ||
return this.model.version | ||
} | ||
get issuer() { | ||
return this.model.issuer | ||
} | ||
get audience() { | ||
return this.model.audience | ||
} | ||
/** | ||
* @returns {C[]} | ||
*/ | ||
get capabilities() { | ||
return this.model.capabilities | ||
} | ||
/** | ||
* @returns {number} | ||
*/ | ||
get expiration() { | ||
return this.model.expiration | ||
} | ||
/** | ||
* @returns {undefined|number} | ||
*/ | ||
get notBefore() { | ||
return this.model.notBefore | ||
} | ||
/** | ||
* @returns {undefined|string} | ||
*/ | ||
get nonce() { | ||
return this.model.nonce | ||
} | ||
/** | ||
* @returns {UCAN.Fact[]} | ||
*/ | ||
get facts() { | ||
return this.model.facts | ||
} | ||
/** | ||
* @returns {UCAN.Proof[]} | ||
*/ | ||
get proofs() { | ||
return this.model.proofs | ||
} | ||
get signature() { | ||
return this.model.signature | ||
} | ||
} | ||
@@ -113,13 +169,13 @@ | ||
* @template {UCAN.Capability} C | ||
* @param {UCAN.Data<C>} data | ||
* @param {UCAN.Model<C>} data | ||
* @returns {UCAN.View<C>} | ||
*/ | ||
export const cbor = data => new View(UCAN.code, data) | ||
export const cbor = data => new View(data) | ||
/** | ||
* @template {UCAN.Capability} C | ||
* @param {UCAN.Data<C>} data | ||
* @param {UCAN.JWT<UCAN.RAW<C>>} jwt | ||
* @param {UCAN.Model<C>} model | ||
* @param {UCAN.ByteView<UCAN.JWT<C>>} bytes | ||
* @returns {UCAN.View<C>} | ||
*/ | ||
export const jwt = (data, jwt) => new RAWView(data, jwt) | ||
export const jwt = (model, bytes) => new JWTView(model, bytes) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
62050
33
1472
130