Skip to content

Commit ec6d598

Browse files
authored
Merge pull request #779 from reactioncommerce/akarshit-feat-account-js-auth
feat: breaking use account-js for authentication
2 parents 7e6f7e0 + c860fd1 commit ec6d598

32 files changed

+2912
-2417
lines changed

.env.example

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,10 @@
11
CANONICAL_URL=http://localhost:4000
2-
ENABLE_SPA_ROUTING=true
32
BUILD_GRAPHQL_URL=http://localhost:3000/graphql
43
EXTERNAL_GRAPHQL_URL=http://localhost:3000/graphql
54
INTERNAL_GRAPHQL_URL=http://api.reaction.localhost:3000/graphql
6-
OAUTH2_ADMIN_PORT=4445
7-
OAUTH2_ADMIN_URL=http://hydra.reaction.localhost:4445
8-
OAUTH2_AUTH_URL=http://localhost:4444/oauth2/auth
9-
OAUTH2_CLIENT_ID=example-storefront
10-
OAUTH2_CLIENT_SECRET=CHANGEME
11-
OAUTH2_PUBLIC_LOGOUT_URL=http://localhost:4444/oauth2/sessions/logout
12-
OAUTH2_HOST=hydra.reaction.localhost
13-
OAUTH2_IDP_PUBLIC_CHANGE_PASSWORD_URL=http://localhost:4100/account/change-password?email=EMAIL&from=FROM
14-
OAUTH2_IDP_HOST_URL=http://identity.reaction.localhost:4100
15-
OAUTH2_TOKEN_URL=http://hydra.reaction.localhost:4444/oauth2/token
165
PORT=4000
176
SEGMENT_ANALYTICS_SKIP_MINIMIZE=true
187
SEGMENT_ANALYTICS_WRITE_KEY=ENTER_KEY_HERE
198
SESSION_MAX_AGE_MS=2592000000
209
SESSION_SECRET=CHANGEME
21-
STRIPE_PUBLIC_API_KEY=ENTER_STRIPE_PUBLIC_KEY_HERE
10+
STRIPE_PUBLIC_API_KEY=ENTER_STRIPE_PUBLIC_KEY_HERE

.env.prod

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,10 @@
11
CANONICAL_URL=http://localhost:4000
2-
ENABLE_SPA_ROUTING=true
32
BUILD_GRAPHQL_URL=http://localhost:3000/graphql
43
EXTERNAL_GRAPHQL_URL=http://localhost:3000/graphql
54
INTERNAL_GRAPHQL_URL=http://api.reaction.localhost:3000/graphql
6-
OAUTH2_ADMIN_PORT=4445
7-
OAUTH2_ADMIN_URL=http://hydra.reaction.localhost:4445
8-
OAUTH2_AUTH_URL=http://localhost:4444/oauth2/auth
9-
OAUTH2_CLIENT_ID=example-storefront
10-
OAUTH2_CLIENT_SECRET=CHANGEME
11-
OAUTH2_PUBLIC_LOGOUT_URL=http://localhost:4444/oauth2/sessions/logout
12-
OAUTH2_HOST=hydra.reaction.localhost
13-
OAUTH2_IDP_PUBLIC_CHANGE_PASSWORD_URL=http://localhost:4100/account/change-password?email=EMAIL&from=FROM
14-
OAUTH2_IDP_HOST_URL=http://identity.reaction.localhost:4100
15-
OAUTH2_TOKEN_URL=http://hydra.reaction.localhost:4444/oauth2/token
165
PORT=4000
176
SEGMENT_ANALYTICS_SKIP_MINIMIZE=true
187
SEGMENT_ANALYTICS_WRITE_KEY=ENTER_KEY_HERE
198
SESSION_MAX_AGE_MS=2592000000
209
SESSION_SECRET=CHANGEME
21-
STRIPE_PUBLIC_API_KEY=ENTER_STRIPE_PUBLIC_KEY_HERE
10+
STRIPE_PUBLIC_API_KEY=ENTER_STRIPE_PUBLIC_KEY_HERE

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ Follow the [Reaction Platform docs](https://docs.reactioncommerce.com/docs/insta
3131
| [`reaction`](https://github.com/reactioncommerce/reaction): GraphQL Playground developer tool | [localhost:3000/graphql](localhost:3000/graphql) |
3232
| [`reaction`](https://github.com/reactioncommerce/reaction): Reaction Admin | [localhost:4080](localhost:4080) |
3333
| [`reaction`](https://github.com/reactioncommerce/reaction): MongoDB | [localhost:27017](localhost:27017) |
34-
| [`reaction-hydra`](https://github.com/reactioncommerce/reaction-hydra): Authentication | [localhost:4444](localhost:4444) |
3534
| [`example-storefront`](https://github.com/reactioncommerce/example-storefront): Storefront | [localhost:4000](localhost:4000) |
3635

3736
**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.

apiUtils/localeMiddleware.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import redirect from "./redirect";
33

44
export default (req, res) => {
55
const {
6-
query: { slug },
7-
_parsedUrl
6+
query: { slug, ...rest }
87
} = req;
98

109
const fallback = "de";
@@ -41,7 +40,7 @@ export default (req, res) => {
4140
found = fallback;
4241
}
4342

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

4645
if (slug) {
4746
return redirect(res, 302, `/${found}${slug ? `/${slug.join("/")}` : ""}${queryPart}`);

apiUtils/passportMiddleware.js

Lines changed: 0 additions & 53 deletions
This file was deleted.

bin/start

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,5 @@ IFS="$(printf "\n\t")"
88

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

components/AccountDropdown/AccountDropdown.js

Lines changed: 80 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import React, { useState, Fragment } from "react";
1+
import React, { useState, Fragment, useEffect } from "react";
22
import inject from "hocs/inject";
33
import { makeStyles } from "@material-ui/core/styles";
4+
import { useRouter } from "next/router";
45
import IconButton from "@material-ui/core/IconButton";
56
import Button from "@material-ui/core/Button";
67
import ButtonBase from "@material-ui/core/ButtonBase";
@@ -9,6 +10,13 @@ import Popover from "@material-ui/core/Popover";
910
import useViewer from "hooks/viewer/useViewer";
1011
import ViewerInfo from "@reactioncommerce/components/ViewerInfo/v1";
1112
import Link from "components/Link";
13+
import Modal from "@material-ui/core/Modal";
14+
import Login from "../Entry/Login";
15+
import SignUp from "../Entry/SignUp";
16+
import ChangePassword from "../Entry/ChangePassword";
17+
import ForgotPassword from "../Entry/ForgotPassword";
18+
import ResetPassword from "../Entry/ResetPassword";
19+
import getAccountsHandler from "../../lib/accountsServer.js";
1220

1321
const useStyles = makeStyles((theme) => ({
1422
accountDropdown: {
@@ -17,34 +25,92 @@ const useStyles = makeStyles((theme) => ({
1725
},
1826
marginBottom: {
1927
marginBottom: theme.spacing(2)
28+
},
29+
paper: {
30+
position: "absolute",
31+
width: 380,
32+
backgroundColor: theme.palette.background.paper,
33+
border: "2px solid #000",
34+
boxShadow: theme.shadows[5],
35+
padding: theme.spacing(2, 4, 3),
36+
top: "50%",
37+
left: "50%",
38+
transform: "translate(-50%, -50%)",
39+
display: "flex",
40+
alignItems: "center",
41+
justifyContent: "center"
2042
}
2143
}));
2244

2345
const AccountDropdown = () => {
46+
const router = useRouter();
47+
const resetToken = router?.query?.resetToken;
2448
const classes = useStyles();
49+
const [open, setOpen] = React.useState(false);
2550
const [anchorElement, setAnchorElement] = useState(null);
26-
const [viewer] = useViewer();
51+
const [modalValue, setModalValue] = useState("");
52+
const [viewer, , refetch] = useViewer();
53+
const { accountsClient } = getAccountsHandler();
2754
const isAuthenticated = viewer && viewer._id;
2855

56+
const onClose = () => {
57+
setAnchorElement(null);
58+
};
59+
60+
useEffect(() => {
61+
if (!resetToken) return;
62+
setOpen(true);
63+
setModalValue("reset-password");
64+
}, [resetToken]);
65+
66+
const openModal = (value) => {
67+
setModalValue(value);
68+
setOpen(true);
69+
onClose();
70+
};
71+
72+
const closeModal = () => {
73+
setOpen(false);
74+
};
75+
76+
const handleSignOut = async () => {
77+
await accountsClient.logout();
78+
await refetch();
79+
onClose();
80+
};
81+
2982
const toggleOpen = (event) => {
3083
setAnchorElement(event.currentTarget);
3184
};
3285

33-
const onClose = () => {
34-
setAnchorElement(null);
86+
const getModalComponent = () => {
87+
let comp = Login;
88+
if (modalValue === "signup") {
89+
comp = SignUp;
90+
} else if (modalValue === "change-password") {
91+
comp = ChangePassword;
92+
} else if (modalValue === "forgot-password") {
93+
comp = ForgotPassword;
94+
} else if (modalValue === "reset-password") {
95+
comp = ResetPassword;
96+
}
97+
return React.createElement(comp, { closeModal, openModal, resetToken });
3598
};
3699

37100
return (
38101
<Fragment>
39-
{ isAuthenticated ?
102+
<Modal open={open} onClose={closeModal} aria-labelledby="entry-modal" aria-describedby="entry-modal">
103+
<div className={classes.paper}>{getModalComponent()}</div>
104+
</Modal>
105+
{isAuthenticated ? (
40106
<ButtonBase onClick={toggleOpen}>
41107
<ViewerInfo viewer={viewer} />
42108
</ButtonBase>
43-
:
109+
) : (
44110
<IconButton color="inherit" onClick={toggleOpen}>
45111
<AccountIcon />
46112
</IconButton>
47-
}
113+
)}
48114

49115
<Popover
50116
anchorEl={anchorElement}
@@ -56,7 +122,7 @@ const AccountDropdown = () => {
56122
onClose={onClose}
57123
>
58124
<div className={classes.accountDropdown}>
59-
{isAuthenticated ?
125+
{isAuthenticated ? (
60126
<Fragment>
61127
<div className={classes.marginBottom}>
62128
<Link href="/profile/address">
@@ -66,26 +132,26 @@ const AccountDropdown = () => {
66132
</Link>
67133
</div>
68134
<div className={classes.marginBottom}>
69-
<Button color="primary" fullWidth href={`/change-password?email=${encodeURIComponent(viewer.emailRecords[0].address)}`}>
135+
<Button color="primary" fullWidth onClick={() => openModal("change-password")}>
70136
Change Password
71137
</Button>
72138
</div>
73-
<Button color="primary" fullWidth href="/logout" variant="contained">
139+
<Button color="primary" fullWidth onClick={handleSignOut} variant="contained">
74140
Sign Out
75141
</Button>
76142
</Fragment>
77-
:
143+
) : (
78144
<Fragment>
79145
<div className={classes.authContent}>
80-
<Button color="primary" fullWidth href="/signin" variant="contained">
146+
<Button color="primary" fullWidth variant="contained" onClick={() => openModal("login")}>
81147
Sign In
82148
</Button>
83149
</div>
84-
<Button color="primary" fullWidth href="/signup">
150+
<Button color="primary" fullWidth onClick={() => openModal("signup")}>
85151
Create Account
86152
</Button>
87153
</Fragment>
88-
}
154+
)}
89155
</div>
90156
</Popover>
91157
</Fragment>

components/Entry/ChangePassword.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import React, { useState } from "react";
2+
import PropTypes from "prop-types";
3+
import { makeStyles } from "@material-ui/core/styles";
4+
import FormControl from "@material-ui/core/FormControl";
5+
import InputLabel from "@material-ui/core/InputLabel";
6+
import Input from "@material-ui/core/Input";
7+
import Button from "@material-ui/core/Button";
8+
import red from "@material-ui/core/colors/red";
9+
10+
import getAccountsHandler from "../../lib/accountsServer.js";
11+
import hashPassword from "../../lib/utils/hashPassword";
12+
13+
14+
const useStyles = makeStyles((theme) => ({
15+
root: {
16+
"display": "flex",
17+
"flexDirection": "column",
18+
"& > *": {
19+
margin: theme.spacing(1)
20+
}
21+
},
22+
error: {
23+
marginTop: theme.spacing(2),
24+
color: red[500],
25+
fontSize: "1.1em",
26+
textAlign: "center"
27+
},
28+
resetButton: {
29+
marginTop: theme.spacing(4)
30+
}
31+
}));
32+
33+
/**
34+
*
35+
* @param {Object} props of the structure { closeModal: function called to close the modal }
36+
* @returns {Object} the component to render for updating password
37+
*/
38+
export default function ChangePassword(props) {
39+
const { closeModal } = props;
40+
const classes = useStyles();
41+
const [oldPassword, setOldPassword] = useState("");
42+
const [newPassword, setNewPassword] = useState("");
43+
const [error, setError] = useState("");
44+
const { passwordClient } = getAccountsHandler();
45+
46+
const handleOldPasswordChange = (event) => {
47+
setOldPassword(event.target.value);
48+
};
49+
const handleNewPasswordChange = (event) => {
50+
setNewPassword(event.target.value);
51+
};
52+
53+
const handleChangePassword = async () => {
54+
try {
55+
await passwordClient.changePassword(hashPassword(oldPassword), hashPassword(newPassword));
56+
closeModal();
57+
} catch (err) {
58+
setError(err.message);
59+
}
60+
};
61+
return (
62+
<form className={classes.root} noValidate>
63+
<h1>Change password</h1>
64+
<FormControl>
65+
<InputLabel htmlFor="old-password">Old Password</InputLabel>
66+
<Input id="old-password" aria-describedby="old-password" onChange={handleOldPasswordChange} value={oldPassword}
67+
type="password"
68+
/>
69+
</FormControl>
70+
<FormControl>
71+
<InputLabel htmlFor="new-password">New Password</InputLabel>
72+
<Input id="new-password" aria-describedby="new-password" onChange={handleNewPasswordChange} value={newPassword}
73+
type="password"
74+
/>
75+
</FormControl>
76+
{!!error && <div className={classes.error}>{error}</div>}
77+
<Button className={classes.resetButton} onClick={handleChangePassword} color="primary" variant="contained"
78+
tabIndex="0" role="button"
79+
>Change</Button>
80+
</form>
81+
);
82+
}
83+
84+
ChangePassword.propTypes = {
85+
closeModal: PropTypes.func
86+
};

0 commit comments

Comments
 (0)