diff --git a/Makefile b/Makefile
index b9b69019..236f5dd7 100644
--- a/Makefile
+++ b/Makefile
@@ -90,6 +90,7 @@ auth-build:
REACT_APP_TYPE=auth-service \
REACT_APP_ENV=staging \
+ REACT_APP_GOOGLE_CLIENT_ID=1043133237396-kebgih109kro71b5c7c8qphtgjbd2gdk.apps.googleusercontent.com \
BUILD_PATH=build_auth \
GENERATE_SOURCEMAP=false \
npx react-scripts build
@@ -101,6 +102,7 @@ auth-production-build:
REACT_APP_TYPE=auth-service \
REACT_APP_ENV=production \
+ REACT_APP_GOOGLE_CLIENT_ID=1043133237396-kebgih109kro71b5c7c8qphtgjbd2gdk.apps.googleusercontent.com \
BUILD_PATH=build_auth \
GENERATE_SOURCEMAP=false \
npx react-scripts build
diff --git a/public/images/oauth/appleID.svg b/public/images/oauth/appleID.svg
new file mode 100644
index 00000000..5398863e
--- /dev/null
+++ b/public/images/oauth/appleID.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/images/oauth/facebook.svg b/public/images/oauth/facebook.svg
new file mode 100644
index 00000000..48b88641
--- /dev/null
+++ b/public/images/oauth/facebook.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/images/oauth/google.svg b/public/images/oauth/google.svg
new file mode 100644
index 00000000..27259e0c
--- /dev/null
+++ b/public/images/oauth/google.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/components/VisuallyHidden/index.tsx b/src/components/VisuallyHidden/index.tsx
new file mode 100644
index 00000000..efcdc8b6
--- /dev/null
+++ b/src/components/VisuallyHidden/index.tsx
@@ -0,0 +1,14 @@
+import { css } from 'styled-components/macro'
+
+export const visuallyHidden = css`
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ border: 0;
+ white-space: nowrap;
+ clip-path: inset(100%);
+ clip: rect(0 0 0 0);
+ overflow: hidden;
+`
diff --git a/src/config/env.tsx b/src/config/env.tsx
index 55241b83..95a5ec4b 100644
--- a/src/config/env.tsx
+++ b/src/config/env.tsx
@@ -15,3 +15,5 @@ export const isProduction = ENV === 'production' || ENV === 'preproduction'
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 || '1043133237396-kebgih109kro71b5c7c8qphtgjbd2gdk.apps.googleusercontent.com'
diff --git a/src/config/index.tsx b/src/config/index.tsx
index d03692c7..4a2c8d17 100644
--- a/src/config/index.tsx
+++ b/src/config/index.tsx
@@ -8,3 +8,4 @@ export * from './history'
export * from './devices'
export * from './currencies'
export * from './dashes'
+export * from './env'
diff --git a/src/features/AuthServiceApp/components/App/index.tsx b/src/features/AuthServiceApp/components/App/index.tsx
index cf83ccbd..dc9002be 100644
--- a/src/features/AuthServiceApp/components/App/index.tsx
+++ b/src/features/AuthServiceApp/components/App/index.tsx
@@ -18,6 +18,7 @@ import { lexics } from '../../config/lexics'
const Login = lazy(() => import('../Login'))
const Registration = lazy(() => import('../Registration'))
const ChangePassword = lazy(() => import('../ChangePassword'))
+const Oauth = lazy(() => import('../Oauth'))
const Main = styled.main`
width: 100%;
@@ -45,6 +46,10 @@ export const App = () => {
+
+
+
+
diff --git a/src/features/AuthServiceApp/components/Login/hooks.tsx b/src/features/AuthServiceApp/components/Login/hooks.tsx
index 636e6a18..04c3b4fb 100644
--- a/src/features/AuthServiceApp/components/Login/hooks.tsx
+++ b/src/features/AuthServiceApp/components/Login/hooks.tsx
@@ -1,19 +1,29 @@
-import type { FormEvent } from 'react'
+import type { FormEvent, MouseEvent } from 'react'
import {
useRef,
useState,
useEffect,
} from 'react'
+import isObject from 'lodash/isObject'
+
+import { redirectToUrl } from 'helpers'
+
+import { useLocalStore } from 'hooks'
+
+import type { Settings } from 'features/AuthStore/helpers'
import { loginCheck } from 'features/AuthServiceApp/requests/auth'
import { getApiUrl } from 'features/AuthServiceApp/config/routes'
import { useAuthFields } from 'features/AuthServiceApp/hooks/useAuthFields'
+import { AuthProviders } from '../../config/authProviders'
+import { getAuthUrl } from '../../helpers/getAuthUrl'
import { useParamsUrl } from '../../hooks/useParamsUrl'
const url = getApiUrl('/authorize')
export const useLoginForm = () => {
+ const urlParams = useParamsUrl()
const {
client_id,
lang,
@@ -21,7 +31,8 @@ export const useLoginForm = () => {
response_mode,
response_type,
scope,
- } = useParamsUrl()
+ } = urlParams
+
const [authError, setAuthError] = useState('')
const [isFetching, setIsFetching] = useState(false)
const [isRecoveryPopupOpen, setIsRecoveryPopupOpen] = useState(false)
@@ -38,6 +49,12 @@ export const useLoginForm = () => {
password,
} = useAuthFields('login')
+ const [, setUrlParams] = useLocalStore>({
+ defaultValue: {},
+ key: 'urlParams',
+ validator: isObject,
+ })
+
const isSubmitDisabled = (
!email
|| !password
@@ -79,6 +96,17 @@ export const useLoginForm = () => {
}
}
+ const handleAuthButtonClick = (authProvider: AuthProviders) => (
+ e: MouseEvent,
+ ) => {
+ e.preventDefault()
+
+ const authUrl = getAuthUrl(authProvider, urlParams)
+
+ setUrlParams(urlParams)
+ redirectToUrl(authUrl)
+ }
+
useEffect(() => {
setAuthError('')
}, [email, password])
@@ -89,6 +117,7 @@ export const useLoginForm = () => {
email,
formError,
formRef,
+ handleAuthButtonClick,
handleModalOpen,
handleSubmit,
isFetching,
diff --git a/src/features/AuthServiceApp/components/Login/index.tsx b/src/features/AuthServiceApp/components/Login/index.tsx
index 867a686f..089db6f4 100644
--- a/src/features/AuthServiceApp/components/Login/index.tsx
+++ b/src/features/AuthServiceApp/components/Login/index.tsx
@@ -4,6 +4,7 @@ import { RecoveryPopup } from 'features/AuthServiceApp/components/RecoveryPopup'
import { client } from 'features/AuthServiceApp/config/clients'
import { PAGES } from '../../config/pages'
+import { AuthProviders } from '../../config/authProviders'
import { LanguageSelect } from '../LanguageSelect'
import { PasswordInput } from '../PasswordInput'
import { Input } from '../../../../components/Input'
@@ -23,7 +24,14 @@ import {
Wrapper,
ScLoaderWrapper,
} from '../../styled'
-import { RegisterButton } from './styled'
+import {
+ RegisterButton,
+ AuthButtonsContainer,
+ AuthButton,
+ AuthButtonText,
+ AuthButtonImage,
+ ContinueWith,
+} from './styled'
import { CompanyInfo } from '../../../CompanyInfo'
const Login = () => {
@@ -33,6 +41,7 @@ const Login = () => {
email,
formError,
formRef,
+ handleAuthButtonClick,
handleModalOpen,
handleSubmit,
isFetching,
@@ -112,6 +121,21 @@ 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 07aa204e..560c25cd 100644
--- a/src/features/AuthServiceApp/components/Login/styled.tsx
+++ b/src/features/AuthServiceApp/components/Login/styled.tsx
@@ -5,6 +5,15 @@ import styled, { css } from 'styled-components/macro'
import { isMobileDevice } from 'config/userAgent'
import { outlineButtonStyles } from 'features/Common/Button'
+import { T9n } from 'features/T9n'
+
+import { visuallyHidden } from 'components/VisuallyHidden'
+
+import { AuthProviders } from '../../config/authProviders'
+
+type TAuthButtonImage = {
+ authProvider: AuthProviders,
+}
export const RegisterButton = styled(Link)`
${outlineButtonStyles}
@@ -29,3 +38,83 @@ export const RegisterButton = styled(Link)`
`
: ''};
`
+
+export const ContinueWith = styled(T9n)`
+ margin: 20px auto;
+ font-size: 16px;
+ color: ${({ theme }) => theme.colors.white};
+`
+
+export const AuthButtonsContainer = styled.div`
+ width: 100%;
+ display: flex;
+ gap: 10px;
+
+ ${isMobileDevice
+ ? css`
+ flex-direction: column;
+ `
+ : ''};
+`
+
+export const AuthButton = styled.button`
+ ${outlineButtonStyles}
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 154px;
+ height: 50px;
+ border-radius: 5px;
+
+ ${isMobileDevice
+ ? css`
+ width: 100%;
+ justify-content: flex-start;
+ height: 44px;
+ border-radius: 10px;
+ `
+ : ''};
+`
+
+export const AuthButtonText = styled.span`
+ opacity: 0.8;
+
+ ${isMobileDevice ? '' : visuallyHidden};
+`
+
+export const AuthButtonImage = styled.div`
+ background-repeat: no-repeat;
+ background-position: center;
+ width: 100%;
+ height: 32px;
+
+ ${isMobileDevice
+ ? css`
+ width: 45%;
+ margin-right: 20px;
+ background-position: right;
+ `
+ : ''}
+
+ ${({ authProvider }) => {
+ switch (authProvider) {
+ case AuthProviders.Google:
+ return css`
+ background-image: url(/images/oauth/google.svg);
+ `
+
+ case AuthProviders.Facebook:
+ return css`
+ background-image: url(/images/oauth/facebook.svg);
+ `
+
+ case AuthProviders.AppleID:
+ return css`
+ background-image: url(/images/oauth/appleID.svg);
+ `
+
+ default: return ''
+ }
+ }}
+`
diff --git a/src/features/AuthServiceApp/components/Oauth/index.tsx b/src/features/AuthServiceApp/components/Oauth/index.tsx
new file mode 100644
index 00000000..dd072031
--- /dev/null
+++ b/src/features/AuthServiceApp/components/Oauth/index.tsx
@@ -0,0 +1,69 @@
+import { useEffect, useRef } from 'react'
+import { Redirect } from 'react-router-dom'
+
+import { parse } from 'querystring'
+import isObject from 'lodash/isObject'
+
+import { useLocalStore } from 'hooks'
+
+import type { Settings } from 'features/AuthStore/helpers'
+import { getClientSettings } from 'features/AuthStore/helpers'
+
+import { API_ROOT } from '../../config/routes'
+import { PAGES } from '../../config/pages'
+
+const url = `${API_ROOT}/oauth`
+
+const Oauth = () => {
+ const {
+ response_mode,
+ response_type,
+ scope,
+ } = getClientSettings()
+
+ const [urlParams] = useLocalStore>({
+ clearOnUnmount: true,
+ defaultValue: {},
+ key: 'urlParams',
+ validator: isObject,
+ })
+
+ const formRef = useRef(null)
+
+ const idToken = parse(window.location.hash).id_token as string | undefined
+
+ const {
+ client_id,
+ lang,
+ nonce,
+ redirect_uri,
+ state,
+ } = urlParams
+
+ useEffect(() => {
+ formRef.current?.submit()
+ }, [])
+
+ if (!idToken || !client_id || !redirect_uri) return
+
+ return (
+
+ )
+}
+
+export default Oauth
diff --git a/src/features/AuthServiceApp/config/authProviders.tsx b/src/features/AuthServiceApp/config/authProviders.tsx
new file mode 100644
index 00000000..77b61a95
--- /dev/null
+++ b/src/features/AuthServiceApp/config/authProviders.tsx
@@ -0,0 +1,5 @@
+export enum AuthProviders {
+ Google,
+ Facebook,
+ AppleID,
+}
diff --git a/src/features/AuthServiceApp/config/lexics.tsx b/src/features/AuthServiceApp/config/lexics.tsx
index ea51c171..8ed595f3 100644
--- a/src/features/AuthServiceApp/config/lexics.tsx
+++ b/src/features/AuthServiceApp/config/lexics.tsx
@@ -31,6 +31,7 @@ export const lexics = {
i_accept: 15737,
i_agree: 15430,
login: 13404,
+ or_continue_with: 15118,
password_new: 15056,
password_repeat: 15057,
privacy_policy_and_statement: 15404,
diff --git a/src/features/AuthServiceApp/config/pages.tsx b/src/features/AuthServiceApp/config/pages.tsx
index 9639849c..a5bb7416 100644
--- a/src/features/AuthServiceApp/config/pages.tsx
+++ b/src/features/AuthServiceApp/config/pages.tsx
@@ -1,5 +1,6 @@
export const PAGES = {
change_password: '/change_password',
login: '/authorize',
+ oauth: '/oauth',
registration: '/registration',
}
diff --git a/src/features/AuthServiceApp/helpers/getAuthUrl/index.tsx b/src/features/AuthServiceApp/helpers/getAuthUrl/index.tsx
new file mode 100644
index 00000000..f33525fc
--- /dev/null
+++ b/src/features/AuthServiceApp/helpers/getAuthUrl/index.tsx
@@ -0,0 +1,34 @@
+import type { Settings } from 'features/AuthStore/helpers'
+import { GOOGLE_CLIENT_ID } from 'config'
+
+import { AuthProviders } from '../../config/authProviders'
+import { PAGES } from '../../config/pages'
+
+const getQueryString = (authProvider: AuthProviders, urlParams: Settings) => {
+ switch (authProvider) {
+ case AuthProviders.Google:
+ return new URLSearchParams({
+ client_id: GOOGLE_CLIENT_ID,
+ nonce: urlParams.nonce || '0394852-3190485-2490358',
+ redirect_uri: `${window.location.origin}${PAGES.oauth}`,
+ response_type: 'token id_token',
+ scope: 'openid email',
+ state: urlParams.state || '',
+ }).toString()
+
+ default:
+ return ''
+ }
+}
+
+export const getAuthUrl = (authProvider: AuthProviders, urlParams: Settings) => {
+ const queryString = getQueryString(AuthProviders.Google, urlParams)
+
+ switch (authProvider) {
+ case AuthProviders.Google:
+ return `https://accounts.google.com/o/oauth2/v2/auth?${queryString}`
+
+ default:
+ return ''
+ }
+}
diff --git a/src/features/AuthServiceApp/hooks/useParamsUrl.tsx b/src/features/AuthServiceApp/hooks/useParamsUrl.tsx
index a3860677..3a8c2427 100644
--- a/src/features/AuthServiceApp/hooks/useParamsUrl.tsx
+++ b/src/features/AuthServiceApp/hooks/useParamsUrl.tsx
@@ -11,10 +11,12 @@ export const useParamsUrl = () => {
const {
client_id,
+ nonce,
redirect_uri,
response_mode,
response_type,
scope,
+ state,
} = getClientSettings()
const urlSearchParams = useMemo(() => new URLSearchParams(location.search), [location.search])
@@ -29,10 +31,12 @@ export const useParamsUrl = () => {
return {
client_id,
+ nonce,
redirect_uri,
response_mode,
response_type,
scope,
+ state,
...params,
lang,
}
diff --git a/src/features/AuthServiceApp/styled.tsx b/src/features/AuthServiceApp/styled.tsx
index 32fa799f..bcbb245d 100644
--- a/src/features/AuthServiceApp/styled.tsx
+++ b/src/features/AuthServiceApp/styled.tsx
@@ -137,7 +137,7 @@ export const ForgotPass = styled.div`
export const Container = styled.div`
min-width: 100%;
- margin-top: 15px;
+ margin-top: 20px;
display: flex;
flex-direction: row;
align-items: center;
diff --git a/src/features/AuthStore/helpers.tsx b/src/features/AuthStore/helpers.tsx
index f7ee6f07..922e1980 100644
--- a/src/features/AuthStore/helpers.tsx
+++ b/src/features/AuthStore/helpers.tsx
@@ -6,7 +6,7 @@ import { AUTH_SERVICE } from 'config/routes'
import { ClientIds, ClientNames } from 'config/clients/types'
import { ENV, stageENV } from 'config/env'
-interface Settings extends UserManagerSettings {
+export interface Settings extends UserManagerSettings {
client_id: ClientIds,
lang?: string,
nonce?: string,
diff --git a/src/helpers/index.tsx b/src/helpers/index.tsx
index 9b48538e..cb43b81d 100644
--- a/src/helpers/index.tsx
+++ b/src/helpers/index.tsx
@@ -5,3 +5,4 @@ export * from './getProfileFallbackLogo'
export * from './getSportLexic'
export * from './msToMinutesAndSeconds'
export * from './secondsToHms'
+export * from './redirectToUrl'
diff --git a/src/helpers/redirectToUrl/index.tsx b/src/helpers/redirectToUrl/index.tsx
new file mode 100644
index 00000000..01085e9a
--- /dev/null
+++ b/src/helpers/redirectToUrl/index.tsx
@@ -0,0 +1,3 @@
+export const redirectToUrl = (url: string) => {
+ window.location.href = url
+}