Skip to content

Commit

Permalink
feat: breaking use account-js for authentication
Browse files Browse the repository at this point in the history
BREAKING CHANGE: the authorization methods have been switched to account-js

Signed-off-by: Akarshit Wal <akarshitwal@gmail.com>
  • Loading branch information
Akarshit committed Aug 16, 2021
1 parent b4935ae commit 7a509c9
Show file tree
Hide file tree
Showing 32 changed files with 2,981 additions and 2,425 deletions.
13 changes: 1 addition & 12 deletions .env.example
@@ -1,21 +1,10 @@
CANONICAL_URL=http://localhost:4000
ENABLE_SPA_ROUTING=true
BUILD_GRAPHQL_URL=http://localhost:3000/graphql
EXTERNAL_GRAPHQL_URL=http://localhost:3000/graphql
INTERNAL_GRAPHQL_URL=http://api.reaction.localhost:3000/graphql
OAUTH2_ADMIN_PORT=4445
OAUTH2_ADMIN_URL=http://hydra.reaction.localhost:4445
OAUTH2_AUTH_URL=http://localhost:4444/oauth2/auth
OAUTH2_CLIENT_ID=example-storefront
OAUTH2_CLIENT_SECRET=CHANGEME
OAUTH2_PUBLIC_LOGOUT_URL=http://localhost:4444/oauth2/sessions/logout
OAUTH2_HOST=hydra.reaction.localhost
OAUTH2_IDP_PUBLIC_CHANGE_PASSWORD_URL=http://localhost:4100/account/change-password?email=EMAIL&from=FROM
OAUTH2_IDP_HOST_URL=http://identity.reaction.localhost:4100
OAUTH2_TOKEN_URL=http://hydra.reaction.localhost:4444/oauth2/token
PORT=4000
SEGMENT_ANALYTICS_SKIP_MINIMIZE=true
SEGMENT_ANALYTICS_WRITE_KEY=ENTER_KEY_HERE
SESSION_MAX_AGE_MS=2592000000
SESSION_SECRET=CHANGEME
STRIPE_PUBLIC_API_KEY=ENTER_STRIPE_PUBLIC_KEY_HERE
STRIPE_PUBLIC_API_KEY=ENTER_STRIPE_PUBLIC_KEY_HERE
13 changes: 1 addition & 12 deletions .env.prod
@@ -1,21 +1,10 @@
CANONICAL_URL=http://localhost:4000
ENABLE_SPA_ROUTING=true
BUILD_GRAPHQL_URL=http://localhost:3000/graphql
EXTERNAL_GRAPHQL_URL=http://localhost:3000/graphql
INTERNAL_GRAPHQL_URL=http://api.reaction.localhost:3000/graphql
OAUTH2_ADMIN_PORT=4445
OAUTH2_ADMIN_URL=http://hydra.reaction.localhost:4445
OAUTH2_AUTH_URL=http://localhost:4444/oauth2/auth
OAUTH2_CLIENT_ID=example-storefront
OAUTH2_CLIENT_SECRET=CHANGEME
OAUTH2_PUBLIC_LOGOUT_URL=http://localhost:4444/oauth2/sessions/logout
OAUTH2_HOST=hydra.reaction.localhost
OAUTH2_IDP_PUBLIC_CHANGE_PASSWORD_URL=http://localhost:4100/account/change-password?email=EMAIL&from=FROM
OAUTH2_IDP_HOST_URL=http://identity.reaction.localhost:4100
OAUTH2_TOKEN_URL=http://hydra.reaction.localhost:4444/oauth2/token
PORT=4000
SEGMENT_ANALYTICS_SKIP_MINIMIZE=true
SEGMENT_ANALYTICS_WRITE_KEY=ENTER_KEY_HERE
SESSION_MAX_AGE_MS=2592000000
SESSION_SECRET=CHANGEME
STRIPE_PUBLIC_API_KEY=ENTER_STRIPE_PUBLIC_KEY_HERE
STRIPE_PUBLIC_API_KEY=ENTER_STRIPE_PUBLIC_KEY_HERE
1 change: 0 additions & 1 deletion README.md
Expand Up @@ -31,7 +31,6 @@ Follow the [Reaction Platform docs](https://docs.reactioncommerce.com/docs/insta
| [`reaction`](https://github.com/reactioncommerce/reaction): GraphQL Playground developer tool | [localhost:3000/graphql](localhost:3000/graphql) |
| [`reaction`](https://github.com/reactioncommerce/reaction): Reaction Admin | [localhost:4080](localhost:4080) |
| [`reaction`](https://github.com/reactioncommerce/reaction): MongoDB | [localhost:27017](localhost:27017) |
| [`reaction-hydra`](https://github.com/reactioncommerce/reaction-hydra): Authentication | [localhost:4444](localhost:4444) |
| [`example-storefront`](https://github.com/reactioncommerce/example-storefront): Storefront | [localhost:4000](localhost:4000) |

**Note**: The storefront has redirects so that if you open [http://localhost:4000/graphql](http://localhost:4000/graphql), you'll be redirected to the GraphQL Playground.
Expand Down
5 changes: 2 additions & 3 deletions apiUtils/localeMiddleware.js
Expand Up @@ -3,8 +3,7 @@ import redirect from "./redirect";

export default (req, res) => {
const {
query: { slug },
_parsedUrl
query: { slug, ...rest }
} = req;

const fallback = "de";
Expand Down Expand Up @@ -41,7 +40,7 @@ export default (req, res) => {
found = fallback;
}

const queryPart = (_parsedUrl && _parsedUrl.query) ? `?${_parsedUrl.query}` : "";
const queryPart = rest ? (`?${Object.keys(rest).map((k) => `${k}=${rest[k]}`).join("&")}`) : "";

if (slug) {
return redirect(res, 302, `/${found}${slug ? `/${slug.join("/")}` : ""}${queryPart}`);
Expand Down
53 changes: 0 additions & 53 deletions apiUtils/passportMiddleware.js

This file was deleted.

6 changes: 2 additions & 4 deletions bin/start
Expand Up @@ -8,7 +8,5 @@ IFS="$(printf "\n\t")"

cd "$(dirname "$0")/.."
yarn install
printf "Waiting for Hydra service... "
./bin/wait-for.sh "${OAUTH2_HOST}:${OAUTH2_ADMIN_PORT}"
printf "Hydra service found!\n"
yarn start:dev

yarn start:dev
94 changes: 80 additions & 14 deletions components/AccountDropdown/AccountDropdown.js
@@ -1,6 +1,7 @@
import React, { useState, Fragment } from "react";
import React, { useState, Fragment, useEffect } from "react";
import inject from "hocs/inject";
import { makeStyles } from "@material-ui/core/styles";
import { useRouter } from "next/router";
import IconButton from "@material-ui/core/IconButton";
import Button from "@material-ui/core/Button";
import ButtonBase from "@material-ui/core/ButtonBase";
Expand All @@ -9,6 +10,13 @@ import Popover from "@material-ui/core/Popover";
import useViewer from "hooks/viewer/useViewer";
import ViewerInfo from "@reactioncommerce/components/ViewerInfo/v1";
import Link from "components/Link";
import Modal from "@material-ui/core/Modal";
import Login from "../Entry/Login";
import SignUp from "../Entry/SignUp";
import ChangePassword from "../Entry/ChangePassword";
import ForgotPassword from "../Entry/ForgotPassword";
import ResetPassword from "../Entry/ResetPassword";
import getAccountsHandler from "../../lib/accountsServer.js";

const useStyles = makeStyles((theme) => ({
accountDropdown: {
Expand All @@ -17,34 +25,92 @@ const useStyles = makeStyles((theme) => ({
},
marginBottom: {
marginBottom: theme.spacing(2)
},
paper: {
position: "absolute",
width: 380,
backgroundColor: theme.palette.background.paper,
border: "2px solid #000",
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
display: "flex",
alignItems: "center",
justifyContent: "center"
}
}));

const AccountDropdown = () => {
const router = useRouter();
const resetToken = router?.query?.resetToken;
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const [anchorElement, setAnchorElement] = useState(null);
const [viewer] = useViewer();
const [modalValue, setModalValue] = useState("");
const [viewer, , refetch] = useViewer();
const { accountsClient } = getAccountsHandler();
const isAuthenticated = viewer && viewer._id;

const onClose = () => {
setAnchorElement(null);
};

useEffect(() => {
if (!resetToken) return;
setOpen(true);
setModalValue("reset-password");
}, [resetToken]);

const openModal = (value) => {
setModalValue(value);
setOpen(true);
onClose();
};

const closeModal = () => {
setOpen(false);
};

const handleSignOut = async () => {
await accountsClient.logout();
await refetch();
onClose();
};

const toggleOpen = (event) => {
setAnchorElement(event.currentTarget);
};

const onClose = () => {
setAnchorElement(null);
const getModalComponent = () => {
let comp = Login;
if (modalValue === "signup") {
comp = SignUp;
} else if (modalValue === "change-password") {
comp = ChangePassword;
} else if (modalValue === "forgot-password") {
comp = ForgotPassword;
} else if (modalValue === "reset-password") {
comp = ResetPassword;
}
return React.createElement(comp, { closeModal, openModal, resetToken });
};

return (
<Fragment>
{ isAuthenticated ?
<Modal open={open} onClose={closeModal} aria-labelledby="entry-modal" aria-describedby="entry-modal">
<div className={classes.paper}>{getModalComponent()}</div>
</Modal>
{isAuthenticated ? (
<ButtonBase onClick={toggleOpen}>
<ViewerInfo viewer={viewer} />
</ButtonBase>
:
) : (
<IconButton color="inherit" onClick={toggleOpen}>
<AccountIcon />
</IconButton>
}
)}

<Popover
anchorEl={anchorElement}
Expand All @@ -56,7 +122,7 @@ const AccountDropdown = () => {
onClose={onClose}
>
<div className={classes.accountDropdown}>
{isAuthenticated ?
{isAuthenticated ? (
<Fragment>
<div className={classes.marginBottom}>
<Link href="/profile/address">
Expand All @@ -66,26 +132,26 @@ const AccountDropdown = () => {
</Link>
</div>
<div className={classes.marginBottom}>
<Button color="primary" fullWidth href={`/change-password?email=${encodeURIComponent(viewer.emailRecords[0].address)}`}>
<Button color="primary" fullWidth onClick={() => openModal("change-password")}>
Change Password
</Button>
</div>
<Button color="primary" fullWidth href="/logout" variant="contained">
<Button color="primary" fullWidth onClick={handleSignOut} variant="contained">
Sign Out
</Button>
</Fragment>
:
) : (
<Fragment>
<div className={classes.authContent}>
<Button color="primary" fullWidth href="/signin" variant="contained">
<Button color="primary" fullWidth variant="contained" onClick={() => openModal("login")}>
Sign In
</Button>
</div>
<Button color="primary" fullWidth href="/signup">
<Button color="primary" fullWidth onClick={() => openModal("signup")}>
Create Account
</Button>
</Fragment>
}
)}
</div>
</Popover>
</Fragment>
Expand Down
86 changes: 86 additions & 0 deletions components/Entry/ChangePassword.js
@@ -0,0 +1,86 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import { makeStyles } from "@material-ui/core/styles";
import FormControl from "@material-ui/core/FormControl";
import InputLabel from "@material-ui/core/InputLabel";
import Input from "@material-ui/core/Input";
import Button from "@material-ui/core/Button";
import red from "@material-ui/core/colors/red";

import getAccountsHandler from "../../lib/accountsServer.js";
import hashPassword from "../../lib/utils/hashPassword";


const useStyles = makeStyles((theme) => ({
root: {
"display": "flex",
"flexDirection": "column",
"& > *": {
margin: theme.spacing(1)
}
},
error: {
marginTop: theme.spacing(2),
color: red[500],
fontSize: "1.1em",
textAlign: "center"
},
resetButton: {
marginTop: theme.spacing(4)
}
}));

/**
*
* @param {Object} props of the structure { closeModal: function called to close the modal }
* @returns {Object} the component to render for updating password
*/
export default function ChangePassword(props) {
const { closeModal } = props;
const classes = useStyles();
const [oldPassword, setOldPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [error, setError] = useState("");
const { passwordClient } = getAccountsHandler();

const handleOldPasswordChange = (event) => {
setOldPassword(event.target.value);
};
const handleNewPasswordChange = (event) => {
setNewPassword(event.target.value);
};

const handleChangePassword = async () => {
try {
await passwordClient.changePassword(hashPassword(oldPassword), hashPassword(newPassword));
closeModal();
} catch (err) {
setError(err.message);
}
};
return (
<form className={classes.root} noValidate>
<h1>Change password</h1>
<FormControl>
<InputLabel htmlFor="old-password">Old Password</InputLabel>
<Input id="old-password" aria-describedby="old-password" onChange={handleOldPasswordChange} value={oldPassword}
type="password"
/>
</FormControl>
<FormControl>
<InputLabel htmlFor="new-password">New Password</InputLabel>
<Input id="new-password" aria-describedby="new-password" onChange={handleNewPasswordChange} value={newPassword}
type="password"
/>
</FormControl>
{!!error && <div className={classes.error}>{error}</div>}
<Button className={classes.resetButton} onClick={handleChangePassword} color="primary" variant="contained"
tabIndex="0" role="button"
>Change</Button>
</form>
);
}

ChangePassword.propTypes = {
closeModal: PropTypes.func
};

0 comments on commit 7a509c9

Please sign in to comment.