Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(next): add email, google, apple sign in #18021

Merged
merged 1 commit into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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