Skip to content

Commit

Permalink
feat(react): Add auth
Browse files Browse the repository at this point in the history
  • Loading branch information
donmccurdy committed Sep 4, 2024
1 parent b12ccb0 commit 467a765
Show file tree
Hide file tree
Showing 15 changed files with 245 additions and 39 deletions.
52 changes: 47 additions & 5 deletions packages/create-common/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ body {

.app-bar img {
float: left;
margin-right: 0.5em;
margin: 0 1em 0 0.5em;
max-height: 32px;
}

Expand All @@ -229,7 +229,7 @@ body {
height: 100%;
}

.app-bar-nav a {
.app-bar a {
opacity: 0.7;
color: inherit;
text-decoration: none;
Expand All @@ -242,16 +242,20 @@ body {
0.2s ease background;
}

.app-bar-nav a:hover {
.app-bar a:hover {
background: var(--app-bar-tab-background-hover);
}

.app-bar-nav:not(:hover) a.active,
.app-bar-nav a:hover {
.app-bar:not(:hover) a.active,
.app-bar a:hover {
opacity: 1;
border-bottom: 2px solid var(--card-background);
}

.app-bar .flex-space {
flex-grow: 1;
}

/******************************************************************************
* SIDEBAR
*/
Expand All @@ -272,6 +276,44 @@ body {
flex-grow: 1;
}

/******************************************************************************
* LOGIN
*/

.login {
background: var(--app-bar-background);
color: var(--app-bar-text-primary);
width: 100%;
height: 100%;
padding: 5em;
}

.login .login-logo {
width: 100px;
}

.login .login-logo,
.login .subtitle {
margin-bottom: 5em;
}

@media screen and (min-width: 500px) {
.login .title {
font-size: 48px;
}
}

.login button {
background: var(--text-link);
color: var(--app-bar-text-primary);
padding: 0.5em 2em;
}

.login button:hover {
text-decoration: none;
filter: brightness(1.2);
}

/******************************************************************************
* MAP
*/
Expand Down
5 changes: 5 additions & 0 deletions packages/create-react/.env
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
# Populated by @carto/create-common.
VITE_APP_TITLE="$title"
VITE_CARTO_ACCESS_TOKEN="$accessToken"

VITE_CARTO_AUTH_ENABLED="$authEnabled"
# VITE_CARTO_AUTH_DOMAIN="$authDomain"
VITE_CARTO_AUTH_CLIENT_ID="$authClientID"
10 changes: 9 additions & 1 deletion packages/create-react/public/carto.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 24 additions & 2 deletions packages/create-react/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
import { Auth0Provider } from '@auth0/auth0-react';
import { RouterProvider } from 'react-router-dom';
import { AppContext, DEFAULT_APP_CONTEXT } from './context';
import { router } from './routes';
import { useEffect, useState } from 'react';

function App() {
const [accessToken, setAccessToken] = useState(
DEFAULT_APP_CONTEXT.accessToken,
);

useEffect(() => void (document.title = DEFAULT_APP_CONTEXT.title), []);

return (
<AppContext.Provider value={DEFAULT_APP_CONTEXT}>
<RouterProvider router={router} />
<AppContext.Provider
value={{ ...DEFAULT_APP_CONTEXT, accessToken, setAccessToken }}
>
<Auth0Provider
domain={DEFAULT_APP_CONTEXT.oauth.domain}
clientId={DEFAULT_APP_CONTEXT.oauth.clientId}
cacheLocation="localstorage"
authorizationParams={{
audience: DEFAULT_APP_CONTEXT.oauth.audience,
organization: DEFAULT_APP_CONTEXT.oauth.organizationId,
redirect_uri: window.location.origin,
scope: DEFAULT_APP_CONTEXT.oauth.scopes.join(' '),
}}
>
<RouterProvider router={router} />
</Auth0Provider>
</AppContext.Provider>
);
}
Expand Down
12 changes: 11 additions & 1 deletion packages/create-react/src/components/common/AppLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ReactNode, useContext } from 'react';
import { AppContext } from '../../context';
import { NAV_ROUTES } from '../../routes';
import { NAV_ROUTES, RoutePath } from '../../routes';
import { NavLink, Outlet } from 'react-router-dom';

export default function AppLayout() {
Expand All @@ -27,6 +27,16 @@ export default function AppLayout() {
)}
<span className="app-bar-text body1 strong">{context.title}</span>
<nav className="app-bar-nav">{navLinks}</nav>
<span className="flex-space" />
{context.oauth.enabled && (
<NavLink
to={RoutePath.LOGOUT}
className="body2 strong"
reloadDocument
>
Sign out
</NavLink>
)}
</header>
<div className="container">
<Outlet />
Expand Down
23 changes: 12 additions & 11 deletions packages/create-react/src/components/common/ProtectedRoute.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import { Navigate } from 'react-router-dom';
// import { useAuth0 } from '@auth0/auth0-react';
import { RoutePath } from '../../routes';
import { useAuth0 } from '@auth0/auth0-react';
import { AppContext } from '../../context';
import { useContext } from 'react';
import { useAuth } from '../../hooks/useAuth';

export default function ProtectedRoute({
children,
}: {
children: JSX.Element;
}) {
// TODO(impl): Auth
// const { isAuthenticated, isLoading } = useAuth0();
useAuth();
const { isAuthenticated, isLoading } = useAuth0();
const { oauth, accessToken } = useContext(AppContext);

// if (!initialState.oauth) {
// return children;
// }

const authenticated = true; //notAuthenticated = !isLoading && !isAuthenticated && !accessToken;
if (!oauth.enabled) {
return children;
}

if (!authenticated) {
if (!isLoading && !isAuthenticated && !accessToken) {
return <Navigate to={RoutePath.LOGIN} />;
}

return children;
// return !!accessToken ? children : null;
return accessToken ? children : null;
}
9 changes: 6 additions & 3 deletions packages/create-react/src/components/views/Default.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from 'react';
import { useContext, useEffect, useMemo, useState } from 'react';

import { Map } from 'react-map-gl/maplibre';
import DeckGL from '@deck.gl/react';
Expand All @@ -11,6 +11,7 @@ import { Layers } from '../common/Layers';
import { FormulaWidget } from '../widgets/FormulaWidget';
import { CategoryWidget } from '../widgets/CategoryWidget';
import { useDebouncedState } from '../../hooks';
import { AppContext } from '../../context';

const MAP_VIEW = new MapView({ repeat: true });
const MAP_STYLE =
Expand All @@ -31,6 +32,7 @@ const RADIO_COLORS: AccessorFunction<unknown, Color> = colorCategories({
});

export default function Default() {
const { accessToken, apiBaseUrl } = useContext(AppContext);
const [filters, setFilters] = useState({} as Record<string, Filter>);
const [attributionHTML, setAttributionHTML] = useState('');
const [viewState, setViewState] = useDebouncedState(INITIAL_VIEW_STATE, 200);
Expand All @@ -41,13 +43,14 @@ export default function Default() {

const data = useMemo(() => {
return vectorQuerySource({
accessToken: import.meta.env.VITE_CARTO_ACCESS_TOKEN,
accessToken,
apiBaseUrl,
connectionName: 'carto_dw',
sqlQuery:
'SELECT * FROM `carto-demo-data.demo_tables.cell_towers_worldwide`',
filters,
});
}, [filters]);
}, [accessToken, apiBaseUrl, filters]);

/****************************************************************************
* Layers (https://deck.gl/docs/api-reference/carto/overview#carto-layers)
Expand Down
18 changes: 17 additions & 1 deletion packages/create-react/src/components/views/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
import { useAuth0 } from '@auth0/auth0-react';
import { useContext } from 'react';
import { AppContext } from '../../context';

export default function Login() {
return <h1>Login</h1>;
const { title, logo } = useContext(AppContext);
const { loginWithRedirect } = useAuth0();
return (
<main className="login">
{logo && <img className="login-logo" src={logo.src} alt={logo.alt} />}
<h1 className="title">{title}</h1>
<p className="subtitle">Discover the power of developing with React</p>
<button className="body1" onClick={() => loginWithRedirect()}>
Login with CARTO
</button>
<p className="caption">Use your CARTO credentials</p>
</main>
);
}
9 changes: 9 additions & 0 deletions packages/create-react/src/components/views/Logout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useAuth0 } from '@auth0/auth0-react';

export default function Logout() {
const { logout } = useAuth0();

logout();

return <p>Logging out…</p>;
}
9 changes: 6 additions & 3 deletions packages/create-react/src/components/views/Secondary.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from 'react';
import { useContext, useEffect, useMemo, useState } from 'react';

import { Map } from 'react-map-gl/maplibre';
import DeckGL from '@deck.gl/react';
Expand All @@ -8,6 +8,7 @@ import { h3TableSource } from '@carto/api-client';
import { Legend } from '../common/Legend';
import { Layers } from '../common/Layers';
import { Card } from '../common/Card';
import { AppContext } from '../../context';

const MAP_VIEW = new MapView({ repeat: true });
const MAP_STYLE =
Expand All @@ -28,6 +29,7 @@ const POP_COLORS: AccessorFunction<unknown, Color> = colorContinuous({
});

export default function Default() {
const { accessToken, apiBaseUrl } = useContext(AppContext);
const [attributionHTML, setAttributionHTML] = useState('');
const [viewState, setViewState] = useState(INITIAL_VIEW_STATE);

Expand All @@ -37,14 +39,15 @@ export default function Default() {

const data = useMemo(() => {
return h3TableSource({
accessToken: import.meta.env.VITE_CARTO_ACCESS_TOKEN,
accessToken,
apiBaseUrl,
connectionName: 'carto_dw',
tableName:
'carto-demo-data.demo_tables.derived_spatialfeatures_usa_h3res8_v1_yearly_v2',
spatialDataColumn: 'h3',
aggregationExp: 'SUM(population) as population_sum',
});
}, []);
}, [accessToken, apiBaseUrl]);

/****************************************************************************
* Layers (https://deck.gl/docs/api-reference/carto/overview#carto-layers)
Expand Down
42 changes: 33 additions & 9 deletions packages/create-react/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,50 @@ export interface AppContextProps {
src: string;
alt: string;
};
credentials: {
accessToken: string;
apiVersion?: string;
apiBaseUrl?: string;
};
accessToken: string;
setAccessToken: (token: string) => void;
apiVersion?: string;
apiBaseUrl?: string;
googleApiKey?: string;
googleMapId?: string;
accountsUrl?: string;
oauth: {
enabled: boolean;
domain: string;
clientId?: string;
organizationId?: string;
namespace: string;
scopes: string[];
audience: string;
authorizeEndPoint?: string;
};
}

export const DEFAULT_APP_CONTEXT = {
title: '$title',
title: import.meta.env.VITE_APP_TITLE,
logo: {
src: cartoLogo,
alt: 'CARTO logo',
},
credentials: {
accessToken: import.meta.env.VITE_CARTO_ACCESS_TOKEN,
},
accessToken: import.meta.env.VITE_CARTO_ACCESS_TOKEN,
setAccessToken: () => {},
apiBaseUrl: 'https://gcp-us-east1.api.carto.com',
accountsUrl: 'http://app.carto.com/',
oauth: {
enabled: import.meta.env.VITE_CARTO_AUTH_ENABLED === 'true',
domain: 'auth.carto.com',
clientId: import.meta.env.VITE_CARTO_AUTH_CLIENT_ID,
organizationId: import.meta.env.VITE_CARTO_AUTH_ORGANIZATION_ID, // Required for SSO.
namespace: 'http://app.carto.com/',
scopes: [
'read:current_user',
'read:connections',
'read:maps',
'read:account',
],
audience: 'carto-cloud-native-api',
authorizeEndPoint: 'https://carto.com/oauth2/authorize', // Only valid if keeping https://localhost:3000/oauthCallback
},
};

export const AppContext = createContext<AppContextProps>(DEFAULT_APP_CONTEXT);
Loading

0 comments on commit 467a765

Please sign in to comment.