Skip to content

Commit

Permalink
feat(frontend): fix multiple auth (#358)
Browse files Browse the repository at this point in the history
* feat(frontend): fix multiple auth

* fix: codacy

* fix: review

* fix: change console.log
  • Loading branch information
lionelB committed Mar 12, 2021
1 parent f519427 commit b662ec0
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 66 deletions.
65 changes: 40 additions & 25 deletions targets/frontend/src/components/login/index.js
@@ -1,35 +1,30 @@
import PropTypes from "prop-types";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { Box, Card, Field, Heading, Text } from "theme-ui";

import { Button } from "../button";
import { Stack } from "../layout/Stack";

const LoginForm = ({ authenticate, resetPassword, onSuccess }) => {
const [status, setStatus] = useState("idle");
const [error, setError] = useState("");
const {
errors,
handleSubmit,
register,
setError,
formState: { isSubmitting },
} = useForm();

const [email, setEmail] = useState("");
const [password, setPassword] = useState("");

const submit = async (event) => {
if (event) {
event.preventDefault();
}
setError(null);
setStatus("loading");
const submit = async ({ email, password }) => {
try {
const result = await authenticate({ email, password });
onSuccess(result);
} catch (err) {
setError("Impossible de vous authentifier");
setStatus("error");
setError("password", {
message: "Utilisateur ou mot passe incorrect",
type: "manual",
});
}
};

const isValidEmail = email && email.indexOf("@") > -1;
const isValid = status !== "loading" && isValidEmail && Boolean(password);

return (
<Box
sx={{
Expand All @@ -40,27 +35,47 @@ const LoginForm = ({ authenticate, resetPassword, onSuccess }) => {
variant="compact"
sx={{ px: ["xsmall", "medium"], py: ["small", "large"] }}
>
<form onSubmit={submit}>
<form onSubmit={handleSubmit(submit)}>
<Stack>
<Heading as="h1">Authentification</Heading>
<Field
sx={{ fontWeight: "body" }}
label="Adresse email"
placeholder="ex: lionel@travail.gouv.fr"
name="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
type="email"
aria-invalid={errors.email ? "true" : "false"}
ref={register({
required: {
message: "ce champ est requis",
value: true,
},
})}
/>
{errors.email && (
<Text role="alert" color="critical">
{errors.email?.message}
</Text>
)}
<Field
sx={{ fontWeight: "body" }}
label="Mot de passe"
name="password"
type="password"
defaultValue={password}
onChange={(e) => setPassword(e.target.value)}
aria-invalid={errors.password ? "true" : "false"}
ref={register({
required: {
message: "ce champ est requis",
value: true,
},
})}
/>
{error && <Text color="critical">{error}</Text>}
<Button type="submit" onClick={submit} disabled={!isValid}>
{errors.password && (
<Text role="alert" color="critical">
{errors.password?.message}
</Text>
)}
<Button type="submit" disabled={isSubmitting}>
Se connecter
</Button>
<Button
Expand Down
55 changes: 29 additions & 26 deletions targets/frontend/src/hoc/CustomUrqlClient.js
Expand Up @@ -6,29 +6,32 @@ import {
import { cacheExchange, dedupExchange, fetchExchange } from "urql";

export const withCustomUrqlClient = (Component) =>
withUrqlClient((ssrExchange, ctx) => {
const url = ctx?.req
? `${process.env.FRONTEND_URL}/api/graphql`
: `/api/graphql`;
console.log(
"[ withUrqlClient ]",
ctx?.pathname,
ctx?.req ? "server" : "client",
url
);
return {
exchanges: [
process.env.NODE_ENV !== "production"
? require("@urql/devtools").devtoolsExchange
: null,
dedupExchange,
cacheExchange,
ssrExchange,
customErrorExchange(),
customAuthExchange(ctx),
fetchExchange,
].filter(Boolean),
requestPolicy: "cache-first",
url,
};
})(Component);
withUrqlClient(
(ssrExchange, ctx) => {
const url = ctx?.req
? `${process.env.FRONTEND_URL}/api/graphql`
: `/api/graphql`;
console.log(
"[ withUrqlClient ]",
ctx ? (ctx?.req ? "server" : "client") : "no ctx",
ctx?.pathname,
url
);
return {
exchanges: [
process.env.NODE_ENV !== "production"
? require("@urql/devtools").devtoolsExchange
: null,
dedupExchange,
cacheExchange,
ssrExchange,
customErrorExchange(),
customAuthExchange(ctx),
fetchExchange,
].filter(Boolean),
requestPolicy: "cache-first",
url,
};
},
{ ssr: true }
)(Component);
2 changes: 1 addition & 1 deletion targets/frontend/src/hoc/UserProvider.js
Expand Up @@ -19,7 +19,7 @@ export function withUserProvider(WrappedComponent) {

static async getInitialProps(ctx) {
const token = await auth(ctx);

console.log("[ withUserProvider ] ctx", ctx ? true : false);
const componentProps =
WrappedComponent.getInitialProps &&
(await WrappedComponent.getInitialProps(ctx));
Expand Down
1 change: 0 additions & 1 deletion targets/frontend/src/lib/auth/jwt.js
Expand Up @@ -10,7 +10,6 @@ export function generateJwtToken(user) {
if (!user_roles.includes(user.default_role)) {
user_roles.push(user.default_role);
}
console.log({ user });
return jwt.sign(
{
"https://hasura.io/jwt/claims": {
Expand Down
28 changes: 15 additions & 13 deletions targets/frontend/src/lib/auth/token.js
Expand Up @@ -20,22 +20,26 @@ export function isTokenExpired() {
}

export async function auth(ctx) {
console.log("[ auth ] ctx ?", ctx ? true : false);
if (ctx?.token) {
return ctx.token;
}
if (inMemoryToken) {
return inMemoryToken;
}

const cookieHeader =
ctx && ctx.req
? {
Cookie: ctx.req.headers.cookie,
}
: {};
const cookieHeader = ctx?.req ? { Cookie: ctx.req.headers.cookie } : {};

if (ctx?.req && !cookieHeader.Cookie) {
console.log("[ auth ] no cookie found -> redirect to login");
ctx.res.writeHead(302, { Location: "/login" });
ctx.res.end();
return null;
}
try {
console.log("[auth] refresh token");
const tokenData = await request(
ctx && ctx.req
ctx?.req
? `${process.env.FRONTEND_URL}/api/refresh_token`
: "/api/refresh_token",
{
Expand All @@ -50,22 +54,20 @@ export async function auth(ctx) {
);
// for ServerSide call, we need to set the Cookie header
// to update the refresh_token value
if (ctx && ctx.res) {
if (ctx?.res) {
setRefreshTokenCookie(ctx.res, tokenData.refresh_token);
// we also store token in context (this is probably a bad idea b)
// to reuse it and avoid refresh token twice
ctx.token = tokenData;
return tokenData;
} else {
// if on client, we store token in memory
inMemoryToken = { ...tokenData };
}
inMemoryToken = { ...tokenData };
console.log("[auth] token", inMemoryToken ? "true" : "false");
return inMemoryToken;
} catch (error) {
console.error("[ auth ] refreshToken error ", { error });

// we are on server side and its response is not ended yet
if (ctx && ctx.res && !ctx.res.writableEnded) {
if (ctx?.res && !ctx.res.writableEnded) {
ctx.res.writeHead(302, { Location: "/login" });
ctx.res.end();
} else if (ctx && !ctx.req) {
Expand Down

0 comments on commit b662ec0

Please sign in to comment.