diff --git a/libs/shared/cms/src/__generated__/gql.ts b/libs/shared/cms/src/__generated__/gql.ts index 0fdab7301ff..9f0d779dd88 100644 --- a/libs/shared/cms/src/__generated__/gql.ts +++ b/libs/shared/cms/src/__generated__/gql.ts @@ -28,7 +28,7 @@ type Documents = { "\n query pageContentByPriceIds($locale: String!, $stripePlanIds: [String]!) {\n purchases(\n filters: {\n or: [\n { stripePlanChoices: { stripePlanChoice: { in: $stripePlanIds } } }\n {\n offering: {\n stripeLegacyPlans: { stripeLegacyPlan: { in: $stripePlanIds } }\n }\n }\n ]\n }\n pagination: { limit: 50 }\n ) {\n offering {\n stripeLegacyPlans(pagination: { limit: 100 }) {\n stripeLegacyPlan\n }\n commonContent {\n emailIcon\n supportUrl\n localizations(filters: { locale: { eq: $locale } }) {\n emailIcon\n supportUrl\n }\n }\n apiIdentifier\n }\n purchaseDetails {\n productName\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n productName\n webIcon\n }\n }\n stripePlanChoices {\n stripePlanChoice\n }\n }\n }\n": typeof types.PageContentByPriceIdsDocument, "\n query PageContentForOffering($locale: String!, $apiIdentifier: String!) {\n offerings(\n filters: { apiIdentifier: { eq: $apiIdentifier } }\n pagination: { limit: 200 }\n ) {\n apiIdentifier\n countries\n stripeProductId\n defaultPurchase {\n purchaseDetails {\n details\n productName\n subtitle\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n details\n productName\n subtitle\n webIcon\n }\n }\n }\n commonContent {\n privacyNoticeUrl\n privacyNoticeDownloadUrl\n termsOfServiceUrl\n termsOfServiceDownloadUrl\n cancellationUrl\n emailIcon\n successActionButtonUrl\n successActionButtonLabel\n newsletterLabelTextCode\n newsletterSlug\n localizations(filters: { locale: { eq: $locale } }) {\n privacyNoticeUrl\n privacyNoticeDownloadUrl\n termsOfServiceUrl\n termsOfServiceDownloadUrl\n cancellationUrl\n emailIcon\n successActionButtonUrl\n successActionButtonLabel\n newsletterLabelTextCode\n newsletterSlug\n }\n }\n }\n }\n": typeof types.PageContentForOfferingDocument, "\n query PurchaseWithDetailsOfferingContent(\n $locale: String!\n $stripePlanIds: [String]!\n ) {\n purchases(\n filters: {\n or: [\n { stripePlanChoices: { stripePlanChoice: { in: $stripePlanIds } } }\n {\n offering: {\n stripeLegacyPlans: { stripeLegacyPlan: { in: $stripePlanIds } }\n }\n }\n ]\n }\n pagination: { limit: 500 }\n ) {\n stripePlanChoices {\n stripePlanChoice\n }\n purchaseDetails {\n details\n productName\n subtitle\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n details\n productName\n subtitle\n webIcon\n }\n }\n offering {\n stripeProductId\n stripeLegacyPlans(pagination: { limit: 200 }) {\n stripeLegacyPlan\n }\n commonContent {\n privacyNoticeUrl\n privacyNoticeDownloadUrl\n termsOfServiceUrl\n termsOfServiceDownloadUrl\n cancellationUrl\n emailIcon\n successActionButtonUrl\n successActionButtonLabel\n newsletterLabelTextCode\n newsletterSlug\n localizations(filters: { locale: { eq: $locale } }) {\n privacyNoticeUrl\n privacyNoticeDownloadUrl\n termsOfServiceUrl\n termsOfServiceDownloadUrl\n cancellationUrl\n emailIcon\n successActionButtonUrl\n successActionButtonLabel\n newsletterLabelTextCode\n newsletterSlug\n }\n }\n }\n }\n }\n": typeof types.PurchaseWithDetailsOfferingContentDocument, - "\n query RelyingParties($clientId: String!, $entrypoint: String!) {\n relyingParties(\n filters: { clientId: { eq: $clientId }, entrypoint: { eq: $entrypoint } }\n ) {\n clientId\n entrypoint\n name\n l10nId\n shared {\n buttonColor\n logoUrl\n logoAltText\n emailFromName\n emailLogoUrl\n emailLogoAltText\n emailLogoWidth\n pageTitle\n headerLogoUrl\n headerLogoAltText\n featureFlags {\n syncConfirmedPageHideCTA\n syncHidePromoAfterLogin\n }\n backgrounds {\n defaultLayout\n header\n splitLayout\n splitLayoutAltText\n }\n illustrationsTheme {\n primary\n primaryAlt\n secondary\n accentBg\n accentFg\n cloudPrimary\n cloudShadow\n hideClouds\n }\n favicon\n headlineFontSize\n headlineTextColor\n additionalAccessibilityInfo\n }\n EmailFirstPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupSetPasswordPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupConfirmCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupConfirmedSyncPage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SignupPasswordlessCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninPage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninCachedPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninPasswordlessCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninTokenCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninUnblockCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninTotpCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninRecoveryChoicePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninRecoveryCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninRecoveryPhonePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n PostVerifySetPasswordPage {\n headline\n description\n primaryButtonText\n pageTitle\n }\n NewDeviceLoginEmail {\n subject\n headline\n description\n }\n PasswordlessSigninOtpEmail {\n subject\n headline\n description\n }\n PasswordlessSignupOtpEmail {\n subject\n headline\n description\n }\n VerifyLoginCodeEmail {\n subject\n headline\n description\n }\n VerifyShortCodeEmail {\n subject\n headline\n description\n }\n }\n }\n": typeof types.RelyingPartiesDocument, + "\n query RelyingParties($clientId: String!, $entrypoint: String!) {\n relyingParties(\n filters: { clientId: { eq: $clientId }, entrypoint: { eq: $entrypoint } }\n ) {\n clientId\n entrypoint\n name\n l10nId\n shared {\n buttonColor\n logoUrl\n logoAltText\n emailFromName\n emailLogoUrl\n emailLogoAltText\n emailLogoWidth\n pageTitle\n headerLogoUrl\n headerLogoAltText\n featureFlags {\n syncConfirmedPageHideCTA\n syncHidePromoAfterLogin\n }\n backgrounds {\n defaultLayout\n header\n splitLayout\n splitLayoutAltText\n }\n illustrationsTheme {\n primary\n primaryAlt\n secondary\n accentBg\n accentFg\n cloudPrimary\n cloudShadow\n hideClouds\n }\n favicon\n headlineFontSize\n headlineTextColor\n additionalAccessibilityInfo\n }\n EmailFirstPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupSetPasswordPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupConfirmCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupConfirmedSyncPage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SignupPasswordlessCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninPage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninCachedPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n AuthorizePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninPasswordlessCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninTokenCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninUnblockCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninTotpCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninRecoveryChoicePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninRecoveryCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninRecoveryPhonePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n PostVerifySetPasswordPage {\n headline\n description\n primaryButtonText\n pageTitle\n }\n NewDeviceLoginEmail {\n subject\n headline\n description\n }\n PasswordlessSigninOtpEmail {\n subject\n headline\n description\n }\n PasswordlessSignupOtpEmail {\n subject\n headline\n description\n }\n VerifyLoginCodeEmail {\n subject\n headline\n description\n }\n VerifyShortCodeEmail {\n subject\n headline\n description\n }\n }\n }\n": typeof types.RelyingPartiesDocument, "\n query ServicesWithCapabilities {\n services(pagination: { limit: 500 }) {\n oauthClientId\n capabilities {\n slug\n }\n }\n }\n": typeof types.ServicesWithCapabilitiesDocument, "\n query ValidationOfferings {\n offerings(pagination: { limit: 500 }) {\n apiIdentifier\n stripeProductId\n countries\n defaultPurchase {\n purchaseDetails {\n details\n productName\n subtitle\n webIcon\n }\n stripePlanChoices {\n stripePlanChoice\n }\n }\n commonContent {\n privacyNoticeUrl\n privacyNoticeDownloadUrl\n termsOfServiceUrl\n termsOfServiceDownloadUrl\n cancellationUrl\n emailIcon\n successActionButtonUrl\n successActionButtonLabel\n newsletterLabelTextCode\n newsletterSlug\n supportUrl\n }\n capabilities {\n slug\n services {\n oauthClientId\n }\n }\n stripeLegacyPlans(pagination: { limit: 200 }) {\n stripeLegacyPlan\n }\n couponConfig {\n internalName\n stripePromotionCodes {\n PromoCode\n }\n }\n subGroups {\n internalName\n groupName\n }\n }\n }\n": typeof types.ValidationOfferingsDocument, "\n query ValidationPurchases {\n purchases(pagination: { limit: 500 }) {\n internalName\n purchaseDetails {\n details\n productName\n subtitle\n webIcon\n }\n stripePlanChoices {\n stripePlanChoice\n }\n }\n }\n": typeof types.ValidationPurchasesDocument, @@ -57,7 +57,7 @@ const documents: Documents = { "\n query pageContentByPriceIds($locale: String!, $stripePlanIds: [String]!) {\n purchases(\n filters: {\n or: [\n { stripePlanChoices: { stripePlanChoice: { in: $stripePlanIds } } }\n {\n offering: {\n stripeLegacyPlans: { stripeLegacyPlan: { in: $stripePlanIds } }\n }\n }\n ]\n }\n pagination: { limit: 50 }\n ) {\n offering {\n stripeLegacyPlans(pagination: { limit: 100 }) {\n stripeLegacyPlan\n }\n commonContent {\n emailIcon\n supportUrl\n localizations(filters: { locale: { eq: $locale } }) {\n emailIcon\n supportUrl\n }\n }\n apiIdentifier\n }\n purchaseDetails {\n productName\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n productName\n webIcon\n }\n }\n stripePlanChoices {\n stripePlanChoice\n }\n }\n }\n": types.PageContentByPriceIdsDocument, "\n query PageContentForOffering($locale: String!, $apiIdentifier: String!) {\n offerings(\n filters: { apiIdentifier: { eq: $apiIdentifier } }\n pagination: { limit: 200 }\n ) {\n apiIdentifier\n countries\n stripeProductId\n defaultPurchase {\n purchaseDetails {\n details\n productName\n subtitle\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n details\n productName\n subtitle\n webIcon\n }\n }\n }\n commonContent {\n privacyNoticeUrl\n privacyNoticeDownloadUrl\n termsOfServiceUrl\n termsOfServiceDownloadUrl\n cancellationUrl\n emailIcon\n successActionButtonUrl\n successActionButtonLabel\n newsletterLabelTextCode\n newsletterSlug\n localizations(filters: { locale: { eq: $locale } }) {\n privacyNoticeUrl\n privacyNoticeDownloadUrl\n termsOfServiceUrl\n termsOfServiceDownloadUrl\n cancellationUrl\n emailIcon\n successActionButtonUrl\n successActionButtonLabel\n newsletterLabelTextCode\n newsletterSlug\n }\n }\n }\n }\n": types.PageContentForOfferingDocument, "\n query PurchaseWithDetailsOfferingContent(\n $locale: String!\n $stripePlanIds: [String]!\n ) {\n purchases(\n filters: {\n or: [\n { stripePlanChoices: { stripePlanChoice: { in: $stripePlanIds } } }\n {\n offering: {\n stripeLegacyPlans: { stripeLegacyPlan: { in: $stripePlanIds } }\n }\n }\n ]\n }\n pagination: { limit: 500 }\n ) {\n stripePlanChoices {\n stripePlanChoice\n }\n purchaseDetails {\n details\n productName\n subtitle\n webIcon\n localizations(filters: { locale: { eq: $locale } }) {\n details\n productName\n subtitle\n webIcon\n }\n }\n offering {\n stripeProductId\n stripeLegacyPlans(pagination: { limit: 200 }) {\n stripeLegacyPlan\n }\n commonContent {\n privacyNoticeUrl\n privacyNoticeDownloadUrl\n termsOfServiceUrl\n termsOfServiceDownloadUrl\n cancellationUrl\n emailIcon\n successActionButtonUrl\n successActionButtonLabel\n newsletterLabelTextCode\n newsletterSlug\n localizations(filters: { locale: { eq: $locale } }) {\n privacyNoticeUrl\n privacyNoticeDownloadUrl\n termsOfServiceUrl\n termsOfServiceDownloadUrl\n cancellationUrl\n emailIcon\n successActionButtonUrl\n successActionButtonLabel\n newsletterLabelTextCode\n newsletterSlug\n }\n }\n }\n }\n }\n": types.PurchaseWithDetailsOfferingContentDocument, - "\n query RelyingParties($clientId: String!, $entrypoint: String!) {\n relyingParties(\n filters: { clientId: { eq: $clientId }, entrypoint: { eq: $entrypoint } }\n ) {\n clientId\n entrypoint\n name\n l10nId\n shared {\n buttonColor\n logoUrl\n logoAltText\n emailFromName\n emailLogoUrl\n emailLogoAltText\n emailLogoWidth\n pageTitle\n headerLogoUrl\n headerLogoAltText\n featureFlags {\n syncConfirmedPageHideCTA\n syncHidePromoAfterLogin\n }\n backgrounds {\n defaultLayout\n header\n splitLayout\n splitLayoutAltText\n }\n illustrationsTheme {\n primary\n primaryAlt\n secondary\n accentBg\n accentFg\n cloudPrimary\n cloudShadow\n hideClouds\n }\n favicon\n headlineFontSize\n headlineTextColor\n additionalAccessibilityInfo\n }\n EmailFirstPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupSetPasswordPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupConfirmCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupConfirmedSyncPage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SignupPasswordlessCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninPage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninCachedPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninPasswordlessCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninTokenCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninUnblockCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninTotpCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninRecoveryChoicePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninRecoveryCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninRecoveryPhonePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n PostVerifySetPasswordPage {\n headline\n description\n primaryButtonText\n pageTitle\n }\n NewDeviceLoginEmail {\n subject\n headline\n description\n }\n PasswordlessSigninOtpEmail {\n subject\n headline\n description\n }\n PasswordlessSignupOtpEmail {\n subject\n headline\n description\n }\n VerifyLoginCodeEmail {\n subject\n headline\n description\n }\n VerifyShortCodeEmail {\n subject\n headline\n description\n }\n }\n }\n": types.RelyingPartiesDocument, + "\n query RelyingParties($clientId: String!, $entrypoint: String!) {\n relyingParties(\n filters: { clientId: { eq: $clientId }, entrypoint: { eq: $entrypoint } }\n ) {\n clientId\n entrypoint\n name\n l10nId\n shared {\n buttonColor\n logoUrl\n logoAltText\n emailFromName\n emailLogoUrl\n emailLogoAltText\n emailLogoWidth\n pageTitle\n headerLogoUrl\n headerLogoAltText\n featureFlags {\n syncConfirmedPageHideCTA\n syncHidePromoAfterLogin\n }\n backgrounds {\n defaultLayout\n header\n splitLayout\n splitLayoutAltText\n }\n illustrationsTheme {\n primary\n primaryAlt\n secondary\n accentBg\n accentFg\n cloudPrimary\n cloudShadow\n hideClouds\n }\n favicon\n headlineFontSize\n headlineTextColor\n additionalAccessibilityInfo\n }\n EmailFirstPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupSetPasswordPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupConfirmCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupConfirmedSyncPage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SignupPasswordlessCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninPage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninCachedPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n AuthorizePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninPasswordlessCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninTokenCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninUnblockCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninTotpCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninRecoveryChoicePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninRecoveryCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninRecoveryPhonePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n PostVerifySetPasswordPage {\n headline\n description\n primaryButtonText\n pageTitle\n }\n NewDeviceLoginEmail {\n subject\n headline\n description\n }\n PasswordlessSigninOtpEmail {\n subject\n headline\n description\n }\n PasswordlessSignupOtpEmail {\n subject\n headline\n description\n }\n VerifyLoginCodeEmail {\n subject\n headline\n description\n }\n VerifyShortCodeEmail {\n subject\n headline\n description\n }\n }\n }\n": types.RelyingPartiesDocument, "\n query ServicesWithCapabilities {\n services(pagination: { limit: 500 }) {\n oauthClientId\n capabilities {\n slug\n }\n }\n }\n": types.ServicesWithCapabilitiesDocument, "\n query ValidationOfferings {\n offerings(pagination: { limit: 500 }) {\n apiIdentifier\n stripeProductId\n countries\n defaultPurchase {\n purchaseDetails {\n details\n productName\n subtitle\n webIcon\n }\n stripePlanChoices {\n stripePlanChoice\n }\n }\n commonContent {\n privacyNoticeUrl\n privacyNoticeDownloadUrl\n termsOfServiceUrl\n termsOfServiceDownloadUrl\n cancellationUrl\n emailIcon\n successActionButtonUrl\n successActionButtonLabel\n newsletterLabelTextCode\n newsletterSlug\n supportUrl\n }\n capabilities {\n slug\n services {\n oauthClientId\n }\n }\n stripeLegacyPlans(pagination: { limit: 200 }) {\n stripeLegacyPlan\n }\n couponConfig {\n internalName\n stripePromotionCodes {\n PromoCode\n }\n }\n subGroups {\n internalName\n groupName\n }\n }\n }\n": types.ValidationOfferingsDocument, "\n query ValidationPurchases {\n purchases(pagination: { limit: 500 }) {\n internalName\n purchaseDetails {\n details\n productName\n subtitle\n webIcon\n }\n stripePlanChoices {\n stripePlanChoice\n }\n }\n }\n": types.ValidationPurchasesDocument, @@ -145,7 +145,7 @@ export function graphql(source: "\n query PurchaseWithDetailsOfferingContent(\n /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n query RelyingParties($clientId: String!, $entrypoint: String!) {\n relyingParties(\n filters: { clientId: { eq: $clientId }, entrypoint: { eq: $entrypoint } }\n ) {\n clientId\n entrypoint\n name\n l10nId\n shared {\n buttonColor\n logoUrl\n logoAltText\n emailFromName\n emailLogoUrl\n emailLogoAltText\n emailLogoWidth\n pageTitle\n headerLogoUrl\n headerLogoAltText\n featureFlags {\n syncConfirmedPageHideCTA\n syncHidePromoAfterLogin\n }\n backgrounds {\n defaultLayout\n header\n splitLayout\n splitLayoutAltText\n }\n illustrationsTheme {\n primary\n primaryAlt\n secondary\n accentBg\n accentFg\n cloudPrimary\n cloudShadow\n hideClouds\n }\n favicon\n headlineFontSize\n headlineTextColor\n additionalAccessibilityInfo\n }\n EmailFirstPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupSetPasswordPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupConfirmCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupConfirmedSyncPage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SignupPasswordlessCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninPage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninCachedPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninPasswordlessCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninTokenCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninUnblockCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninTotpCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninRecoveryChoicePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninRecoveryCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninRecoveryPhonePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n PostVerifySetPasswordPage {\n headline\n description\n primaryButtonText\n pageTitle\n }\n NewDeviceLoginEmail {\n subject\n headline\n description\n }\n PasswordlessSigninOtpEmail {\n subject\n headline\n description\n }\n PasswordlessSignupOtpEmail {\n subject\n headline\n description\n }\n VerifyLoginCodeEmail {\n subject\n headline\n description\n }\n VerifyShortCodeEmail {\n subject\n headline\n description\n }\n }\n }\n"): (typeof documents)["\n query RelyingParties($clientId: String!, $entrypoint: String!) {\n relyingParties(\n filters: { clientId: { eq: $clientId }, entrypoint: { eq: $entrypoint } }\n ) {\n clientId\n entrypoint\n name\n l10nId\n shared {\n buttonColor\n logoUrl\n logoAltText\n emailFromName\n emailLogoUrl\n emailLogoAltText\n emailLogoWidth\n pageTitle\n headerLogoUrl\n headerLogoAltText\n featureFlags {\n syncConfirmedPageHideCTA\n syncHidePromoAfterLogin\n }\n backgrounds {\n defaultLayout\n header\n splitLayout\n splitLayoutAltText\n }\n illustrationsTheme {\n primary\n primaryAlt\n secondary\n accentBg\n accentFg\n cloudPrimary\n cloudShadow\n hideClouds\n }\n favicon\n headlineFontSize\n headlineTextColor\n additionalAccessibilityInfo\n }\n EmailFirstPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupSetPasswordPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupConfirmCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupConfirmedSyncPage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SignupPasswordlessCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninPage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninCachedPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninPasswordlessCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninTokenCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninUnblockCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninTotpCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninRecoveryChoicePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninRecoveryCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninRecoveryPhonePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n PostVerifySetPasswordPage {\n headline\n description\n primaryButtonText\n pageTitle\n }\n NewDeviceLoginEmail {\n subject\n headline\n description\n }\n PasswordlessSigninOtpEmail {\n subject\n headline\n description\n }\n PasswordlessSignupOtpEmail {\n subject\n headline\n description\n }\n VerifyLoginCodeEmail {\n subject\n headline\n description\n }\n VerifyShortCodeEmail {\n subject\n headline\n description\n }\n }\n }\n"]; +export function graphql(source: "\n query RelyingParties($clientId: String!, $entrypoint: String!) {\n relyingParties(\n filters: { clientId: { eq: $clientId }, entrypoint: { eq: $entrypoint } }\n ) {\n clientId\n entrypoint\n name\n l10nId\n shared {\n buttonColor\n logoUrl\n logoAltText\n emailFromName\n emailLogoUrl\n emailLogoAltText\n emailLogoWidth\n pageTitle\n headerLogoUrl\n headerLogoAltText\n featureFlags {\n syncConfirmedPageHideCTA\n syncHidePromoAfterLogin\n }\n backgrounds {\n defaultLayout\n header\n splitLayout\n splitLayoutAltText\n }\n illustrationsTheme {\n primary\n primaryAlt\n secondary\n accentBg\n accentFg\n cloudPrimary\n cloudShadow\n hideClouds\n }\n favicon\n headlineFontSize\n headlineTextColor\n additionalAccessibilityInfo\n }\n EmailFirstPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupSetPasswordPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupConfirmCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupConfirmedSyncPage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SignupPasswordlessCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninPage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninCachedPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n AuthorizePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninPasswordlessCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninTokenCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninUnblockCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninTotpCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninRecoveryChoicePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninRecoveryCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninRecoveryPhonePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n PostVerifySetPasswordPage {\n headline\n description\n primaryButtonText\n pageTitle\n }\n NewDeviceLoginEmail {\n subject\n headline\n description\n }\n PasswordlessSigninOtpEmail {\n subject\n headline\n description\n }\n PasswordlessSignupOtpEmail {\n subject\n headline\n description\n }\n VerifyLoginCodeEmail {\n subject\n headline\n description\n }\n VerifyShortCodeEmail {\n subject\n headline\n description\n }\n }\n }\n"): (typeof documents)["\n query RelyingParties($clientId: String!, $entrypoint: String!) {\n relyingParties(\n filters: { clientId: { eq: $clientId }, entrypoint: { eq: $entrypoint } }\n ) {\n clientId\n entrypoint\n name\n l10nId\n shared {\n buttonColor\n logoUrl\n logoAltText\n emailFromName\n emailLogoUrl\n emailLogoAltText\n emailLogoWidth\n pageTitle\n headerLogoUrl\n headerLogoAltText\n featureFlags {\n syncConfirmedPageHideCTA\n syncHidePromoAfterLogin\n }\n backgrounds {\n defaultLayout\n header\n splitLayout\n splitLayoutAltText\n }\n illustrationsTheme {\n primary\n primaryAlt\n secondary\n accentBg\n accentFg\n cloudPrimary\n cloudShadow\n hideClouds\n }\n favicon\n headlineFontSize\n headlineTextColor\n additionalAccessibilityInfo\n }\n EmailFirstPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupSetPasswordPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupConfirmCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SignupConfirmedSyncPage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SignupPasswordlessCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninPage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninCachedPage {\n logoUrl\n logoAltText\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n AuthorizePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninPasswordlessCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninTokenCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninUnblockCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninTotpCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninRecoveryChoicePage {\n headline\n description\n primaryButtonText\n pageTitle\n splitLayout\n }\n SigninRecoveryCodePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n SigninRecoveryPhonePage {\n headline\n description\n primaryButtonText\n pageTitle\n primaryImage {\n url\n altText\n }\n splitLayout\n }\n PostVerifySetPasswordPage {\n headline\n description\n primaryButtonText\n pageTitle\n }\n NewDeviceLoginEmail {\n subject\n headline\n description\n }\n PasswordlessSigninOtpEmail {\n subject\n headline\n description\n }\n PasswordlessSignupOtpEmail {\n subject\n headline\n description\n }\n VerifyLoginCodeEmail {\n subject\n headline\n description\n }\n VerifyShortCodeEmail {\n subject\n headline\n description\n }\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/libs/shared/cms/src/__generated__/graphql.ts b/libs/shared/cms/src/__generated__/graphql.ts index 5f2ca8b295d..0b34d04eb40 100644 --- a/libs/shared/cms/src/__generated__/graphql.ts +++ b/libs/shared/cms/src/__generated__/graphql.ts @@ -2029,6 +2029,7 @@ export type Query = { export type QueryCancelInterstitialOfferArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; locale: InputMaybe; status?: InputMaybe; }; @@ -2036,6 +2037,7 @@ export type QueryCancelInterstitialOfferArgs = { export type QueryCancelInterstitialOffersArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; locale: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; @@ -2045,6 +2047,7 @@ export type QueryCancelInterstitialOffersArgs = { export type QueryCancelInterstitialOffers_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; locale: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; @@ -2054,6 +2057,7 @@ export type QueryCancelInterstitialOffers_ConnectionArgs = { export type QueryCapabilitiesArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2062,6 +2066,7 @@ export type QueryCapabilitiesArgs = { export type QueryCapabilities_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2070,12 +2075,14 @@ export type QueryCapabilities_ConnectionArgs = { export type QueryCapabilityArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; status?: InputMaybe; }; export type QueryChurnInterventionArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; locale: InputMaybe; status?: InputMaybe; }; @@ -2083,6 +2090,7 @@ export type QueryChurnInterventionArgs = { export type QueryChurnInterventionsArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; locale: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; @@ -2092,6 +2100,7 @@ export type QueryChurnInterventionsArgs = { export type QueryChurnInterventions_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; locale: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; @@ -2101,6 +2110,7 @@ export type QueryChurnInterventions_ConnectionArgs = { export type QueryCommonContentArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; locale: InputMaybe; status?: InputMaybe; }; @@ -2108,6 +2118,7 @@ export type QueryCommonContentArgs = { export type QueryCommonContentsArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; locale: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; @@ -2117,6 +2128,7 @@ export type QueryCommonContentsArgs = { export type QueryCommonContents_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; locale: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; @@ -2126,12 +2138,14 @@ export type QueryCommonContents_ConnectionArgs = { export type QueryCouponConfigArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; status?: InputMaybe; }; export type QueryCouponConfigsArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2140,6 +2154,7 @@ export type QueryCouponConfigsArgs = { export type QueryCouponConfigs_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2147,6 +2162,7 @@ export type QueryCouponConfigs_ConnectionArgs = { export type QueryDefaultArgs = { + hasPublishedVersion: InputMaybe; locale: InputMaybe; status?: InputMaybe; }; @@ -2154,12 +2170,14 @@ export type QueryDefaultArgs = { export type QueryFreeTrialArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; status?: InputMaybe; }; export type QueryFreeTrialsArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2168,6 +2186,7 @@ export type QueryFreeTrialsArgs = { export type QueryFreeTrials_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2176,12 +2195,14 @@ export type QueryFreeTrials_ConnectionArgs = { export type QueryI18NLocaleArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; status?: InputMaybe; }; export type QueryI18NLocalesArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2190,6 +2211,7 @@ export type QueryI18NLocalesArgs = { export type QueryI18NLocales_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2198,12 +2220,14 @@ export type QueryI18NLocales_ConnectionArgs = { export type QueryIapArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; status?: InputMaybe; }; export type QueryIapsArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2212,6 +2236,7 @@ export type QueryIapsArgs = { export type QueryIaps_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2220,12 +2245,14 @@ export type QueryIaps_ConnectionArgs = { export type QueryLegalNoticeArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; status?: InputMaybe; }; export type QueryLegalNoticesArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2234,6 +2261,7 @@ export type QueryLegalNoticesArgs = { export type QueryLegalNotices_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2242,12 +2270,14 @@ export type QueryLegalNotices_ConnectionArgs = { export type QueryOfferingArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; status?: InputMaybe; }; export type QueryOfferingsArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2256,6 +2286,7 @@ export type QueryOfferingsArgs = { export type QueryOfferings_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2264,12 +2295,14 @@ export type QueryOfferings_ConnectionArgs = { export type QueryPurchaseArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; status?: InputMaybe; }; export type QueryPurchaseDetailArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; locale: InputMaybe; status?: InputMaybe; }; @@ -2277,6 +2310,7 @@ export type QueryPurchaseDetailArgs = { export type QueryPurchaseDetailsArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; locale: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; @@ -2286,6 +2320,7 @@ export type QueryPurchaseDetailsArgs = { export type QueryPurchaseDetails_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; locale: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; @@ -2295,6 +2330,7 @@ export type QueryPurchaseDetails_ConnectionArgs = { export type QueryPurchasesArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2303,6 +2339,7 @@ export type QueryPurchasesArgs = { export type QueryPurchases_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2311,6 +2348,7 @@ export type QueryPurchases_ConnectionArgs = { export type QueryRelyingPartiesArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2319,6 +2357,7 @@ export type QueryRelyingPartiesArgs = { export type QueryRelyingParties_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2327,24 +2366,28 @@ export type QueryRelyingParties_ConnectionArgs = { export type QueryRelyingPartyArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; status?: InputMaybe; }; export type QueryReviewWorkflowsWorkflowArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; status?: InputMaybe; }; export type QueryReviewWorkflowsWorkflowStageArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; status?: InputMaybe; }; export type QueryReviewWorkflowsWorkflowStagesArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2353,6 +2396,7 @@ export type QueryReviewWorkflowsWorkflowStagesArgs = { export type QueryReviewWorkflowsWorkflowStages_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2361,6 +2405,7 @@ export type QueryReviewWorkflowsWorkflowStages_ConnectionArgs = { export type QueryReviewWorkflowsWorkflowsArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2369,6 +2414,7 @@ export type QueryReviewWorkflowsWorkflowsArgs = { export type QueryReviewWorkflowsWorkflows_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2377,12 +2423,14 @@ export type QueryReviewWorkflowsWorkflows_ConnectionArgs = { export type QueryServiceArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; status?: InputMaybe; }; export type QueryServicesArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2391,6 +2439,7 @@ export type QueryServicesArgs = { export type QueryServices_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2399,12 +2448,14 @@ export type QueryServices_ConnectionArgs = { export type QuerySubgroupArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; status?: InputMaybe; }; export type QuerySubgroupsArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2413,6 +2464,7 @@ export type QuerySubgroupsArgs = { export type QuerySubgroups_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2421,12 +2473,14 @@ export type QuerySubgroups_ConnectionArgs = { export type QueryUploadFileArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; status?: InputMaybe; }; export type QueryUploadFilesArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2435,6 +2489,7 @@ export type QueryUploadFilesArgs = { export type QueryUploadFiles_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2443,12 +2498,14 @@ export type QueryUploadFiles_ConnectionArgs = { export type QueryUsersPermissionsRoleArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; status?: InputMaybe; }; export type QueryUsersPermissionsRolesArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2457,6 +2514,7 @@ export type QueryUsersPermissionsRolesArgs = { export type QueryUsersPermissionsRoles_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2465,12 +2523,14 @@ export type QueryUsersPermissionsRoles_ConnectionArgs = { export type QueryUsersPermissionsUserArgs = { documentId: Scalars['ID']['input']; + hasPublishedVersion: InputMaybe; status?: InputMaybe; }; export type QueryUsersPermissionsUsersArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2479,6 +2539,7 @@ export type QueryUsersPermissionsUsersArgs = { export type QueryUsersPermissionsUsers_ConnectionArgs = { filters: InputMaybe; + hasPublishedVersion: InputMaybe; pagination?: InputMaybe; sort?: InputMaybe>>; status?: InputMaybe; @@ -2486,6 +2547,7 @@ export type QueryUsersPermissionsUsers_ConnectionArgs = { export type RelyingParty = { __typename?: 'RelyingParty'; + AuthorizePage: Maybe; EmailFirstPage: ComponentAccountsPageConfig; NewDeviceLoginEmail: Maybe; PasswordlessSigninOtpEmail: Maybe; @@ -2524,6 +2586,7 @@ export type RelyingPartyEntityResponseCollection = { }; export type RelyingPartyFiltersInput = { + AuthorizePage: InputMaybe; EmailFirstPage: InputMaybe; NewDeviceLoginEmail: InputMaybe; PasswordlessSigninOtpEmail: InputMaybe; @@ -2559,6 +2622,7 @@ export type RelyingPartyFiltersInput = { }; export type RelyingPartyInput = { + AuthorizePage: InputMaybe; EmailFirstPage: InputMaybe; NewDeviceLoginEmail: InputMaybe; PasswordlessSigninOtpEmail: InputMaybe; @@ -2833,6 +2897,7 @@ export type UploadFile = { createdAt: Maybe; documentId: Scalars['ID']['output']; ext: Maybe; + focalPoint: Maybe; formats: Maybe; hash: Scalars['String']['output']; height: Maybe; @@ -2862,6 +2927,7 @@ export type UploadFileFiltersInput = { createdAt: InputMaybe; documentId: InputMaybe; ext: InputMaybe; + focalPoint: InputMaybe; formats: InputMaybe; hash: InputMaybe; height: InputMaybe; @@ -3205,7 +3271,7 @@ export type RelyingPartiesQueryVariables = Exact<{ }>; -export type RelyingPartiesQuery = { __typename?: 'Query', relyingParties: Array<{ __typename?: 'RelyingParty', clientId: string | null, entrypoint: string | null, name: string | null, l10nId: string | null, shared: { __typename?: 'ComponentAccountsShared', buttonColor: string | null, logoUrl: string | null, logoAltText: string | null, emailFromName: string | null, emailLogoUrl: string | null, emailLogoAltText: string | null, emailLogoWidth: string | null, pageTitle: string | null, headerLogoUrl: string | null, headerLogoAltText: string | null, favicon: string | null, headlineFontSize: Enum_Componentaccountsshared_Headlinefontsize | null, headlineTextColor: string | null, additionalAccessibilityInfo: string | null, featureFlags: { __typename?: 'ComponentAccountsFeatureFlags', syncConfirmedPageHideCTA: boolean | null, syncHidePromoAfterLogin: boolean | null } | null, backgrounds: { __typename?: 'ComponentAccountsSharedBackgrounds', defaultLayout: string | null, header: string | null, splitLayout: string | null, splitLayoutAltText: string | null } | null, illustrationsTheme: { __typename?: 'ComponentAccountsIllustrationsTheme', primary: string | null, primaryAlt: string | null, secondary: string | null, accentBg: string | null, accentFg: string | null, cloudPrimary: string | null, cloudShadow: string | null, hideClouds: boolean | null } | null }, EmailFirstPage: { __typename?: 'ComponentAccountsPageConfig', logoUrl: string | null, logoAltText: string | null, headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null }, SignupSetPasswordPage: { __typename?: 'ComponentAccountsPageConfig', logoUrl: string | null, logoAltText: string | null, headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null }, SignupConfirmCodePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null }, SignupConfirmedSyncPage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null, primaryImage: { __typename?: 'ComponentAccountsImage', url: string, altText: string } | null } | null, SignupPasswordlessCodePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null, primaryImage: { __typename?: 'ComponentAccountsImage', url: string, altText: string } | null } | null, SigninPage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null }, SigninCachedPage: { __typename?: 'ComponentAccountsPageConfig', logoUrl: string | null, logoAltText: string | null, headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null } | null, SigninPasswordlessCodePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null, primaryImage: { __typename?: 'ComponentAccountsImage', url: string, altText: string } | null } | null, SigninTokenCodePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null } | null, SigninUnblockCodePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null } | null, SigninTotpCodePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null } | null, SigninRecoveryChoicePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null } | null, SigninRecoveryCodePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null, primaryImage: { __typename?: 'ComponentAccountsImage', url: string, altText: string } | null } | null, SigninRecoveryPhonePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null, primaryImage: { __typename?: 'ComponentAccountsImage', url: string, altText: string } | null } | null, PostVerifySetPasswordPage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null } | null, NewDeviceLoginEmail: { __typename?: 'ComponentAccountsEmailConfig', subject: string, headline: string, description: string } | null, PasswordlessSigninOtpEmail: { __typename?: 'ComponentAccountsEmailConfig', subject: string, headline: string, description: string } | null, PasswordlessSignupOtpEmail: { __typename?: 'ComponentAccountsEmailConfig', subject: string, headline: string, description: string } | null, VerifyLoginCodeEmail: { __typename?: 'ComponentAccountsEmailConfig', subject: string, headline: string, description: string } | null, VerifyShortCodeEmail: { __typename?: 'ComponentAccountsEmailConfig', subject: string, headline: string, description: string } | null } | null> }; +export type RelyingPartiesQuery = { __typename?: 'Query', relyingParties: Array<{ __typename?: 'RelyingParty', clientId: string | null, entrypoint: string | null, name: string | null, l10nId: string | null, shared: { __typename?: 'ComponentAccountsShared', buttonColor: string | null, logoUrl: string | null, logoAltText: string | null, emailFromName: string | null, emailLogoUrl: string | null, emailLogoAltText: string | null, emailLogoWidth: string | null, pageTitle: string | null, headerLogoUrl: string | null, headerLogoAltText: string | null, favicon: string | null, headlineFontSize: Enum_Componentaccountsshared_Headlinefontsize | null, headlineTextColor: string | null, additionalAccessibilityInfo: string | null, featureFlags: { __typename?: 'ComponentAccountsFeatureFlags', syncConfirmedPageHideCTA: boolean | null, syncHidePromoAfterLogin: boolean | null } | null, backgrounds: { __typename?: 'ComponentAccountsSharedBackgrounds', defaultLayout: string | null, header: string | null, splitLayout: string | null, splitLayoutAltText: string | null } | null, illustrationsTheme: { __typename?: 'ComponentAccountsIllustrationsTheme', primary: string | null, primaryAlt: string | null, secondary: string | null, accentBg: string | null, accentFg: string | null, cloudPrimary: string | null, cloudShadow: string | null, hideClouds: boolean | null } | null }, EmailFirstPage: { __typename?: 'ComponentAccountsPageConfig', logoUrl: string | null, logoAltText: string | null, headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null }, SignupSetPasswordPage: { __typename?: 'ComponentAccountsPageConfig', logoUrl: string | null, logoAltText: string | null, headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null }, SignupConfirmCodePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null }, SignupConfirmedSyncPage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null, primaryImage: { __typename?: 'ComponentAccountsImage', url: string, altText: string } | null } | null, SignupPasswordlessCodePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null, primaryImage: { __typename?: 'ComponentAccountsImage', url: string, altText: string } | null } | null, SigninPage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null }, SigninCachedPage: { __typename?: 'ComponentAccountsPageConfig', logoUrl: string | null, logoAltText: string | null, headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null } | null, AuthorizePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null } | null, SigninPasswordlessCodePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null, primaryImage: { __typename?: 'ComponentAccountsImage', url: string, altText: string } | null } | null, SigninTokenCodePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null } | null, SigninUnblockCodePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null } | null, SigninTotpCodePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null } | null, SigninRecoveryChoicePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null } | null, SigninRecoveryCodePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null, primaryImage: { __typename?: 'ComponentAccountsImage', url: string, altText: string } | null } | null, SigninRecoveryPhonePage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null, splitLayout: boolean | null, primaryImage: { __typename?: 'ComponentAccountsImage', url: string, altText: string } | null } | null, PostVerifySetPasswordPage: { __typename?: 'ComponentAccountsPageConfig', headline: string | null, description: string | null, primaryButtonText: string | null, pageTitle: string | null } | null, NewDeviceLoginEmail: { __typename?: 'ComponentAccountsEmailConfig', subject: string, headline: string, description: string } | null, PasswordlessSigninOtpEmail: { __typename?: 'ComponentAccountsEmailConfig', subject: string, headline: string, description: string } | null, PasswordlessSignupOtpEmail: { __typename?: 'ComponentAccountsEmailConfig', subject: string, headline: string, description: string } | null, VerifyLoginCodeEmail: { __typename?: 'ComponentAccountsEmailConfig', subject: string, headline: string, description: string } | null, VerifyShortCodeEmail: { __typename?: 'ComponentAccountsEmailConfig', subject: string, headline: string, description: string } | null } | null> }; export type ServicesWithCapabilitiesQueryVariables = Exact<{ [key: string]: never; }>; @@ -3282,7 +3348,7 @@ export const OfferingDocument = {"kind":"Document","definitions":[{"kind":"Opera export const PageContentByPriceIdsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"pageContentByPriceIds"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"locale"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"stripePlanIds"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"purchases"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"stripePlanChoices"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"stripePlanChoice"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"in"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stripePlanIds"}}}]}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"offering"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"stripeLegacyPlans"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"stripeLegacyPlan"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"in"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stripePlanIds"}}}]}}]}}]}}]}]}}]}},{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"50"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"offering"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stripeLegacyPlans"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"100"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stripeLegacyPlan"}}]}},{"kind":"Field","name":{"kind":"Name","value":"commonContent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"emailIcon"}},{"kind":"Field","name":{"kind":"Name","value":"supportUrl"}},{"kind":"Field","name":{"kind":"Name","value":"localizations"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"locale"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"emailIcon"}},{"kind":"Field","name":{"kind":"Name","value":"supportUrl"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"apiIdentifier"}}]}},{"kind":"Field","name":{"kind":"Name","value":"purchaseDetails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"productName"}},{"kind":"Field","name":{"kind":"Name","value":"webIcon"}},{"kind":"Field","name":{"kind":"Name","value":"localizations"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"locale"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"productName"}},{"kind":"Field","name":{"kind":"Name","value":"webIcon"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"stripePlanChoices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stripePlanChoice"}}]}}]}}]}}]} as unknown as DocumentNode; export const PageContentForOfferingDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PageContentForOffering"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"locale"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"apiIdentifier"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"offerings"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"apiIdentifier"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"apiIdentifier"}}}]}}]}},{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"200"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apiIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"countries"}},{"kind":"Field","name":{"kind":"Name","value":"stripeProductId"}},{"kind":"Field","name":{"kind":"Name","value":"defaultPurchase"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"purchaseDetails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"productName"}},{"kind":"Field","name":{"kind":"Name","value":"subtitle"}},{"kind":"Field","name":{"kind":"Name","value":"webIcon"}},{"kind":"Field","name":{"kind":"Name","value":"localizations"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"locale"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"productName"}},{"kind":"Field","name":{"kind":"Name","value":"subtitle"}},{"kind":"Field","name":{"kind":"Name","value":"webIcon"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"commonContent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"privacyNoticeUrl"}},{"kind":"Field","name":{"kind":"Name","value":"privacyNoticeDownloadUrl"}},{"kind":"Field","name":{"kind":"Name","value":"termsOfServiceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"termsOfServiceDownloadUrl"}},{"kind":"Field","name":{"kind":"Name","value":"cancellationUrl"}},{"kind":"Field","name":{"kind":"Name","value":"emailIcon"}},{"kind":"Field","name":{"kind":"Name","value":"successActionButtonUrl"}},{"kind":"Field","name":{"kind":"Name","value":"successActionButtonLabel"}},{"kind":"Field","name":{"kind":"Name","value":"newsletterLabelTextCode"}},{"kind":"Field","name":{"kind":"Name","value":"newsletterSlug"}},{"kind":"Field","name":{"kind":"Name","value":"localizations"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"locale"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"privacyNoticeUrl"}},{"kind":"Field","name":{"kind":"Name","value":"privacyNoticeDownloadUrl"}},{"kind":"Field","name":{"kind":"Name","value":"termsOfServiceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"termsOfServiceDownloadUrl"}},{"kind":"Field","name":{"kind":"Name","value":"cancellationUrl"}},{"kind":"Field","name":{"kind":"Name","value":"emailIcon"}},{"kind":"Field","name":{"kind":"Name","value":"successActionButtonUrl"}},{"kind":"Field","name":{"kind":"Name","value":"successActionButtonLabel"}},{"kind":"Field","name":{"kind":"Name","value":"newsletterLabelTextCode"}},{"kind":"Field","name":{"kind":"Name","value":"newsletterSlug"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const PurchaseWithDetailsOfferingContentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PurchaseWithDetailsOfferingContent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"locale"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"stripePlanIds"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"purchases"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"stripePlanChoices"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"stripePlanChoice"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"in"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stripePlanIds"}}}]}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"offering"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"stripeLegacyPlans"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"stripeLegacyPlan"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"in"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stripePlanIds"}}}]}}]}}]}}]}]}}]}},{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"500"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stripePlanChoices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stripePlanChoice"}}]}},{"kind":"Field","name":{"kind":"Name","value":"purchaseDetails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"productName"}},{"kind":"Field","name":{"kind":"Name","value":"subtitle"}},{"kind":"Field","name":{"kind":"Name","value":"webIcon"}},{"kind":"Field","name":{"kind":"Name","value":"localizations"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"locale"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"productName"}},{"kind":"Field","name":{"kind":"Name","value":"subtitle"}},{"kind":"Field","name":{"kind":"Name","value":"webIcon"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"offering"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stripeProductId"}},{"kind":"Field","name":{"kind":"Name","value":"stripeLegacyPlans"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"200"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stripeLegacyPlan"}}]}},{"kind":"Field","name":{"kind":"Name","value":"commonContent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"privacyNoticeUrl"}},{"kind":"Field","name":{"kind":"Name","value":"privacyNoticeDownloadUrl"}},{"kind":"Field","name":{"kind":"Name","value":"termsOfServiceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"termsOfServiceDownloadUrl"}},{"kind":"Field","name":{"kind":"Name","value":"cancellationUrl"}},{"kind":"Field","name":{"kind":"Name","value":"emailIcon"}},{"kind":"Field","name":{"kind":"Name","value":"successActionButtonUrl"}},{"kind":"Field","name":{"kind":"Name","value":"successActionButtonLabel"}},{"kind":"Field","name":{"kind":"Name","value":"newsletterLabelTextCode"}},{"kind":"Field","name":{"kind":"Name","value":"newsletterSlug"}},{"kind":"Field","name":{"kind":"Name","value":"localizations"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"locale"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"privacyNoticeUrl"}},{"kind":"Field","name":{"kind":"Name","value":"privacyNoticeDownloadUrl"}},{"kind":"Field","name":{"kind":"Name","value":"termsOfServiceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"termsOfServiceDownloadUrl"}},{"kind":"Field","name":{"kind":"Name","value":"cancellationUrl"}},{"kind":"Field","name":{"kind":"Name","value":"emailIcon"}},{"kind":"Field","name":{"kind":"Name","value":"successActionButtonUrl"}},{"kind":"Field","name":{"kind":"Name","value":"successActionButtonLabel"}},{"kind":"Field","name":{"kind":"Name","value":"newsletterLabelTextCode"}},{"kind":"Field","name":{"kind":"Name","value":"newsletterSlug"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; -export const RelyingPartiesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"RelyingParties"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"clientId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"entrypoint"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"relyingParties"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"clientId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"clientId"}}}]}},{"kind":"ObjectField","name":{"kind":"Name","value":"entrypoint"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"entrypoint"}}}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"clientId"}},{"kind":"Field","name":{"kind":"Name","value":"entrypoint"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"l10nId"}},{"kind":"Field","name":{"kind":"Name","value":"shared"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"buttonColor"}},{"kind":"Field","name":{"kind":"Name","value":"logoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"logoAltText"}},{"kind":"Field","name":{"kind":"Name","value":"emailFromName"}},{"kind":"Field","name":{"kind":"Name","value":"emailLogoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"emailLogoAltText"}},{"kind":"Field","name":{"kind":"Name","value":"emailLogoWidth"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"headerLogoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"headerLogoAltText"}},{"kind":"Field","name":{"kind":"Name","value":"featureFlags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"syncConfirmedPageHideCTA"}},{"kind":"Field","name":{"kind":"Name","value":"syncHidePromoAfterLogin"}}]}},{"kind":"Field","name":{"kind":"Name","value":"backgrounds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"defaultLayout"}},{"kind":"Field","name":{"kind":"Name","value":"header"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayoutAltText"}}]}},{"kind":"Field","name":{"kind":"Name","value":"illustrationsTheme"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"primary"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAlt"}},{"kind":"Field","name":{"kind":"Name","value":"secondary"}},{"kind":"Field","name":{"kind":"Name","value":"accentBg"}},{"kind":"Field","name":{"kind":"Name","value":"accentFg"}},{"kind":"Field","name":{"kind":"Name","value":"cloudPrimary"}},{"kind":"Field","name":{"kind":"Name","value":"cloudShadow"}},{"kind":"Field","name":{"kind":"Name","value":"hideClouds"}}]}},{"kind":"Field","name":{"kind":"Name","value":"favicon"}},{"kind":"Field","name":{"kind":"Name","value":"headlineFontSize"}},{"kind":"Field","name":{"kind":"Name","value":"headlineTextColor"}},{"kind":"Field","name":{"kind":"Name","value":"additionalAccessibilityInfo"}}]}},{"kind":"Field","name":{"kind":"Name","value":"EmailFirstPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"logoAltText"}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SignupSetPasswordPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"logoAltText"}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SignupConfirmCodePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SignupConfirmedSyncPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"primaryImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"altText"}}]}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SignupPasswordlessCodePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"primaryImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"altText"}}]}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninCachedPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"logoAltText"}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninPasswordlessCodePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"primaryImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"altText"}}]}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninTokenCodePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninUnblockCodePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninTotpCodePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninRecoveryChoicePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninRecoveryCodePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"primaryImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"altText"}}]}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninRecoveryPhonePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"primaryImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"altText"}}]}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"PostVerifySetPasswordPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}}]}},{"kind":"Field","name":{"kind":"Name","value":"NewDeviceLoginEmail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"PasswordlessSigninOtpEmail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"PasswordlessSignupOtpEmail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"VerifyLoginCodeEmail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"VerifyShortCodeEmail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]}}]}}]} as unknown as DocumentNode; +export const RelyingPartiesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"RelyingParties"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"clientId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"entrypoint"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"relyingParties"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"clientId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"clientId"}}}]}},{"kind":"ObjectField","name":{"kind":"Name","value":"entrypoint"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"entrypoint"}}}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"clientId"}},{"kind":"Field","name":{"kind":"Name","value":"entrypoint"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"l10nId"}},{"kind":"Field","name":{"kind":"Name","value":"shared"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"buttonColor"}},{"kind":"Field","name":{"kind":"Name","value":"logoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"logoAltText"}},{"kind":"Field","name":{"kind":"Name","value":"emailFromName"}},{"kind":"Field","name":{"kind":"Name","value":"emailLogoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"emailLogoAltText"}},{"kind":"Field","name":{"kind":"Name","value":"emailLogoWidth"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"headerLogoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"headerLogoAltText"}},{"kind":"Field","name":{"kind":"Name","value":"featureFlags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"syncConfirmedPageHideCTA"}},{"kind":"Field","name":{"kind":"Name","value":"syncHidePromoAfterLogin"}}]}},{"kind":"Field","name":{"kind":"Name","value":"backgrounds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"defaultLayout"}},{"kind":"Field","name":{"kind":"Name","value":"header"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayoutAltText"}}]}},{"kind":"Field","name":{"kind":"Name","value":"illustrationsTheme"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"primary"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAlt"}},{"kind":"Field","name":{"kind":"Name","value":"secondary"}},{"kind":"Field","name":{"kind":"Name","value":"accentBg"}},{"kind":"Field","name":{"kind":"Name","value":"accentFg"}},{"kind":"Field","name":{"kind":"Name","value":"cloudPrimary"}},{"kind":"Field","name":{"kind":"Name","value":"cloudShadow"}},{"kind":"Field","name":{"kind":"Name","value":"hideClouds"}}]}},{"kind":"Field","name":{"kind":"Name","value":"favicon"}},{"kind":"Field","name":{"kind":"Name","value":"headlineFontSize"}},{"kind":"Field","name":{"kind":"Name","value":"headlineTextColor"}},{"kind":"Field","name":{"kind":"Name","value":"additionalAccessibilityInfo"}}]}},{"kind":"Field","name":{"kind":"Name","value":"EmailFirstPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"logoAltText"}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SignupSetPasswordPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"logoAltText"}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SignupConfirmCodePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SignupConfirmedSyncPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"primaryImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"altText"}}]}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SignupPasswordlessCodePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"primaryImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"altText"}}]}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninCachedPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"logoAltText"}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"AuthorizePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninPasswordlessCodePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"primaryImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"altText"}}]}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninTokenCodePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninUnblockCodePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninTotpCodePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninRecoveryChoicePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninRecoveryCodePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"primaryImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"altText"}}]}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"SigninRecoveryPhonePage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}},{"kind":"Field","name":{"kind":"Name","value":"primaryImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"altText"}}]}},{"kind":"Field","name":{"kind":"Name","value":"splitLayout"}}]}},{"kind":"Field","name":{"kind":"Name","value":"PostVerifySetPasswordPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"primaryButtonText"}},{"kind":"Field","name":{"kind":"Name","value":"pageTitle"}}]}},{"kind":"Field","name":{"kind":"Name","value":"NewDeviceLoginEmail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"PasswordlessSigninOtpEmail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"PasswordlessSignupOtpEmail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"VerifyLoginCodeEmail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"VerifyShortCodeEmail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]}}]}}]} as unknown as DocumentNode; export const ServicesWithCapabilitiesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ServicesWithCapabilities"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"services"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"500"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"oauthClientId"}},{"kind":"Field","name":{"kind":"Name","value":"capabilities"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}}]}}]}}]} as unknown as DocumentNode; export const ValidationOfferingsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ValidationOfferings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"offerings"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"500"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apiIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"stripeProductId"}},{"kind":"Field","name":{"kind":"Name","value":"countries"}},{"kind":"Field","name":{"kind":"Name","value":"defaultPurchase"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"purchaseDetails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"productName"}},{"kind":"Field","name":{"kind":"Name","value":"subtitle"}},{"kind":"Field","name":{"kind":"Name","value":"webIcon"}}]}},{"kind":"Field","name":{"kind":"Name","value":"stripePlanChoices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stripePlanChoice"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"commonContent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"privacyNoticeUrl"}},{"kind":"Field","name":{"kind":"Name","value":"privacyNoticeDownloadUrl"}},{"kind":"Field","name":{"kind":"Name","value":"termsOfServiceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"termsOfServiceDownloadUrl"}},{"kind":"Field","name":{"kind":"Name","value":"cancellationUrl"}},{"kind":"Field","name":{"kind":"Name","value":"emailIcon"}},{"kind":"Field","name":{"kind":"Name","value":"successActionButtonUrl"}},{"kind":"Field","name":{"kind":"Name","value":"successActionButtonLabel"}},{"kind":"Field","name":{"kind":"Name","value":"newsletterLabelTextCode"}},{"kind":"Field","name":{"kind":"Name","value":"newsletterSlug"}},{"kind":"Field","name":{"kind":"Name","value":"supportUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"capabilities"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"services"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"oauthClientId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"stripeLegacyPlans"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"200"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stripeLegacyPlan"}}]}},{"kind":"Field","name":{"kind":"Name","value":"couponConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"internalName"}},{"kind":"Field","name":{"kind":"Name","value":"stripePromotionCodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"PromoCode"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"subGroups"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"internalName"}},{"kind":"Field","name":{"kind":"Name","value":"groupName"}}]}}]}}]}}]} as unknown as DocumentNode; export const ValidationPurchasesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ValidationPurchases"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"purchases"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"500"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"internalName"}},{"kind":"Field","name":{"kind":"Name","value":"purchaseDetails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"productName"}},{"kind":"Field","name":{"kind":"Name","value":"subtitle"}},{"kind":"Field","name":{"kind":"Name","value":"webIcon"}}]}},{"kind":"Field","name":{"kind":"Name","value":"stripePlanChoices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stripePlanChoice"}}]}}]}}]}}]} as unknown as DocumentNode; diff --git a/libs/shared/cms/src/lib/queries/relying-party/factories.ts b/libs/shared/cms/src/lib/queries/relying-party/factories.ts index c6837e9505e..23c26e63821 100644 --- a/libs/shared/cms/src/lib/queries/relying-party/factories.ts +++ b/libs/shared/cms/src/lib/queries/relying-party/factories.ts @@ -175,6 +175,13 @@ export const RelyingPartyResultFactory = ( pageTitle: faker.string.sample(), splitLayout: faker.datatype.boolean(), }, + AuthorizePage: { + headline: faker.string.sample(), + description: faker.string.sample(), + primaryButtonText: faker.string.sample(), + pageTitle: faker.string.sample(), + splitLayout: faker.datatype.boolean(), + }, SigninPasswordlessCodePage: { logoUrl: faker.internet.url(), logoAltText: faker.internet.url(), diff --git a/libs/shared/cms/src/lib/queries/relying-party/query.ts b/libs/shared/cms/src/lib/queries/relying-party/query.ts index 7a780427689..6ab3afb26a7 100644 --- a/libs/shared/cms/src/lib/queries/relying-party/query.ts +++ b/libs/shared/cms/src/lib/queries/relying-party/query.ts @@ -112,6 +112,13 @@ export const relyingPartyQuery = graphql(` pageTitle splitLayout } + AuthorizePage { + headline + description + primaryButtonText + pageTitle + splitLayout + } SigninPasswordlessCodePage { headline description diff --git a/libs/shared/cms/src/lib/queries/relying-party/types.ts b/libs/shared/cms/src/lib/queries/relying-party/types.ts index aff3df62074..e3c618acc9f 100644 --- a/libs/shared/cms/src/lib/queries/relying-party/types.ts +++ b/libs/shared/cms/src/lib/queries/relying-party/types.ts @@ -73,6 +73,7 @@ export interface RelyingPartyResult { SignupConfirmedSyncPage: Page; SigninPage: Page; SigninCachedPage?: Page; + AuthorizePage?: Page; SigninTokenCodePage?: Page; SigninUnblockCodePage?: Page; SigninTotpCodePage?: Page; diff --git a/packages/functional-tests/lib/query-params.ts b/packages/functional-tests/lib/query-params.ts index 4afeca720c7..89611a4b614 100644 --- a/packages/functional-tests/lib/query-params.ts +++ b/packages/functional-tests/lib/query-params.ts @@ -69,6 +69,32 @@ export const smartWindowDesktopOAuthQueryParams = new URLSearchParams({ service: 'smartwindow', }); +export const syncMobileOAuthFenixQueryParams = new URLSearchParams({ + ...Object.fromEntries(oauthWebchannelV1.entries()), + client_id: 'a2270f727f45f648', // Fenix (Android) + code_challenge_method: 'S256', + code_challenge: '2oc_C4v1qHeefWAGu5LI5oDG1oX4FV_Itc148D8_oQI', + // eslint-disable-next-line camelcase + keys_jwk: + 'eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImdUejVIWFJfa2pxSFRtMG43ZjhxcDMybVZFaHZ1cGo1dXNUV1h5TWZsb1kiLCJ5IjoiVER5TlhkalhibHZld1pWLVc5MXNDZU9fRWd0NU9WYXhpblBzOEFTQ3owZyJ9', + scope: + 'https://identity.mozilla.com/apps/oldsync https://identity.mozilla.com/tokens/session', + state: 'fakestate', + automatedBrowser: 'true', + service: 'sync', +}); + +export const vpnMobileOAuthQueryParams = new URLSearchParams({ + ...Object.fromEntries(oauthWebchannelV1.entries()), + client_id: 'a2270f727f45f648', // Fenix (Android) + code_challenge_method: 'S256', + code_challenge: '2oc_C4v1qHeefWAGu5LI5oDG1oX4FV_Itc148D8_oQI', + scope: 'https://identity.mozilla.com/apps/vpn', + state: 'fakestate', + automatedBrowser: 'true', + service: 'vpn', +}); + export const syncDesktopV3QueryParams = new URLSearchParams({ context: 'fx_desktop_v3', service: 'sync', diff --git a/packages/functional-tests/pages/layout.ts b/packages/functional-tests/pages/layout.ts index 5060be9f623..6a2df4a5df2 100644 --- a/packages/functional-tests/pages/layout.ts +++ b/packages/functional-tests/pages/layout.ts @@ -95,6 +95,12 @@ export abstract class BaseLayout { }); } + async clearWebChannelEvents() { + await this.page.evaluate(() => { + sessionStorage.removeItem('webChannelEvents'); + }); + } + /** * Asserts that a web channel message with the given command was sent * and contains the expected services object in its data. @@ -117,6 +123,28 @@ export abstract class BaseLayout { } } + /** + * Asserts that a web channel message with the given command was sent + * and contains the expected scope string in its data. + */ + async checkWebChannelMessageScopes( + command: FirefoxCommand, + expectedScope: string + ) { + await this.checkWebChannelMessage(command); + const events = await this.getWebChannelEvents(); + const event = events.find((e) => e.command === command); + if (!event) { + throw new Error(`No web channel event found for command: ${command}`); + } + const scopes = (event.data as { scopes?: string })?.scopes; + if (!scopes?.includes(expectedScope)) { + throw new Error( + `Expected scopes to contain "${expectedScope}" but got "${scopes}"` + ); + } + } + async listenToWebChannelMessages() { await this.page.evaluate(() => { function listener(msg: { detail: string }) { diff --git a/packages/functional-tests/tests/misc/vpnIntegration.spec.ts b/packages/functional-tests/tests/misc/vpnIntegration.spec.ts new file mode 100644 index 00000000000..b33ff51ddc0 --- /dev/null +++ b/packages/functional-tests/tests/misc/vpnIntegration.spec.ts @@ -0,0 +1,49 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import { FirefoxCommand } from '../../lib/channels'; +import { expect, test } from '../../lib/fixtures/standard'; +import { + syncMobileOAuthFenixQueryParams, + vpnMobileOAuthQueryParams, +} from '../../lib/query-params'; + +test.describe('vpn integration', () => { + test('authorization flow - user already signed into Firefox', async ({ + syncOAuthBrowserPages: { page, signin }, + testAccountTracker, + }) => { + const { email, password } = await testAccountTracker.signUp(); + + // First, sign into Sync with Fenix (Android) client ID + await signin.goto('/authorization', syncMobileOAuthFenixQueryParams); + await signin.fillOutEmailFirstForm(email); + await signin.fillOutPasswordForm(password); + + // Wait for Sync sign-in to complete, then clear events for the + // VPN scope check later in the test + await signin.checkWebChannelMessage(FirefoxCommand.OAuthLogin); + await signin.clearWebChannelEvents(); + + // Now navigate to VPN authorization — user is already signed into Firefox + await signin.goto('/authorization', vpnMobileOAuthQueryParams); + + // User is already signed in — cached signin view, no password required + await expect(signin.cachedSigninHeading).toBeVisible(); + await expect(page.getByText(email)).toBeVisible(); + + await signin.signInButton.click(); + + // Verify fxaOAuthLogin was sent with VPN scopes + await signin.checkWebChannelMessageScopes( + FirefoxCommand.OAuthLogin, + 'https://identity.mozilla.com/apps/vpn' + ); + + // Verify services data includes vpn + await signin.checkWebChannelMessageServices(FirefoxCommand.Login, { + vpn: {}, + }); + }); +}); diff --git a/packages/functional-tests/tests/oauth/signinTokenCode.spec.ts b/packages/functional-tests/tests/oauth/signinTokenCode.spec.ts index 2335091045b..6c4a03d6919 100644 --- a/packages/functional-tests/tests/oauth/signinTokenCode.spec.ts +++ b/packages/functional-tests/tests/oauth/signinTokenCode.spec.ts @@ -5,7 +5,7 @@ import { expect, test } from '../../lib/fixtures/standard'; test.describe('severity-2 #smoke', () => { - test.describe('OAuth signin token code', () => { + test.describe('signin token code for OAuth RP redirect with client requesting scoped keys', () => { function toQueryString(obj: Record) { return Object.entries(obj) .map((x) => `${x[0]}=${x[1]}`) @@ -19,7 +19,6 @@ test.describe('severity-2 #smoke', () => { code_challenge_method: 'S256', forceUA: 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Mobile Safari/537.36', - keys_jwk: 'eyJrdHkiOiJFQyIsImtpZCI6Im9DNGFudFBBSFZRX1pmQ09RRUYycTRaQlZYblVNZ2xISGpVRzdtSjZHOEEiLCJjcnYiOiJQLTI1NiIsIngiOiJDeUpUSjVwbUNZb2lQQnVWOTk1UjNvNTFLZVBMaEg1Y3JaQlkwbXNxTDk0IiwieSI6IkJCWDhfcFVZeHpTaldsdXU5MFdPTVZwamIzTlpVRDAyN0xwcC04RW9vckEifQ', redirect_uri: 'https://mozilla.github.io/notes/fxa/android-redirect.html', diff --git a/packages/fxa-settings/src/components/App/index.tsx b/packages/fxa-settings/src/components/App/index.tsx index b9d50749a85..8875de60d19 100644 --- a/packages/fxa-settings/src/components/App/index.tsx +++ b/packages/fxa-settings/src/components/App/index.tsx @@ -219,9 +219,10 @@ export const App = ({ // Determine if user is actually signed in const [isSignedIn, setIsSignedIn] = useState(undefined); - // Track whether the user is signed into Firefox Desktop via WebChannel - const [isSignedIntoFirefoxDesktop, setIsSignedIntoFirefoxDesktop] = - useState(false); + // Determine whether the user is signed into Firefox via WebChannel. + // This also partially tells us if the user is in a Firefox "authorization" flow, + // where they are already signed in but need to consent to a new scope. + const [isSignedIntoFirefox, setIsSignedIntoFirefox] = useState(false); // Track current page's split layout state to prevent visual flashing during navigation. // This state is updated by AppLayout and read by the Suspense fallback to preserve @@ -256,10 +257,7 @@ export const App = ({ userFromBrowser.sessionToken ); if (isValidSession) { - setIsSignedIntoFirefoxDesktop( - !!userFromBrowser?.sessionToken && - integration.isFirefoxDesktopClient() - ); + setIsSignedIntoFirefox(true); const cachedUser = getAccountByUid(userFromBrowser.uid); // Refresh the token without switching the "current" account. persistAccount( @@ -421,7 +419,7 @@ export const App = ({ isSignedIn, integration, flowQueryParams: updatedFlowQueryParams, - isSignedIntoFirefoxDesktop, + isSignedIntoFirefox, setCurrentSplitLayout, }} path="/*" @@ -496,13 +494,13 @@ const AuthAndAccountSetupRoutes = ({ isSignedIn, integration, flowQueryParams, - isSignedIntoFirefoxDesktop, + isSignedIntoFirefox, setCurrentSplitLayout, }: { isSignedIn: boolean; integration: Integration; flowQueryParams: QueryParams; - isSignedIntoFirefoxDesktop: boolean; + isSignedIntoFirefox: boolean; setCurrentSplitLayout: (value: boolean) => void; } & RouteComponentProps) => { const localAccount = currentAccount(); @@ -639,7 +637,7 @@ const AuthAndAccountSetupRoutes = ({ serviceName, flowQueryParams, useFxAStatusResult, - isSignedIntoFirefoxDesktop, + isSignedIntoFirefox, setCurrentSplitLayout, }} /> @@ -650,7 +648,7 @@ const AuthAndAccountSetupRoutes = ({ serviceName, flowQueryParams, useFxAStatusResult, - isSignedIntoFirefoxDesktop, + isSignedIntoFirefox, setCurrentSplitLayout, }} /> @@ -679,7 +677,7 @@ const AuthAndAccountSetupRoutes = ({ serviceName, flowQueryParams, useFxAStatusResult, - isSignedIntoFirefoxDesktop, + isSignedIntoFirefox, setCurrentSplitLayout, }} /> @@ -690,7 +688,7 @@ const AuthAndAccountSetupRoutes = ({ serviceName, flowQueryParams, useFxAStatusResult, - isSignedIntoFirefoxDesktop, + isSignedIntoFirefox, setCurrentSplitLayout, }} /> diff --git a/packages/fxa-settings/src/lib/channels/firefox.ts b/packages/fxa-settings/src/lib/channels/firefox.ts index 8f90909d881..7b2bb408183 100644 --- a/packages/fxa-settings/src/lib/channels/firefox.ts +++ b/packages/fxa-settings/src/lib/channels/firefox.ts @@ -146,6 +146,9 @@ export type FxAOAuthLogin = { // eventually move to look at fxaLogin as well to prevent FXA-10596. declinedSyncEngines?: string[]; offeredSyncEngines?: string[]; + // Space-separated list of granted scopes, sent so the browser knows + // which scopes were authorized in this flow. + scopes?: string; }; // ref: https://searchfox.org/mozilla-central/rev/82828dba9e290914eddd294a0871533875b3a0b5/services/fxaccounts/FxAccountsWebChannel.sys.mjs#230 diff --git a/packages/fxa-settings/src/lib/integrations/integration-factory.test.ts b/packages/fxa-settings/src/lib/integrations/integration-factory.test.ts index 452679a929f..da7c758d605 100644 --- a/packages/fxa-settings/src/lib/integrations/integration-factory.test.ts +++ b/packages/fxa-settings/src/lib/integrations/integration-factory.test.ts @@ -238,8 +238,15 @@ describe('lib/integrations/integration-factory', () => { expect(integration.type).toEqual(IntegrationType.OAuthWeb); expect(integration.isSync()).toBeFalsy(); expect(integration.wantsKeys()).toBeFalsy(); + expect(integration.requiresKeys()).toBeFalsy(); expect(integration.isTrusted()).toBeTruthy(); }); + + it('getGrantedScopes returns undefined for non-native integration', () => { + // This setup produces OAuthWeb (not OAuthNative) since + // isOAuthWebChannelContext is not set. Base class returns undefined. + expect(integration.getGrantedScopes()).toBeUndefined(); + }); }); describe('with sync', () => { @@ -263,8 +270,29 @@ describe('lib/integrations/integration-factory', () => { it('has correct state', async () => { expect(integration.type).toEqual(IntegrationType.OAuthNative); expect(integration.isSync()).toBeTruthy(); - expect(integration.wantsKeys()).toBeTruthy(); expect(integration.isTrusted()).toBeTruthy(); + expect(integration.isFirefoxClient()).toBeTruthy(); + }); + + it('wantsKeys is false when keysJwk is not provided', () => { + // Without keysJwk in the data, _scopeRequestsKeys returns false + expect(integration.wantsKeys()).toBeFalsy(); + expect(integration.requiresKeys()).toBeFalsy(); + }); + + it('wantsKeys is true when keysJwk and scoped key validation are configured', () => { + integration.data.keysJwk = 'fakeKeysJwk'; + sandbox.stub(integration, 'opts').value({ + ...integration.opts, + scopedKeysEnabled: true, + scopedKeysValidation: { + [Constants.OAUTH_OLDSYNC_SCOPE]: { + redirectUris: [clientInfo.redirectUri], + }, + }, + }); + expect(integration.requiresKeys()).toBeTruthy(); + expect(integration.wantsKeys()).toBeTruthy(); }); }); }); diff --git a/packages/fxa-settings/src/lib/oauth/hooks.tsx b/packages/fxa-settings/src/lib/oauth/hooks.tsx index 4e8ceb534e7..46d0ae61b21 100644 --- a/packages/fxa-settings/src/lib/oauth/hooks.tsx +++ b/packages/fxa-settings/src/lib/oauth/hooks.tsx @@ -305,7 +305,7 @@ export function useFinishOAuthFlowHandler( * to /signin instead of showing an error component? FXA-10889 */ export function useOAuthKeysCheck( - integration: Pick, + integration: Pick, keyFetchToken?: hexstring, unwrapBKey?: hexstring, isSignInWithThirdPartyAuth?: boolean @@ -313,7 +313,7 @@ export function useOAuthKeysCheck( if ( (isOAuthIntegration(integration) || isSyncDesktopV3Integration(integration)) && - integration.wantsKeys() && + integration.requiresKeys() && // If the user has 2FA enabled but chose to login to the browser via third party // auth, keys are not fetched because the user didn't enter a password. // For this case, skip the keys check, the browser expects them to be undefined. diff --git a/packages/fxa-settings/src/models/integrations/data/data.ts b/packages/fxa-settings/src/models/integrations/data/data.ts index 0e28938dc70..a0928dcba64 100644 --- a/packages/fxa-settings/src/models/integrations/data/data.ts +++ b/packages/fxa-settings/src/models/integrations/data/data.ts @@ -22,7 +22,11 @@ import { KeyTransforms as T, ModelDataProvider, } from '../../../lib/model-data'; -import { IsEmailOrEmpty, IsFxaRedirectToUrl, IsFxaRedirectUri } from '../../../lib/validation'; +import { + IsEmailOrEmpty, + IsFxaRedirectToUrl, + IsFxaRedirectUri, +} from '../../../lib/validation'; /** * Base integration class. Fields in this class represents data commonly accessed across many pages and is useful for various flows. diff --git a/packages/fxa-settings/src/models/integrations/integration.ts b/packages/fxa-settings/src/models/integrations/integration.ts index eeabb06c687..06b423df474 100644 --- a/packages/fxa-settings/src/models/integrations/integration.ts +++ b/packages/fxa-settings/src/models/integrations/integration.ts @@ -106,6 +106,10 @@ export class GenericIntegration< return undefined; } + isFirefoxClient() { + return this.isFirefoxDesktopClient() || this.isFirefoxMobileClient(); + } + isFirefoxMobileClient() { return false; } @@ -151,11 +155,8 @@ export class GenericIntegration< } /** - * Currently only Sync _requires_ keys (entering a password). Other - * services may see cached signin or choose to sign in with third party auth. - * However, for non-Sync browser service signins, if the password is entered, - * we go ahead and get Sync keys so users can turn Sync on in the browser - * without being bounced back to FxA. + * Whether this integration strictly requires keys, forcing password entry. + * Currently only Sync requires keys. * * Note, the Relay browser service login launched in Firefox desktop 135, and * the "keys optional" capability launched in Fx desktop 147, meaning all Relay @@ -163,11 +164,43 @@ export class GenericIntegration< * * Desktop OAuth launched with Fx 134. SyncDesktopV3 users don't have non-Sync * browser support. - * */ - wantsKeys(): boolean { + */ + requiresKeys(): boolean { + return false; + } + + /** + * Whether this integration wants keys opportunistically — if the user + * enters a password for another reason, we should also fetch keys. + * This applies to non-Sync browser services (Relay, VPN, SmartWindow) that + * request the Sync scope so users can turn Sync on in the browser without + * being bounced back to FxA. + */ + wantsKeysIfPasswordEntered(): boolean { return false; } + /** + * Whether this integration requests scoped keys. When true, `keys: true` + * is sent to the auth server, which may trigger email verification. + * Use `requiresKeys()` when you need to know if keys are mandatory + * (e.g., forcing password entry), and `wantsKeys()` when you need to + * know if scoped keys are being requested, either because they're + * required or because the user entered a password and keys can be + * derived opportunistically. + */ + wantsKeys(): boolean { + return this.requiresKeys() || this.wantsKeysIfPasswordEntered(); + } + + /** + * Returns the scopes that were granted for this integration. + * Overridden in OAuthNativeIntegration; returns undefined for all other types. + */ + getGrantedScopes(): string | undefined { + return undefined; + } + wantsLogin(): boolean { return false; } diff --git a/packages/fxa-settings/src/models/integrations/oauth-native-integration.test.ts b/packages/fxa-settings/src/models/integrations/oauth-native-integration.test.ts index 4e7bf93954e..ff39d861ec0 100644 --- a/packages/fxa-settings/src/models/integrations/oauth-native-integration.test.ts +++ b/packages/fxa-settings/src/models/integrations/oauth-native-integration.test.ts @@ -131,9 +131,116 @@ describe('OAuthNativeIntegration', function () { }); }); - describe('wantsKeys', () => { - it('returns true', () => { + describe('requiresKeys', () => { + it('returns true for Sync when scope has scoped keys', () => { + model.clientInfo = mockClientInfo(OAuthNativeClients.FirefoxDesktop); + model.data.service = OAuthNativeServices.Sync; + model.data.keysJwk = 'mock-keys-jwk'; + model.data.scope = 'https://identity.mozilla.com/apps/oldsync'; + model = new OAuthNativeIntegration(data, oauthData, { + scopedKeysEnabled: true, + scopedKeysValidation: { + 'https://identity.mozilla.com/apps/oldsync': { + redirectUris: [model.clientInfo?.redirectUri], + }, + }, + isPromptNoneEnabled: true, + isPromptNoneEnabledClientIds: [], + }); + model.clientInfo = mockClientInfo(OAuthNativeClients.FirefoxDesktop); + model.data.service = OAuthNativeServices.Sync; + model.data.keysJwk = 'mock-keys-jwk'; + model.data.scope = 'https://identity.mozilla.com/apps/oldsync'; + expect(model.requiresKeys()).toBe(true); + }); + + it('returns false for non-Sync services', () => { + model.clientInfo = mockClientInfo(OAuthNativeClients.FirefoxDesktop); + model.data.service = OAuthNativeServices.Relay; + expect(model.requiresKeys()).toBe(false); + }); + + it('returns false when scopedKeysEnabled is false', () => { + model = new OAuthNativeIntegration(data, oauthData, { + scopedKeysEnabled: false, + scopedKeysValidation: {}, + isPromptNoneEnabled: true, + isPromptNoneEnabledClientIds: [], + }); + model.clientInfo = mockClientInfo(OAuthNativeClients.FirefoxDesktop); + model.data.service = OAuthNativeServices.Sync; + expect(model.requiresKeys()).toBe(false); + }); + }); + + describe('wantsKeysIfPasswordEntered', () => { + it('returns true for non-Sync browser services with scoped keys', () => { + model = new OAuthNativeIntegration(data, oauthData, { + scopedKeysEnabled: true, + scopedKeysValidation: { + 'https://identity.mozilla.com/apps/oldsync': { + redirectUris: ['https://mock.com'], + }, + }, + isPromptNoneEnabled: true, + isPromptNoneEnabledClientIds: [], + }); + model.clientInfo = mockClientInfo(OAuthNativeClients.FirefoxDesktop); + model.data.service = OAuthNativeServices.Relay; + model.data.keysJwk = 'mock-keys-jwk'; + model.data.scope = 'https://identity.mozilla.com/apps/oldsync'; + expect(model.wantsKeysIfPasswordEntered()).toBe(true); + }); + + it('returns false for Sync', () => { + model.clientInfo = mockClientInfo(OAuthNativeClients.FirefoxDesktop); + model.data.service = OAuthNativeServices.Sync; + expect(model.wantsKeysIfPasswordEntered()).toBe(false); + }); + }); + + describe('wantsKeys (combined)', () => { + it('returns true when requiresKeys is true', () => { + model = new OAuthNativeIntegration(data, oauthData, { + scopedKeysEnabled: true, + scopedKeysValidation: { + 'https://identity.mozilla.com/apps/oldsync': { + redirectUris: ['https://mock.com'], + }, + }, + isPromptNoneEnabled: true, + isPromptNoneEnabledClientIds: [], + }); + model.clientInfo = mockClientInfo(OAuthNativeClients.FirefoxDesktop); + model.data.service = OAuthNativeServices.Sync; + model.data.keysJwk = 'mock-keys-jwk'; + model.data.scope = 'https://identity.mozilla.com/apps/oldsync'; expect(model.wantsKeys()).toBe(true); + expect(model.requiresKeys()).toBe(true); + }); + + it('returns true when wantsKeysIfPasswordEntered is true', () => { + model = new OAuthNativeIntegration(data, oauthData, { + scopedKeysEnabled: true, + scopedKeysValidation: { + 'https://identity.mozilla.com/apps/oldsync': { + redirectUris: ['https://mock.com'], + }, + }, + isPromptNoneEnabled: true, + isPromptNoneEnabledClientIds: [], + }); + model.clientInfo = mockClientInfo(OAuthNativeClients.FirefoxDesktop); + model.data.service = OAuthNativeServices.Relay; + model.data.keysJwk = 'mock-keys-jwk'; + model.data.scope = 'https://identity.mozilla.com/apps/oldsync'; + expect(model.wantsKeys()).toBe(true); + expect(model.requiresKeys()).toBe(false); + expect(model.wantsKeysIfPasswordEntered()).toBe(true); + }); + + it('returns false when no scoped keys are requested', () => { + expect(model.wantsKeys()).toBe(false); }); }); diff --git a/packages/fxa-settings/src/models/integrations/oauth-native-integration.ts b/packages/fxa-settings/src/models/integrations/oauth-native-integration.ts index a5f1e0f74b2..a736ff5c278 100644 --- a/packages/fxa-settings/src/models/integrations/oauth-native-integration.ts +++ b/packages/fxa-settings/src/models/integrations/oauth-native-integration.ts @@ -68,10 +68,6 @@ export class OAuthNativeIntegration extends OAuthWebIntegration { return this.isFirefoxDesktopClient() && this.isDefaultSyncService(); } - private isFirefoxClient() { - return this.isFirefoxDesktopClient() || this.isFirefoxMobileClient(); - } - // Sync should always provide a `service=sync` parameter for all Fx Desktop versions // and newer mobile versions. We'll default to Sync if it's missing. private isDefaultSyncService() { @@ -126,9 +122,16 @@ export class OAuthNativeIntegration extends OAuthWebIntegration { ); } - // See JSDoc comment above the generic integration base class wantsKeys - wantsKeys() { - return true; + // Sync requires keys, which forces password entry. + requiresKeys(): boolean { + return this.isSync() && this._scopeRequestsKeys(); + } + + // Non-Sync browser services (Relay, VPN, SmartWindow) want keys + // opportunistically if the user enters a password for another reason, + // so they can turn Sync on without being bounced back to FxA. + wantsKeysIfPasswordEntered(): boolean { + return this.isFirefoxNonSync() && this._scopeRequestsKeys(); } getWebChannelServices(syncEngines?: SyncEngines) { @@ -147,6 +150,13 @@ export class OAuthNativeIntegration extends OAuthWebIntegration { return undefined; } + // TODO: When server-side scope resolution (ADR 0049) is implemented, + // granted scopes may differ from requested scopes. Update this to return + // the actual granted scopes from the server response. + getGrantedScopes(): string | undefined { + return this.data.scope; + } + getServiceName() { if (this.isDefaultSyncService()) { return Constants.RELIER_SYNC_SERVICE_NAME; diff --git a/packages/fxa-settings/src/models/integrations/oauth-web-integration.ts b/packages/fxa-settings/src/models/integrations/oauth-web-integration.ts index 7ca671c8894..41debc3e355 100644 --- a/packages/fxa-settings/src/models/integrations/oauth-web-integration.ts +++ b/packages/fxa-settings/src/models/integrations/oauth-web-integration.ts @@ -188,38 +188,6 @@ export class OAuthWebIntegration extends GenericIntegration< return tokens.includes(Constants.TWO_STEP_AUTHENTICATION_ACR); } - wantsKeys(): boolean { - if (!this.opts.scopedKeysEnabled) { - return false; - } - if (this.data.keysJwk == null) { - return false; - } - if (!this.data.scope) { - return false; - } - - const validation = this.opts.scopedKeysValidation; - const individualScopes = scopeStrToArray(this.data.scope || ''); - - let wantsScopeThatHasKeys = false; - individualScopes.forEach((scope) => { - // eslint-disable-next-line no-prototype-builtins - if (validation.hasOwnProperty(scope)) { - if ( - validation[scope].redirectUris.includes(this.clientInfo?.redirectUri) - ) { - wantsScopeThatHasKeys = true; - } else { - // Requesting keys, but trying to deliver them to an unexpected uri? Nope. - throw new Error('Invalid redirect parameter'); - } - } - }); - - return wantsScopeThatHasKeys; - } - getPermissions() { // Ported from content server, search for _normalizeScopesAndPermissions let permissions = Array.from(scopeStrToArray(this.data.scope || '')); @@ -288,7 +256,7 @@ export class OAuthWebIntegration extends GenericIntegration< throw new OAuthError('PROMPT_NONE_NOT_ENABLED_FOR_CLIENT'); } - if (this.wantsKeys()) { + if (this._scopeRequestsKeys()) { throw new OAuthError('PROMPT_NONE_WITH_KEYS'); } @@ -319,12 +287,57 @@ export class OAuthWebIntegration extends GenericIntegration< } } + // Currently only Firefox Sync requires scoped keys, but historically other + // redirect-based RPs have used them (e.g. Notes). Our functional tests verify + // this case. When keys are requested, `keys: true` must be sent to the auth + // server to trigger email verification. + wantsKeys(): boolean { + return this._scopeRequestsKeys(); + } + + /** + * Checks whether any requested scope has scoped keys configured, + * validating the redirect URI matches. Used by OAuthNativeIntegration's + * requiresKeys() and wantsKeysIfPasswordEntered(), and by + * validatePromptNoneRequest() to reject prompt=none with keys. + */ + protected _scopeRequestsKeys(): boolean { + if (!this.opts.scopedKeysEnabled) { + return false; + } + if (this.data.keysJwk == null) { + return false; + } + if (!this.data.scope) { + return false; + } + + const validation = this.opts.scopedKeysValidation; + const individualScopes = scopeStrToArray(this.data.scope || ''); + + let wantsScopeThatHasKeys = false; + individualScopes.forEach((scope) => { + // eslint-disable-next-line no-prototype-builtins + if (validation.hasOwnProperty(scope)) { + if ( + validation[scope].redirectUris.includes(this.clientInfo?.redirectUri) + ) { + wantsScopeThatHasKeys = true; + } else { + throw new Error('Invalid redirect parameter'); + } + } + }); + + return wantsScopeThatHasKeys; + } + getRedirectTo(): string { return this.data.redirectTo || ''; } } -function scopeStrToArray(scopes: string) { +export function scopeStrToArray(scopes: string) { const arrScopes = scopes .trim() .split(/\s+|\++/g) diff --git a/packages/fxa-settings/src/models/integrations/relier-interfaces.ts b/packages/fxa-settings/src/models/integrations/relier-interfaces.ts index dd8373646f2..9416c450dfe 100644 --- a/packages/fxa-settings/src/models/integrations/relier-interfaces.ts +++ b/packages/fxa-settings/src/models/integrations/relier-interfaces.ts @@ -127,6 +127,7 @@ export interface RelierCmsInfo { SigninPage?: PageRelierCmsInfo; SigninCachedPage?: PageRelierCmsInfo; + AuthorizePage?: PageRelierCmsInfo; SigninTotpCodePage?: PageRelierCmsInfo; SigninPasswordlessCodePage?: PageRelierCmsInfo; SigninTokenCodePage?: PageRelierCmsInfo; diff --git a/packages/fxa-settings/src/models/integrations/sync-basic-integration.ts b/packages/fxa-settings/src/models/integrations/sync-basic-integration.ts index e943c308970..680178ffd93 100644 --- a/packages/fxa-settings/src/models/integrations/sync-basic-integration.ts +++ b/packages/fxa-settings/src/models/integrations/sync-basic-integration.ts @@ -50,7 +50,7 @@ export class SyncBasicIntegration extends GenericIntegration< } } - wantsKeys() { + requiresKeys() { return true; } diff --git a/packages/fxa-settings/src/pages/Index/interfaces.ts b/packages/fxa-settings/src/pages/Index/interfaces.ts index f6b12e03620..e8528b9b834 100644 --- a/packages/fxa-settings/src/pages/Index/interfaces.ts +++ b/packages/fxa-settings/src/pages/Index/interfaces.ts @@ -13,7 +13,6 @@ export type IndexIntegration = Pick< | 'isSync' | 'getClientId' | 'getService' - | 'wantsKeys' | 'isFirefoxClientServiceRelay' | 'isFirefoxClientServiceSmartWindow' | 'isFirefoxClientServiceVpn' diff --git a/packages/fxa-settings/src/pages/Index/mocks.tsx b/packages/fxa-settings/src/pages/Index/mocks.tsx index bbf484d316c..1a73c4a8744 100644 --- a/packages/fxa-settings/src/pages/Index/mocks.tsx +++ b/packages/fxa-settings/src/pages/Index/mocks.tsx @@ -68,7 +68,6 @@ export function createMockIndexOAuthNativeIntegration({ isSync: () => isSync, getClientId: () => MOCK_CLIENT_ID, getService: () => MOCK_CLIENT_ID, - wantsKeys: () => false, isFirefoxClientServiceRelay: () => isFirefoxClientServiceRelay, isFirefoxClientServiceSmartWindow: () => isFirefoxClientServiceSmartWindow, isFirefoxClientServiceVpn: () => isFirefoxClientServiceVpn, @@ -92,7 +91,6 @@ export function createMockIndexWebIntegration(): IndexIntegration { isSync: () => false, getClientId: () => undefined, getService: () => undefined, - wantsKeys: () => false, isFirefoxClientServiceRelay: () => false, isFirefoxClientServiceSmartWindow: () => false, isFirefoxClientServiceVpn: () => false, @@ -139,7 +137,7 @@ export const Subject = ({ return ( { }} + processEmailSubmission={async () => {}} {...{ prefillEmail, integration, diff --git a/packages/fxa-settings/src/pages/PostVerify/SetPassword/container.test.tsx b/packages/fxa-settings/src/pages/PostVerify/SetPassword/container.test.tsx index 26105fe7800..1cd5ba8a977 100644 --- a/packages/fxa-settings/src/pages/PostVerify/SetPassword/container.test.tsx +++ b/packages/fxa-settings/src/pages/PostVerify/SetPassword/container.test.tsx @@ -147,6 +147,7 @@ function mockSyncDesktopV3Integration() { getService: () => 'sync', getClientId: () => undefined, isSync: () => true, + requiresKeys: () => true, wantsKeys: () => true, data: { service: 'sync' }, isDesktopSync: () => true, @@ -169,7 +170,9 @@ function mockOAuthNativeIntegration( getService: () => 'sync', getClientId: () => undefined, isSync: () => true, + requiresKeys: () => true, wantsKeys: () => true, + getGrantedScopes: () => undefined, data: { service: 'sync' }, isDesktopSync: () => true, isFirefoxClientServiceRelay: () => false, @@ -292,6 +295,7 @@ describe('SetPassword-container', () => { expect(firefox.fxaOAuthLogin).toHaveBeenCalledWith({ action: 'signin', ...MOCK_OAUTH_FLOW_HANDLER_RESPONSE, + scopes: undefined, }); }); diff --git a/packages/fxa-settings/src/pages/Signin/SigninPushCode/mocks.tsx b/packages/fxa-settings/src/pages/Signin/SigninPushCode/mocks.tsx index f6b3b145e70..a5fd5e8c531 100644 --- a/packages/fxa-settings/src/pages/Signin/SigninPushCode/mocks.tsx +++ b/packages/fxa-settings/src/pages/Signin/SigninPushCode/mocks.tsx @@ -50,6 +50,7 @@ export function createMockSyncIntegration() { type: IntegrationType.SyncDesktopV3, getService: () => MozServices.FirefoxSync, isSync: () => true, + requiresKeys: () => true, wantsKeys: () => true, getCmsInfo: () => undefined, data: {}, diff --git a/packages/fxa-settings/src/pages/Signin/SigninTotpCode/mocks.tsx b/packages/fxa-settings/src/pages/Signin/SigninTotpCode/mocks.tsx index fc4a7e060c5..b74fad10790 100644 --- a/packages/fxa-settings/src/pages/Signin/SigninTotpCode/mocks.tsx +++ b/packages/fxa-settings/src/pages/Signin/SigninTotpCode/mocks.tsx @@ -27,14 +27,19 @@ export const mockWebSigninIntegration = { type: IntegrationType.Web, getService: () => MozServices.Default, isSync: () => false, + requiresKeys: () => false, + wantsKeysIfPasswordEntered: () => false, wantsKeys: () => false, + getGrantedScopes: () => undefined, isFirefoxClientServiceRelay: () => false, isFirefoxClientServiceSmartWindow: () => false, isFirefoxClientServiceVpn: () => false, isFirefoxNonSync: () => false, getWebChannelServices: mockGetWebChannelServices(), getCmsInfo: () => undefined, + isFirefoxClient: () => false, isFirefoxMobileClient: () => false, + isFirefoxDesktopClient: () => false, } as SigninIntegration; export const mockOAuthNativeSigninIntegration = ( @@ -47,7 +52,10 @@ export const mockOAuthNativeSigninIntegration = ( type: IntegrationType.OAuthNative, getService: () => (isSync ? MozServices.FirefoxSync : MozServices.Relay), isSync: () => isSync, + requiresKeys: () => false, + wantsKeysIfPasswordEntered: () => false, wantsKeys: () => false, + getGrantedScopes: () => undefined, isFirefoxClientServiceRelay: () => isRelay, isFirefoxClientServiceSmartWindow: () => false, isFirefoxClientServiceVpn: () => false, @@ -59,7 +67,9 @@ export const mockOAuthNativeSigninIntegration = ( }) ), getCmsInfo: () => cmsInfo, + isFirefoxClient: () => true, isFirefoxMobileClient: () => false, + isFirefoxDesktopClient: () => true, } as SigninIntegration; }; diff --git a/packages/fxa-settings/src/pages/Signin/SigninUnblock/mocks.tsx b/packages/fxa-settings/src/pages/Signin/SigninUnblock/mocks.tsx index d50befaccf1..5880d342e86 100644 --- a/packages/fxa-settings/src/pages/Signin/SigninUnblock/mocks.tsx +++ b/packages/fxa-settings/src/pages/Signin/SigninUnblock/mocks.tsx @@ -73,7 +73,10 @@ export function createMockSigninWebSyncIntegration() { isSync: () => true, getService: () => MozServices.FirefoxSync, getClientId: () => undefined, + requiresKeys: () => true, + wantsKeysIfPasswordEntered: () => false, wantsKeys: () => true, + getGrantedScopes: () => undefined, wantsTwoStepAuthentication: () => false, data: new WebIntegrationData(new GenericData({})), isDesktopSync: () => true, @@ -84,6 +87,8 @@ export function createMockSigninWebSyncIntegration() { getWebChannelServices: mockGetWebChannelServices({ isSync: true }), wantsLogin: () => false, getCmsInfo: () => undefined, + isFirefoxClient: () => true, + isFirefoxDesktopClient: () => true, getLegalTerms: () => undefined, }; } diff --git a/packages/fxa-settings/src/pages/Signin/container.tsx b/packages/fxa-settings/src/pages/Signin/container.tsx index 863aadb9990..e55d9a4c73f 100644 --- a/packages/fxa-settings/src/pages/Signin/container.tsx +++ b/packages/fxa-settings/src/pages/Signin/container.tsx @@ -139,14 +139,14 @@ const SigninContainer = ({ serviceName, flowQueryParams, useFxAStatusResult, - isSignedIntoFirefoxDesktop = false, + isSignedIntoFirefox = false, setCurrentSplitLayout, }: { integration: Integration; serviceName: MozServices; flowQueryParams?: QueryParams; useFxAStatusResult: UseFxAStatusResult; - isSignedIntoFirefoxDesktop?: boolean; + isSignedIntoFirefox?: boolean; setCurrentSplitLayout?: (value: boolean) => void; } & RouteComponentProps) => { const config = useConfig(); @@ -714,7 +714,7 @@ const SigninContainer = ({ localizedSuccessBannerDescription, flowQueryParams, useFxAStatusResult, - isSignedIntoFirefoxDesktop, + isSignedIntoFirefox, setCurrentSplitLayout, }} /> diff --git a/packages/fxa-settings/src/pages/Signin/index.stories.tsx b/packages/fxa-settings/src/pages/Signin/index.stories.tsx index 46b4884479f..cff536ea46c 100644 --- a/packages/fxa-settings/src/pages/Signin/index.stories.tsx +++ b/packages/fxa-settings/src/pages/Signin/index.stories.tsx @@ -4,7 +4,7 @@ import React from 'react'; import Signin from '.'; -import { Meta } from '@storybook/react'; +import { Meta, StoryObj } from '@storybook/react'; import { Subject, createMockSigninOAuthIntegration, @@ -20,144 +20,146 @@ import { BeginSigninError } from '../../lib/error-utils'; import { MozServices } from '../../lib/types'; import { OAuthNativeServices } from '@fxa/accounts/oauth'; -export default { +const meta: Meta = { title: 'Pages/Signin', component: Signin, decorators: [withLocalization], -} as Meta; - -const storyWithProps = ( - props: Partial & { supportsKeysOptionalLogin?: boolean } = {}, - storyName?: string -) => { - const story = () => ; - if (storyName) story.storyName = storyName; - return story; }; +export default meta; -export const NonCachedAccountHasPasswordSettingsOrRP = storyWithProps( - {}, - 'Non-Cached > Account has password > Settings or Relying Party' -); +type Story = StoryObj< + Partial & { supportsKeysOptionalLogin?: boolean } +>; -export const NonCachedAccountHasPasswordSettingsAccountLockedError = - storyWithProps( - { - beginSigninHandler: () => { - return Promise.resolve({ - error: AuthUiErrors.ACCOUNT_RESET as BeginSigninError, - }); - }, - }, - 'Non-Cached > Account has password > Settings, account locked error (click sign in)' - ); +const story = ( + props: Partial & { supportsKeysOptionalLogin?: boolean } = {} +): Story => ({ + render: () => , +}); -export const NonCachedPasswordlessAccountSettingsOrRelyingParty = - storyWithProps( - { - hasLinkedAccount: true, - hasPassword: false, - }, - 'Non-Cached > Passwordless account > Settings or Relying Party' - ); +export const NonCachedAccountHasPasswordSettingsOrRP: Story = { + ...story(), + name: 'Non-Cached > Account has password > Settings or Relying Party', +}; -export const NonCachedSyncBrowserServiceAccountHasPassword = storyWithProps( - { +export const NonCachedAccountHasPasswordSettingsAccountLockedError: Story = { + ...story({ + beginSigninHandler: () => + Promise.resolve({ + error: AuthUiErrors.ACCOUNT_RESET as BeginSigninError, + }), + }), + name: 'Non-Cached > Account has password > Settings, account locked error (click sign in)', +}; + +export const NonCachedPasswordlessAccountSettingsOrRelyingParty: Story = { + ...story({ + hasLinkedAccount: true, + hasPassword: false, + }), + name: 'Non-Cached > Passwordless account > Settings or Relying Party', +}; + +export const NonCachedSyncBrowserServiceAccountHasPassword: Story = { + ...story({ serviceName: MozServices.FirefoxSync, hasLinkedAccount: true, hasPassword: true, integration: createMockSigninOAuthNativeSyncIntegration(), - }, - 'Non-Cached > Sync browser service > Account has password' -); + }), + name: 'Non-Cached > Sync browser service > Account has password', +}; -export const NonCachedSyncBrowserServicePasswordlessAccount = storyWithProps( - { +export const NonCachedSyncBrowserServicePasswordlessAccount: Story = { + ...story({ serviceName: MozServices.FirefoxSync, hasLinkedAccount: true, hasPassword: false, integration: createMockSigninOAuthNativeSyncIntegration(), - }, - 'Non-Cached > Sync browser service > Passwordless account (user will be taken to Set Password page)' -); + }), + name: 'Non-Cached > Sync browser service > Passwordless account (user will be taken to Set Password page)', +}; -export const NonCachedNonSyncBrowserServiceBrowserHasPasswordlessCapability = - storyWithProps( - { +export const NonCachedNonSyncBrowserServiceBrowserHasPasswordlessCapability: Story = + { + ...story({ serviceName: MozServices.SmartWindow, integration: createMockSigninOAuthNativeIntegration({ service: OAuthNativeServices.SmartWindow, isSync: false, }), supportsKeysOptionalLogin: true, - }, - 'Non-Cached > Non-Sync browser service > Browser has Sync keys optional capability' - ); + }), + name: 'Non-Cached > Non-Sync browser service > Browser has Sync keys optional capability', + }; -export const NonCachedNonSyncBrowserServiceBrowserDoesNotHavePasswordlessCapability = - storyWithProps( - { +export const NonCachedNonSyncBrowserServiceBrowserDoesNotHavePasswordlessCapability: Story = + { + ...story({ serviceName: MozServices.Relay, integration: createMockSigninOAuthNativeIntegration({ service: OAuthNativeServices.Relay, isSync: false, }), supportsKeysOptionalLogin: false, - }, - 'Non-Cached > Non-Sync browser service > Browser does not have Sync keys optional capability' - ); + }), + name: 'Non-Cached > Non-Sync browser service > Browser does not have Sync keys optional capability', + }; -export const CachedAccountHasPasswordSettings = storyWithProps( - { +export const CachedAccountHasPasswordSettings: Story = { + ...story({ sessionToken: MOCK_SESSION_TOKEN, - }, - 'Cached > Account has password > Settings' -); + }), + name: 'Cached > Account has password > Settings', +}; -export const CachedAccountHasPasswordRelyingParty = storyWithProps( - { +export const CachedAccountHasPasswordRelyingParty: Story = { + ...story({ sessionToken: MOCK_SESSION_TOKEN, serviceName: MOCK_SERVICE, hasPassword: false, integration: createMockSigninOAuthIntegration({ service: MOCK_SERVICE, }), - }, - 'Cached > Passwordless account > Relying Party' -); -export const CachedSyncBrowserService = storyWithProps( - { + }), + name: 'Cached > Passwordless account > Relying Party', +}; + +export const CachedSyncBrowserService: Story = { + ...story({ sessionToken: MOCK_SESSION_TOKEN, integration: createMockSigninOAuthNativeSyncIntegration(), - }, - 'Cached > Sync browser service > Account has password' -); -export const CachedSyncBrowserServicePasswordlessAccount = storyWithProps( - { + }), + name: 'Cached > Sync browser service > Account has password', +}; + +export const CachedSyncBrowserServicePasswordlessAccount: Story = { + ...story({ sessionToken: MOCK_SESSION_TOKEN, serviceName: MozServices.FirefoxSync, hasLinkedAccount: true, hasPassword: false, integration: createMockSigninOAuthNativeSyncIntegration(), - }, - 'Cached > Sync browser service > Passwordless account (user will be taken to Set Password page)' -); -export const CachedNonSyncBrowserServiceWithoutPasswordlessCapability = - storyWithProps( - { - sessionToken: MOCK_SESSION_TOKEN, - serviceName: MozServices.SmartWindow, - integration: createMockSigninOAuthNativeIntegration({ - service: OAuthNativeServices.SmartWindow, - isSync: false, - }), - supportsKeysOptionalLogin: false, - }, - 'Cached > Non-Sync browser service > Browser does not have Sync keys optional capability' - ); -export const CachedNonSyncBrowserServiceWithPasswordlessCapabilitySignedIntoDesktop = - storyWithProps( - { + }), + name: 'Cached > Sync browser service > Passwordless account (user will be taken to Set Password page)', +}; + +export const CachedNonSyncBrowserServiceWithoutPasswordlessCapability: Story = { + ...story({ + sessionToken: MOCK_SESSION_TOKEN, + serviceName: MozServices.SmartWindow, + integration: createMockSigninOAuthNativeIntegration({ + service: OAuthNativeServices.SmartWindow, + isSync: false, + }), + supportsKeysOptionalLogin: false, + }), + name: 'Cached > Non-Sync browser service > Browser does not have Sync keys optional capability', +}; + +export const CachedNonSyncBrowserServiceWithPasswordlessCapabilitySignedIntoDesktop: Story = + { + ...story({ sessionToken: MOCK_SESSION_TOKEN, serviceName: MozServices.SmartWindow, integration: createMockSigninOAuthNativeIntegration({ @@ -165,13 +167,14 @@ export const CachedNonSyncBrowserServiceWithPasswordlessCapabilitySignedIntoDesk isSync: false, }), supportsKeysOptionalLogin: true, - isSignedIntoFirefoxDesktop: true, - }, - 'Cached > Non-Sync browser service > Browser has Sync keys optional capability > Account is signed into Firefox Desktop' - ); -export const CachedNonSyncBrowserServiceWithPasswordlessCapabilityNotSignedIntoDesktop = - storyWithProps( - { + isSignedIntoFirefox: true, + }), + name: 'Cached > Non-Sync browser service > Browser has Sync keys optional capability > Account is signed into Firefox Desktop', + }; + +export const CachedNonSyncBrowserServiceWithPasswordlessCapabilityNotSignedIntoDesktop: Story = + { + ...story({ sessionToken: MOCK_SESSION_TOKEN, serviceName: MozServices.SmartWindow, hasLinkedAccount: true, @@ -181,20 +184,21 @@ export const CachedNonSyncBrowserServiceWithPasswordlessCapabilityNotSignedIntoD isSync: false, }), supportsKeysOptionalLogin: true, - }, - 'Cached > Non-Sync browser service > Browser has Sync keys optional capability > Account is not signed into Firefox Desktop' - ); + }), + name: 'Cached > Non-Sync browser service > Browser has Sync keys optional capability > Account is not signed into Firefox Desktop', + }; -export const CmsNonCachedDefault = storyWithProps( - { +export const CmsNonCachedDefault: Story = { + ...story({ integration: createMockSigninOAuthIntegration({ cmsInfo: MOCK_CMS_INFO, }), - }, - 'CMS > Regular layout > Non-Cached' -); -export const CmsNonCachedSplitLayout = storyWithProps( - { + }), + name: 'CMS > Regular layout > Non-Cached', +}; + +export const CmsNonCachedSplitLayout: Story = { + ...story({ integration: createMockSigninOAuthIntegration({ cmsInfo: { ...MOCK_CMS_INFO, @@ -204,11 +208,12 @@ export const CmsNonCachedSplitLayout = storyWithProps( }, }, }), - }, - 'CMS > Split layout > Non-Cached' -); -export const CmsCachedSplitLayout = storyWithProps( - { + }), + name: 'CMS > Split layout > Non-Cached', +}; + +export const CmsCachedSplitLayout: Story = { + ...story({ sessionToken: MOCK_SESSION_TOKEN, integration: createMockSigninOAuthIntegration({ cmsInfo: { @@ -219,11 +224,12 @@ export const CmsCachedSplitLayout = storyWithProps( }, }, }), - }, - 'CMS > Split layout > Cached' -); -export const CmsCachedCachedPage = storyWithProps( - { + }), + name: 'CMS > Split layout > Cached', +}; + +export const CmsCachedCachedPage: Story = { + ...story({ sessionToken: MOCK_SESSION_TOKEN, integration: createMockSigninOAuthIntegration({ cmsInfo: { @@ -236,11 +242,45 @@ export const CmsCachedCachedPage = storyWithProps( }, }, }), - }, - 'CMS > Regular layout > Cached' -); -export const CmsCachedNoCachedPageConfig = storyWithProps( - { + }), + name: 'CMS > Regular layout > Cached', +}; + +export const AuthorizationDefault: Story = { + ...story({ + sessionToken: MOCK_SESSION_TOKEN, + isSignedIntoFirefox: true, + integration: createMockSigninOAuthNativeIntegration({ + service: OAuthNativeServices.Vpn, + isSync: false, + }), + }), + name: 'Authorization flow > Default', +}; + +export const AuthorizationWithCmsOverrides: Story = { + ...story({ + sessionToken: MOCK_SESSION_TOKEN, + isSignedIntoFirefox: true, + integration: createMockSigninOAuthNativeIntegration({ + service: OAuthNativeServices.Vpn, + isSync: false, + cmsInfo: { + ...MOCK_CMS_INFO, + AuthorizePage: { + headline: 'Authorize Mozilla VPN', + description: 'Grant access to Mozilla VPN', + primaryButtonText: 'Authorize', + pageTitle: 'Authorize Mozilla VPN', + }, + }, + }), + }), + name: 'Authorization flow > With CMS overrides', +}; + +export const CmsCachedNoCachedPageConfig: Story = { + ...story({ sessionToken: MOCK_SESSION_TOKEN, integration: createMockSigninOAuthIntegration({ cmsInfo: { @@ -254,6 +294,6 @@ export const CmsCachedNoCachedPageConfig = storyWithProps( }, }, }), - }, - 'CMS > Regular layout > Cached > No SigninCachedPage config' -); + }), + name: 'CMS > Regular layout > Cached > No SigninCachedPage config', +}; diff --git a/packages/fxa-settings/src/pages/Signin/index.test.tsx b/packages/fxa-settings/src/pages/Signin/index.test.tsx index 2bd2cb7f7a4..4508a3d8f70 100644 --- a/packages/fxa-settings/src/pages/Signin/index.test.tsx +++ b/packages/fxa-settings/src/pages/Signin/index.test.tsx @@ -253,7 +253,7 @@ describe('Signin component', () => { it('does not render third party auth for sync, emits expected Glean event', async () => { const hardNavigateSpy = jest .spyOn(utils, 'hardNavigate') - .mockImplementation(() => { }); + .mockImplementation(() => {}); const integration = createMockSigninOAuthNativeSyncIntegration(); render({ integration }); @@ -622,7 +622,7 @@ describe('Signin component', () => { fxaLoginSpy = jest.spyOn(firefox, 'fxaLogin'); hardNavigateSpy = jest .spyOn(utils, 'hardNavigate') - .mockImplementation(() => { }); + .mockImplementation(() => {}); }); it('is sent if Sync integration and navigates to CAD', async () => { const beginSigninHandler = jest.fn().mockReturnValueOnce( @@ -688,7 +688,7 @@ describe('Signin component', () => { fxaLoginSpy = jest.spyOn(firefox, 'fxaLogin'); hardNavigateSpy = jest .spyOn(utils, 'hardNavigate') - .mockImplementation(() => { }); + .mockImplementation(() => {}); finishOAuthFlowHandler = jest .fn() .mockReturnValueOnce(MOCK_OAUTH_FLOW_HANDLER_RESPONSE); @@ -1069,6 +1069,7 @@ describe('Signin component', () => { integration, sessionToken: MOCK_SESSION_TOKEN, supportsKeysOptionalLogin: false, + isSignedIntoFirefox: false, }); passwordInputRendered(); @@ -1126,7 +1127,7 @@ describe('Signin component', () => { beforeEach(() => { hardNavigateSpy = jest .spyOn(utils, 'hardNavigate') - .mockImplementation(() => { }); + .mockImplementation(() => {}); }); afterEach(() => { @@ -1183,10 +1184,9 @@ describe('Signin component', () => { }); it('allows navigation for cached Sync passwordless user on mobile', async () => { - const ensureCanLinkSpy = jest.spyOn( - SigninUtils, - 'ensureCanLinkAcountOrRedirect' - ).mockResolvedValue(true); + const ensureCanLinkSpy = jest + .spyOn(SigninUtils, 'ensureCanLinkAcountOrRedirect') + .mockResolvedValue(true); const handleNavigationSpy = jest.spyOn( SigninUtils, 'handleNavigation' @@ -1218,10 +1218,9 @@ describe('Signin component', () => { }); it('shows merge warning for cached Sync passwordless signin when user accepts', async () => { - const ensureCanLinkSpy = jest.spyOn( - SigninUtils, - 'ensureCanLinkAcountOrRedirect' - ).mockResolvedValue(true); + const ensureCanLinkSpy = jest + .spyOn(SigninUtils, 'ensureCanLinkAcountOrRedirect') + .mockResolvedValue(true); const handleNavigationSpy = jest.spyOn( SigninUtils, 'handleNavigation' @@ -1258,10 +1257,9 @@ describe('Signin component', () => { }); it('aborts cached Sync passwordless signin when user rejects merge', async () => { - const ensureCanLinkSpy = jest.spyOn( - SigninUtils, - 'ensureCanLinkAcountOrRedirect' - ).mockResolvedValue(false); + const ensureCanLinkSpy = jest + .spyOn(SigninUtils, 'ensureCanLinkAcountOrRedirect') + .mockResolvedValue(false); const handleNavigationSpy = jest.spyOn( SigninUtils, 'handleNavigation' @@ -1810,11 +1808,32 @@ describe('Signin component', () => { ).not.toBeInTheDocument(); }); - it('hides "use a different account" link for Firefox Desktop users', () => { + it('hides "use a different account" link in authorization flow (desktop)', () => { + const integration = createMockSigninOAuthNativeSyncIntegration(); + renderWithLocalizationProvider( + + ); + + expect( + screen.queryByRole('link', { name: 'Use a different account' }) + ).not.toBeInTheDocument(); + }); + + it('hides "use a different account" link in authorization flow (mobile)', () => { + const integration = createMockSigninOAuthNativeIntegration({ + service: OAuthNativeServices.Vpn, + isSync: false, + isMobile: true, + }); renderWithLocalizationProvider( ); @@ -1823,7 +1842,7 @@ describe('Signin component', () => { ).not.toBeInTheDocument(); }); - it('shows "use a different account" link for non-Firefox Desktop users', () => { + it('shows "use a different account" link outside authorization flow', () => { renderWithLocalizationProvider( ); @@ -1866,7 +1885,7 @@ describe('Signin component', () => { beforeEach(() => { hardNavigateSpy = jest .spyOn(utils, 'hardNavigate') - .mockImplementation(() => { }); + .mockImplementation(() => {}); }); afterEach(() => { hardNavigateSpy.mockRestore(); @@ -2016,22 +2035,32 @@ describe('Signin component', () => { expect(additionalInfo).toBeInTheDocument(); }); - describe('SigninCachedPage CMS config', () => { - const cachedCmsProps = { - cmsInfo: { - ...MOCK_CMS_INFO, - SigninCachedPage: { - headline: 'Welcome back', - description: 'Continue to your Mozilla account', - primaryButtonText: 'Continue', - pageTitle: 'Welcome back', - }, + describe('activePageCms resolution', () => { + const cachedCmsInfo: RelierCmsInfo = { + ...MOCK_CMS_INFO, + SigninCachedPage: { + headline: 'Welcome back', + description: 'Continue to your Mozilla account', + primaryButtonText: 'Continue', + pageTitle: 'Welcome back', + }, + }; + + const authorizeCmsInfo: RelierCmsInfo = { + ...cachedCmsInfo, + AuthorizePage: { + headline: 'Authorize VPN', + description: 'Grant access to Mozilla VPN', + primaryButtonText: 'Authorize', + pageTitle: 'Authorize VPN', }, }; - it('renders cached CMS headline and description for cached user', () => { + it('cached user sees SigninCachedPage CMS content', () => { render({ - integration: createMockSigninWebIntegration(cachedCmsProps), + integration: createMockSigninWebIntegration({ + cmsInfo: cachedCmsInfo, + }), sessionToken: MOCK_SESSION_TOKEN, hasPassword: true, }); @@ -2044,9 +2073,11 @@ describe('Signin component', () => { ).toBeInTheDocument(); }); - it('renders cached CMS button text for cached user', () => { + it('cached user sees SigninCachedPage button text', () => { render({ - integration: createMockSigninWebIntegration(cachedCmsProps), + integration: createMockSigninWebIntegration({ + cmsInfo: cachedCmsInfo, + }), sessionToken: MOCK_SESSION_TOKEN, hasPassword: true, }); @@ -2056,41 +2087,115 @@ describe('Signin component', () => { ).toBeInTheDocument(); }); - it('falls back to headingText when SigninCachedPage is not set', () => { + it('cached user falls back to default when SigninCachedPage is not set', () => { render({ integration: createMockSigninWebIntegration(cmsProps), sessionToken: MOCK_SESSION_TOKEN, hasPassword: true, }); - // Cached users should not see SigninPage CMS headline expect( screen.queryByRole('heading', { name: cmsProps.cmsInfo.SigninPage.headline, }) ).not.toBeInTheDocument(); - // Should fall back to default headingText expect( screen.getByRole('heading', { name: 'Sign in' }) ).toBeInTheDocument(); }); - it('uses SigninPage CMS config for non-cached user even when SigninCachedPage is set', () => { + it('non-cached user uses SigninPage CMS even when SigninCachedPage is set', () => { render({ - integration: createMockSigninWebIntegration(cachedCmsProps), + integration: createMockSigninWebIntegration({ + cmsInfo: cachedCmsInfo, + }), hasPassword: true, sessionToken: undefined, }); expect( screen.getByRole('heading', { - name: cachedCmsProps.cmsInfo.SigninPage.headline, + name: cachedCmsInfo.SigninPage?.headline, }) ).toBeInTheDocument(); expect( screen.queryByRole('heading', { name: 'Welcome back' }) ).not.toBeInTheDocument(); }); + + it('authorization flow uses AuthorizePage CMS content', () => { + render({ + integration: createMockSigninOAuthNativeIntegration({ + service: OAuthNativeServices.Vpn, + isSync: false, + cmsInfo: authorizeCmsInfo, + }), + isSignedIntoFirefox: true, + sessionToken: MOCK_SESSION_TOKEN, + hasPassword: true, + }); + + expect( + screen.getByRole('heading', { name: 'Authorize VPN' }) + ).toBeInTheDocument(); + expect( + screen.getByText('Grant access to Mozilla VPN') + ).toBeInTheDocument(); + expect( + screen.queryByRole('heading', { name: 'Welcome back' }) + ).not.toBeInTheDocument(); + }); + + it('authorization flow falls back to SigninCachedPage when no AuthorizePage', () => { + render({ + integration: createMockSigninOAuthNativeIntegration({ + service: OAuthNativeServices.Vpn, + isSync: false, + cmsInfo: cachedCmsInfo, + }), + isSignedIntoFirefox: true, + sessionToken: MOCK_SESSION_TOKEN, + hasPassword: true, + }); + + expect( + screen.getByRole('heading', { name: 'Welcome back' }) + ).toBeInTheDocument(); + }); + + it('authorization flow falls back to default when neither page is set', () => { + render({ + integration: createMockSigninOAuthNativeIntegration({ + service: OAuthNativeServices.Vpn, + isSync: false, + }), + isSignedIntoFirefox: true, + sessionToken: MOCK_SESSION_TOKEN, + hasPassword: true, + }); + + expect( + screen.getByRole('heading', { name: 'Sign in' }) + ).toBeInTheDocument(); + }); + + it('non-authorization flow ignores AuthorizePage', () => { + render({ + integration: createMockSigninWebIntegration({ + cmsInfo: authorizeCmsInfo, + }), + isSignedIntoFirefox: false, + sessionToken: MOCK_SESSION_TOKEN, + hasPassword: true, + }); + + expect( + screen.queryByRole('heading', { name: 'Authorize VPN' }) + ).not.toBeInTheDocument(); + expect( + screen.getByRole('heading', { name: 'Welcome back' }) + ).toBeInTheDocument(); + }); }); }); }); diff --git a/packages/fxa-settings/src/pages/Signin/index.tsx b/packages/fxa-settings/src/pages/Signin/index.tsx index b23ab697bdc..9bdf51a6b46 100644 --- a/packages/fxa-settings/src/pages/Signin/index.tsx +++ b/packages/fxa-settings/src/pages/Signin/index.tsx @@ -25,6 +25,7 @@ import { isWebIntegration, isOAuthIntegration, isOAuthNativeIntegration, + isOAuthWebIntegration, useConfig, } from '../../models'; import { SigninFormData, SigninProps } from './interfaces'; @@ -58,7 +59,7 @@ const Signin = ({ localizedSuccessBannerDescription, flowQueryParams, useFxAStatusResult: { supportsKeysOptionalLogin }, - isSignedIntoFirefoxDesktop = false, + isSignedIntoFirefox = false, setCurrentSplitLayout, }: SigninProps & RouteComponentProps) => { const config = useConfig(); @@ -90,31 +91,54 @@ const Signin = ({ const legalTerms = integration.getLegalTerms(); + // The user is in an authorization flow when they're signed into Firefox, + // it's a Firefox client (desktop or mobile), and a specific service is requested. + const isAuthorizationFlow = + isSignedIntoFirefox && + integration.isFirefoxClient() && + !!integration.getService(); + const isServiceWithEmailVerification = !!clientId && config.servicesWithEmailVerification.includes(clientId); const [hasCachedAccount, setHasCachedAccount] = useState(!!sessionToken); - // A password is needed and password input rendered if the user has a password AND: - // * There is no session token in local storage, OR - // * The integration wants keys (e.g. Sync always wants keys, and non-sync browser - // services like Smart Window also request keys if there's no cached sign-in - // to prevent a redirect to FxA to turn Sync on), OR - // * The integration is OAuth and wants login (prompt=login) - // UNLESS the user has a cached account AND they are in an OAuth Native flow AND - // the browser supports the "keys optional" capability for non-Sync browser signins. - // These users will be redirected to FxA later to enter a password to turn Sync on. + // Relay browser service login launched in Firefox desktop 135, and the "keys optional" + // capability (Sync decoupling) launched in Fx desktop 147, meaning all Relay service users + // in those Fx versions require a password. This also covers Mobile until Sync has been + // decoupled. If the user is already signed into Firefox (authorization flow), they've + // already entered their password — skip it. + const syncNotDecoupledRequiresPassword = + !supportsKeysOptionalLogin && + !isSignedIntoFirefox && + integration.wantsKeysIfPasswordEntered(); + + // Redirect-based RPs (OAuthWeb) that request scoped keys always need a password for key + // derivation. In practice today, we don't have RPs that need this, but we do support it. + const redirectRpRequiresKeys = + isOAuthWebIntegration(integration) && integration.wantsKeys(); + + const passwordNeeded = + !hasCachedAccount || + integration.requiresKeys() || + syncNotDecoupledRequiresPassword || + redirectRpRequiresKeys || + // The password is forced when the RP requests prompt=login + (isOAuth && integration.wantsLogin()); + + // Do we have a session token, and can we defer the key fetch? + const keysOptional = hasCachedAccount && supportsKeysOptionalLogin; + + // Determine whether to show the password input. Keys always require a + // password for derivation, but we can skip it when: + // - The user is already signed into Firefox (authorization flow) AND the + // service doesn't require keys (Sync always requires them), OR + // - The browser supports "keys optional" (Sync decoupled from other services) // - // If the user has no password (e.g. third-party auth only), we show the cached - // sign-in view instead. After session verification, Sync users will be redirected - // to set a password via post_verify/third_party_auth/set_password. - const isPasswordNeeded = - hasPassword && - (!hasCachedAccount || - integration.wantsKeys() || - (isOAuth && integration.wantsLogin())) && - !(hasCachedAccount && supportsKeysOptionalLogin); + // Passwordless users always see cached sign-in and are redirected to set a + // password after signing in, if a password is required (e.g. for Sync). + const showPasswordInput = hasPassword && passwordNeeded && !keysOptional; const localizedPasswordFormLabel = ftlMsgResolver.getMsg( 'signin-password-button-label', @@ -145,7 +169,7 @@ const Signin = ({ (isSync ? hasPassword : isOAuthNative && !supportsKeysOptionalLogin); useEffect(() => { - if (!isPasswordNeeded) { + if (!showPasswordInput) { GleanMetrics.cachedLogin.view({ event: { thirdPartyLinks: !hideThirdPartyAuth }, }); @@ -154,7 +178,7 @@ const Signin = ({ event: { thirdPartyLinks: !hideThirdPartyAuth }, }); } - }, [isPasswordNeeded, hideThirdPartyAuth]); + }, [showPasswordInput, hideThirdPartyAuth]); const signInWithCachedAccount = useCallback( async (sessionToken: hexstring) => { @@ -168,7 +192,11 @@ const Signin = ({ // Sync merge check for cached signin // Pattern matches SigninPasswordlessCode (line 201-211) - if ((integration.isSync() || integration.isFirefoxNonSync()) && !hasPassword && !hasLinkedAccount) { + if ( + (integration.isSync() || integration.isFirefoxNonSync()) && + !hasPassword && + !hasLinkedAccount + ) { const canLink = await ensureCanLinkAcountOrRedirect({ email, uid: data.uid, @@ -203,7 +231,8 @@ const Signin = ({ // to set_password within the webview, even on mobile clients. No webchannel // messages are sent (deferred until after password creation), so the webview // must handle navigation internally. - performNavigation: (isSync && !hasPassword) || !integration.isFirefoxMobileClient(), + performNavigation: + (isSync && !hasPassword) || !integration.isFirefoxMobileClient(), isServiceWithEmailVerification, // Sync users in the cached path are passwordless (third-party auth or OTP); // defer web channel messages until after password creation. @@ -212,7 +241,6 @@ const Signin = ({ // Redirect passwordless Sync users to set_password after session verification. isSignInWithThirdPartyAuth: isSync, }; - const { error: navError } = await handleNavigation(navigationOptions); if (navError) { setLocalizedBannerError( @@ -386,19 +414,19 @@ const Signin = ({ const onSubmit = useCallback( async ({ password }: { password: string }) => { - if (isPasswordNeeded && password === '') { + if (showPasswordInput && password === '') { setPasswordTooltipErrorText(localizedValidPasswordError); return; } - !isPasswordNeeded && sessionToken + !showPasswordInput && sessionToken ? signInWithCachedAccount(sessionToken) : signInWithPassword(password); }, [ signInWithCachedAccount, signInWithPassword, - isPasswordNeeded, + showPasswordInput, localizedValidPasswordError, sessionToken, ] @@ -407,7 +435,12 @@ const Signin = ({ const cmsInfo = integration.getCmsInfo(); const cachedPageCms = cmsInfo?.SigninCachedPage; const signinPageCms = cmsInfo?.SigninPage; - const activePageCms = isPasswordNeeded ? signinPageCms : cachedPageCms; + const authorizePageCms = cmsInfo?.AuthorizePage; + const activePageCms = showPasswordInput + ? signinPageCms + : isAuthorizationFlow + ? authorizePageCms || cachedPageCms + : cachedPageCms; const title = activePageCms?.pageTitle; // If cachedPageCms is the active page but does not have a CMS entry, // we reference the splitLayout property from the signinPageCms. @@ -428,7 +461,7 @@ const Signin = ({ }} /> )} - {isPasswordNeeded ? ( + {showPasswordInput ? ( - {isPasswordNeeded && ( + {showPasswordInput && (
- {!isSignedIntoFirefoxDesktop && ( + {/* Hide the account change link in the authorization flow — the user is + * already signed into Firefox and can't switch accounts in this context */} + {!isAuthorizationFlow && ( )} - {isPasswordNeeded && !hasLinkedAccountAndNoPassword && ( + {showPasswordInput && !hasLinkedAccountAndNoPassword && ( - !isPasswordNeeded + !showPasswordInput ? GleanMetrics.cachedLogin.forgotPassword() : GleanMetrics.login.forgotPassword() } diff --git a/packages/fxa-settings/src/pages/Signin/interfaces.ts b/packages/fxa-settings/src/pages/Signin/interfaces.ts index 5ddf45b63f4..11eff6b0192 100644 --- a/packages/fxa-settings/src/pages/Signin/interfaces.ts +++ b/packages/fxa-settings/src/pages/Signin/interfaces.ts @@ -27,7 +27,10 @@ export type SigninUnblockIntegration = Pick< | 'getClientId' | 'wantsTwoStepAuthentication' | 'clientInfo' + | 'requiresKeys' + | 'wantsKeysIfPasswordEntered' | 'wantsKeys' + | 'getGrantedScopes' | 'data' | 'isDesktopSync' | 'isFirefoxClientServiceRelay' @@ -37,7 +40,9 @@ export type SigninUnblockIntegration = Pick< | 'getWebChannelServices' | 'wantsLogin' | 'getCmsInfo' + | 'isFirefoxClient' | 'isFirefoxMobileClient' + | 'isFirefoxDesktopClient' | 'getLegalTerms' >; @@ -48,7 +53,10 @@ export type SigninIntegration = | 'isSync' | 'getService' | 'getClientId' + | 'requiresKeys' + | 'wantsKeysIfPasswordEntered' | 'wantsKeys' + | 'getGrantedScopes' | 'data' | 'isDesktopSync' | 'isFirefoxClientServiceRelay' @@ -57,7 +65,9 @@ export type SigninIntegration = | 'isFirefoxNonSync' | 'getWebChannelServices' | 'getCmsInfo' + | 'isFirefoxClient' | 'isFirefoxMobileClient' + | 'isFirefoxDesktopClient' | 'getLegalTerms' > | SigninOAuthIntegration; @@ -69,7 +79,10 @@ export type SigninOAuthIntegration = Pick< | 'getService' | 'getClientId' | 'wantsTwoStepAuthentication' + | 'requiresKeys' + | 'wantsKeysIfPasswordEntered' | 'wantsKeys' + | 'getGrantedScopes' | 'wantsLogin' | 'data' | 'isDesktopSync' @@ -79,7 +92,9 @@ export type SigninOAuthIntegration = Pick< | 'isFirefoxNonSync' | 'getWebChannelServices' | 'getCmsInfo' + | 'isFirefoxClient' | 'isFirefoxMobileClient' + | 'isFirefoxDesktopClient' | 'getLegalTerms' >; @@ -115,7 +130,7 @@ export interface SigninProps { localizedSuccessBannerDescription?: string; flowQueryParams?: QueryParams; useFxAStatusResult: UseFxAStatusResult; - isSignedIntoFirefoxDesktop?: boolean; + isSignedIntoFirefox?: boolean; setCurrentSplitLayout?: (value: boolean) => void; } diff --git a/packages/fxa-settings/src/pages/Signin/mocks.tsx b/packages/fxa-settings/src/pages/Signin/mocks.tsx index d049003581a..4cc153a9d25 100644 --- a/packages/fxa-settings/src/pages/Signin/mocks.tsx +++ b/packages/fxa-settings/src/pages/Signin/mocks.tsx @@ -96,7 +96,10 @@ export function createMockSigninWebIntegration({ isSync: () => false, getService: () => MozServices.Default, getClientId: () => undefined, + requiresKeys: () => false, + wantsKeysIfPasswordEntered: () => false, wantsKeys: () => false, + getGrantedScopes: () => undefined, data: new IntegrationData(new GenericData({})), isDesktopSync: () => false, isFirefoxClientServiceRelay: () => false, @@ -107,7 +110,9 @@ export function createMockSigninWebIntegration({ wantsLogin: () => false, wantsTwoStepAuthentication: () => false, getCmsInfo: () => cmsInfo, + isFirefoxClient: () => false, isFirefoxMobileClient: () => false, + isFirefoxDesktopClient: () => false, getLegalTerms: () => undefined, }; } @@ -124,7 +129,10 @@ export function createMockSigninOAuthNativeSyncIntegration({ return { type, isSync: () => isSync, + requiresKeys: () => isSync, + wantsKeysIfPasswordEntered: () => !isSync, wantsKeys: () => true, + getGrantedScopes: () => undefined, getService: () => MozServices.FirefoxSync, getClientId: () => MOCK_CLIENT_ID, data: new IntegrationData(new GenericData({})), @@ -137,7 +145,9 @@ export function createMockSigninOAuthNativeSyncIntegration({ wantsLogin: () => false, wantsTwoStepAuthentication: () => false, getCmsInfo: () => undefined, + isFirefoxClient: () => true, isFirefoxMobileClient: () => isSync && isMobile, + isFirefoxDesktopClient: () => isSync && !isMobile, getLegalTerms: () => undefined, }; } @@ -158,7 +168,10 @@ export function createMockSigninOAuthIntegration({ getService: () => service || MozServices.Default, getClientId: () => clientId || MOCK_CLIENT_ID, isSync: () => isSync, + requiresKeys: () => false, + wantsKeysIfPasswordEntered: () => false, wantsKeys: () => false, + getGrantedScopes: () => undefined, wantsLogin: () => false, wantsTwoStepAuthentication: () => false, isDesktopSync: () => isSync, @@ -169,7 +182,9 @@ export function createMockSigninOAuthIntegration({ isFirefoxNonSync: () => false, getWebChannelServices: mockGetWebChannelServices({ isSync }), getCmsInfo: () => cmsInfo, + isFirefoxClient: () => false, isFirefoxMobileClient: () => false, + isFirefoxDesktopClient: () => false, getLegalTerms: () => undefined, }; } @@ -192,7 +207,10 @@ export function createMockSigninOAuthNativeIntegration({ type: IntegrationType.OAuthNative, getService: () => service, isSync: () => isSync, + requiresKeys: () => isSync, + wantsKeysIfPasswordEntered: () => isRelay || isSmartWindow || isVpn, wantsKeys: () => true, + getGrantedScopes: () => undefined, wantsLogin: () => false, wantsTwoStepAuthentication: () => false, isDesktopSync: () => isSync && !isMobile, @@ -209,7 +227,9 @@ export function createMockSigninOAuthNativeIntegration({ }), getClientId: () => MOCK_CLIENT_ID, getCmsInfo: () => cmsInfo, + isFirefoxClient: () => true, isFirefoxMobileClient: () => isSync && isMobile, + isFirefoxDesktopClient: () => !isMobile, getLegalTerms: () => undefined, }; } @@ -330,7 +350,7 @@ export const Subject = ({ cachedSigninHandler = mockCachedSigninHandler, sendUnblockEmailHandler = mockSendUnblockEmailHandler, finishOAuthFlowHandler = mockFinishOAuthFlowHandler, - isSignedIntoFirefoxDesktop = false, + isSignedIntoFirefox = false, supportsKeysOptionalLogin = false, ...props // overrides }: Partial & { @@ -355,7 +375,7 @@ export const Subject = ({ avatarData, avatarLoading, useFxAStatusResult, - isSignedIntoFirefoxDesktop, + isSignedIntoFirefox, ...props, }} /> diff --git a/packages/fxa-settings/src/pages/Signin/utils.test.ts b/packages/fxa-settings/src/pages/Signin/utils.test.ts index 75079a948d0..c4f776d67f4 100644 --- a/packages/fxa-settings/src/pages/Signin/utils.test.ts +++ b/packages/fxa-settings/src/pages/Signin/utils.test.ts @@ -206,7 +206,7 @@ describe('Signin utils', () => { it('navigates to OAuth redirect for successful OAuth flow', async () => { const mockOAuthIntegration = createMockSigninOAuthIntegration(); - (mockOAuthIntegration as any).wantsKeys = jest + (mockOAuthIntegration as any).requiresKeys = jest .fn() .mockReturnValue(false); diff --git a/packages/fxa-settings/src/pages/Signin/utils.ts b/packages/fxa-settings/src/pages/Signin/utils.ts index b642068fea7..1c0ffb47063 100644 --- a/packages/fxa-settings/src/pages/Signin/utils.ts +++ b/packages/fxa-settings/src/pages/Signin/utils.ts @@ -378,6 +378,7 @@ export async function handleNavigation(navigationOptions: NavigationOptions) { code: oauthData.code, redirect: oauthData.redirect, state: oauthData.state, + scopes: integration.getGrantedScopes(), }); } if (navigationOptions.performNavigation !== false) { diff --git a/packages/fxa-settings/src/pages/Signup/ConfirmSignupCode/container.test.tsx b/packages/fxa-settings/src/pages/Signup/ConfirmSignupCode/container.test.tsx index a73b024f760..38365a8ae6b 100644 --- a/packages/fxa-settings/src/pages/Signup/ConfirmSignupCode/container.test.tsx +++ b/packages/fxa-settings/src/pages/Signup/ConfirmSignupCode/container.test.tsx @@ -109,6 +109,7 @@ function applyMocks() { integration = { type: ModelsModule.IntegrationType.OAuthWeb, + requiresKeys: () => false, wantsKeys: () => false, getCmsInfo: () => undefined, } as Integration; @@ -296,6 +297,7 @@ describe('confirm-signup-container', () => { it('renders error component when value is undefined for non-OAuthNative', () => { integration = { type: ModelsModule.IntegrationType.OAuthWeb, + requiresKeys: () => true, wantsKeys: () => true, getCmsInfo: () => undefined, } as Integration; @@ -314,6 +316,7 @@ describe('confirm-signup-container', () => { it('navigates to signin with error when recovery fails for OAuthNative', async () => { integration = { type: ModelsModule.IntegrationType.OAuthNative, + requiresKeys: () => true, wantsKeys: () => true, getCmsInfo: () => undefined, } as Integration; @@ -326,7 +329,9 @@ describe('confirm-signup-container', () => { .mockReturnValue({ isRecovering: false, recoveryFailed: true, - attemptOAuthFlowRecovery: jest.fn().mockResolvedValue({ success: false }), + attemptOAuthFlowRecovery: jest + .fn() + .mockResolvedValue({ success: false }), }); render(); @@ -334,7 +339,8 @@ describe('confirm-signup-container', () => { await waitFor(() => { expect(mockNavigate).toHaveBeenCalledWith('/signin', { state: { - localizedErrorMessage: 'Something went wrong. Please sign in again.', + localizedErrorMessage: + 'Something went wrong. Please sign in again.', }, }); }); diff --git a/packages/fxa-settings/src/pages/Signup/ConfirmSignupCode/interfaces.ts b/packages/fxa-settings/src/pages/Signup/ConfirmSignupCode/interfaces.ts index 75c60f66019..6e4439666e3 100644 --- a/packages/fxa-settings/src/pages/Signup/ConfirmSignupCode/interfaces.ts +++ b/packages/fxa-settings/src/pages/Signup/ConfirmSignupCode/interfaces.ts @@ -50,6 +50,8 @@ export type ConfirmSignupCodeBaseIntegration = Pick< | 'isFirefoxClientServiceVpn' | 'isFirefoxNonSync' | 'isSync' + | 'requiresKeys' + | 'getGrantedScopes' | 'getCmsInfo' >; @@ -62,11 +64,13 @@ export type ConfirmSignupCodeOAuthIntegration = Pick< | 'getRedirectUri' | 'wantsTwoStepAuthentication' | 'isSync' + | 'requiresKeys' | 'getPermissions' | 'isFirefoxClientServiceRelay' | 'isFirefoxClientServiceSmartWindow' | 'isFirefoxClientServiceVpn' | 'isFirefoxNonSync' + | 'getGrantedScopes' | 'getCmsInfo' | 'isFirefoxMobileClient' >; diff --git a/packages/fxa-settings/src/pages/Signup/container.test.tsx b/packages/fxa-settings/src/pages/Signup/container.test.tsx index 92fe5cec68c..c7b172f1525 100644 --- a/packages/fxa-settings/src/pages/Signup/container.test.tsx +++ b/packages/fxa-settings/src/pages/Signup/container.test.tsx @@ -56,6 +56,7 @@ function mockIntegration() { getService: () => MozServices.Default, getClientId: () => undefined, isSync: () => true, + requiresKeys: () => true, wantsKeys: () => true, isFirefoxClientServiceRelay: () => false, isFirefoxClientServiceSmartWindow: () => false, diff --git a/packages/fxa-settings/src/pages/Signup/container.tsx b/packages/fxa-settings/src/pages/Signup/container.tsx index 62f5a5e7f16..24726ca5dd8 100644 --- a/packages/fxa-settings/src/pages/Signup/container.tsx +++ b/packages/fxa-settings/src/pages/Signup/container.tsx @@ -85,6 +85,7 @@ const SignupContainer = ({ queryParamModel.emailStatusChecked || location.state?.emailStatusChecked; const email = queryParamModel.email || location.state?.email; + const requiresKeys = integration.requiresKeys(); const wantsKeys = integration.wantsKeys(); const attemptedEmailStatusCheck = useRef(false); @@ -115,7 +116,7 @@ const SignupContainer = ({ hasPassword, }, }); - } else if (passwordlessSupported && !wantsKeys) { + } else if (passwordlessSupported && !requiresKeys) { // New account can use passwordless signup (non-Sync RPs only) navigateWithQuery('/signin_passwordless_code', { replace: true, diff --git a/packages/fxa-settings/src/pages/Signup/interfaces.ts b/packages/fxa-settings/src/pages/Signup/interfaces.ts index b32ffbaf3b7..dc614dcdb38 100644 --- a/packages/fxa-settings/src/pages/Signup/interfaces.ts +++ b/packages/fxa-settings/src/pages/Signup/interfaces.ts @@ -52,6 +52,7 @@ export type SignupOAuthIntegration = Pick< | 'isFirefoxClientServiceVpn' | 'isFirefoxNonSync' | 'getWebChannelServices' + | 'requiresKeys' | 'wantsKeys' | 'getClientId' | 'getCmsInfo' @@ -68,6 +69,7 @@ export type SignupBaseIntegration = Pick< | 'isFirefoxClientServiceVpn' | 'isFirefoxNonSync' | 'getWebChannelServices' + | 'requiresKeys' | 'wantsKeys' | 'getClientId' | 'getCmsInfo' diff --git a/packages/fxa-settings/src/pages/Signup/mocks.tsx b/packages/fxa-settings/src/pages/Signup/mocks.tsx index 9f92075995c..9f0f3e3aa55 100644 --- a/packages/fxa-settings/src/pages/Signup/mocks.tsx +++ b/packages/fxa-settings/src/pages/Signup/mocks.tsx @@ -37,6 +37,7 @@ export function createMockSignupWebIntegration(): SignupBaseIntegration { isFirefoxClientServiceVpn: () => false, isFirefoxNonSync: () => false, getWebChannelServices: mockGetWebChannelServices(), + requiresKeys: () => false, wantsKeys: () => false, getCmsInfo: () => undefined, getLegalTerms: () => undefined, @@ -54,7 +55,8 @@ export function createMockSignupSyncDesktopV3Integration(): SignupBaseIntegratio isFirefoxClientServiceVpn: () => false, isFirefoxNonSync: () => false, getWebChannelServices: mockGetWebChannelServices({ isSync: true }), - wantsKeys: () => false, + requiresKeys: () => true, + wantsKeys: () => true, getCmsInfo: () => undefined, getLegalTerms: () => undefined, }; @@ -77,6 +79,7 @@ export function createMockSignupOAuthWebIntegration( isFirefoxClientServiceVpn: () => false, isFirefoxNonSync: () => false, getWebChannelServices: mockGetWebChannelServices(), + requiresKeys: () => false, wantsKeys: () => false, getCmsInfo: () => cmsInfo, getLegalTerms: () => undefined, @@ -108,6 +111,7 @@ export function createMockSignupOAuthNativeIntegration( isSmartWindow, isVpn, }), + requiresKeys: () => true, wantsKeys: () => true, getCmsInfo: () => cmsInfo, getLegalTerms: () => undefined,