Skip to content

Instantly share code, notes, and snippets.

@dmost714
Last active April 27, 2023 12:22
Show Gist options
  • Save dmost714/f2df53309653b9619fa4ff0f1aad31b5 to your computer and use it in GitHub Desktop.
Save dmost714/f2df53309653b9619fa4ff0f1aad31b5 to your computer and use it in GitHub Desktop.
I have a SAAS build with AWS Amplify. Unauthenticated users will see the marketing page, which has a 'sign-up/sign-in' button on it. Authenticated users will get the dashboard. Amplify's useAuthenticator hook lets you know if the user is signed in or not. The routing is handled using react-router-v6. Only the / route (index) shows the marketing …
import { Authenticator, View, Image, Text, Heading, useTheme, useAuthenticator } from '@aws-amplify/ui-react'
import { BrowserRouter } from "react-router-dom"
import { Routes, Route } from "react-router-dom"
import AppRoutes from "./AppRoutes"
import MarketingPage from './routes/MarketingPage'
import logoSvg from './logo.svg'
function App() {
const { route, user, signOut } = useAuthenticator(context => [context.route, context.user, context.isPending])
const UnauthenticatedUserRoutes = () =>
<Routes>
{/* Wrap marketing page in a hidden authenticator to work around a bug. */}
<Route index element={<><Authenticator className='hidden' /> <MarketingPage /></>} />
{/* <Route index element={<MarketingPage />} /> */}
<Route path="*" element={
<Authenticator
className='flex flex-col items-center justify-center w-screen h-screen bg-slate-50 min-w-max' />
} />
</Routes >
return route === 'authenticated' ? <AppRoutes user={user} signOut={signOut} /> : <UnauthenticatedUserRoutes />
}
const AppWithRouter = () => <BrowserRouter><App /></BrowserRouter>
export default AppWithRouter
import { useState } from "react"
import PropTypes from 'prop-types'
import {
Routes,
Route,
Navigate
} from "react-router-dom"
import { CognitoUserAmplify } from '@aws-amplify/ui-react/node_modules/@aws-amplify/ui'
import PageWrapper from './components/PageWrapper'
import NoMatch from "./routes/NoMatch"
import NotAuthorized from "./routes/NotAuthorized"
import Authorize from "./routes/Authorize"
import Home from "./routes/Home"
import SignOut from "./routes/SignOut"
import Orders from './routes/Orders'
import OrderPage from './routes/OrderPage'
import Campaigns from './routes/Campaigns'
import CampaignPage from './routes/CampaignPage'
import CampaignNew from './routes/CampaignNew'
import useGQLList from './hooks/useGQLList'
const AppRoutes = ({ user, signOut }: { user: CognitoUserAmplify, signOut: (data?: Record<string | number | symbol, any> | undefined) => void }) => {
const groups = user.getSignInUserSession()?.getAccessToken().payload["cognito:groups"] || []
const [accountId, setAccountId] = useState<string>(groups[0])
const isAdministrator = groups.find((group: string) => group === "ADMINISTRATOR")
// const isAgent = groups.find(group => group === "AGENT")
const { items: accounts, loading, error } = useGQLList('listAccounts', null)
const unauthorizedUserRoutes = (
<Routes>
<Route path="*" element={<NotAuthorized />} />
</Routes>
)
const agentRoutes = (
<Routes>
<Route path="/" element={
<PageWrapper
user={user}
signOut={signOut}
accounts={accounts}
currentAccount={accountId}
setAccountId={setAccountId} />
}>
<Route index element={<Home account={accounts.find(account => account.id === accountId)} />} />
<Route path="*" element={<NoMatch />} />
<Route path="enter" element={<Navigate to={'/'} replace />} />
<Route path='signout' element={<SignOut signOut={signOut} />} />
<Route path="orders" element={<Orders accountId={accountId!} />}>
<Route path=":orderId" element={<OrderPage />} />
<Route index element={
<div className='p-4'>
<p>Select an order</p>
</div>
} />
</Route>
<Route path="campaigns" element={<Campaigns accountId={accountId!} />}>
<Route path=":campaignId" element={<CampaignPage />} />
<Route index element={<CampaignNew accountId={accountId!} />} />
</Route>
{isAdministrator &&
<Route path='authorize/:userName' element={<Authorize />} />}
</Route>
</Routes>
)
if (loading && 0 === accounts.length) return null
if (error) return <div className="m-4">An error occurred loading accounts.</div>
return (
accountId ? agentRoutes : unauthorizedUserRoutes
)
}
AppRoutes.propTypes = {
children: PropTypes.any,
user: PropTypes.object
}
export default AppRoutes
import React from 'react'
import ReactDOM from 'react-dom'
import './tailwind.css'
import { Amplify } from "aws-amplify"
import App from './App'
import { AmplifyProvider, Authenticator } from '@aws-amplify/ui-react'
// @ts-ignore
import awsExports from "./aws-exports"
Amplify.configure(awsExports)
ReactDOM.render(
<React.StrictMode>
<AmplifyProvider>
<Authenticator.Provider>
<App />
</Authenticator.Provider>
</AmplifyProvider>
</React.StrictMode>,
document.getElementById('root')
)
import logoSvg from '../logo.svg'
import { Link } from "react-router-dom"
const MarketingPage = () => {
return <>
<div className='w-screen h-screen bg-slate-50 min-w-max'>
<Link to="enter" className='absolute right-2 top-6 btn animate-wiggle'>sign-up / sign-in</Link>
<div className='w-screen h-full overflow-auto'>
<div className='flex flex-row items-center p-2'>
<img
alt="logo"
src={logoSvg}
width="460"
height="460"
className='w-20'
/>
<div className='ml-2 text-lg font-bold'>Yet another SASS app</div>
</div>
<div className='p-2 h-[200px] m-2 bg-slate-200' />
<div className='flex flex-row p-2 m-2 bg-slate-200' >
<div className='p-2 w-1/2 h-[200px] m-2 bg-slate-400' />
<div className='p-2 w-1/2 h-[200px] m-2 bg-slate-400' />
</div>
<div className='flex flex-row p-2 m-2 bg-slate-200' >
<div className='p-2 w-1/3 h-[200px] m-2 bg-slate-400' />
<div className='p-2 w-1/3 h-[200px] m-2 bg-slate-400' />
<div className='p-2 w-1/3 h-[200px] m-2 bg-slate-400' />
</div>
<div className='p-2 h-[100px] m-2 bg-slate-200' />
<div className='flex flex-row p-2 m-2 bg-slate-200' >
<div className='p-2 w-1/2 h-[200px] m-2 bg-slate-400' />
<div className='p-2 w-1/2 h-[200px] m-2 bg-slate-400' />
</div>
<div className='flex flex-row p-2 m-2 bg-slate-200' >
<div className='p-2 w-1/3 h-[200px] m-2 bg-slate-400' />
<div className='p-2 w-1/3 h-[200px] m-2 bg-slate-400' />
<div className='p-2 w-1/3 h-[200px] m-2 bg-slate-400' />
</div>
<div className='p-2 h-[100px] m-2 bg-slate-200' />
</div>
</div>
</>
}
export default MarketingPage
@dmost714
Copy link
Author

With the above, if you hit a deep link and you're signed-in, you'll skip though the auth experience and see the page.
If you visit a deep link and you're not signed-in, you'll be prompted to sign-in, then see your page.
If you visit "/" while not signed-in, you see the unauthenticated routes (marketing page).

The marketing page's sign-in button goes to /enter, and there is an authenticated route that replaces that with / so people will landing on the default dashboard page.

BUG: If you are signed-in and on the default dashboard page "/" and refresh the browser, you'll see the marketing page. This is because useAuthenticator() needs <Authenticator> to be rendered once to get things started. This should be fixed shortly. Until then, you can put a hidden Authenticator component NEXT TO your marketing page (as the code above does).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment