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

[draft] email account sign up & settings QA demo branch #811

Open
wants to merge 111 commits into
base: shakao/auth-endpoints
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
8c091e4
sc-12766 handle authcheck object properly, clearing and updating
kiwansim Jul 5, 2023
a05a004
sc-12808 identify auth required routes to trigger redirect on logout
kiwansim Jul 7, 2023
e167596
prettier
kiwansim Jul 7, 2023
eb24022
call auth server api to check if email has acct
kiwansim Jul 20, 2023
dae81f8
sc-12776 login vs register screens toggle depending on existing accou…
kiwansim Jul 24, 2023
2126f2f
run black and lingui extract
kiwansim Jul 24, 2023
044ceb4
react context not updating when clicking log out on building page
kiwansim Aug 16, 2023
9dc0b3a
login edge cases, password regex fix, styling for alerts and errors
kiwansim Aug 30, 2023
210fe90
merge shakao/auth-endpoints
kiwansim Aug 30, 2023
1d983ff
run prettier and black
kiwansim Aug 30, 2023
988a3b4
eslint useless escape regex moved to top of file
kiwansim Aug 30, 2023
e775145
add preview site to whitelist
kiwansim Aug 30, 2023
1188537
handle submit debugging
kiwansim Aug 30, 2023
0f415e8
Revert "handle submit debugging"
kiwansim Aug 31, 2023
5e81dde
refactor login error logic
kiwansim Sep 1, 2023
4fdb100
refactor again
kiwansim Sep 1, 2023
da9ce0c
sc-13022 stricter regex for client side email validation
kiwansim Sep 12, 2023
348dd64
sc-13035 forgot password screen UI parity
kiwansim Sep 12, 2023
4d3de29
sc-13035 most of the UI parity issues found from VQA
kiwansim Sep 21, 2023
3c09c18
global button style update
kiwansim Sep 21, 2023
d8ba40f
UI parity for building page email alert container - add new pill, inf…
kiwansim Sep 24, 2023
9b3e75f
sc-13023 add redirect after successful password reset
kiwansim Oct 10, 2023
e070798
sc-13124 add standalone email verification success page
kiwansim Oct 10, 2023
7f6d8d7
modify email rule to include + symbol
kiwansim Oct 10, 2023
a05fc8d
update dead links to agency websites on timeline (#831)
austensen Nov 28, 2023
e1bb931
Use new combined hpd complaints and problems table (#832)
austensen Nov 28, 2023
82ccab1
add new compliant types for translation (#834)
austensen Nov 30, 2023
d9e312f
New Crowdin updates (#835)
JustFix-org Nov 30, 2023
2f1055f
Feature: Rent Stabilized Units Indicator (#838)
kiwansim Dec 8, 2023
f50ff8a
New translations messages.po (Spanish) (#839)
JustFix-org Dec 8, 2023
dc26f7d
Update indicators timeline start years & select components (#841)
kiwansim Jan 2, 2024
e1a8c0f
New Crowdin updates (#840)
JustFix-org Jan 2, 2024
a76e120
Merge branch 'shakao/auth-endpoints' into email-alerts-account-signup
kiwansim Jan 9, 2024
007c885
replace modal close button from plaintext to CloseButton import
kiwansim Jan 9, 2024
0d4d1ea
update modal style, add CTA modal for email verification
kiwansim Jan 10, 2024
c8a2ce4
ui parity for login on buildings page and prettier
kiwansim Jan 10, 2024
12b3cbc
Add API endpoint for email alerts data (#827)
austensen Jan 24, 2024
7b76e48
comment out unused vars
austensen Jan 31, 2024
6ae727e
add preview site to cors
austensen Jan 31, 2024
c1174a1
formatting
austensen Jan 31, 2024
4da1f9b
add another preview site to cors origin
austensen Feb 20, 2024
76b49a0
remove numbered deploy preview links, add regex
kiwansim Feb 22, 2024
19d2f6d
add wow demo site to cors allowed origins
austensen Feb 23, 2024
985ff15
remove "share" from nav (#851)
austensen Feb 27, 2024
79fba68
remove alert on overview map to filter portfolio (#844)
austensen Feb 27, 2024
3f2aef0
remove new pill on portfolio tab (#845)
austensen Feb 27, 2024
f081287
show modal & prevent subscription above 15 bldg limit (#842)
austensen Feb 27, 2024
aa253ec
Base font size change (#853)
austensen Mar 4, 2024
c5fba60
remove legacy/new banner (#850)
austensen Mar 5, 2024
6a0a1a8
Multi-step log in/sign up modal, with user type (#849)
austensen Mar 6, 2024
8182046
Add validation/error handling to account settings update credentials …
austensen Mar 7, 2024
8314f4f
change privacy info modal to new-tab links (#859)
austensen Mar 7, 2024
ce24640
lingui (#860)
austensen Mar 7, 2024
0a7dfd4
update unsubscribe page for unsubscribe-all via email (#858)
austensen Mar 8, 2024
c43f470
add tests and error validation for alert endpoints (#846)
austensen Mar 8, 2024
ad141cc
feature: autofocus the address search field (#863)
kfinn Mar 11, 2024
ea47e5b
Add react-error-overlay@6.0.9 (#856)
steve52 Mar 11, 2024
37d5014
Remove onsite landing page for email verification (#855)
kiwansim Mar 15, 2024
aa37c92
update dead links to agency websites on timeline (#831)
austensen Nov 28, 2023
9947f6d
Use new combined hpd complaints and problems table (#832)
austensen Nov 28, 2023
a186873
add new compliant types for translation (#834)
austensen Nov 30, 2023
d31a5d9
New Crowdin updates (#835)
JustFix-org Nov 30, 2023
c90650a
Feature: Rent Stabilized Units Indicator (#838)
kiwansim Dec 8, 2023
18bfc41
New translations messages.po (Spanish) (#839)
JustFix-org Dec 8, 2023
05ee194
Update indicators timeline start years & select components (#841)
kiwansim Jan 2, 2024
285eca2
New Crowdin updates (#840)
JustFix-org Jan 2, 2024
adcd385
Add API endpoint for email alerts data (#827)
austensen Jan 24, 2024
a8dc8d5
add tests and error validation for alert endpoints (#846)
austensen Mar 8, 2024
03a9b28
feature: autofocus the address search field (#863)
kfinn Mar 11, 2024
fbddf80
Add react-error-overlay@6.0.9 (#856)
steve52 Mar 11, 2024
8b07477
update hpd complaints table for alerts api (#864)
austensen Mar 18, 2024
73e021d
limit subscriptions to 15 (not 16) (#866)
austensen Mar 19, 2024
474227a
Email verification landing page states, expiration, resend email with…
kiwansim Mar 20, 2024
7c0a5b4
wip
austensen Mar 21, 2024
c45299b
Merge branch 'email-alerts-account-signup' of https://github.com/Just…
austensen Mar 21, 2024
fac68c8
more reset flow changes
austensen Mar 21, 2024
8de1111
Revert "more reset flow changes"
austensen Mar 21, 2024
6fad638
Revert "wip"
austensen Mar 21, 2024
dae2ac1
Button integration [sc-13707] (#857)
steve52 Mar 22, 2024
9c4b4f9
clear alert and input-errors on toggle login/signup (#865)
austensen Mar 22, 2024
24b672e
add reset password landing pages, validate new password inputs (#868)
austensen Mar 22, 2024
61af847
Login mirror bug (#870)
austensen Mar 25, 2024
d3ad521
Content updates (#869)
austensen Mar 26, 2024
49b94c2
refactor unsubscribe page (#872)
austensen Mar 26, 2024
3ce19b7
add component library buttons, fix verified condition on building pag…
austensen Mar 26, 2024
c97c19a
make all emails lowercase before calling api (#874)
kiwansim Mar 27, 2024
7d94f54
Use component library buttons in email alerts (#875)
austensen Mar 27, 2024
6033993
don't clear errors on login/signup toggle, only hide them (#876)
austensen Mar 27, 2024
c11a08a
component-library to v32.2 for alert style fixes (#880)
austensen Mar 27, 2024
ecd7ea1
custom "other" user type not required (#877)
austensen Mar 27, 2024
da4a2a7
Layout change and other style updates (#878)
kiwansim Mar 28, 2024
12288a1
More component library button updates (#879)
austensen Mar 28, 2024
6fbdddf
fix input error spacing, add util classes (#881)
austensen Mar 28, 2024
589ee09
fix forgot password link type styles on login (#883)
austensen Mar 28, 2024
70c6569
fix alignment issues (#882)
kiwansim Mar 28, 2024
b0fb06e
add focus state outline for all text inputs (#884)
austensen Mar 28, 2024
d901ba7
update component-library for tertiary button and link icon updates (#…
austensen Mar 29, 2024
c5ffb2b
add jfcl-locale-link, link focus, pw toggle focus (#886)
austensen Apr 1, 2024
57a178b
use 16px in text inputs to prevent zooming on mobile safari (#887)
austensen Apr 1, 2024
7926083
update HPD Online link for bbl search results, useful links and alert…
austensen Apr 2, 2024
37ceff8
Content updates for login flow (#888)
austensen Apr 2, 2024
fbff02e
Nav bar updates (#890)
kiwansim Apr 2, 2024
7a4c579
add verify step to nav signup flow, same as in modal (#891)
austensen Apr 4, 2024
55cd1d4
update content changes for user type options (#892)
austensen Apr 4, 2024
99717a2
Standalone page (#893)
austensen Apr 5, 2024
5aafdc5
[sc-13983] remove flickering between add building / success state (#894)
kiwansim Apr 5, 2024
4a235f6
Account settings and manage subscriptions page styles (#895)
kiwansim Apr 5, 2024
5634588
lingui extract (#896)
austensen Apr 5, 2024
aa74363
fix missing plural in lingui (#897)
austensen Apr 5, 2024
5f9cc25
merge master
kiwansim Apr 5, 2024
d4b0824
remove unused import
kiwansim Apr 5, 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
11 changes: 11 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,14 @@ CLIENT_SECRET=
#
# Head over to the `client` folder to see some client-specific ENV variables.
#

# =============
# EMAIL ALERTS API AUTHORIZATION
# =============
#
# Authorization token required to access protected WOW API endpoints
# used for Email Alerts. It can be set to any unique string with no spaces.
# Must be provided to our email alerts server. Even if not using this
# feature, a value must be provided.

ALERTS_API_TOKEN="token_required"
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ __pycache__/
db.sqlite3
*.log
.ssh/
.yalc/
yalc*
12 changes: 7 additions & 5 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"@amplitude/analytics-browser": "^1.2.3",
"@babel/core": "^7.7.2",
"@contentful/rich-text-react-renderer": "^15.0.0",
"@justfixnyc/component-library": "0.33.0",
"@justfixnyc/contentful-common-strings": "^0.1.0",
"@justfixnyc/geosearch-requester": "1.0.0",
"@justfixnyc/util": "^0.4.1",
Expand Down Expand Up @@ -75,7 +76,6 @@
"resize-observer-polyfill": "^1.5.1",
"rollbar": "^2.14.4",
"sass": "^1.49.9",
"spectre.scss": "0.0.2",
"typescript": "^4.9.3",
"widont": "^0.3.3",
"xstate": "^4.11.0"
Expand All @@ -88,8 +88,8 @@
"contentful": "node pull-contentful-data.js",
"contentful-common-strings": "contentful-common-strings fetch -o src/data/common-strings.json",
"typecheck": "tsc",
"build-css": "sass src/:src/ --no-source-map --load-path src/ --load-path node_modules/ --load-path node_modules/spectre.scss/src/",
"watch-css": "yarn build-css && sass src/:src/ --no-source-map --load-path src/ --load-path node_modules/ --load-path node_modules/spectre.scss/src/ --watch",
"build-css": "sass src/:src/ --no-source-map --load-path src/ --load-path node_modules/",
"watch-css": "yarn build-css && sass src/:src/ --no-source-map --load-path src/ --load-path node_modules/ --watch",
"start-js": "lingui compile && react-scripts start",
"start": "yarn contentful-common-strings --dev && npm-run-all -p watch-css start-js",
"build": "yarn contentful-common-strings && node write-version.js && yarn build-css && lingui compile && react-scripts build",
Expand All @@ -104,7 +104,8 @@
"dotenv": "^8.2.0",
"jest-fetch-mock": "^3.0.3",
"npm-run-all": "^4.0.2",
"prettier": "2.0.5"
"prettier": "2.0.5",
"react-error-overlay": "6.0.9"
},
"overrides": {
"@svgr/webpack": "$@svgr/webpack"
Expand All @@ -115,7 +116,8 @@
"loader-utils": "^2.0.4",
"set-value": "^2.0.1",
"minimist": "^0.2.4",
"immer": "^9.0.6"
"immer": "^9.0.6",
"react-error-overlay": "6.0.9"
},
"engines": {
"node": ">=12.0.0"
Expand Down
3 changes: 3 additions & 0 deletions client/src/assets/img/logo-divider.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions client/src/components/APIDataTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ export type MonthlyTimelineData = {
dobviolations_ecb: number;
dobviolations_total: number;
evictionfilings_total: number;
rentstabilizedunits_total: number;
};

export type IndicatorsHistoryResults = {
Expand Down
1 change: 1 addition & 0 deletions client/src/components/AddressSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ export default class AddressSearch extends React.Component<AddressSearchProps, S
{this.props.labelText}
</label>
<input
autoFocus
placeholder="Search places"
className="geosuggest__input form-input"
{...downshift.getInputProps(inputOptions)}
Expand Down
14 changes: 8 additions & 6 deletions client/src/components/AddressToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import ExportDataButton from "./ExportData";
import { useLocation } from "react-router-dom";
import { createWhoOwnsWhatRoutePaths } from "routes";
import { logAmplitudeEvent } from "./Amplitude";
import { Button, Link as JFCLLink } from "@justfixnyc/component-library";
import { i18n } from "@lingui/core";

export type AddressToolbarProps = {
searchAddr: SearchAddress;
Expand All @@ -28,25 +30,25 @@ const AddressToolbar: React.FC<AddressToolbarProps> = ({ searchAddr, assocAddrs
<div className="AddressToolbar">
<div className="btn-group">
<Link
className="btn"
onClick={() => {
logAmplitudeEvent("newSearch");
window.gtag("event", "new-search");
}}
to={isLegacyPath(pathname) ? legacy.home : home}
component={JFCLLink}
>
<Trans>New Search</Trans>
</Link>
<button
className="btn"
<Button
labelText={i18n._("Export Data")}
variant="secondary"
size="small"
onClick={() => {
setExportModalVisibility(true);
logAmplitudeEvent("clickExportData");
window.gtag("event", "click-export-data");
}}
>
<Trans>Export Data</Trans>
</button>
/>
</div>
<Modal showModal={showExportModal} onClose={() => setExportModalVisibility(false)}>
<Trans render="p">
Expand Down
161 changes: 141 additions & 20 deletions client/src/components/AuthClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ type VerifyEmailResponse = {
error?: string;
};

export enum ResetStatusCode {
Success = 200,
Accepted = 202,
Invalid = 400,
Expired = 410,
Unknown = 500,
}

type PasswordResetResponse = Omit<VerifyEmailResponse, "statusCode"> & {
statusCode: ResetStatusCode;
};

let _user: JustfixUser | undefined;
const user = () => _user;
const fetchUser = async () => {
Expand Down Expand Up @@ -47,8 +59,12 @@ const clearUser = () => (_user = undefined);
* Authenticates a user with the given email and password.
* Creates an account for this user if one does not already exist.
*/
const register = async (username: string, password: string) => {
const json = await postAuthRequest(`${BASE_URL}auth/register`, { username, password });
const register = async (username: string, password: string, userType: string) => {
const json = await postAuthRequest(`${BASE_URL}auth/register`, {
username: username.toLowerCase(),
password,
user_type: userType,
});
fetchUser();
return json;
};
Expand All @@ -58,26 +74,84 @@ const register = async (username: string, password: string) => {
* and expiry time.
*/
const login = async (username: string, password: string) => {
const json = await postAuthRequest(`${BASE_URL}auth/login`, { username, password });
const json = await postLoginCredentials(`${BASE_URL}auth/login`, {
username: username.toLowerCase(),
password,
});
return json;
};

/**
* Revokes the current access token, if one is present
*/
const logout = async () => {
await postAuthRequest(`${BASE_URL}auth/logout`);
clearUser();
return await postAuthRequest(`${BASE_URL}auth/logout`);
};

const resetPasswordRequest = async (username: string) => {
return await postAuthRequest(`${BASE_URL}auth/reset_password`, { username });
/**
* Sends request to send a password reset link to the user's email
*/
const resetPasswordRequest = async (username?: string) => {
try {
if (username) {
await postAuthRequest(`${BASE_URL}auth/reset_password_request`, {
username: username.toLowerCase(),
});
} else {
const params = new URLSearchParams(window.location.search);
await postAuthRequest(
`${BASE_URL}auth/reset_password_request_with_token?token=${params.get("token")}`
);
}
return true;
} catch {
return false;
}
};

/**
* Sends an unauthenticated request to checks if the password reset token is valid
*/
const resetPasswordCheck = async () => {
const params = new URLSearchParams(window.location.search);

let result: PasswordResetResponse = {
statusCode: ResetStatusCode.Unknown,
statusText: "",
};

try {
const response = await postAuthRequest(
`${BASE_URL}auth/reset_password/check?token=${params.get("token")}`
);
result.statusCode = response.status_code;
result.statusText = response.status_text;
} catch (e) {
if (e instanceof Error) {
result.error = e.message;
}
}
return result;
};

const resetPassword = async (token: string, newPassword: string) => {
return await postAuthRequest(`${BASE_URL}auth/set_password?token=${token}`, {
new_password: newPassword,
});
let result: PasswordResetResponse = {
statusCode: ResetStatusCode.Unknown,
statusText: "",
};

try {
const response = await postAuthRequest(`${BASE_URL}auth/set_password?token=${token}`, {
new_password: newPassword,
});
result.statusCode = response.status_code;
result.statusText = response.status_text;
} catch (e) {
if (e instanceof Error) {
result.error = e.message;
}
}
return result;
};

/**
Expand All @@ -88,7 +162,7 @@ const userAuthenticated = async () => {
};

/**
* Sends an authenticated request to verify the user email
* Sends an unauthenticated request to verify the user email
*/
const verifyEmail = async () => {
const params = new URLSearchParams(window.location.search);
Expand All @@ -115,9 +189,13 @@ const verifyEmail = async () => {
/**
* Sends request to resend the account verification link to the user's email
*/
const resendVerifyEmail = async () => {
const resendVerifyEmail = async (token?: string) => {
try {
await postAuthRequest(`${BASE_URL}auth/resend_verify_email`);
if (token) {
await postAuthRequest(`${BASE_URL}auth/resend_verification_with_token?u=${token}`);
} else {
await postAuthRequest(`${BASE_URL}auth/resend_verification`);
}
return true;
} catch {
return false;
Expand All @@ -128,7 +206,7 @@ const resendVerifyEmail = async () => {
* Sends an authenticated request to update the user email
*/
const updateEmail = async (newEmail: string) => {
return await postAuthRequest(`${BASE_URL}auth/update`, { new_email: newEmail });
return await postAuthRequest(`${BASE_URL}auth/update`, { new_email: newEmail.toLowerCase() });
};

/**
Expand Down Expand Up @@ -173,7 +251,9 @@ const buildingUnsubscribe = async (bbl: string) => {
};

const isEmailAlreadyUsed = async (email: string) => {
const result = await friendlyFetch(`${BASE_URL}auth/account_exists/${email}`, {
const sanitizedEmail = email.toLowerCase();

const result = await friendlyFetch(`${BASE_URL}auth/account_exists/${sanitizedEmail}`, {
method: "GET",
mode: "cors",
headers: {
Expand All @@ -187,15 +267,22 @@ const isEmailAlreadyUsed = async (email: string) => {
/**
* Sends an unauthenticated request to unsubscribe the user from the building
*/
const emailBuildingUnsubscribe = async (bbl: string, token: string) => {
const emailUnsubscribeBuilding = async (bbl: string, token: string) => {
return await postAuthRequest(`${BASE_URL}auth/unsubscribe/${bbl}?u=${token}`);
};

/**
* Sends an unauthenticated request to unsubscribe the user from all buildings
*/
const emailUnsubscribeAll = async (token: string) => {
return await postAuthRequest(`${BASE_URL}auth/email/unsubscribe?u=${token}`);
};

/**
* Fetches the list of all subscriptions associated with a user
*/
const userSubscriptions = async (token: string) => {
return await postAuthRequest(`${BASE_URL}auth/subscriptions?u=${token}`);
const emailUserSubscriptions = async (token: string) => {
return await postAuthRequest(`${BASE_URL}auth/email/subscriptions?u=${token}`);
};

/**
Expand Down Expand Up @@ -227,17 +314,49 @@ const postAuthRequest = async (
try {
if (result.ok) {
return await result.json();
} else {
return await result.json();
}
} catch {
return result;
}
};

/**
* Wrapper function for POST requests returning JSON response body
*/

const postLoginCredentials = async (
url: string,
params?: { [key: string]: string },
headers?: { [key: string]: string },
method: string = "POST"
) => {
const body = params
? Object.keys(params)
.map((k) => `${k}=${encodeURIComponent(params[k])}`)
.join("&")
: "";
const result = await friendlyFetch(url, {
method,
mode: "cors",
body,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
...headers,
},
credentials: "include",
});

return await result.json();
};

// TODO shakao Move shared APIClient functions to util
const friendlyFetch: typeof fetch = async (input, init) => {
let response: Response;
try {
response = await fetch(input, init);
console.log(response);
} catch (e) {
if (e instanceof Error) {
throw new NetworkError(e.message);
Expand All @@ -262,11 +381,13 @@ const Client = {
updateEmail,
updatePassword,
resetPasswordRequest,
resetPasswordCheck,
resetPassword,
buildingSubscribe,
buildingUnsubscribe,
userSubscriptions,
emailBuildingUnsubscribe,
emailUserSubscriptions,
emailUnsubscribeBuilding,
emailUnsubscribeAll,
};

export default Client;
Loading