diff --git a/Makefile b/Makefile
index 76e14f10..8345e806 100644
--- a/Makefile
+++ b/Makefile
@@ -91,6 +91,7 @@ auth-build:
REACT_APP_TYPE=auth-service \
REACT_APP_ENV=staging \
REACT_APP_GOOGLE_CLIENT_ID=1022673777479-43mqb9lsqkpcr47umuvsoo0eemfpljf5.apps.googleusercontent.com \
+ REACT_APP_FACEBOOK_CLIENT_ID=798254931203361 \
BUILD_PATH=build_auth \
GENERATE_SOURCEMAP=false \
npx react-scripts build
@@ -103,6 +104,7 @@ auth-production-build:
REACT_APP_TYPE=auth-service \
REACT_APP_ENV=production \
REACT_APP_GOOGLE_CLIENT_ID=1022673777479-43mqb9lsqkpcr47umuvsoo0eemfpljf5.apps.googleusercontent.com \
+ REACT_APP_FACEBOOK_CLIENT_ID=798254931203361 \
BUILD_PATH=build_auth \
GENERATE_SOURCEMAP=false \
npx react-scripts build
diff --git a/public/images/done.svg b/public/images/done.svg
new file mode 100644
index 00000000..97084d71
--- /dev/null
+++ b/public/images/done.svg
@@ -0,0 +1,17 @@
+
diff --git a/src/config/env.tsx b/src/config/env.tsx
index ea7cc476..d3749e39 100644
--- a/src/config/env.tsx
+++ b/src/config/env.tsx
@@ -16,4 +16,5 @@ export const stageENV = process.env.REACT_APP_STAGE || 'staging'
export const STRIPE_PUBLIC_KEY = process.env.REACT_APP_STRIPE_PK || 'pk_test_51J5TEYEDSxVnTgDWhKLstuDAhx9XmGJmj2awyZ1HghpWdU46MhXqbQt1PyW9XsRlES5JFyuQWbPRjoSsiW3wvXOH00KMirJEGZ'
-export const GOOGLE_CLIENT_ID = process.env.REACT_APP_GOOGLE_CLIENT_ID || '1022673777479-43mqb9lsqkpcr47umuvsoo0eemfpljf5.apps.googleusercontent.com'
+export const GOOGLE_CLIENT_ID = process.env.REACT_APP_GOOGLE_CLIENT_ID || '1043133237396-kebgih109kro71b5c7c8qphtgjbd2gdk.apps.googleusercontent.com'
+export const FACEBOOK_CLIENT_ID = process.env.REACT_APP_FACEBOOK_CLIENT_ID || '798254931203361'
diff --git a/src/features/AuthServiceApp/components/Login/index.tsx b/src/features/AuthServiceApp/components/Login/index.tsx
index 089db6f4..5cf1c7cb 100644
--- a/src/features/AuthServiceApp/components/Login/index.tsx
+++ b/src/features/AuthServiceApp/components/Login/index.tsx
@@ -127,10 +127,10 @@ const Login = () => {
Google
- {/*
+
Facebook
- */}
+
{/*
Apple ID
diff --git a/src/features/AuthServiceApp/components/Login/styled.tsx b/src/features/AuthServiceApp/components/Login/styled.tsx
index 3d3012a5..d83a9cbd 100644
--- a/src/features/AuthServiceApp/components/Login/styled.tsx
+++ b/src/features/AuthServiceApp/components/Login/styled.tsx
@@ -90,12 +90,15 @@ export const AuthButtonImage = styled.div`
return css`
width: 32px;
height: 32px;
- background-image: url(/images/oauth/google.svg);
- `
+ background-image: url(/images/oauth/google.svg); `
case AuthProviders.Facebook:
return css`
+ width: 32px;
+ height: 32px;
background-image: url(/images/oauth/facebook.svg);
+
+ ${isMobileDevice ? css`width: 57px;` : ''};
`
case AuthProviders.AppleID:
diff --git a/src/features/AuthServiceApp/components/Oauth/hooks.tsx b/src/features/AuthServiceApp/components/Oauth/hooks.tsx
new file mode 100644
index 00000000..5c94d4d7
--- /dev/null
+++ b/src/features/AuthServiceApp/components/Oauth/hooks.tsx
@@ -0,0 +1,79 @@
+import {
+ useEffect,
+ useRef,
+ useState,
+ useCallback,
+ FormEvent,
+ FocusEvent,
+ ChangeEvent,
+} from 'react'
+
+import { isValidEmail } from 'features/AuthServiceApp/helpers/isValidEmail'
+
+import { API_ROOT } from '../../config/routes'
+
+export const useOauth = () => {
+ const [email, setEmail] = useState('')
+ const [error, setError] = useState('')
+ const [showEmailField, showShowEmailField] = useState(false)
+
+ const formRef = useRef(null)
+
+ const authorize = useCallback(async () => {
+ if (!formRef.current) return
+
+ const url = `${API_ROOT}/oauth`
+
+ const res = await fetch(url, {
+ body: new FormData(formRef.current),
+ method: 'POST',
+ })
+
+ if (res.ok && res.redirected) {
+ formRef.current.submit()
+ } else {
+ const data = await res.json()
+
+ if (data.error === 'Token missing email field') {
+ showShowEmailField(true)
+ }
+ }
+ }, [])
+
+ const handleEmailChange = ({ target: { value } }: ChangeEvent) => {
+ setEmail(value)
+ }
+
+ const handleEmailFocus = () => {
+ setError('')
+ }
+
+ const handleEmailBlur = ({ target: { value } }: FocusEvent) => {
+ if (!isValidEmail(value) && value) {
+ setError('error_invalid_email_format')
+ }
+ }
+
+ const handleSubmit = (e: FormEvent) => {
+ e.preventDefault()
+
+ if (error) return
+
+ authorize()
+ }
+
+ useEffect(() => {
+ authorize()
+ }, [authorize])
+
+ return {
+ email,
+ error,
+ formRef,
+ handleEmailBlur,
+ handleEmailChange,
+ handleEmailFocus,
+ handleSubmit,
+ showEmailField,
+ }
+}
diff --git a/src/features/AuthServiceApp/components/Oauth/index.tsx b/src/features/AuthServiceApp/components/Oauth/index.tsx
index dd072031..124b9014 100644
--- a/src/features/AuthServiceApp/components/Oauth/index.tsx
+++ b/src/features/AuthServiceApp/components/Oauth/index.tsx
@@ -1,4 +1,4 @@
-import { useEffect, useRef } from 'react'
+import { Fragment } from 'react'
import { Redirect } from 'react-router-dom'
import { parse } from 'querystring'
@@ -8,9 +8,25 @@ import { useLocalStore } from 'hooks'
import type { Settings } from 'features/AuthStore/helpers'
import { getClientSettings } from 'features/AuthStore/helpers'
+import { T9n } from 'features/T9n'
import { API_ROOT } from '../../config/routes'
import { PAGES } from '../../config/pages'
+import { useOauth } from './hooks'
+import {
+ Header,
+ HeaderLogo,
+ Content,
+ DoneIcon,
+ Title,
+ Subtitle,
+ Form,
+ Input,
+ SubmitButton,
+ Text,
+ StyledLink,
+ Error,
+} from './styled'
const url = `${API_ROOT}/oauth`
@@ -28,9 +44,7 @@ const Oauth = () => {
validator: isObject,
})
- const formRef = useRef(null)
-
- const idToken = parse(window.location.hash).id_token as string | undefined
+ const idToken = parse(window.location.hash.replace('#', '')).id_token as string | undefined
const {
client_id,
@@ -40,28 +54,162 @@ const Oauth = () => {
state,
} = urlParams
- useEffect(() => {
- formRef.current?.submit()
- }, [])
+ const {
+ email,
+ error,
+ formRef,
+ handleEmailBlur,
+ handleEmailChange,
+ handleEmailFocus,
+ handleSubmit,
+ showEmailField,
+ } = useOauth()
if (!idToken || !client_id || !redirect_uri) return
+ if (showEmailField) {
+ return (
+
+
+
+
+ You are one step away from creating inSports account!
+ Complete the registration form by entering your email:
+
+
+ Also you can use any{' '}
+ other method of authorization
+
+
+
+ )
+ }
+
return (
)
}
diff --git a/src/features/AuthServiceApp/components/Oauth/styled.tsx b/src/features/AuthServiceApp/components/Oauth/styled.tsx
new file mode 100644
index 00000000..b6586c0f
--- /dev/null
+++ b/src/features/AuthServiceApp/components/Oauth/styled.tsx
@@ -0,0 +1,137 @@
+import { Link } from 'react-router-dom'
+
+import styled, { css } from 'styled-components/macro'
+
+import { isMobileDevice } from 'config/userAgent'
+
+import { ButtonSolid } from 'features/Common'
+import { Error as ErrorBase } from 'features/AuthServiceApp/styled'
+import { HeaderLogo as HeaderLogoBase } from 'features/ProfileHeader/styled'
+
+import { Input as InputBase } from 'components/Input'
+
+export const Header = styled.div`
+ position: absolute;
+ padding: 30px 25px 0;
+`
+
+export const HeaderLogo = styled(HeaderLogoBase)`
+ ${isMobileDevice
+ ? css`
+ top: 20px;
+ `
+ : ''};
+`
+
+export const Content = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ padding: 20px 25px;
+ text-align: center;
+`
+
+export const DoneIcon = styled.div`
+ width: 88px;
+ height: 88px;
+ margin-bottom: 20px;
+ background-image: url(/images/done.svg);
+ background-size: cover;
+
+ ${isMobileDevice
+ ? css`
+ width: 65px;
+ height: 65px;
+ `
+ : ''};
+`
+
+export const Title = styled.div`
+ margin-bottom: 40px;
+ font-size: 24px;
+ font-weight: bold;
+ color: ${({ theme }) => theme.colors.white};
+
+ ${isMobileDevice
+ ? css`
+ margin-bottom: 15px;
+ line-height: 20px;
+ font-size: 16px;
+ `
+ : ''};
+`
+
+export const Subtitle = styled.div`
+ margin-bottom: 40px;
+ font-size: 18px;
+ line-height: 18px;
+ color: ${({ theme }) => theme.colors.white};
+
+ ${isMobileDevice
+ ? css`
+ margin-bottom: 15px;
+ font-size: 12px;
+ `
+ : ''};
+`
+
+export const Form = styled.form`
+ position: relative;
+ display: flex;
+ gap: 20px;
+ margin-bottom: 30px;
+
+ ${isMobileDevice
+ ? css`
+ gap: 10px;
+ width: 100%;
+ flex-direction: column;
+ `
+ : ''};
+`
+
+export const Input = styled(InputBase)`
+ width: 406px;
+ height: 50px;
+
+ ${isMobileDevice
+ ? css`
+ width: 100%;
+ height: 32px;
+ `
+ : ''};
+`
+
+export const SubmitButton = styled(ButtonSolid)`
+ width: 117.65px;
+ height: 50px;
+ font-size: 20px;
+ font-weight: 600;
+
+ ${isMobileDevice
+ ? css`
+ width: 100%;
+ height: 32px;
+ font-size: 12px;
+ `
+ : ''};
+`
+
+export const Text = styled.div`
+ font-size: 12px;
+ white-space: nowrap;
+ color: rgba(255, 255, 255, 0.7);
+`
+
+export const StyledLink = styled(Link)`
+ color: rgba(255, 255, 255, 0.7);
+ text-decoration: underline;
+`
+
+export const Error = styled(ErrorBase)`
+ position: absolute;
+ left: 0;
+ bottom: -25px;
+`
diff --git a/src/features/AuthServiceApp/helpers/getAuthUrl/index.tsx b/src/features/AuthServiceApp/helpers/getAuthUrl/index.tsx
index 83cc78cf..f0e8a8d3 100644
--- a/src/features/AuthServiceApp/helpers/getAuthUrl/index.tsx
+++ b/src/features/AuthServiceApp/helpers/getAuthUrl/index.tsx
@@ -1,5 +1,8 @@
+import { GOOGLE_CLIENT_ID, FACEBOOK_CLIENT_ID } from 'config'
+
+import { getRandomString } from 'helpers'
+
import type { Settings } from 'features/AuthStore/helpers'
-import { GOOGLE_CLIENT_ID } from 'config'
import { AuthProviders } from '../../config/authProviders'
import { PAGES } from '../../config/pages'
@@ -16,18 +19,32 @@ const getQueryString = (authProvider: AuthProviders, urlParams: Settings) => {
state: urlParams.state || '',
}).toString()
+ case AuthProviders.Facebook:
+ return new URLSearchParams({
+ client_id: FACEBOOK_CLIENT_ID,
+ code_challenge: getRandomString(43),
+ nonce: urlParams.nonce || '0394852-3190485-2490358',
+ redirect_uri: `${window.location.origin}${PAGES.oauth}`,
+ response_type: 'id_token',
+ scope: 'openid email public_profile',
+ state: urlParams.state || '',
+ }).toString()
+
default:
return ''
}
}
export const getAuthUrl = (authProvider: AuthProviders, urlParams: Settings) => {
- const queryString = getQueryString(AuthProviders.Google, urlParams)
+ const queryString = getQueryString(authProvider, urlParams)
switch (authProvider) {
case AuthProviders.Google:
return `https://accounts.google.com/o/oauth2/v2/auth?${queryString}`
+ case AuthProviders.Facebook:
+ return `https://www.facebook.com/v11.0/dialog/oauth?${queryString}`
+
default:
return ''
}
diff --git a/src/helpers/getRandomString/index.tsx b/src/helpers/getRandomString/index.tsx
new file mode 100644
index 00000000..c23816b3
--- /dev/null
+++ b/src/helpers/getRandomString/index.tsx
@@ -0,0 +1,11 @@
+export const getRandomString = (length: number) => {
+ let result = ''
+ const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
+ const charactersLength = characters.length
+
+ for (let i = 0; i < length; i++) {
+ result += characters.charAt(Math.floor(Math.random() * charactersLength))
+ }
+
+ return result
+}
diff --git a/src/helpers/index.tsx b/src/helpers/index.tsx
index cb43b81d..06dc4502 100644
--- a/src/helpers/index.tsx
+++ b/src/helpers/index.tsx
@@ -6,3 +6,4 @@ export * from './getSportLexic'
export * from './msToMinutesAndSeconds'
export * from './secondsToHms'
export * from './redirectToUrl'
+export * from './getRandomString'