Skip to content

Commit

Permalink
Merge pull request #18021 from mozilla/fxa-10548-checkout-signinup
Browse files Browse the repository at this point in the history
feat(next): add email, google, apple sign in
  • Loading branch information
StaberindeZA authored Nov 15, 2024
2 parents a5fa00a + 182c00b commit 09096fb
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 50 deletions.
5 changes: 5 additions & 0 deletions apps/payments/next/.env.development
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@
# Auth
AUTH__ISSUER_URL=http://localhost:3030
AUTH__WELL_KNOWN_URL=http://localhost:3030/.well-known/openid-configuration
AUTH__TOKEN_URL=http://localhost:9000/v1/token
AUTH__USERINFO_URL=http://localhost:1111/v1/profile
AUTH__CLIENT_ID=32aaeb6f1c21316a
AUTH__CLIENT_SECRET=024f261551f122335424aee381701615a659b91a6f2017429a5832c231c19f9a

# PayPal
PAYPAL__CLIENT_ID=sb

# NextAuth
AUTH_SECRET=replacewithsecret
AUTH_TRUST_HOST=true

# MySQLConfig
MYSQL_CONFIG__DATABASE=fxa
Expand Down Expand Up @@ -106,6 +110,7 @@ PROFILE_CLIENT_CONFIG__SERVICE_NAME='subhub'
# Other
CONTENT_SERVER_URL=http://localhost:3030
SUPPORT_URL=https://support.mozilla.org
BASE_URL=http://localhost:3035

# Nextjs Public Environment Variables

Expand Down
5 changes: 5 additions & 0 deletions apps/payments/next/.env.production
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@
# Auth
AUTH__ISSUER_URL=http://localhost:3030
AUTH__WELL_KNOWN_URL=http://localhost:3030/.well-known/openid-configuration
AUTH__TOKEN_URL=http://localhost:9000/v1/token
AUTH__USERINFO_URL=http://localhost:1111/v1/profile
AUTH__CLIENT_ID=
AUTH__CLIENT_SECRET=placeholder

# PayPal
PAYPAL__CLIENT_ID=sb

# NextAuth
AUTH_SECRET=placeholder
AUTH_TRUST_HOST=true

# MySQLConfig
MYSQL_CONFIG__DATABASE=fxa
Expand Down Expand Up @@ -102,6 +106,7 @@ PROFILE_CLIENT_CONFIG__SERVICE_NAME='subhub'
# Other
CONTENT_SERVER_URL=https://accounts.firefox.com
SUPPORT_URL=https://support.mozilla.org
BASE_URL=http://localhost:3035

# Nextjs Public Environment Variables

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { headers } from 'next/headers';
import Image from 'next/image';
import {
BaseButton,
buildRedirectUrl,
ButtonVariant,
PaymentSection,
SignInForm,
Expand All @@ -19,12 +20,22 @@ import {
import AppleLogo from '@fxa/shared/assets/images/apple-logo.svg';
import GoogleLogo from '@fxa/shared/assets/images/google-logo.svg';
import { DEFAULT_LOCALE } from '@fxa/shared/l10n';
import { auth } from 'apps/payments/next/auth';
import { fetchCMSData, getCartOrRedirectAction } from '@fxa/payments/ui/actions';
import { auth, signIn } from 'apps/payments/next/auth';
import {
fetchCMSData,
getCartOrRedirectAction,
} from '@fxa/payments/ui/actions';
import { config } from 'apps/payments/next/config';

export const dynamic = 'force-dynamic';

export default async function Checkout({ params }: { params: CheckoutParams }) {
export default async function Checkout({
params,
searchParams,
}: {
params: CheckoutParams;
searchParams: Record<string, string> | undefined;
}) {
// Temporarily defaulting to `accept-language`
// This to be updated in FXA-9404
//const locale = getLocaleFromRequest(
Expand All @@ -46,9 +57,17 @@ export default async function Checkout({ params }: { params: CheckoutParams }) {
cmsDataPromise,
]);

const redirectTo = buildRedirectUrl(
params.offeringId,
params.interval,
'new',
'checkout',
{ baseUrl: config.baseUrl, locale: params.locale, searchParams }
);

return (
<section aria-label="Checkout">
{!session && (
{!session?.user && (
<>
<h2 className="font-semibold text-grey-600 text-lg mt-10">
{l10n.getString(
Expand All @@ -58,6 +77,13 @@ export default async function Checkout({ params }: { params: CheckoutParams }) {
</h2>

<SignInForm
submitAction={async (email?: string) => {
'use server';
const additionalParams = email
? { login_hint: email }
: undefined;
await signIn('fxa', { redirectTo }, additionalParams);
}}
newsletterLabel={cms.commonContent.newsletterLabelTextCode}
/>

Expand All @@ -69,20 +95,38 @@ export default async function Checkout({ params }: { params: CheckoutParams }) {
</h3>

<div className="flex flex-col gap-4 mt-6 mb-10 desktop:flex-row desktop:items-center desktop:justify-center">
<BaseButton variant={ButtonVariant.ThirdParty}>
<Image src={GoogleLogo} alt="" />
{l10n.getString(
'next-continue-with-google-button',
'Continue with Google'
)}
</BaseButton>
<BaseButton variant={ButtonVariant.ThirdParty}>
<Image src={AppleLogo} alt="" />
{l10n.getString(
'next-continue-with-apple-button',
'Continue with Apple'
)}
</BaseButton>
<form
action={async () => {
'use server';
await signIn(
'fxa',
{ redirectTo },
{ deeplink: 'googleLogin' }
);
}}
>
<BaseButton variant={ButtonVariant.ThirdParty}>
<Image src={GoogleLogo} alt="" />
{l10n.getString(
'next-continue-with-google-button',
'Continue with Google'
)}
</BaseButton>
</form>
<form
action={async () => {
'use server';
await signIn('fxa', { redirectTo }, { deeplink: 'appleLogin' });
}}
>
<BaseButton variant={ButtonVariant.ThirdParty}>
<Image src={AppleLogo} alt="" />
{l10n.getString(
'next-continue-with-apple-button',
'Continue with Apple'
)}
</BaseButton>
</form>
</div>

<hr className="mx-auto w-full border-grey-200" />
Expand Down
17 changes: 9 additions & 8 deletions apps/payments/next/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,25 @@ export const {
{
id: 'fxa',
name: 'Firefox Accounts',
type: 'oidc',
type: 'oauth',
issuer: config.auth.issuerUrl,
wellKnown: config.auth.wellKnownUrl,
checks: ['pkce', 'state'],
client: {
token_endpoint_auth_method: 'none',
},
authorization: { params: { scope: 'openid email profile' } },
checks: ['state'],
authorization: { params: { scope: 'email profile' } },
clientId: config.auth.clientId,
clientSecret: config.auth.clientSecret,
token: config.auth.tokenUrl,
userinfo: config.auth.userinfoUrl,
},
],
callbacks: {
async session(params: any) {
params.session.user.id = params.token.fxaUid;
return params.session;
},
async jwt({ token, account }) {
const fxaUid = token.fxaUid || account?.providerAccountId;
async jwt({ token, profile }) {
// Note profile is only defined once after user sign in.
const fxaUid = token.fxaUid || profile?.uid;
return {
...token,
fxaUid,
Expand Down
16 changes: 16 additions & 0 deletions apps/payments/next/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
IsUrl,
IsNumber,
IsOptional,
IsBoolean,
} from 'class-validator';
import {
RootConfig as NestAppRootConfig,
Expand Down Expand Up @@ -46,8 +47,17 @@ class AuthJSConfig {
@IsUrl({ require_tld: false })
wellKnownUrl!: string;

@IsUrl({ require_tld: false })
tokenUrl!: string;

@IsUrl({ require_tld: false })
userinfoUrl!: string;

@IsString()
clientId!: string;

@IsString()
clientSecret!: string;
}

export class PaymentsNextConfig extends NestAppRootConfig {
Expand All @@ -74,12 +84,18 @@ export class PaymentsNextConfig extends NestAppRootConfig {
@IsString()
authSecret!: string;

@IsBoolean()
authTrustHost!: boolean;

@IsString()
stripePublicApiKey!: string;

@IsUrl({ require_tld: false })
contentServerUrl!: string;

@IsUrl({ require_tld: false })
baseUrl!: string;

/**
* Nextjs Public Environment Variables
*/
Expand Down
1 change: 1 addition & 0 deletions libs/payments/ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ export * from './lib/utils/helpers';
export * from './lib/utils/types';
export * from './lib/utils/get-cart';
export * from './lib/utils/poll-cart';
export * from './lib/utils/buildRedirectUrl';
14 changes: 12 additions & 2 deletions libs/payments/ui/src/lib/client/components/SignInForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,23 @@ function getNewsletterStringInfo(newsletterLabelTextCode?: string | null) {
}

interface SignInFormProps {
submitAction: (email?: string) => Promise<void>;
newsletterLabel?: string | null;
}

export const SignInForm = ({ newsletterLabel }: SignInFormProps) => {
export const SignInForm = ({
newsletterLabel,
submitAction,
}: SignInFormProps) => {
const { newsletterStringId, newsletterStringFallbackText } =
getNewsletterStringInfo(newsletterLabel);
return (
<Form.Root aria-label="Sign-in/sign-up form">
<Form.Root
action={async (formData: FormData) =>
submitAction(formData.get('email')?.toString())
}
aria-label="Sign-in/sign-up form"
>
<Form.Field name="email" className="my-6">
<Form.Label className="text-grey-400 block mb-1 text-start">
<Localized id="checkout-enter-your-email">Enter your email</Localized>
Expand All @@ -54,6 +63,7 @@ export const SignInForm = ({ newsletterLabel }: SignInFormProps) => {
<input
className="w-full border rounded-md border-black/30 p-3 placeholder:text-grey-500 placeholder:font-normal focus:border focus:!border-black/30 focus:!shadow-[0_0_0_3px_rgba(10,132,255,0.3)] focus-visible:outline-none data-[invalid=true]:border-alert-red data-[invalid=true]:text-alert-red data-[invalid=true]:shadow-inputError"
type="email"
name="email"
data-testid="email"
required
aria-required
Expand Down
32 changes: 32 additions & 0 deletions libs/payments/ui/src/lib/utils/buildRedirectUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* 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/. */

type Page = 'landing' | 'new' | 'start' | 'success' | 'error';
type PageType = 'checkout' | 'upgrade';

interface Optional {
baseUrl?: string;
locale?: string;
cartId?: string;
searchParams?: Record<string, string>;
}

export function buildRedirectUrl(
offeringId: string,
interval: string,
page: Page,
pageType: PageType,
optional?: Optional
) {
const baseUrl = optional?.baseUrl ? optional?.baseUrl : '';

const startUrl = baseUrl === '/' ? baseUrl : `${baseUrl}/`;
const endUrl = optional?.cartId ? `${optional?.cartId}/${page}` : page;

const searchParamsString = optional?.searchParams
? `?${new URLSearchParams(optional?.searchParams).toString()}`
: '';

return `${startUrl}${optional?.locale}/${offeringId}/${pageType}/${interval}/${endUrl}${searchParamsString}`;
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
"nest-typed-config": "^2.9.2",
"nest-winston": "^1.10.0",
"next": "^14.2.14",
"next-auth": "beta",
"next-auth": "5.0.0-beta.22",
"node-fetch": "^2.6.7",
"node-hkdf": "^0.0.2",
"node-jose": "^2.2.0",
Expand Down
5 changes: 2 additions & 3 deletions packages/fxa-auth-server/config/dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -369,15 +369,14 @@
{
"id": "32aaeb6f1c21316a",
"name": "Firefox Accounts Payments Next",
"hashedSecret": "b312c769bfce7682c2482c701199d88a8b67e40cee43cbe03bafe6eca47fff75",
"hashedSecret": "674609e00c64cedb4b486caf5ed3914f1fa86be25cc5cc6927a8a3d1c6981c4e",
"redirectUri": "http://localhost:3035/api/auth/callback/fxa",
"imageUri": "",
"canGrant": true,
"termsUri": "",
"privacyUri": "",
"trusted": true,
"allowedScopes": "https://identity.mozilla.com/account/subscriptions https://identity.mozilla.com/account/newsletters",
"publicClient": true
"allowedScopes": "https://identity.mozilla.com/account/subscriptions https://identity.mozilla.com/account/newsletters"
},
{
"name": "Thunderbird",
Expand Down
Loading

0 comments on commit 09096fb

Please sign in to comment.