DEV Community

Jacob Colborn
Jacob Colborn

Posted on

User Authorization with Next.js and Apollo

What Happened

I have been working on an esports news submission web application for the last 2 months now (with a hearty break during December, so maybe closer to a month). Recently, I completed the article submission component. I mounted this component through a submit article page on the site. After getting the functionality running I wanted to make it so only users could submit an article. And, rather than having them write out the article to only be denied, I thought "why not let them know upfront you have to be logged in to submit an article?"

I started working through this process. My initial idea was to run a current user query within the component to get access to the current user's id. If this didn't return any data then I would know that the user was not logged in. After writing this up I could not find a way to encapsulate the submission Mutation within a user Query. After trying a few different methods everyone returned errors. Taking a step back I saw a solution.

The Solution

Taking that step back I saw a solution on the Next.js page that is used to mount the article submission component. By utilizing the <User> component, I could wrap the <Submit> component within the <User> component's returned data. For further clarity, here is the full <User> component:

import { Query } from "react-apollo";
import gql from "graphql-tag";
import PropTypes from "prop-types";

const CURRENT_USER_QUERY = gql`
  query {
    me {
      id
      email
      name
      permissions
    }
  }
`;

const User = props => (
  <Query {...props} query={CURRENT_USER_QUERY}>
    {payload => props.children(payload)}
  </Query>
);

User.propTypes = {
  children: PropTypes.func.isRequired
};

export default User;
export { CURRENT_USER_QUERY };

So, if we take that payload returned from the component we can pass this to the <Submit> component via a prop. Taking this approach we can change our submit.js page from

import React, { Component } from "react";
import Submit from "../components/Submit";

const submit = () => <Submit />;

export default submit;

To something that will gather data from the <User> component.

import React, { Component } from "react";
import { Query } from "react-apollo";
import Submit from "../components/Submit";
import User from "../components/User";
import Signin from "../components/Signin";

const submit = () => <User>{({ data }) => <Submit isLoggedIn={data} />}</User>;
export default submit;

The payload here is then passed to <Submit> within the isLoggedIn prop. Taking that prop, we can use some if/else statements to either render the submission form or render a login page, depending on what the current user status is.

render() {
    if (this.props.isLoggedIn.me) {
      return (
       Submission form code here
      )
    } else {
      login form, message, or redirect here
    }
  }

So, if isLoggedIn.me exists, then the user is logged in. The me part of this comes from the CURRENT_USER_QUERY. The query returns id, email, name, permission. We could use any one of these, including isLoggedIn.me.permission to make sure they are part of a group authorized to access this, but within the web application, any logged-in user is allowed to submit an article.

The Conclusion

This strategy could be utilized for any level of authorization. If this was an admin form I could take the returned data and look for the permission part of the object (this is defined in the query from <User> and stored in the database for each user). This particular time, I only look for any data at all. As long as that data exists, the user is logged in. This is another excellent lesson in always taking a step back in what we are doing. I spent longer on this then I should but it was because I tried so many different iterations of how I thought it was supposed to work, rather than taking a few minutes to review my thought process and take a different approach.

Thank you for reading. As always, feel free to leave any comments about the code, my thought process, what I could be doing better, or just saying hey.

Top comments (0)