Skip to content

Commit

Permalink
feat: authorize gateway
Browse files Browse the repository at this point in the history
  • Loading branch information
fforbeck committed Nov 27, 2024
1 parent 3eb0f8e commit ca1b6da
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 10 deletions.
24 changes: 14 additions & 10 deletions packages/capabilities/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,16 +131,6 @@ export type UsageReport = InferInvokedCapability<typeof UsageCaps.report>
export type UsageReportSuccess = Record<ProviderDID, UsageData>
export type UsageReportFailure = Ucanto.Failure

export type EgressRecord = InferInvokedCapability<typeof SpaceCaps.egressRecord>
export type EgressRecordSuccess = {
space: SpaceDID
resource: UnknownLink
bytes: number
servedAt: ISO8601Date
cause: UnknownLink
}
export type EgressRecordFailure = ConsumerNotFound | Ucanto.Failure

export interface UsageData {
/** Provider the report concerns, e.g. `did:web:web3.storage` */
provider: ProviderDID
Expand Down Expand Up @@ -284,6 +274,18 @@ export type RateLimitListFailure = Ucanto.Failure
// Space
export type Space = InferInvokedCapability<typeof SpaceCaps.space>
export type SpaceInfo = InferInvokedCapability<typeof SpaceCaps.info>
export type SpaceContentServe = InferInvokedCapability<
typeof SpaceCaps.contentServe
>
export type EgressRecord = InferInvokedCapability<typeof SpaceCaps.egressRecord>
export type EgressRecordSuccess = {
space: SpaceDID
resource: UnknownLink
bytes: number
servedAt: ISO8601Date
cause: UnknownLink
}
export type EgressRecordFailure = ConsumerNotFound | Ucanto.Failure

// filecoin
export interface DealMetadata {
Expand Down Expand Up @@ -895,6 +897,8 @@ export type ServiceAbilityArray = [
ProviderAdd['can'],
Space['can'],
SpaceInfo['can'],
SpaceContentServe['can'],
EgressRecord['can'],
Upload['can'],
UploadAdd['can'],
UploadGet['can'],
Expand Down
71 changes: 71 additions & 0 deletions packages/w3up-client/src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Index as IndexCapabilities,
Upload as UploadCapabilities,
Filecoin as FilecoinCapabilities,
Space as SpaceCapabilities,
} from '@web3-storage/capabilities'
import * as DIDMailto from '@web3-storage/did-mailto'
import { Base } from './base.js'
Expand Down Expand Up @@ -253,6 +254,9 @@ export class Client extends Base {
*
* @typedef {object} CreateOptions
* @property {Account.Account} [account]
* @property {boolean} [unauthorizeGateway] - If true, the gateway will not be authorized to serve content from the space.
* @property {`did:web:${string}`} [gateway] - The gateway to be authorized to serve content from the space. If not provided, the default did:web:w3s.link gateway will be authorized.
* @property {number} [gatewayExpiration] - The time in seconds to expire the gateway authorization.
*
* @param {string} name
* @param {CreateOptions} options
Expand Down Expand Up @@ -291,9 +295,76 @@ export class Client extends Base {
)
}
}

if (!options.unauthorizeGateway) {
await this.authorizeGateway(space, {
gateway: options.gateway,
expiration: options.gatewayExpiration,
})
}

return space
}

/**
* Authorizes a gateway to serve content from the provided space and record egress events.
* Delegates the following capabilities to the gateway:
* - `space/content/serve/*`
*
* @param {import('./types.js').OwnedSpace} space - The space to authorize the gateway for.
* @param {object} [options] - Options for the authorization.
* @param {`did:web:${string}`} [options.gateway] - The Web DID of the gateway to authorize. If not provided, the default `did:web:w3s.link` gateway will be authorized.
* @param {number} [options.expiration] - The time in seconds to expire the authorization.
* @returns {Promise<void>}
*/
async authorizeGateway(space, options = {}) {
const currentSpace = this.currentSpace()
try {
if (currentSpace && currentSpace.did() !== space.did()) {
// Set the current space to the space we are authorizing the gateway for
await this.setCurrentSpace(space.did())
}

// Authorize the agent to access the space
const authProof = await space.createAuthorization(this.agent)

// Create a delegation for the Gateway to serve content from the space
const delegation = await this.createDelegation(
/** @type {import('@ucanto/client').Principal<`did:${string}:${string}`>} */
{
did: () => options.gateway ?? `did:web:w3s.link`,
},
[SpaceCapabilities.contentServe.can],
{
expiration: options.expiration ?? Infinity,
proofs: [authProof],
}
)

// Authorize the gateway to serve content from the space
const result = await this.capability.access.delegate({
delegations: [delegation],
})

if (result.error) {
throw new Error(
`failed to authorize gateway: ${result.error.message}`,
{
cause: result.error,
}
)
}

// TODO: save the delegation into the DelegationsStore
// delegation
} finally {
// Reset the current space to the original space
if (currentSpace && currentSpace.did() !== space.did()) {
await this.setCurrentSpace(currentSpace.did())
}
}
}

/**
* Share an existing space with another Storacha account via email address delegation.
* Delegates access to the space to the specified email account with the following permissions:
Expand Down

0 comments on commit ca1b6da

Please sign in to comment.