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

IR-346: Add Apple SSO support to the IR Engine. #10101

Merged
merged 44 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
e832475
Add Apple SSO support to the IR Engine.
muradkhateeb78 Apr 29, 2024
8bcda46
Fix path for Apple Callback
muradkhateeb78 Apr 29, 2024
5f0855e
Cosmetic changes for Apple logo
muradkhateeb78 Apr 29, 2024
84c5020
Fix OAuth issue with Apple
muradkhateeb78 May 1, 2024
2271a96
Testing Apple SSO
muradkhateeb78 May 2, 2024
103e9cc
Add scopes
muradkhateeb78 May 3, 2024
b5c4bc3
Add additional loging
muradkhateeb78 May 4, 2024
60622d7
Remove redundant code
muradkhateeb78 May 4, 2024
e8b3a5e
Add additional Loggin
muradkhateeb78 May 4, 2024
dbddd86
Add additional logging
muradkhateeb78 May 5, 2024
393d97c
Push additional lgging
muradkhateeb78 May 5, 2024
2c8c977
Add additional logging
muradkhateeb78 May 5, 2024
b3eef42
remove redundant code
muradkhateeb78 May 6, 2024
17ab891
Adding additional loggin
muradkhateeb78 May 6, 2024
10a94db
Remove redundant coe
muradkhateeb78 May 6, 2024
37d2b29
Remove additional logging
muradkhateeb78 May 6, 2024
1ea1209
Add required vars for refreshing seed data for authentication
muradkhateeb78 May 6, 2024
7961295
Add additional loggin
muradkhateeb78 May 7, 2024
ba84995
Add additional logging
muradkhateeb78 May 13, 2024
af4151a
Update configurations to request Email and Name scope from Apple
muradkhateeb78 Jun 11, 2024
f41fd1b
Update Auth configurations to request Name and Email scopes from Apple
muradkhateeb78 Jun 11, 2024
8ba6827
Fix the issue with @feathers/authentication-oauth
muradkhateeb78 Jun 25, 2024
6c16f1d
Update version number for feathers packages
muradkhateeb78 Jul 11, 2024
b50544d
Merge branch 'dev' into feat/Enable_Apple_SSO
muradkhateeb78 Jul 12, 2024
3bb4b4f
Fix Apple Icon in Account Identifiers
muradkhateeb78 Jul 12, 2024
a656253
Update Profile returned from GetProfile
muradkhateeb78 Jul 12, 2024
2571317
Merge branch 'dev' into feat/Enable_Apple_SSO
hanzlamateen Jul 15, 2024
f3c235a
Updated logging
hanzlamateen Jul 15, 2024
3f91957
Removed unncessary checks for saving auth settings in DB
muradkhateeb78 Jul 15, 2024
3069d5b
Merge lateste dev
muradkhateeb78 Jul 15, 2024
cd4c333
Update Identity Providers
muradkhateeb78 Jul 15, 2024
e49ddb0
Trying out different configurations for Redirect URL
muradkhateeb78 Jul 15, 2024
86eccc3
Update introspection of access token
muradkhateeb78 Jul 15, 2024
fcf23ac
Fix version numbers for feathers packages
muradkhateeb78 Jul 16, 2024
b9ccb5b
Udpate version numbers for feathers packages to latest available version
muradkhateeb78 Jul 16, 2024
55f920d
Remove redundant code
muradkhateeb78 Jul 16, 2024
a6f9c67
Update feathers pacakge version to version 5.0.27
muradkhateeb78 Jul 16, 2024
b0261e8
Update feathers packages to latest in server
muradkhateeb78 Jul 16, 2024
a988836
Update version number for feathers package
muradkhateeb78 Jul 16, 2024
a0ecc14
Add patch packages for feathers improvments
muradkhateeb78 Jul 16, 2024
9f9689b
Remove Unnecessary logging
muradkhateeb78 Jul 16, 2024
a9fa426
Merge branch 'dev' into feat/Enable_Apple_SSO
muradkhateeb78 Jul 16, 2024
fcf14ca
Resolved comments on PR
muradkhateeb78 Jul 17, 2024
7bc8fe9
Merge branch 'feat/Enable_Apple_SSO' of https://github.com/EtherealEn…
muradkhateeb78 Jul 17, 2024
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
2 changes: 2 additions & 0 deletions packages/client-core/i18n/en/admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@
"defaults": "Defaults",
"host": "Host",
"protocol": "Protocol",
"apple": "Apple",
"discord": "Discord",
"facebook": "Meta",
"google": "Google",
Expand Down Expand Up @@ -653,6 +654,7 @@
"clearAllScopes": "Clear All Scopes",
"userSearch": "user by id, name or account identifier",
"linkedAccounts": "Linked Accounts",
"apple": "Apple",
"discord": "Discord",
"facebook": "Meta",
"google": "Google",
Expand Down
1 change: 1 addition & 0 deletions packages/client-core/i18n/en/social.json
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@
},
"registration": {
"ph-phoneEmail": "Use phone / email",
"apple": "Continue with Apple",
"facebook": "Continue with Meta",
"google": "Continue with Google",
"linkedin": "Continue with Linkedin",
Expand Down
1 change: 1 addition & 0 deletions packages/client-core/i18n/en/user.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"sms-sent-msg": "Login Magic Link was sent. Please check your SMS."
},
"social": {
"apple": "Login with Apple",
"gitHub": "Login with GitHub",
"google": "Login with Google",
"facebook": "Login with Meta",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { initialAuthState } from '../../../../common/initialAuthState'
import { NotificationService } from '../../../../common/services/NotificationService'

const OAUTH_TYPES = {
APPLE: 'apple',
DISCORD: 'discord',
FACEBOOK: 'facebook',
GITHUB: 'github',
Expand All @@ -62,6 +63,7 @@ const AuthenticationTab = forwardRef(({ open }: { open: boolean }, ref: React.Mu
const state = useHookstate(initialAuthState)
const holdAuth = useHookstate(initialAuthState)
const keySecret = useHookstate({
apple: authSetting?.oauth?.apple,
discord: authSetting?.oauth?.discord,
github: authSetting?.oauth?.github,
google: authSetting?.oauth?.google,
Expand All @@ -84,6 +86,7 @@ const AuthenticationTab = forwardRef(({ open }: { open: boolean }, ref: React.Mu

const tempKeySecret = JSON.parse(
JSON.stringify({
apple: authSetting?.oauth?.apple,
discord: authSetting?.oauth?.discord,
github: authSetting?.oauth?.github,
google: authSetting?.oauth?.google,
Expand Down Expand Up @@ -131,6 +134,7 @@ const AuthenticationTab = forwardRef(({ open }: { open: boolean }, ref: React.Mu

const tempKeySecret = JSON.parse(
JSON.stringify({
apple: authSetting?.oauth?.apple,
discord: authSetting?.oauth?.discord,
github: authSetting?.oauth?.github,
google: authSetting?.oauth?.google,
Expand Down Expand Up @@ -258,6 +262,34 @@ const AuthenticationTab = forwardRef(({ open }: { open: boolean }, ref: React.Mu

<hr className="my-6 border border-theme-primary" />

{holdAuth?.apple?.value && (
<div className="col-span-1">
<Text component="h4" fontSize="base" fontWeight="medium" className="my-4 w-full">
{t('admin:components.setting.apple')}
</Text>

<PasswordInput
label={t('admin:components.setting.key')}
value={keySecret?.value?.apple?.key || ''}
onChange={(e) => handleOnChangeKey(e, OAUTH_TYPES.APPLE)}
/>

<PasswordInput
containerClassname="mt-2"
label={t('admin:components.setting.secret')}
value={keySecret?.value?.apple?.secret || ''}
onChange={(e) => handleOnChangeSecret(e, OAUTH_TYPES.APPLE)}
/>

<Input
containerClassname="mt-2"
label={t('admin:components.setting.callback')}
value={authSetting?.callback?.apple || ''}
disabled
/>
</div>
)}

<div className="grid grid-cols-3 gap-4">
{holdAuth?.discord?.value && (
<div className="col-span-1">
Expand Down Expand Up @@ -343,9 +375,10 @@ const AuthenticationTab = forwardRef(({ open }: { open: boolean }, ref: React.Mu
</div>
)}

{(holdAuth?.discord?.value || holdAuth?.linkedin?.value || holdAuth?.facebook?.value) && (
<hr className="col-span-full my-6 border border-theme-primary" />
)}
{(holdAuth?.apple?.value ||
holdAuth?.discord?.value ||
holdAuth?.linkedin?.value ||
holdAuth?.facebook?.value) && <hr className="col-span-full my-6 border border-theme-primary" />}

{holdAuth?.google?.value && (
<div className="col-span-1">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Ethereal Engine. All Rights Reserved.
import React from 'react'
import { MdEmail } from 'react-icons/md'
import {
RiAppleFill,
RiDiscordFill,
RiGithubFill,
RiGoogleFill,
Expand All @@ -39,6 +40,7 @@ import { UserType } from '@etherealengine/common/src/schema.type.module'
import Tooltip from '@etherealengine/ui/src/primitives/tailwind/Tooltip'

export default function AccountIdentifiers({ user }: { user: UserType }) {
const appleIp = user.identityProviders.find((ip) => ip.type === 'apple')
const discordIp = user.identityProviders.find((ip) => ip.type === 'discord')
const googleIp = user.identityProviders.find((ip) => ip.type === 'google')
const facebookIp = user.identityProviders.find((ip) => ip.type === 'facebook')
Expand All @@ -50,6 +52,11 @@ export default function AccountIdentifiers({ user }: { user: UserType }) {

return (
<div className="flex items-center gap-2">
{appleIp ? (
<Tooltip title={appleIp.accountIdentifier!}>
<RiAppleFill className="h-6 w-6" />
</Tooltip>
) : null}
{discordIp ? (
<Tooltip title={discordIp.accountIdentifier!}>
<RiDiscordFill className="h-6 w-6" />
Expand Down
36 changes: 36 additions & 0 deletions packages/client-core/src/common/components/Icons/AppleIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
CPAL-1.0 License

The contents of this file are subject to the Common Public Attribution License
Version 1.0. (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE.
The License is based on the Mozilla Public License Version 1.1, but Sections 14
and 15 have been added to cover use of software over a computer network and
provide for limited attribution for the Original Developer. In addition,
Exhibit A has been modified to be consistent with Exhibit B.

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
specific language governing rights and limitations under the License.

The Original Code is Ethereal Engine.

The Original Developer is the Initial Developer. The Initial Developer of the
Original Code is the Ethereal Engine team.

All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023
Ethereal Engine. All Rights Reserved.
*/

import React from 'react'

import { SvgIconProps } from '@mui/material/SvgIcon'

export const AppleIcon = (props: SvgIconProps) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="0.82em" height="1em" viewBox="0 0 256 315">
<path d="M213.803 167.03c.442 47.58 41.74 63.413 42.197 63.615c-.35 1.116-6.599 22.563-21.757 44.716c-13.104 19.153-26.705 38.235-48.13 38.63c-21.05.388-27.82-12.483-51.888-12.483c-24.061 0-31.582 12.088-51.51 12.871c-20.68.783-36.428-20.71-49.64-39.793c-27-39.033-47.633-110.3-19.928-158.406c13.763-23.89 38.36-39.017 65.056-39.405c20.307-.387 39.475 13.662 51.889 13.662c12.406 0 35.699-16.895 60.186-14.414c10.25.427 39.026 4.14 57.503 31.186c-1.49.923-34.335 20.044-33.978 59.822M174.24 50.199c10.98-13.29 18.369-31.79 16.353-50.199c-15.826.636-34.962 10.546-46.314 23.828c-10.173 11.763-19.082 30.589-16.678 48.633c17.64 1.365 35.66-8.964 46.64-22.262" />
</svg>
)
}
2 changes: 2 additions & 0 deletions packages/client-core/src/common/initialAuthState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Ethereal Engine. All Rights Reserved.

export const initialAuthState = {
jwt: true,
apple: false,
discord: false,
facebook: false,
github: false,
Expand All @@ -37,6 +38,7 @@ export const initialAuthState = {
}

export const initialOAuthConnectedState = {
apple: false,
discord: false,
facebook: false,
github: false,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
CPAL-1.0 License

The contents of this file are subject to the Common Public Attribution License
Version 1.0. (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE.
The License is based on the Mozilla Public License Version 1.1, but Sections 14
and 15 have been added to cover use of software over a computer network and
provide for limited attribution for the Original Developer. In addition,
Exhibit A has been modified to be consistent with Exhibit B.

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
specific language governing rights and limitations under the License.

The Original Code is Ethereal Engine.

The Original Developer is the Initial Developer. The Initial Developer of the
Original Code is the Ethereal Engine team.

All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023
Ethereal Engine. All Rights Reserved.
*/

import multiLogger from '@etherealengine/common/src/logger'
import { getMutableState, useHookstate } from '@etherealengine/hyperflux'
import Button from '@etherealengine/ui/src/primitives/mui/Button'
import Container from '@etherealengine/ui/src/primitives/mui/Container'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useLocation } from 'react-router-dom'

import { InstanceID } from '@etherealengine/common/src/schema.type.module'
import { AuthService, AuthState } from '../../services/AuthService'
import styles from './styles.module.scss'
const logger = multiLogger.child({ component: 'engine:ecs:AppleCallback' })
const AppleCallbackComponent = (props): JSX.Element => {
const { t } = useTranslation()
const initialState = { error: '', token: '' }
const [state, setState] = useState(initialState)
const search = new URLSearchParams(useLocation().search)

useEffect(() => {
const error = search.get('error') as string
const token = search.get('token') as string
const type = search.get('type') as string
const path = search.get('path') as string
const instanceId = search.get('instanceId') as InstanceID

if (!error) {
if (type === 'connection') {
const user = useHookstate(getMutableState(AuthState)).user
AuthService.refreshConnections(user.id.value!)
} else {
let redirectSuccess = `${path}`
if (instanceId != null) redirectSuccess += `?instanceId=${instanceId}`
AuthService.loginUserByJwt(token, redirectSuccess || '/', '/')
}
}

setState({ ...state, error, token })
}, [])

function redirectToRoot() {
window.location.href = '/'
}

return state.error && state.error !== '' ? (
<Container className={styles.oauthError}>
<div className={styles.title}>{t('user:oauth.authFailed', { service: 'Apple' })}</div>
<div className={styles.message}>{state.error}</div>
<Button onClick={redirectToRoot} className={styles.gradientButton}>
{t('user:oauth.redirectToRoot')}
</Button>
</Container>
) : (
<Container>{t('user:oauth.authenticating')}</Container>
)
}

export const AppleCallback = AppleCallbackComponent as any
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import Avatar from '@etherealengine/client-core/src/common/components/Avatar'
import Button from '@etherealengine/client-core/src/common/components/Button'
import commonStyles from '@etherealengine/client-core/src/common/components/common.module.scss'
import ConfirmDialog from '@etherealengine/client-core/src/common/components/ConfirmDialog'
import { AppleIcon } from '@etherealengine/client-core/src/common/components/Icons/AppleIcon'
import { DiscordIcon } from '@etherealengine/client-core/src/common/components/Icons/DiscordIcon'
import { GoogleIcon } from '@etherealengine/client-core/src/common/components/Icons/GoogleIcon'
import { LinkedInIcon } from '@etherealengine/client-core/src/common/components/Icons/LinkedInIcon'
Expand All @@ -42,6 +43,7 @@ import InputText from '@etherealengine/client-core/src/common/components/InputTe
import Menu from '@etherealengine/client-core/src/common/components/Menu'
import Text from '@etherealengine/client-core/src/common/components/Text'
import config, { validateEmail, validatePhoneNumber } from '@etherealengine/common/src/config'
import multiLogger from '@etherealengine/common/src/logger'
import { authenticationSettingPath, clientSettingPath, UserName } from '@etherealengine/common/src/schema.type.module'
import { getMutableState, useHookstate } from '@etherealengine/hyperflux'
import { useFind } from '@etherealengine/spatial/src/common/functions/FeathersHooks'
Expand All @@ -61,6 +63,8 @@ import { UserMenus } from '../../../UserUISystem'
import styles from '../index.module.scss'
import { PopupMenuServices } from '../PopupMenuService'

const logger = multiLogger.child({ component: 'engine:ecs:ProfileMenu' })

interface Props {
className?: string
hideLogin?: boolean
Expand Down Expand Up @@ -111,6 +115,7 @@ const ProfileMenu = ({ hideLogin, onClose, isPopover }: Props): JSX.Element => {

let type = ''
const addMoreSocial =
(authState?.value?.apple && !oauthConnectedState.apple.value) ||
(authState?.value?.discord && !oauthConnectedState.discord.value) ||
(authState?.value?.facebook && !oauthConnectedState.facebook.value) ||
(authState?.value?.github && !oauthConnectedState.github.value) ||
Expand Down Expand Up @@ -144,6 +149,9 @@ const ProfileMenu = ({ hideLogin, onClose, isPopover }: Props): JSX.Element => {
if (selfUser.identityProviders.get({ noproxy: true }))
for (const ip of selfUser.identityProviders.get({ noproxy: true })!) {
switch (ip.type) {
case 'apple':
oauthConnectedState.merge({ apple: true })
break
case 'discord':
oauthConnectedState.merge({ discord: true })
break
Expand Down Expand Up @@ -349,6 +357,7 @@ const ProfileMenu = ({ hideLogin, onClose, isPopover }: Props): JSX.Element => {
const enableWalletLogin = false // authState?.didWallet

const enableSocial =
authState?.value?.apple ||
authState?.value?.discord ||
authState?.value?.facebook ||
authState?.value?.github ||
Expand Down Expand Up @@ -592,7 +601,6 @@ const ProfileMenu = ({ hideLogin, onClose, isPopover }: Props): JSX.Element => {
{t('user:usermenu.profile.addSocial')}
</Text>
)}

<div className={styles.socialContainer}>
{authState?.value?.discord && !oauthConnectedState.discord.value && (
<IconButton
Expand All @@ -608,6 +616,9 @@ const ProfileMenu = ({ hideLogin, onClose, isPopover }: Props): JSX.Element => {
onClick={handleOAuthServiceClick}
/>
)}
{authState?.value?.apple && !oauthConnectedState.apple.value && (
<IconButton id="apple" icon={<AppleIcon viewBox="0 0 40 40" />} onClick={handleOAuthServiceClick} />
)}
{authState?.value?.facebook && !oauthConnectedState.facebook.value && (
<IconButton
id="facebook"
Expand Down Expand Up @@ -639,8 +650,14 @@ const ProfileMenu = ({ hideLogin, onClose, isPopover }: Props): JSX.Element => {
<Text align="center" variant="body2" mb={1} mt={2}>
{t('user:usermenu.profile.removeSocial')}
</Text>

<div className={styles.socialContainer}>
{authState?.apple.value && oauthConnectedState.apple.value && (
<IconButton
id="apple"
icon={<AppleIcon viewBox="0 0 40 40" />}
onClick={handleRemoveOAuthServiceClick}
/>
)}
{authState?.discord.value && oauthConnectedState.discord.value && (
<IconButton
id="discord"
Expand Down
6 changes: 5 additions & 1 deletion packages/client-core/src/user/services/AuthService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@
password: string
}

export interface AppleLoginForm {
email: string
}

export interface GithubLoginForm {
email: string
}
Expand Down Expand Up @@ -415,16 +419,16 @@
const storedToken = authData.authUser?.accessToken?.value
if (storedToken === accessToken) {
clearInterval(waitForTokenStored)
window.location.href = redirectSuccess

Check warning

Code scanning / CodeQL

Client-side URL redirect Medium

Untrusted URL redirection depends on a
user-provided value
.
Untrusted URL redirection depends on a
user-provided value
.
Untrusted URL redirection depends on a
user-provided value
.
Untrusted URL redirection depends on a
user-provided value
.
Untrusted URL redirection depends on a
user-provided value
.
Untrusted URL redirection depends on a
user-provided value
.
Untrusted URL redirection depends on a
user-provided value
.
Untrusted URL redirection depends on a
user-provided value
.
}
// After 3 seconds without the token getting updated, send the user back anyway - something seems to have
// gone wrong, and we don't want them stuck on the page they were on indefinitely.
if (timeoutTimer > 3000) window.location.href = redirectSuccess

Check warning

Code scanning / CodeQL

Client-side URL redirect Medium

Untrusted URL redirection depends on a
user-provided value
.
Untrusted URL redirection depends on a
user-provided value
.
Untrusted URL redirection depends on a
user-provided value
.
Untrusted URL redirection depends on a
user-provided value
.
Untrusted URL redirection depends on a
user-provided value
.
Untrusted URL redirection depends on a
user-provided value
.
Untrusted URL redirection depends on a
user-provided value
.
Untrusted URL redirection depends on a
user-provided value
.
}, TIMEOUT_INTERVAL)
} catch (err) {
authState.merge({ error: i18n.t('common:error.login-error') })
NotificationService.dispatchNotify(err.message, { variant: 'error' })
window.location.href = `${redirectError}?error=${err.message}`

Check warning

Code scanning / CodeQL

Exception text reinterpreted as HTML Medium

Exception text
is reinterpreted as HTML without escaping meta-characters.
Exception text
is reinterpreted as HTML without escaping meta-characters.
Exception text
is reinterpreted as HTML without escaping meta-characters.
Exception text
is reinterpreted as HTML without escaping meta-characters.
Exception text
is reinterpreted as HTML without escaping meta-characters.
Exception text
is reinterpreted as HTML without escaping meta-characters.
Exception text
is reinterpreted as HTML without escaping meta-characters.
Exception text
is reinterpreted as HTML without escaping meta-characters.
} finally {
authState.merge({ isProcessing: false, error: '' })
}
Expand Down Expand Up @@ -605,7 +609,7 @@
},

async addConnectionByOauth(
oauth: 'facebook' | 'google' | 'github' | 'linkedin' | 'twitter' | 'discord',
oauth: 'apple' | 'facebook' | 'google' | 'github' | 'linkedin' | 'twitter' | 'discord',
userId: UserID
) {
window.open(`https://${config.client.serverHost}/auth/oauth/${oauth}?userId=${userId}`, '_blank')
Expand Down
2 changes: 2 additions & 0 deletions packages/client/src/pages/auth/authRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { Route, Routes } from 'react-router-dom'
import { LoadingCircle } from '@etherealengine/client-core/src/components/LoadingCircle'

import $magiclink from './magiclink'
import $apple from './oauth/apple'
import $discord from './oauth/discord'
import $facebook from './oauth/facebook'
import $github from './oauth/github'
Expand All @@ -41,6 +42,7 @@ const AuthRoutes = () => {
return (
<Suspense fallback={<LoadingCircle message={t('common:loader.loadingAuth')} />}>
<Routes>
<Route path="oauth/apple" element={<$apple />} />
<Route path="oauth/discord" element={<$discord />} />
<Route path="oauth/facebook" element={<$facebook />} />
<Route path="oauth/github" element={<$github />} />
Expand Down
Loading
Loading