Skip to content

Commit 2c12c23

Browse files
authored
feat: add app to access/authorize and access/confirm (#264)
We need a way to ensure users are redirected back to `https://bsky.storage` after completing the Stripe checkout process. To facilitate this, pass facts from `access/authorize` to`access/confirm` - users can now add a fact like `{app: 'bsky-backups'}` to `access/authorize`, which will tell the `access/confirm` handling code in `w3infra` to render the same pricing table we use on https://bsky.storage which sends the user back to that app after picking a plan via the email flow. Add support for this to the client code via a new `appName` option in the account creation functions that gets converted to a fact at the lowest level. There are a bunch of ways we could do this, but in the interest of shipping ASAP I think this is a reasonable and fast way to go. See storacha/w3infra#472 for the other part of this.
1 parent 9c6267d commit 2c12c23

File tree

9 files changed

+24
-2
lines changed

9 files changed

+24
-2
lines changed

packages/access-client/src/access.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export const delegate = async (
5555
* @param {API.ProviderDID} [input.provider] - Provider that will receive the invocation.
5656
* @param {API.DID} [input.audience] - Principal requesting an access.
5757
* @param {API.Access} [input.access] - Access been requested.
58+
* @param {API.AppName} [input.appName] - A list of Facts to pass to the access/authorize invocation
5859
* @returns {Promise<API.Result<PendingAccessRequest, API.AccessAuthorizeFailure|API.InvocationError>>}
5960
*/
6061
export const request = async (
@@ -64,6 +65,7 @@ export const request = async (
6465
provider = /** @type {API.ProviderDID} */ (agent.connection.id.did()),
6566
audience: audience = agent.did(),
6667
access = spaceAccess,
68+
appName,
6769
}
6870
) => {
6971
// Request access from the account.
@@ -77,6 +79,7 @@ export const request = async (
7779
// in the meantime we translate new format to legacy format here.
7880
att: [...toCapabilities(access)],
7981
},
82+
facts: appName ? [{ appName }] : undefined,
8083
})
8184

8285
return result.error

packages/access-client/src/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,3 +341,7 @@ export type EncodedDelegation<C extends Capabilities = Capabilities> = string &
341341

342342
export type BytesDelegation<C extends Capabilities = Capabilities> =
343343
Uint8Array & Phantom<Delegation<C>>
344+
345+
export enum AppName {
346+
BskyBackups = 'bsky-backups',
347+
}

packages/ui/packages/react/src/Authenticator.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import React, {
1111
} from 'react'
1212
import { createComponent, createElement } from 'ariakit-react-utils'
1313
import { useW3, ContextState, ContextActions } from './providers/Provider.js'
14-
import { EmailAddress } from '@storacha/ui-core'
14+
import { EmailAddress, AppName } from '@storacha/ui-core'
1515

1616
export type AuthenticatorContextState = ContextState & {
1717
/**
@@ -72,7 +72,9 @@ export type AuthenticatorRootOptions<T extends As = typeof Fragment> =
7272
Options<T>
7373
export type AuthenticatorRootProps<T extends As = typeof Fragment> = Props<
7474
AuthenticatorRootOptions<T>
75-
>
75+
> & {
76+
appName?: AppName
77+
}
7678

7779
/**
7880
* Top level component of the headless Authenticator.
@@ -101,6 +103,7 @@ export const AuthenticatorRoot: Component<AuthenticatorRootProps> =
101103
if (client === undefined) throw new Error('missing client')
102104
await client.login(email as EmailAddress, {
103105
signal: controller?.signal,
106+
appName: props.appName
104107
})
105108
} catch (error: any) {
106109
if (!controller.signal.aborted) {

packages/upload-api/src/access/authorize.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ export const authorize = async ({ capability, invocation }, ctx) => {
8282
// Link to the invocation that requested the authorization.
8383
cause: invocation.cid,
8484
},
85+
// we copy the facts in so that information can be passed
86+
// from the invoker of this capability to the invoker of the confirm
87+
// capability - we use this, for example, to let bsky.storage users
88+
// specify that they should be redirected back to bsky.storage after
89+
// completing the Stripe plan selection flow
90+
facts: invocation.facts,
8591
})
8692
.delegate()
8793

packages/upload-api/src/validate.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export async function authorize(encodedUcan, env) {
5959
email: DidMailto.toEmail(DidMailto.fromString(account.did())),
6060
audience: agent.did(),
6161
ucan: delegationsToString(confirmDelegations),
62+
facts: request.facts,
6263
},
6364
}
6465
} catch (error) {

packages/w3up-client/src/account.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export const list = ({ agent }, { account } = {}) => {
8080
* @param {EmailAddress} email
8181
* @param {object} [options]
8282
* @param {AbortSignal} [options.signal]
83+
* @param {API.AppName} [options.appName]
8384
* @returns {Promise<API.Result<Account, Error>>}
8485
*/
8586
export const login = async ({ agent }, email, options = {}) => {
@@ -105,6 +106,7 @@ export const login = async ({ agent }, email, options = {}) => {
105106
{
106107
account,
107108
access: Access.accountAccess,
109+
appName: options.appName,
108110
}
109111
)
110112

packages/w3up-client/src/capability/access.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ export const claim = async ({ agent }, input) =>
8888
* @param {API.AccountDID} input.account
8989
* @param {API.Access} [input.access]
9090
* @param {API.DID} [input.audience]
91+
* @param {Agent.AppName} [input.appName]
9192
*/
9293
export const request = async ({ agent }, input) =>
9394
Agent.Access.request(agent, input)

packages/w3up-client/src/client.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export class Client extends Base {
9090
* @param {Account.EmailAddress} email
9191
* @param {object} [options]
9292
* @param {AbortSignal} [options.signal]
93+
* @param {import('@storacha/client/types').AppName} [options.appName]
9394
*/
9495
async login(email, options = {}) {
9596
const account = Result.unwrap(await Account.login(this, email, options))

packages/w3up-client/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { type Client } from './client.js'
2020
import { StorefrontService } from '@storacha/filecoin-client/storefront'
2121
export * from '@ucanto/interface'
2222
export * from '@storacha/did-mailto'
23+
export { AppName } from '@storacha/access/types'
2324
export type { Agent, CapabilityQuery } from '@storacha/access/agent'
2425
export type {
2526
Access,

0 commit comments

Comments
 (0)