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

689 authentication #1320

Merged
merged 27 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
92cd980
Passwordless login prototype. First stab at adding auth client to con…
Oct 16, 2023
24b7789
Merge branch 'master' into 689-authentication
Oct 18, 2023
f74af9f
Begin building out AuthService SessionStorage API and Context object …
Oct 25, 2023
ac1abee
Read sessionStorage for user session upon loading page
Oct 31, 2023
7d8f932
689 Create logout functionality. Create call to Auth0 that resets acc…
Nov 1, 2023
4e46dbe
689 add sign out functionality and update sign-in/sign-up UI and veri…
Nov 7, 2023
7bf9444
Complete basic user sign up flow and check if user exists prior to si…
Dec 8, 2023
b541a93
Update user exists route
Jan 3, 2024
bbf2da2
PR cleanup, clarifications, and comments.
Jan 5, 2024
d763060
Rename sign in and sign out pages and routes to log in and log out
Jan 8, 2024
67e35a8
689 Change a few instances of sign-in to log-in
Feb 5, 2024
76f01dc
689 save user to database after creating them in Auth0
Feb 8, 2024
84b26d0
689 Run prettier and lean up auth PR
Feb 9, 2024
e732230
689 no longer check if user exists prior to creating user. For variou…
Feb 21, 2024
9d7f87c
Create user endpoint argument name change
Feb 23, 2024
379cd02
Prettier
Feb 23, 2024
2182b67
689 use config value for logout redirect param
Feb 26, 2024
a1f1d3b
Merge branch 'master' of https://github.com/ShelterTechSF/askdarcel-w…
Mar 5, 2024
605e87a
Add types to AppContext object
Mar 6, 2024
c4b4ba7
689 migrate AuthService and SessionCacher from class based component …
Mar 14, 2024
b169d0c
More misc PR updates
Mar 14, 2024
7c8ae80
689 combine useAppContext and AppProvider files. Remove isAuthenticat…
schroerbrian Mar 19, 2024
bf686b4
Prettier
schroerbrian Mar 19, 2024
e3631cf
689 moar PR cleanup
schroerbrian Mar 21, 2024
8554f21
Lint fix
schroerbrian Mar 21, 2024
8b9be56
689 PR feedback and cleanup
schroerbrian Mar 25, 2024
97cde91
Fix argument name
schroerbrian Mar 25, 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
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ module.exports = {
"@typescript-eslint/require-await": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/unbound-method": "off",
"arrow-body-style": "off",
Copy link
Contributor Author

@schroerbrian schroerbrian Feb 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a not necessarily helpful, and I am getting tired of the linter yelling at me every time I need to change a function from being implicitly returned to being in block body when extra logic is needed

"no-unused-expressions": "off",
"no-unused-vars": "off",
"no-use-before-define": "off",
Expand Down
17 changes: 10 additions & 7 deletions app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Intercom from "react-intercom";
import { Helmet } from "react-helmet-async";
import { Redirect, Route, Switch, useHistory } from "react-router-dom";

import { AppContext, GeoCoordinates, getLocation, whiteLabel } from "./utils";
import { GeoCoordinates, getLocation, whiteLabel, AppProvider } from "./utils";
import {
Banner,
HamburgerMenu,
Expand All @@ -19,6 +19,7 @@ import {
PopupMessageProp,
UserWay,
} from "./components/ui";

import config from "./config";
import MetaImage from "./assets/img/sfsg-preview.png";

Expand All @@ -43,6 +44,9 @@ import OrganizationEditPage from "./pages/OrganizationEditPage";
import { EditBreakingNewsPage } from "./pages/EditBreakingNewsPage";
import { ServiceDiscoveryForm } from "./pages/ServiceDiscoveryForm";
import { ServiceDiscoveryResults } from "./pages/ServiceDiscoveryResults";
import { LoginPage } from "./pages/Auth/LoginPage";
import { SignUpPage } from "./pages/Auth/SignUpPage";
import { LogoutPage } from "./pages/Auth/LogoutPage";

import styles from "./App.module.scss";

Expand Down Expand Up @@ -99,8 +103,7 @@ export const App = () => {

return (
<div id={outerContainerId} className={styles.outerContainer}>
{/* eslint-disable-next-line react/jsx-no-constructed-context-values */}
<AppContext.Provider value={{ userLocation }}>
<AppProvider userLocation={userLocation}>
<Helmet>
<title>{title}</title>
<meta property="og:url" content={siteUrl} />
Expand Down Expand Up @@ -144,7 +147,6 @@ export const App = () => {
/>
<Route exact path="/about" component={AboutPage} />
<Route exact path="/demo/listing" component={ListingDebugPage} />

{/* NB: /organizations/new must be listed before /organizations/:id or else the /new
step will be interpreted as an ID and will thus break the OrganizationEditPage */}
<Route
Expand Down Expand Up @@ -219,14 +221,16 @@ export const App = () => {
path="/breaking-news/edit"
component={EditBreakingNewsPage}
/>
<Route exact path="/log-in" component={LoginPage} />
<Route exact path="/sign-up" component={SignUpPage} />
<Route exact path="/log-out" component={LogoutPage} />

{/* UCSF white label paths */}
<Route
exact
path="/find-services/:selectedResourceSlug"
component={UcsfDiscoveryForm}
/>

{/* Legacy redirects */}
<Redirect path="/resource/new" to="/organizations/new" />
<Route
Expand All @@ -239,13 +243,12 @@ export const App = () => {
path="/resource"
component={RedirectToOrganizations}
/>

<Redirect to="/" />
</Switch>
</div>
{popUpMessage && <PopUpMessage popUpMessage={popUpMessage} />}
</div>
</AppContext.Provider>
</AppProvider>
</div>
);
};
83 changes: 48 additions & 35 deletions app/components/ui/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React, { FormEvent, useState } from "react";
import { Link, useHistory } from "react-router-dom";
import cn from "classnames";
import qs from "qs";
import { useAppContext, whiteLabel } from "utils";
import Translate from "./Translate";
import whiteLabel from "../../utils/whitelabel";
import styles from "./Navigation.module.scss";

const {
Expand Down Expand Up @@ -89,46 +89,59 @@ const SiteLogo = () =>
</Link>
);

const SiteLinks = () => (
<ul className={styles.navRight}>
<li>
<Link to="/about">About</Link>
</li>
<li>
<a
href="https://help.sfserviceguide.org"
target="_blank"
rel="noopener noreferrer"
>
FAQ
</a>
</li>
<li>
<a
href="https://help.sfserviceguide.org/en/collections/1719243-contact-us"
target="_blank"
rel="noopener noreferrer"
>
Contact Us
</a>
</li>
{showReportCrisis && (
const SiteLinks = () => {
const { authState } = useAppContext();

return (
<ul className={styles.navRight}>
{/* Todo: This will eventually be replaced by a user icon with a dropdown menu of account related options.
The designs are still forthcoming. For now, it serves as a basic log-out functionality for the purposes
of development and testing.
*/}
{authState && (
<li>
<Link to="/log-out">Log Out</Link>
</li>
)}
<li>
<Link to="/about">About</Link>
</li>
<li>
<a
href="https://help.sfserviceguide.org"
target="_blank"
rel="noopener noreferrer"
>
FAQ
</a>
</li>
<li>
<a
type="button"
aria-label="report street crisis"
href="https://sf.gov/information/reporting-concerns-about-street-crises-and-conditions"
className={styles.buttonLink}
target="blank"
href="https://help.sfserviceguide.org/en/collections/1719243-contact-us"
target="_blank"
rel="noopener noreferrer"
>
Report Street Crisis
Contact Us
</a>
</li>
)}
<Translate />
</ul>
);
{showReportCrisis && (
<li>
<a
type="button"
aria-label="report street crisis"
href="https://sf.gov/information/reporting-concerns-about-street-crises-and-conditions"
className={styles.buttonLink}
target="blank"
rel="noopener noreferrer"
>
Report Street Crisis
</a>
</li>
)}
<Translate />
</ul>
);
};

const SiteSearch = ({
query,
Expand Down
17 changes: 16 additions & 1 deletion app/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,35 @@ declare global {
}

interface Config {
ALGOLIA_APPLICATION_ID: string;
ALGOLIA_INDEX_PREFIX: string;
ALGOLIA_READ_ONLY_API_KEY: string;
AUTH0_AUDIENCE: string;
AUTH0_CLIENT_ID: string;
AUTH0_DOMAIN: string;
AUTH0_REDIRECT_URI: string;
GOOGLE_ANALYTICS_ID: string;
GOOGLE_ANALYTICS_GA4_ID: string;
GOOGLE_API_KEY: string;
INTERCOM_APP_ID: string;
LINKSF_DOMAIN: string;
MOHCD_DOMAIN: string;
MOHCD_SUBDOMAIN: string;
SENTRY_PROJECT_ID: string;
SENTRY_PUBLIC_KEY: string;
SFFAMILIES_DOMAIN: string;
SFFAMILIES_USERWAY_APP_ID: string;
UCSF_DOMAIN: string;
[key: string]: any;
}

const config: Config = {
ALGOLIA_APPLICATION_ID: CONFIG.ALGOLIA_APPLICATION_ID,
ALGOLIA_INDEX_PREFIX: CONFIG.ALGOLIA_INDEX_PREFIX,
ALGOLIA_READ_ONLY_API_KEY: CONFIG.ALGOLIA_READ_ONLY_API_KEY,
AUTH0_AUDIENCE: CONFIG.AUTH0_AUDIENCE,
AUTH0_CLIENT_ID: CONFIG.AUTH0_CLIENT_ID,
AUTH0_DOMAIN: CONFIG.AUTH0_DOMAIN,
AUTH0_REDIRECT_URI: CONFIG.AUTH0_REDIRECT_URI,
// When GA sunsets Universal Analytics wit GA4 in July 2023, this prop can be removed
GOOGLE_ANALYTICS_ID:
process.env.NODE_ENV === "production" ? "UA-116318550-1" : "UA-116318550-2",
Expand Down
28 changes: 28 additions & 0 deletions app/pages/Auth/Auth.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@import "~styles/utils/_helpers.scss";

.authPage {
display: grid;
grid-column: 2 / span 10;
gap: 10px;
margin: 20px;
}

.verificationDigits {
display: flex;
width: 400px;
}

.title {
font-size: 24px;
}

.authForm {
width: 66%;
display: grid;
gap: 8px;
}

.authFormButton {
max-width: 300px;
margin-top: 8px;
}
57 changes: 57 additions & 0 deletions app/pages/Auth/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { useState } from "react";
import { Link } from "react-router-dom";
import { Button } from "components/ui/inline/Button/Button";
import { useAppContext, passwordlessLogin, passwordlessStart } from "utils";

import { VerificationModal } from "./VerificationModal";

import styles from "./Auth.module.scss";

export const LoginPage = () => {
const [modalIsOpen, setModalIsOpen] = useState(false);
const [email, setEmail] = useState("");
const { authClient } = useAppContext();

const logIn = (evt: React.SyntheticEvent) => {
evt.preventDefault();
passwordlessStart(authClient, email).then(() => {
setModalIsOpen(true);
});
};

return (
<div className={styles.authPage}>
<h1 className={styles.title}>For Case Managers</h1>
<Link to="/sign-up">New here? Sign up!</Link>
<p>
We want to make sure that your account information is safe, so you will
be sent a verification code to your email each time you log in. Please
enter in your email address and then check your email to find a 6 digit
verification code.
</p>
<form className={styles.authForm} onSubmit={logIn}>
<input
type="text"
name="email"
placeholder="Email address"
value={email}
onChange={(evt) => {
setEmail(evt.target.value);
}}
/>
<Button addClass={styles.authFormButton} buttonType="submit">
Sign In
</Button>
</form>

<VerificationModal
email={email}
modalIsOpen={modalIsOpen}
setModalIsOpen={setModalIsOpen}
verifyCode={(code) => passwordlessLogin(authClient, email, code)}
resendCode={() => passwordlessStart(authClient, email)}
buttonText="Log in"
/>
</div>
);
};
18 changes: 18 additions & 0 deletions app/pages/Auth/LogoutPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, { useEffect } from "react";
import { Redirect } from "react-router-dom";
import * as AuthService from "utils/AuthService";
import { useAppContext } from "../../utils";

import Config from "../../config";

export const LogoutPage = () => {
const context = useAppContext();
const { setAuthState } = context;
const { authClient } = context;

useEffect(() => {
AuthService.logout(authClient, Config.AUTH0_CLIENT_ID, setAuthState);
});

return <Redirect to="/" />;
};
Loading
Loading