Skip to content

Commit

Permalink
fix: auth broken :( (#103)
Browse files Browse the repository at this point in the history
* fix: fix auth

* fix: review
  • Loading branch information
lionelB committed Sep 8, 2020
1 parent b8aca05 commit 26bacb5
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 71 deletions.
33 changes: 11 additions & 22 deletions targets/frontend/src/hoc/UserProvider.js
@@ -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
@@ -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
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
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 };

0 comments on commit 26bacb5

Please sign in to comment.