Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/console-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
echo "SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" >> .env
echo "NEXT_PUBLIC_PRIVATE_SPACES_DOMAINS=dmail.ai,storacha.network" >> .env
echo "NEXT_PUBLIC_UCAN_KMS_URL=https://ucan-kms-staging.protocol-labs.workers.dev" >> .env
echo "NEXT_PUBLIC_UCAN_KMS_DID=did:key:z6MkmRf149D6oc9wq9ioXCsT5fgTn6esd7JjB9S5JnM4Y9qj" >> .env
echo "NEXT_PUBLIC_UCAN_KMS_DID=did:web:staging.kms.storacha.network" >> .env
echo "NEXT_PUBLIC_ENABLE_TEST_IFRAME=true" >> .env
echo "NEXT_PUBLIC_SSO_ALLOWED_ORIGINS=https://mail.dmail.ai,https://testmailhu9fg9h.dmail.ai" >> .env
echo "NEXT_PUBLIC_SSO_IFRAME_STRIPE_PRICING_TABLE_ID=prctbl_1RrNd0F6A5ufQX5vpB0sYPHm" >> .env
Expand Down Expand Up @@ -188,7 +188,7 @@ jobs:
echo "SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" >> .env
echo "NEXT_PUBLIC_PRIVATE_SPACES_DOMAINS=dmail.ai,storacha.network" >> .env
echo "NEXT_PUBLIC_UCAN_KMS_URL=https://ucan-kms-production.protocol-labs.workers.dev" >> .env
echo "NEXT_PUBLIC_UCAN_KMS_DID=did:key:z6MksQJobJmBfPhjHWgFXVppqM6Fcjc1k7xu4z6xvusVrtKv" >> .env
echo "NEXT_PUBLIC_UCAN_KMS_DID=did:web:kms.storacha.network" >> .env
echo "NEXT_PUBLIC_ENABLE_TEST_IFRAME=true" >> .env
echo "NEXT_PUBLIC_SSO_ALLOWED_ORIGINS=https://mail.dmail.ai,https://testmailhu9fg9h.dmail.ai" >> .env
echo "NEXT_PUBLIC_SSO_IFRAME_STRIPE_PRICING_TABLE_ID=prctbl_1RrNZSF6A5ufQX5vryBeKnFe" >> .env
Expand Down
33 changes: 7 additions & 26 deletions packages/console/src/hooks/useFileDecryption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import type { Space, UnknownLink } from '@storacha/ui-react'
import { parse as parseLink } from 'multiformats/link'
import { create as createEncryptedClient } from '@storacha/encrypt-upload-client'
import { useKMSConfig } from '@storacha/ui-react'
import * as PlanCapabilities from '@storacha/capabilities/plan'
import * as SpaceCapabilities from '@storacha/capabilities/space'
import { delegate } from '@ucanto/core'
import { decrypt } from '@storacha/capabilities/space'
import type { FileMetadata } from '@storacha/encrypt-upload-client/types'

interface DecryptionState {
Expand All @@ -16,14 +14,14 @@ interface DecryptionState {
}

export const useFileDecryption = (space?: Space) => {
const [{ client, accounts }] = useW3()
const [{ client }] = useW3()
const [state, setState] = useState<DecryptionState>({
loading: false,
error: null,
fileMetadata: undefined
})

const { createKMSAdapter, isConfigured, kmsConfig } = useKMSConfig()
const { createKMSAdapter, isConfigured } = useKMSConfig()

const downloadBlob = (blob: Blob, filename: string) => {
const url = URL.createObjectURL(blob)
Expand Down Expand Up @@ -64,7 +62,7 @@ export const useFileDecryption = (space?: Space) => {
try {
// Create crypto adapter using shared KMS config
const cryptoAdapter = await createKMSAdapter()
if (!cryptoAdapter || !kmsConfig) {
if (!cryptoAdapter) {
throw new Error('KMS configuration required for decryption')
}

Expand All @@ -76,26 +74,9 @@ export const useFileDecryption = (space?: Space) => {

// Parse CID if it's a string
const encryptionMetadataCID = typeof cid === 'string' ? parseLink(cid) : cid

// Get account for plan delegation
const [account] = accounts ?? []
if (!account) {
throw new Error('No account available for plan/get delegation')
}

// Authorize the UCAN KMS server to check user's plan
const getPlanDelegation = await delegate({
issuer: client.agent.issuer,
audience: { did: () => kmsConfig.keyManagerServiceDID as `did:${string}:${string}` },
capabilities: [
{ can: PlanCapabilities.get.can, with: account.did() },
],
proofs: client.proofs(),
expiration: Math.floor((Date.now() + 60 * 15 * 1000) / 1000) // 15 minutes
})
const proofs = client.proofs([
{
can: SpaceCapabilities.decrypt.can,
can: 'space/content/decrypt',
with: space.did()
}
])
Expand All @@ -104,7 +85,7 @@ export const useFileDecryption = (space?: Space) => {
cap.can === 'ucan/attest' ||
cap.with === 'ucan:*'))

const decryptDelegation = await SpaceCapabilities.decrypt.delegate({
const decryptDelegation = await decrypt.delegate({
issuer: client.agent.issuer,
audience: client.agent.issuer,
with: space.did(),
Expand All @@ -121,7 +102,7 @@ export const useFileDecryption = (space?: Space) => {
{
spaceDID: space.did(),
decryptDelegation,
proofs: [...proofs, getPlanDelegation],
proofs,
}
)

Expand Down
2 changes: 0 additions & 2 deletions packages/ui/packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,9 @@
"devDependencies": {
"@ipld/dag-ucan": "^3.2.0",
"@storacha/eslint-config-ui": "workspace:^",
"@storacha/capabilities": "workspace:^",
"@testing-library/react": "catalog:",
"@testing-library/user-event": "catalog:",
"@types/react": "catalog:",
"@ucanto/core": "catalog:",
"@ucanto/client": "catalog:",
"@ucanto/interface": "catalog:",
"@ucanto/principal": "catalog:",
Expand Down
35 changes: 8 additions & 27 deletions packages/ui/packages/react/src/Uploader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ import { useW3 } from './providers/Provider.js'
import { create as createEncryptedClient } from '@storacha/encrypt-upload-client'
import { EncryptionConfig, EncryptionStrategy, FileMetadata } from '@storacha/encrypt-upload-client/types'
import { useKMSConfig, type KMSConfig } from './hooks.js'
import * as SpaceCapabilities from '@storacha/capabilities/space'
import * as PlanCapabilities from '@storacha/capabilities/plan'
import { delegate } from '@ucanto/core'

export type UploadProgress = Record<string, ProgressStatus>

Expand Down Expand Up @@ -198,8 +195,7 @@ export const UploaderRoot: Component<UploaderRootProps> = createComponent(
kmsConfig,
...props
}) => {
const [{ client, accounts }] = useW3()
const [account] = accounts ?? []
const [{ client }] = useW3()
const [files, setFiles] = useState<File[]>()
const file = files?.[0]
const setFile = (file: File | undefined): void => {
Expand Down Expand Up @@ -258,9 +254,6 @@ export const UploaderRoot: Component<UploaderRootProps> = createComponent(
if (files.length > 1) {
throw new Error('Encrypted uploads currently only support single files')
}
if (!account) {
throw new Error('No account selected for upload encryption')
}
const space = client.currentSpace()
if (!space) {
throw new Error('Missing private space for upload encryption')
Expand All @@ -269,7 +262,7 @@ export const UploaderRoot: Component<UploaderRootProps> = createComponent(
if (spaceAccess?.type !== 'private') {
throw new Error('Encrypted uploads currently only supported in private spaces')
}

let cryptoAdapter
if (spaceAccess.encryption.provider === 'google-kms') {
// Use KMS strategy with config from shared hook
Expand All @@ -281,7 +274,7 @@ export const UploaderRoot: Component<UploaderRootProps> = createComponent(
}
// else if - add other providers here...

if (!cryptoAdapter || !kmsConfigState) {
if (!cryptoAdapter) {
throw new Error('Encryption provider not supported')
}

Expand All @@ -290,28 +283,16 @@ export const UploaderRoot: Component<UploaderRootProps> = createComponent(
cryptoAdapter,
})

// Authorize the UCAN KMS server to check user's plan
const getPlanDelegation = await delegate({
issuer: client.agent.issuer,
audience: { did: () => kmsConfigState.keyManagerServiceDID as `did:${string}:${string}` },
capabilities: [
{ can: PlanCapabilities.get.can, with: account.did() },
],
proofs: client.proofs(),
expiration: Math.floor((Date.now() + 60 * 15 * 1000) / 1000) // 15 minutes
})

// Agent needs to have access to the space
const proofs = await client.agent.proofs([
{ can: SpaceCapabilities.EncryptionSetup.can, with: space.did() },
])
// Extract file metadata
const fileMetadata = extractFileMetadata(file)

// Prepare encryption config
const proofs = await client.agent.proofs([{ can: "space/encryption/setup", with: space.did() }]) // Agent needs to have access to the space
const encryptionConfig: EncryptionConfig = {
issuer: client.agent.issuer,
spaceDID: space.did(),
proofs: [...proofs, getPlanDelegation],
fileMetadata: extractFileMetadata(file),
proofs,
fileMetadata,
...(kmsConfigState?.location && encryptionStrategy === 'kms' && { location: kmsConfigState?.location }),
...(kmsConfigState?.keyring && encryptionStrategy === 'kms' && { keyring: kmsConfigState?.keyring }),
}
Expand Down
Loading
Loading