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

fix: auth broken :( #103

Merged
merged 2 commits into from
Sep 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 11 additions & 22 deletions targets/frontend/src/hoc/UserProvider.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { decode } from "jsonwebtoken";
import { getDisplayName } from "next/dist/next-server/lib/utils";
import PropTypes from "prop-types";
import React, { createContext } from "react";
import { auth, getToken, setToken } from "src/lib/auth/token";
import { getDisplayName } from "src/hoc/getDisplayName";
import { auth } from "src/lib/auth/token";
import { request } from "src/lib/request";

function withUserProvider(WrappedComponent) {
export const UserContext = createContext({});

export function withUserProvider(WrappedComponent) {
return class extends React.Component {
static displayName = `withUserProvider(${getDisplayName(
WrappedComponent
Expand All @@ -16,27 +18,18 @@ function withUserProvider(WrappedComponent) {
};

static async getInitialProps(ctx) {
console.log(
"[withUserProvider] getInitialProps ",
ctx.pathname,
ctx.req ? "server" : "client",
getToken() ? "found token" : "no token"
);

const token = await auth(ctx);
if (!getToken()) {
setToken(token);
}

const tokenData = decode(getToken());

const componentProps =
WrappedComponent.getInitialProps &&
(await WrappedComponent.getInitialProps(ctx));
return { ...componentProps, tokenData };

return {
...componentProps,
tokenData: token ? decode(token.jwt_token) : null,
};
}
render() {
console.log("---- withUserProvider", this.props.tokenData);
return (
<ProvideUser tokenData={this.props.tokenData}>
<WrappedComponent {...this.props} />
Expand All @@ -46,9 +39,7 @@ function withUserProvider(WrappedComponent) {
};
}

const UserContext = createContext({});

const ProvideUser = ({ children, tokenData }) => {
export const ProvideUser = ({ children, tokenData }) => {
let user = null;
if (tokenData) {
const claims = tokenData["https://hasura.io/jwt/claims"];
Expand Down Expand Up @@ -86,5 +77,3 @@ ProvideUser.propTypes = {
children: PropTypes.node.isRequired,
tokenData: PropTypes.object,
};

export { withUserProvider, UserContext };
4 changes: 4 additions & 0 deletions targets/frontend/src/hoc/getDisplayName.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Gets the display name of a JSX component for dev tools
export function getDisplayName(Component) {
return Component.displayName || Component.name || "Component";
}
5 changes: 4 additions & 1 deletion targets/frontend/src/lib/auth/authTokenExchange.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ export const authExchange = (ctx) => ({ forward }) => {

// If it's expired and we aren't refreshing it yet, start refreshing it
if (isExpired && !authPromise) {
authPromise = auth(ctx).then(({ jwt_token }) => jwt_token);
authPromise = auth(ctx).then(({ jwt_token }) => {
console.log("[ authExchange ]", { jwt_token });
return jwt_token;
});
}

const { key } = operation;
Expand Down
105 changes: 57 additions & 48 deletions targets/frontend/src/lib/auth/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,67 +3,76 @@ import Router from "next/router";
import { request } from "../request";
import { setRefreshTokenCookie } from "./setRefreshTokenCookie";

let token = null;
let inMemoryToken;

function getToken() {
return token ? token.jwt_token : null;
export function getToken() {
return inMemoryToken ? inMemoryToken.jwt_token : null;
}

function isTokenExpired() {
const expired = !token || Date.now() > new Date(token.jwt_token_expiry);
export function isTokenExpired() {
const expired =
!inMemoryToken || Date.now() > new Date(inMemoryToken.jwt_token_expiry);
return expired;
}

async function auth(ctx) {
if (!inMemoryToken) {
const cookieHeader =
ctx && ctx.req
? {
Cookie: ctx.req.headers.cookie,
}
: {};
try {
const tokenData = await request(
ctx && ctx.req
? `${process.env.FRONTEND_URL}/api/refresh_token`
: "/api/refresh_token",
{
body: {},
credentials: "include",
headers: {
"Cache-Control": "no-cache",
...cookieHeader,
},
mode: "same-origin",
export async function auth(ctx) {
console.log("[ auth ] ");
if (ctx.token) {
return ctx.token;
}
if (inMemoryToken) {
return inMemoryToken;
}

const cookieHeader =
ctx && ctx.req
? {
Cookie: ctx.req.headers.cookie,
}
);
// for ServerSide call, we need to set the Cookie header
// to update the refresh_token value
if (ctx && ctx.res) {
setRefreshTokenCookie(ctx.res, tokenData.refresh_token);
: {};
try {
const tokenData = await request(
ctx && ctx.req
? `${process.env.FRONTEND_URL}/api/refresh_token`
: "/api/refresh_token",
{
body: {},
credentials: "include",
headers: {
"Cache-Control": "no-cache",
...cookieHeader,
},
mode: "same-origin",
}
);
// for ServerSide call, we need to set the Cookie header
// to update the refresh_token value
if (ctx && 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 };
} catch (error) {
console.error("[ auth ] refreshToken error ", { error });
}
return inMemoryToken;
} catch (error) {
console.error("[ auth ] refreshToken error ", { error });

if (ctx && ctx.req) {
ctx.res.writeHead(302, { Location: "/login" });
ctx.res.end();
} else {
Router.push("/login");
}
// we are on server side and its response is not ended yet
if (ctx && ctx.res && !ctx.res.writableEnded) {
ctx.res.writeHead(302, { Location: "/login" });
ctx.res.end();
} else if (ctx && !ctx.req) {
// if we are on the client
Router.push("/login");
}
return Promise.reject(error);
}
const jwt_token = inMemoryToken;
if (!jwt_token) {
Router.push("/login");
}
return jwt_token;
}

function setToken(tokenData) {
token = tokenData;
export function setToken(token) {
inMemoryToken = token;
}

export { auth, getToken, isTokenExpired, setToken };