Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: update session API #227

Merged
merged 12 commits into from
Feb 28, 2023
2 changes: 1 addition & 1 deletion packages/client/test/fixtures.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as ed25519 from '@ucanto/principal/ed25519'

/** did:key:z6Mkqa4oY9Z5Pf5tUcjLHLUsDjKwMC95HGXdE1j22jkbhz6r */
/** did:key:z6Mkk89bC3JrVqKie71YEcc5M1SMVxuCgNx6zLZ8SYJsxALi */
export const alice = ed25519.parse(
'MgCZT5vOnYZoVAeyjnzuJIVY9J4LNtJ+f8Js0cTPuKUpFne0BVEDJjEu6quFIU8yp91/TY/+MYK8GvlKoTDnqOCovCVM='
)
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/delegation.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import * as UCAN from '@ipld/dag-ucan'
import * as Signature from '@ipld/dag-ucan/signature'
import { from as toPrincipal } from '@ipld/dag-ucan/did'
import * as API from '@ucanto/interface'
import * as Link from './link.js'
import * as CBOR from '@ipld/dag-cbor'
import { sha256 } from 'multiformats/hashes/sha2'

/**
* @deprecated
Expand Down
2 changes: 1 addition & 1 deletion packages/core/test/fixtures.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as ed25519 from '@ucanto/principal/ed25519'

/** did:key:z6Mkqa4oY9Z5Pf5tUcjLHLUsDjKwMC95HGXdE1j22jkbhz6r */
/** did:key:z6Mkk89bC3JrVqKie71YEcc5M1SMVxuCgNx6zLZ8SYJsxALi */
export const alice = ed25519.parse(
'MgCZT5vOnYZoVAeyjnzuJIVY9J4LNtJ+f8Js0cTPuKUpFne0BVEDJjEu6quFIU8yp91/TY/+MYK8GvlKoTDnqOCovCVM='
)
Expand Down
14 changes: 10 additions & 4 deletions packages/interface/src/capability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
export interface Source {
capability: { can: Ability; with: URI; nb?: Caveats }
delegation: Delegation
index: number
}

export interface Match<T = unknown, M extends Match = UnknownMatch>
Expand Down Expand Up @@ -207,13 +206,13 @@ export type InferCreateOptions<R extends Resource, C extends {} | undefined> =
export type InferInvokeOptions<
R extends Resource,
C extends {} | undefined
> = UCANOptions & { issuer: Signer } & InferCreateOptions<R, C>
> = UCANOptions & { issuer: UCAN.Signer } & InferCreateOptions<R, C>

export type InferDelegationOptions<
R extends Resource,
C extends {} | undefined
> = UCANOptions & {
issuer: Signer
issuer: UCAN.Signer
with: R
nb?: Partial<InferCreateOptions<R, C>['nb']>
}
Expand Down Expand Up @@ -414,7 +413,7 @@ export interface DIDKeyResolutionError extends Failure {
readonly name: 'DIDKeyResolutionError'
readonly did: UCAN.DID

readonly cause?: Unauthorized
readonly cause?: Failure
}

export interface Expired extends Failure {
Expand All @@ -436,6 +435,12 @@ export interface InvalidSignature extends Failure {
readonly delegation: Delegation
}

export interface SessionEscalation extends Failure {
Gozala marked this conversation as resolved.
Show resolved Hide resolved
readonly name: 'SessionEscalation'
readonly delegation: Delegation
readonly cause: Failure
}

/**
* Error produces by invalid proof
*/
Expand All @@ -444,6 +449,7 @@ export type InvalidProof =
| NotValidBefore
| InvalidSignature
| InvalidAudience
| SessionEscalation
| DIDKeyResolutionError
| UnavailableProof

Expand Down
4 changes: 2 additions & 2 deletions packages/interface/src/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export interface DelegationOptions<C extends Capabilities> extends UCANOptions {
* the `audience` {@link Principal}.
*
*/
issuer: Signer
issuer: UCAN.Signer

/**
* The `audience` for a {@link Delegation} is the party being delegated to, or the
Expand Down Expand Up @@ -224,7 +224,7 @@ export interface Invocation<C extends Capability = Capability>
export interface InvocationOptions<C extends Capability = Capability>
extends UCANOptions {
/** The `issuer` of an invocation is the "caller" of the RPC method and the party that signs the invocation UCAN token. */
issuer: Signer
issuer: UCAN.Signer

/** The {@link Capability} that is being invoked. */
capability: C
Expand Down
7 changes: 7 additions & 0 deletions packages/principal/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@
],
"rsa": [
"dist/src/rsa.d.ts"
],
"account": [
"dist/src/account.d.ts"
]
}
},
Expand All @@ -71,6 +74,10 @@
"./rsa": {
"types": "./dist/src/rsa.d.ts",
"import": "./src/rsa.js"
},
"./account": {
"types": "./dist/src/account.d.ts",
"import": "./src/account.js"
}
},
"c8": {
Expand Down
41 changes: 41 additions & 0 deletions packages/principal/src/account.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as Signature from '@ipld/dag-ucan/signature'
import * as UCAN from '@ipld/dag-ucan'

/**
* @template {UCAN.DID} ID
* @param {{id: ID }} id
* @returns {UCAN.Signer<ID, Signature.NON_STANDARD>}
*/
export const from = ({ id }) => new Account(id)

/**
* An account is a special type of signer that produces empty signature, which
* signals that verifier needs to verify authorization interactively.
*
* @template {UCAN.DID} ID
* @implements {UCAN.Signer<ID, Signature.NON_STANDARD>}
*/
class Account {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: if possible I'd avoid using Account at all here (it's too easy to misinterpret) and call this something like NonStandardSigner. just a thought

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed it to Absentee which I think reflect the better than either misleading Account or NonStandardSigner which isn't specific enough. I'll assume it's ok, if not we can rename in the followup change.

/**
* @param {ID} id
*/
constructor(id) {
this.id = id
}
did() {
return this.id
}
/* c8 ignore next 3 */
get signatureCode() {
return Signature.NON_STANDARD
}
get signatureAlgorithm() {
return ''
}
sign() {
return Signature.createNonStandard(
this.signatureAlgorithm,
new Uint8Array(0)
)
}
}
1 change: 0 additions & 1 deletion packages/principal/src/ed25519/signer.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ export const from = ({ id, keys }) => {
throw new TypeError(`Unsupported archive format`)
}

from
/**
* @template {API.SignerImporter} O
* @param {O} other
Expand Down
3 changes: 2 additions & 1 deletion packages/principal/src/lib.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as ed25519 from './ed25519.js'
import * as RSA from './rsa.js'
import * as Account from './account.js'

export const Verifier = ed25519.Verifier.or(RSA.Verifier)
export const Signer = ed25519.or(RSA)

export { ed25519, RSA }
export { ed25519, RSA, Account }
17 changes: 17 additions & 0 deletions packages/principal/test/account.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Account } from '../src/lib.js'
import { assert } from 'chai'

export const utf8 = new TextEncoder()
describe('Account', async () => {
it('it can sign', async () => {
const account = Account.from({ id: 'did:mailto:[email protected]' })
Gozala marked this conversation as resolved.
Show resolved Hide resolved
Gozala marked this conversation as resolved.
Show resolved Hide resolved
assert.deepEqual(account.did(), 'did:mailto:[email protected]')
Gozala marked this conversation as resolved.
Show resolved Hide resolved
assert.deepEqual(account.signatureAlgorithm, '')
assert.deepEqual(account.signatureCode, 0xd000)

const signature = await account.sign(utf8.encode('hello world'))
assert.deepEqual(signature.code, 0xd000)
assert.deepEqual(signature.algorithm, '')
assert.deepEqual(signature.raw, new Uint8Array())
})
})
2 changes: 1 addition & 1 deletion packages/server/test/fixtures.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as ed25519 from '@ucanto/principal/ed25519'

/** did:key:z6Mkqa4oY9Z5Pf5tUcjLHLUsDjKwMC95HGXdE1j22jkbhz6r */
/** did:key:z6Mkk89bC3JrVqKie71YEcc5M1SMVxuCgNx6zLZ8SYJsxALi */
export const alice = ed25519.parse(
'MgCZT5vOnYZoVAeyjnzuJIVY9J4LNtJ+f8Js0cTPuKUpFne0BVEDJjEu6quFIU8yp91/TY/+MYK8GvlKoTDnqOCovCVM='
)
Expand Down
2 changes: 1 addition & 1 deletion packages/transport/test/fixtures.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as ed25519 from '@ucanto/principal/ed25519'

/** did:key:z6Mkqa4oY9Z5Pf5tUcjLHLUsDjKwMC95HGXdE1j22jkbhz6r */
/** did:key:z6Mkk89bC3JrVqKie71YEcc5M1SMVxuCgNx6zLZ8SYJsxALi */
export const alice = ed25519.parse(
'MgCZT5vOnYZoVAeyjnzuJIVY9J4LNtJ+f8Js0cTPuKUpFne0BVEDJjEu6quFIU8yp91/TY/+MYK8GvlKoTDnqOCovCVM='
)
Expand Down
31 changes: 26 additions & 5 deletions packages/validator/src/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,30 @@ export class DelegationError extends Failure {
}
}

/**
* @implements {API.SessionEscalation}
*/
export class SessionEscalation extends Failure {
/**
* @param {object} source
* @param {API.Delegation} source.delegation
* @param {API.Failure} source.cause
*/
constructor({ delegation, cause }) {
super()
this.name = the('SessionEscalation')
this.delegation = delegation
this.cause = cause
}
describe() {
const issuer = this.delegation.issuer.did()
return [
`Delegation ${this.delegation.cid} issued by ${issuer} has an invalid session`,
li(this.cause.message),
].join('\n')
}
}

/**
* @implements {API.InvalidSignature}
*/
Expand Down Expand Up @@ -145,7 +169,7 @@ export class UnavailableProof extends Failure {
export class DIDKeyResolutionError extends Failure {
/**
* @param {API.UCAN.DID} did
* @param {API.Unauthorized} [cause]
* @param {API.Failure} [cause]
*/
constructor(did, cause) {
super()
Expand All @@ -154,10 +178,7 @@ export class DIDKeyResolutionError extends Failure {
this.cause = cause
}
describe() {
return [
`Unable to resolve '${this.did}' key`,
...(this.cause ? [li(`Resolution failed: ${this.cause.message}`)] : []),
].join('\n')
return `Unable to resolve '${this.did}' key`
}
}

Expand Down
Loading