Skip to content

Preview Stores prototype: import cli_identity_bootstrap on store create preview#7610

Draft
alfonso-noriega wants to merge 1 commit into
store-list-commandfrom
preview-create-import-identity-bootstrap
Draft

Preview Stores prototype: import cli_identity_bootstrap on store create preview#7610
alfonso-noriega wants to merge 1 commit into
store-list-commandfrom
preview-create-import-identity-bootstrap

Conversation

@alfonso-noriega
Copy link
Copy Markdown
Contributor

@alfonso-noriega alfonso-noriega commented May 22, 2026

WHY are these changes introduced?

This stacks on top of store-list-command and continues the Preview Stores prototype work led by Donald (shop/world#730858, #7574). The original store create preview PoC handed users an explicit admin API token and a magic link, leaving every downstream CLI interaction (Identity-backed reads, whoami, future cross-org commands) on a separate auth path.

Donald's stacked World PR extends the orchestrator response with two new bootstrap blocks:

"cli_identity_bootstrap": { access_token, refresh_token, expires_in, user_id }
"store_auth_bootstrap":   { access_token, scopes, api_key, shop_domain }

…and Donald's CLI prototype (cli#7574) consumes them through importIdentitySession + importStoreAuthBootstrap shims that live alongside the existing PoC commands.

This PR brings the equivalent into the store create preview command itself, so the existing PoC stack (#7557#7558store-list-command) becomes the canonical place where preview-store creation also leaves the CLI fully authenticated. No new preview-only commands, no parallel auth paths — store create preview is now the only entry point and it lands the user in a real (placeholder-backed) Identity session.

WHAT is this pull request doing?

@shopify/cli-kit — new importIdentitySession primitive

  • public/node/session.ts: adds IdentitySessionBootstrap + importIdentitySession({accessToken, refreshToken, expiresAt, userId, adminApiStoreFqdns?}). The function writes the bootstrap tokens directly into Sessions[identityFqdn][userId] storage and marks the row as current. To satisfy validateSession without triggering device-auth re-prompts or the multi-audience token exchange, the import pre-seeds:
    • identity.scopes with allDefaultScopes() (plus employee when firstPartyDev() is true, because validateScopes requires the firstPartyDev flag and the employee scope to track each other);
    • applications with the bootstrap accessToken aliased under every non-store-scoped appId (partners / business-platform / storefront-renderer / app-management) AND under the store-prefixed Admin key (${storeFqdn}-${adminAppId}) for each entry in adminApiStoreFqdns. tokensFor reads the bare appId for non-store-scoped APIs and the store-prefixed key for Admin; aliasing the bootstrap under both lets ensureAuthenticatedBusinessPlatform / ensureAuthenticatedAdmin(<shop>) read it straight out of the cache without re-running the exchange. Without the store-prefixed seed, theme push --store <preview>, store execute --store <preview>, and any other ensureAuthenticatedAdmin-gated command would miss the cache and attempt an Identity token exchange that fails for placeholders (they aren't standard UserAccount resource owners that Identity can mint shop-scoped Admin tokens for).
  • public/node/environment.ts: getIdentityTokenInformation() now accepts an env arg and surfaces expiresAt + an explicit userId when SHOPIFY_CLI_IDENTITY_TOKEN_EXPIRES_AT / SHOPIFY_CLI_IDENTITY_USER_ID are present.
  • private/node/constants.ts: new env-var names (SHOPIFY_CLI_IDENTITY_USER_ID, SHOPIFY_CLI_IDENTITY_TOKEN_EXPIRES_AT).
  • private/node/session.ts: executeCompleteFlow / ensureAuthenticated accept an explicit env arg so the bootstrap path can opt into noPrompt: true without prompting the user mid-import. throwOnNoPrompt no longer calls logout() on failure — wiping the entire Sessions store on every noPrompt rejection is destructive when the caller is in a 401-retry cascade against a backend-issued session that can't be reconstructed without re-creating a shop. Users who explicitly want to clear sessions can run shopify auth logout.
  • private/node/session/exchange.ts: exchangeAccessForApplicationTokens is now tolerant of per-audience failures via Promise.allSettled. When at least one exchange succeeds the result is the union of successes; only when every exchange fails do we surface the first identity-level error (matching the previous "all-or-nothing" behavior). Motivation: backend-issued sessions (preview-store placeholders, app-automation tokens) may have a narrower audience grant than a real PKCE user; the old all-or-nothing semantics would surface that as a single confusing invalid_request mid-Promise.all rather than as a scoped "no <api> token found" at the actual call site.

@shopify/storestore create preview consumes the new bootstrap shape

  • services/store/create/preview/client.ts: parses cli_identity_bootstrap and store_auth_bootstrap from the orchestrator JSON. Defensive narrowing drops partial/malformed payloads silently so the create flow can't be stranded by a half-formed bootstrap (analytics still report the store as created on Core; the user just doesn't get the import side effects).
  • services/store/create/preview/index.ts: persists the store-auth bucket first (so a failed Identity import never orphans the just-created shop on the CLI side), then best-effort imports the Identity session via importIdentitySession with adminApiStoreFqdns: [resolvedShopDomain] so the store-prefixed Admin entry lands alongside the bare-audience entries. When store_auth_bootstrap is present, the bucket is keyed under bootstrap.shop_domain because that's the host that actually answers Admin API requests for this shop in the local rig (top-level shop_permanent_domain is canonical/display-only and doesn't route to a Spin instance on local). That same shop_domain is also what the result struct surfaces as shopPermanentDomain, so users can pipe --json output straight into store execute --store ... and the Admin URL the CLI builds resolves.
  • services/store/create/preview/result.ts: JSON output now includes identityImported: boolean so agents can tell whether the bootstrap was actually consumed; text output gains a "CLI identity" section explaining the outcome.

How to test your changes?

Automated

pnpm --filter @shopify/cli-kit vitest run src/private/node/session src/public/node/session
pnpm --filter @shopify/store vitest run preview
pnpm --filter @shopify/cli-kit --filter @shopify/store type-check

Expected: 126 cli-kit session tests pass, 22 preview tests pass (4 new for bootstrap behavior + the previously-existing baseline), and the only type-check error is the pre-existing client.test.ts Response-vs-node-fetch.Response mismatch that's unrelated to this PR.

Manual against the local rig (with Donald's paired World branch checked out)

export SHOPIFY_SERVICE_ENV=local

# 1) Wipe any stale session row so we're starting clean
rm ~/Library/Preferences/shopify-cli-kit-nodejs/config.json
rm ~/Library/Preferences/shopify-cli-store-nodejs/config.json

# 2) Create a preview store. Expect both bootstrap blocks in the orchestrator
#    response, `identityImported: true` in the CLI output, and a placeholder
#    Identity session persisted on disk.
node packages/cli/bin/dev.js store create preview --json \
  | tee /tmp/preview.json \
  | jq '{shopPermanentDomain, identityImported, userId}'

# 3) Confirm the placeholder Identity session is keyed correctly:
jq '.sessions | fromjson | to_entries[] | .value | keys' \
  ~/Library/Preferences/shopify-cli-kit-nodejs/config.json

# 4) Use the surfaced shopPermanentDomain to round-trip through store execute.
#    No `--token` flag needed — the store-auth bucket is hit via the standard seam.
DOMAIN=$(jq -r .shopPermanentDomain /tmp/preview.json)
node packages/cli/bin/dev.js store execute --store "$DOMAIN" \
  --query 'query { shop { name myshopifyDomain } }' --json

Post-release steps

None. This is part of the Preview Stores prototype stack and is paired to Donald's local-only shop/world#730858; not intended to ship as-is.

Checklist

  • I've considered possible cross-platform impacts (Mac, Linux, Windows)
  • I've considered possible documentation changes
  • I've considered analytics changes to measure impact
  • The change is user-facing — no changeset added because this is a prototype stack paired to local-only World endpoints and is not intended to ship as-is

@alfonso-noriega alfonso-noriega changed the title feat(store): import cli_identity_bootstrap on store create preview Preview Stores prototype: import cli_identity_bootstrap on store create preview May 22, 2026
Copy link
Copy Markdown
Contributor Author

alfonso-noriega commented May 22, 2026

@github-actions github-actions Bot added the no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users. label May 22, 2026
@alfonso-noriega alfonso-noriega force-pushed the preview-create-import-identity-bootstrap branch 2 times, most recently from 7a36a25 to 9b52c0a Compare May 22, 2026 14:28
Consumes the new `cli_identity_bootstrap` and `store_auth_bootstrap`
fields returned by Donald's PreviewStoresController extension, so the
canonical `store create preview` command lands the user in a real
(placeholder-backed) Identity session in addition to the per-shop Admin
token. Adds `importIdentitySession` to cli-kit, makes `throwOnNoPrompt`
non-destructive, and makes app-token exchange tolerant of per-audience
failures.
@alfonso-noriega alfonso-noriega force-pushed the preview-create-import-identity-bootstrap branch from 9b52c0a to 9d7ec9b Compare May 22, 2026 15:16
@github-actions
Copy link
Copy Markdown
Contributor

Differences in type declarations

We detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:

  • Some seemingly private modules might be re-exported through public modules.
  • If the branch is behind main you might see odd diffs, rebase main into this branch.

New type declarations

We found no new type declarations in this PR

Existing type declarations

packages/cli-kit/dist/private/node/constants.d.ts
@@ -28,6 +28,8 @@ export declare const environmentVariables: {
     spinAppHost: string;
     organization: string;
     identityToken: string;
+    identityTokenUserId: string;
+    identityTokenExpiresAt: string;
     refreshToken: string;
     otelURL: string;
     themeKitAccessDomain: string;
packages/cli-kit/dist/public/node/environment.d.ts
@@ -33,10 +33,11 @@ export declare function getBackendPort(): number | undefined;
  *
  * @returns The identity token information in case it exists.
  */
-export declare function getIdentityTokenInformation(): {
+export declare function getIdentityTokenInformation(env?: NodeJS.ProcessEnv): {
     accessToken: string;
     refreshToken: string;
     userId: string;
+    expiresAt?: Date;
 } | undefined;
 /**
  * Checks if the JSON output is enabled via flag (--json or -j) or environment variable (SHOPIFY_FLAG_JSON).
packages/cli-kit/dist/public/node/local-storage.d.ts
@@ -34,6 +34,19 @@ export declare class LocalStorage<T extends Record<string, any>> {
      * @throws BugError if an unexpected error occurs.
      */
     delete<TKey extends keyof T>(key: TKey): void;
+    /**
+     * Get every  pair currently held in the local storage.
+     *
+     * Useful for callers that need to enumerate all stored values without knowing the
+     * full set of keys in advance (for example, a  command iterating over every
+     * stored session). The  package stores its entire state as a single JSON
+     * object, so this is just a typed wrapper around that object.
+     *
+     * @returns An array of  tuples.
+     * @throws AbortError if a permission error occurs.
+     * @throws BugError if an unexpected error occurs.
+     */
+    entries(): [keyof T, T[keyof T]][];
     /**
      * Clear the local storage (delete all values).
      *
packages/cli-kit/dist/public/node/session.d.ts
 import { AdminAPIScope, AppManagementAPIScope, BusinessPlatformScope, EnsureAuthenticatedAdditionalOptions, PartnersAPIScope, StorefrontRendererScope } from '../../private/node/session.js';
+import type { Session as IdentityStoredSession, Sessions } from '../../private/node/session/schema.js';
 /**
  * Session Object to access the Admin API, includes the token and the store FQDN.
  */
 export interface AdminSession {
     token: string;
     storeFqdn: string;
 }
 /**
  * Session Object for Partners API and App Management API access.
  */
 export interface Session {
     token: string;
     businessPlatformToken: string;
     accountInfo: AccountInfo;
     userId: string;
 }
 export type AccountInfo = UserAccountInfo | ServiceAccountInfo | UnknownAccountInfo;
 /**
  * Records the user ID that should be attached to command analytics for this process.
  *
  * @param userId - User identifier to report on the command analytics event.
  */
 export declare function setLastSeenUserId(userId: string): void;
 interface UserAccountInfo {
     type: 'UserAccount';
     email: string;
 }
 interface ServiceAccountInfo {
     type: 'ServiceAccount';
     orgName: string;
 }
 interface UnknownAccountInfo {
     type: 'UnknownAccount';
 }
 /**
  * Type guard to check if an account is a UserAccount.
  *
  * @param account - The account to check.
  * @returns True if the account is a UserAccount.
  */
 export declare function isUserAccount(account: AccountInfo): account is UserAccountInfo;
 /**
  * Type guard to check if an account is a ServiceAccount.
  *
  * @param account - The account to check.
  * @returns True if the account is a ServiceAccount.
  */
 export declare function isServiceAccount(account: AccountInfo): account is ServiceAccountInfo;
 /**
+ * Diagnostic snapshot of the currently-active CLI session. Returned by
+ * `getCurrentSessionInfo`. Intended for `shopify auth whoami` and similar
+ * inspection commands; not part of the normal request-execution path.
+ *
+ * All token values are masked to their first 8 characters + length to avoid
+ * leaking credentials into logs/screenshots, while still letting an operator
+ * eyeball that a token is present and roughly which one.
+ */
+export interface CurrentSessionInfo {
+    /** True when a Sessions row resolves to the current session id. */
+    loggedIn: boolean;
+    /** Identity FQDN this run resolved to (e.g. `accounts.shopify.com`, `identity.shop.dev`). */
+    identityFqdn: string;
+    /** Active session id (= bucket key in `Sessions[fqdn]`). */
+    userId?: string;
+    /** Heuristic: `userId` looks like a UUID and matches the imported placeholder convention. */
+    looksLikePlaceholder?: boolean;
+    /** Display alias from the identity record (typically the user email, undefined for placeholders). */
+    alias?: string;
+    /** Number of scopes claimed by the identity token. */
+    scopeCount?: number;
+    /** Scopes claimed by the identity token. */
+    scopes?: string[];
+    /** Masked preview of the identity access token + raw length. */
+    identityToken?: {
+        preview: string;
+        length: number;
+        expiresAt: string;
+    };
+    /** Whether the identity refresh token is present + its length. */
+    refreshToken?: {
+        present: true;
+        length: number;
+    } | {
+        present: false;
+    };
+    /** Per-audience application tokens cached for this session. */
+    applications?: {
+        appId: string;
+        preview: string;
+        length: number;
+        expiresAt: string;
+        storeFqdn?: string;
+    }[];
+    /**
+     * The raw, unredacted session row read from disk. Populated only when the
+     * caller explicitly opts in via `getCurrentSessionInfo({raw: true})`. Use
+     * sparingly — contains live access tokens and refresh tokens.
+     */
+    raw?: IdentityStoredSession;
+    /**
+     * The full `Sessions` blob (all fqdns, all users) read from disk. Populated
+     * only when `{raw: true}`. Useful for diagnosing why a particular session
+     * is/isn't being resolved as the current one.
+     */
+    rawAllSessions?: Sessions;
+}
+/**
+ * Options for `getCurrentSessionInfo`.
+ */
+export interface GetCurrentSessionInfoOptions {
+    /**
+     * When true, include the raw (unredacted) session row and the full Sessions
+     * blob on disk. Contains live tokens — do not log this in shared channels.
+     * Intended for `shopify auth whoami --raw` and equivalent diagnostic uses.
+     */
+    raw?: boolean;
+}
+/**
+ * Read the currently-active CLI session from disk and return a non-secret
+ * snapshot of its shape. Does NOT validate scopes, refresh expired tokens, or
+ * make any network calls — it's a pure inspection of `sessionStore.fetch()`.
+ *
+ * Returns `{ loggedIn: false }` when there is no current session row on disk.
+ *
+ * @param options - Optional flags. Pass `{raw: true}` to include unredacted
+ * tokens and the full Sessions blob in the returned object.
+ */
+export declare function getCurrentSessionInfo(options?: GetCurrentSessionInfoOptions): Promise<CurrentSessionInfo>;
+/**
+ * A bootstrap payload describing a backend-issued Identity OAuth session that
+ * the CLI should adopt as if `auth login` had just succeeded.
+ *
+ * Used by `importIdentitySession` to make `preview create` (and similar
+ * server-driven account-provisioning flows) leave the CLI authenticated with
+ * a real `IdentityToken` + `refreshToken` + per-application tokens, persisted
+ * under the standard `Sessions[identityFqdn][userId]` storage.
+ */
+export interface IdentitySessionBootstrap {
+    /** Identity access token (`shpat_...`-style or Identity-issued). Required. */
+    accessToken: string;
+    /** Identity refresh token. Required. */
+    refreshToken: string;
+    /** When the `accessToken` is expected to expire. Required. */
+    expiresAt: Date;
+    /**
+     * The Identity-side user id this session represents. Optional; when omitted,
+     * a deterministic UUID derived from `accessToken` is used. For placeholder
+     * accounts the backend should pass the placeholder UUID directly so the
+     * resulting bucket lines up with `ResourceOwner` rows on the server.
+     */
+    userId?: string;
+    /**
+     * Per-shop Admin API tokens to cache alongside the Identity session.
+     *
+     * The Admin API only accepts shop-app tokens (`shpat_*`), not Identity-issued
+     * OAuth access tokens. The bootstrap's `accessToken` above is the Identity
+     * OAuth token — valid for Identity-fronted APIs (partners / BP / storefront-
+     * renderer / app-management) but rejected by the Admin API with
+     * `[API] Service is not valid for authentication`. To make
+     * `ensureAuthenticatedAdmin(storeFqdn)` resolve to a working token, callers
+     * must pass the per-shop `shpat_*` token here (e.g. the value of
+     * `store_auth_bootstrap.access_token` from a preview-store create response).
+     *
+     * Each entry's key should be the same domain the user will type as `--store`
+     * (e.g. `preview-X.dev-api.shop.dev` on the rig, where `.my.shop.dev` isn't
+     * routable). The token is seeded into `applications[`${storeFqdn}-${adminAppId}`]`
+     * with a 1-year expiry; that's the exact key `tokensFor` looks up when an
+     * Admin API call requests `storeFqdn`. Omit when the bootstrap isn't per-shop.
+     */
+    adminStoreTokens?: Record<string, string>;
+}
+/**
+ * Adopt a backend-issued Identity OAuth session as the active CLI account.
+ *
+ * Writes the bootstrap tokens directly into the `Sessions[identityFqdn][userId]`
+ * storage and marks the row as the current session. To satisfy `validateSession`
+ * without triggering the device-auth re-prompt or the multi-audience token
+ * exchange, the import pre-seeds:
+ *
+ *   - `identity.scopes`: the union of all default CLI scopes (plus `employee`
+ *     when running as a first-party dev). The placeholder's effective scopes
+ *     are opaque to the CLI; we claim coverage so `validateScopes` returns true
+ *     and the cached session is used verbatim.
+ *   - `applications`: the same bootstrap accessToken aliased under every
+ *     standard appId (`admin`, `partners`, `business-platform`,
+ *     `storefront-renderer`, `app-management`). The placeholder bootstrap is
+ *     usable directly against the audiences the backend authorized for it
+ *     (typically Admin + Business Platform); aliasing it lets
+ *     `ensureAuthenticatedBusinessPlatform` / `ensureAuthenticatedAdmin` read
+ *     it straight out of the cache without re-running the exchange. APIs that
+ *     the bootstrap is not authorized for will reject the request with a
+ *     clean 401/403 at the call site — better than a mid-import re-auth loop.
+ *
+ * The caller is expected to have already obtained a valid Identity refresh
+ * token from a trusted backend path (e.g. `POST /services/preview-stores`).
+ * No browser, no device-code prompt, no consent UI.
+ *
+ * @param bootstrap - Backend-issued Identity tokens to import.
+ * @returns The userId under which the session was persisted.
+ */
+export declare function importIdentitySession(bootstrap: IdentitySessionBootstrap): Promise<{
+    userId: string;
+}>;
+/**
  * Ensure that we have a valid session with no particular scopes.
  *
  * @param env - Optional environment variables to use.
  * @param options - Optional extra options to use.
  * @returns The user ID.
  */
 export declare function ensureAuthenticatedUser(env?: NodeJS.ProcessEnv, options?: EnsureAuthenticatedAdditionalOptions): Promise<{
     userId: string;
 }>;
 /**
  * Ensure that we have a valid session to access the Partners API.
  * If SHOPIFY_CLI_PARTNERS_TOKEN exists, that token will be used to obtain a valid Partners Token
  * If SHOPIFY_CLI_PARTNERS_TOKEN exists, scopes will be ignored.
  *
  * @param scopes - Optional array of extra scopes to authenticate with.
  * @param env - Optional environment variables to use.
  * @param options - Optional extra options to use.
  * @returns The access token for the Partners API.
  */
 export declare function ensureAuthenticatedPartners(scopes?: PartnersAPIScope[], env?: NodeJS.ProcessEnv, options?: EnsureAuthenticatedAdditionalOptions): Promise<{
     token: string;
     userId: string;
 }>;
 /**
  * Ensure that we have a valid session to access the App Management API.
  *
  * @param options - Optional extra options to use.
  * @param appManagementScopes - Optional array of extra scopes to authenticate with.
  * @param businessPlatformScopes - Optional array of extra scopes to authenticate with.
  * @param env - Optional environment variables to use.
  * @returns The access token for the App Management API.
  */
 export declare function ensureAuthenticatedAppManagementAndBusinessPlatform(options?: EnsureAuthenticatedAdditionalOptions, appManagementScopes?: AppManagementAPIScope[], businessPlatformScopes?: BusinessPlatformScope[], env?: NodeJS.ProcessEnv): Promise<{
     appManagementToken: string;
     userId: string;
     businessPlatformToken: string;
 }>;
 /**
  * Ensure that we have a valid session to access the Storefront API.
  *
  * @param scopes - Optional array of extra scopes to authenticate with.
  * @param password - Optional password to use.
  * @param options - Optional extra options to use.
  * @returns The access token for the Storefront API.
  */
 export declare function ensureAuthenticatedStorefront(scopes?: StorefrontRendererScope[], password?: string | undefined, options?: EnsureAuthenticatedAdditionalOptions): Promise<string>;
 /**
  * Ensure that we have a valid Admin session for the given store.
  *
  * @param store - Store fqdn to request auth for.
  * @param scopes - Optional array of extra scopes to authenticate with.
  * @param options - Optional extra options to use.
  * @returns The access token for the Admin API.
  */
 export declare function ensureAuthenticatedAdmin(store: string, scopes?: AdminAPIScope[], options?: EnsureAuthenticatedAdditionalOptions): Promise<AdminSession>;
 /**
  * Ensure that we have a valid session to access the Theme API.
  * If a password is provided, that token will be used against Theme Access API.
  * Otherwise, it will ensure that the user is authenticated with the Admin API.
  *
  * @param store - Store fqdn to request auth for.
  * @param password - Password generated from Theme Access app.
  * @param scopes - Optional array of extra scopes to authenticate with.
  * @param options - Optional extra options to use.
  * @returns The access token and store.
  */
 export declare function ensureAuthenticatedThemes(store: string, password: string | undefined, scopes?: AdminAPIScope[], options?: EnsureAuthenticatedAdditionalOptions): Promise<AdminSession>;
 /**
  * Ensure that we have a valid session to access the Business Platform API.
  *
  * @param scopes - Optional array of extra scopes to authenticate with.
  * @returns The access token for the Business Platform API.
  */
 export declare function ensureAuthenticatedBusinessPlatform(scopes?: BusinessPlatformScope[]): Promise<string>;
 /**
  * Logout from Shopify.
  *
  * @returns A promise that resolves when the logout is complete.
  */
 export declare function logout(): Promise<void>;
 /**
  * Ensure that we have a valid Admin session for the given store, with access on behalf of the app.
  *
  * See `ensureAuthenticatedAdmin` for access on behalf of a user.
  *
  * @param storeFqdn - Store fqdn to request auth for.
  * @param clientId - Client ID of the app.
  * @param clientSecret - Client secret of the app.
  * @returns The access token for the Admin API.
  */
 export declare function ensureAuthenticatedAdminAsApp(storeFqdn: string, clientId: string, clientSecret: string): Promise<AdminSession>;
 export {};
packages/cli-kit/dist/private/node/session/exchange.d.ts
@@ -14,10 +14,26 @@ export interface ExchangeScopes {
     appManagement: string[];
 }
 /**
- * Given an identity token, request an application token.
+ * Given an identity token, request an application token for each Shopify API
+ * (partners / storefront-renderer / business-platform / admin / app-management).
+ *
+ * Per-API failures are tolerated: a token exchange that returns 
+ * or other non-fatal errors for one audience is logged at debug level and skipped,
+ * while the successful exchanges are merged into the returned record. Callers
+ * already validate that the specific API token they need is present (see the
+ *  BugError throws in
+ * ), so partial success surfaces a clear, scoped error at
+ * the call site rather than a confusing  mid-Promise.all.
+ *
+ * The motivating case is server-issued Identity bootstraps (e.g. preview-store
+ * ) whose Identity token is bound to a single OAuth
+ * application and therefore can only be exchanged for a subset of audiences. For
+ * normal device-auth logins all five exchanges still succeed exactly as before.
+ *
  * @param identityToken - access token obtained in a previous step
+ * @param scopes - per-API scope sets to request
  * @param store - the store to use, only needed for admin API
- * @returns An array with the application access tokens.
+ * @returns A merged record of every application token that was successfully minted.
  */
 export declare function exchangeAccessForApplicationTokens(identityToken: IdentityToken, scopes: ExchangeScopes, store?: string): Promise<Record<string, ApplicationToken>>;
 /**
packages/cli-kit/dist/private/node/session/schema.d.ts
@@ -12,8 +12,8 @@ declare const IdentityTokenSchema: zod.ZodObject<{
 }, "strip", zod.ZodTypeAny, {
     accessToken: string;
     refreshToken: string;
-    scopes: string[];
     expiresAt: Date;
+    scopes: string[];
     userId: string;
     alias?: string | undefined;
 }, {
@@ -34,8 +34,8 @@ declare const ApplicationTokenSchema: zod.ZodObject<{
     storeFqdn: zod.ZodOptional<zod.ZodString>;
 }, "strip", zod.ZodTypeAny, {
     accessToken: string;
-    scopes: string[];
     expiresAt: Date;
+    scopes: string[];
     storeFqdn?: string | undefined;
 }, {
     accessToken: string;
@@ -54,8 +54,8 @@ declare const SessionSchema: zod.ZodObject<{
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     }, {
@@ -73,8 +73,8 @@ declare const SessionSchema: zod.ZodObject<{
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -88,8 +88,8 @@ declare const SessionSchema: zod.ZodObject<{
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -103,8 +103,8 @@ declare const SessionSchema: zod.ZodObject<{
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -116,16 +116,16 @@ declare const SessionSchema: zod.ZodObject<{
     identity: {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     };
     applications: {} & {
         [k: string]: {
             accessToken: string;
-            scopes: string[];
             expiresAt: Date;
+            scopes: string[];
             storeFqdn?: string | undefined;
         };
     };
@@ -166,8 +166,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     }, {
@@ -185,8 +185,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -200,8 +200,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -215,8 +215,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -228,16 +228,16 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     identity: {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     };
     applications: {} & {
         [k: string]: {
             accessToken: string;
-            scopes: string[];
             expiresAt: Date;
+            scopes: string[];
             storeFqdn?: string | undefined;
         };
     };
@@ -269,8 +269,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     }, {
@@ -288,8 +288,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -303,8 +303,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -318,8 +318,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -331,16 +331,16 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     identity: {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     };
     applications: {} & {
         [k: string]: {
             accessToken: string;
-            scopes: string[];
             expiresAt: Date;
+            scopes: string[];
             storeFqdn?: string | undefined;
         };
     };
@@ -372,8 +372,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     }, {
@@ -391,8 +391,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -406,8 +406,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -421,8 +421,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -434,16 +434,16 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     identity: {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     };
     applications: {} & {
         [k: string]: {
             accessToken: string;
-            scopes: string[];
             expiresAt: Date;
+            scopes: string[];
             storeFqdn?: string | undefined;
         };
     };
@@ -475,8 +475,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     }, {
@@ -494,8 +494,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -509,8 +509,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -524,8 +524,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -537,16 +537,16 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     identity: {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     };
     applications: {} & {
         [k: string]: {
             accessToken: string;
-            scopes: string[];
             expiresAt: Date;
+            scopes: string[];
             storeFqdn?: string | undefined;
         };
     };
@@ -578,8 +578,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     }, {
@@ -597,8 +597,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -612,8 +612,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -627,8 +627,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -640,16 +640,16 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     identity: {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     };
     applications: {} & {
         [k: string]: {
             accessToken: string;
-            scopes: string[];
             expiresAt: Date;
+            scopes: string[];
             storeFqdn?: string | undefined;
         };
     };
@@ -681,8 +681,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     }, {
@@ -700,8 +700,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -715,8 +715,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -730,8 +730,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -743,16 +743,16 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     identity: {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     };
     applications: {} & {
         [k: string]: {
             accessToken: string;
-            scopes: string[];
             expiresAt: Date;
+            scopes: string[];
             storeFqdn?: string | undefined;
         };
     };
@@ -784,8 +784,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     }, {
@@ -803,8 +803,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -818,8 +818,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -833,8 +833,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -846,16 +846,16 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     identity: {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     };
     applications: {} & {
         [k: string]: {
             accessToken: string;
-            scopes: string[];
             expiresAt: Date;
+            scopes: string[];
             storeFqdn?: string | undefined;
         };
     };
@@ -887,8 +887,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     }, {
@@ -906,8 +906,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -921,8 +921,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -936,8 +936,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -949,16 +949,16 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     identity: {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     };
     applications: {} & {
         [k: string]: {
             accessToken: string;
-            scopes: string[];
             expiresAt: Date;
+            scopes: string[];
             storeFqdn?: string | undefined;
         };
     };
@@ -990,8 +990,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     }, {
@@ -1009,8 +1009,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -1024,8 +1024,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -1039,8 +1039,8 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
         storeFqdn: zod.ZodOptional<zod.ZodString>;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         storeFqdn?: string | undefined;
     }, {
         accessToken: string;
@@ -1052,16 +1052,16 @@ export declare const SessionsSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{}
     identity: {
         accessToken: string;
         refreshToken: string;
-        scopes: string[];
         expiresAt: Date;
+        scopes: string[];
         userId: string;
         alias?: string | undefined;
     };
     applications: {} & {
         [k: string]: {
             accessToken: string;
-            scopes: string[];
             expiresAt: Date;
+            scopes: string[];
             storeFqdn?: string | undefined;
         };
     };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant