Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Is this the correct and secure way to connect Next.js + NextAuth with a Django Rest Framework API? #1343

Closed
4 tasks
mahieyin-rahmun opened this issue Feb 20, 2021 · 0 comments
Labels
question Ask how to do something or how something works

Comments

@mahieyin-rahmun
Copy link
Contributor

Your question
I have been working on a Next.js app with a custom backend using Django Rest Framework, with the main focus on authentication with social platforms (Google, Github etc). My question is whether the way I am doing it is secure and will not cause security issues.

What are you trying to do
I am trying to make my Next.js app interact securely with a Django Rest Framework backend. Here's the flow I am wanting to use:

  1. Have the NextAuth do the heavy lifting for social authentication. It gets back, for instance, an access token and an id token when the user wants to login with his/her Google account.
  2. Put the id token and access token given back by Google into the NextAuth session object.
  3. In the frontend, use those two tokens in the session object to make a POST request to the DRF backend which essentially accepts the access token and id token and returns a access token and a refresh token. NB. The DRF backend has dj-rest-auth and django-allauth setup to handle social authentication.
  4. The DRF backend sends back the tokens in the form of HTTPOnly cookies. So, next time I want to make a request to the DRF API, the cookies should be passed along the request.

Reproduction
Here's the code for context:

index.tsx

import React, { useEffect } from "react";
import { signIn, signOut, useSession } from "next-auth/client";
import { Typography, Button, Box } from "@material-ui/core";
import { makeUrl, BASE_URL, SOCIAL_LOGIN_ENDPOINT } from "../urls";
import axios from "axios";
axios.defaults.withCredentials = true;

function index() {
  const [session, loading] = useSession();

  useEffect(() => {
    const getTokenFromServer = async () => {
      // TODO: handle error when the access token expires
      const response = await axios.post(
        // DRF backend endpoint, api/social/google/ for example
        // this returns accessToken and refresh_token in the form of HTTPOnly cookies
        makeUrl(BASE_URL, SOCIAL_LOGIN_ENDPOINT, session.provider),
        {
          access_token: session.accessToken,
          id_token: session.idToken,
        },
      );
    };

    if (session) {
      getTokenFromServer();
    }
  }, [session]);

  return (
    <React.Fragment>
      <Box
        display="flex"
        justifyContent="center"
        alignItems="center"
        m={5}
        p={5}
        flexDirection="column"
      >
        {!loading && !session && (
          <React.Fragment>
            <Typography variant="button">Not logged in</Typography>
            <Button
              variant="outlined"
              color="secondary"
              onClick={() => signIn()}
            >
              Login
            </Button>
          </React.Fragment>
        )}
        {!loading && session && (
          <React.Fragment>
            <Typography>Logged in as {session.user.email}</Typography>
            <pre>{JSON.stringify(session, null, 2)}</pre>
            <Button
              variant="outlined"
              color="primary"
              onClick={() => signOut()}
            >
              Sign Out
            </Button>
          </React.Fragment>
        )}
      </Box>
    </React.Fragment>
  );
}

export default index;

api/auth/[...nextauth].ts

import NextAuth from "next-auth";
import { InitOptions } from "next-auth";
import Providers from "next-auth/providers";
import { NextApiRequest, NextApiResponse } from "next";
import axios from "axios";

import { BASE_URL, SOCIAL_LOGIN_ENDPOINT, makeUrl } from "../../../urls";
import { AuthenticatedUser, CustomSessionObject } from "../../../types";
import { GenericObject } from "next-auth/_utils";

const settings: InitOptions = {
  providers: [
    Providers.Google({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      authorizationUrl:
        "https://accounts.google.com/o/oauth2/v2/auth?prompt=consent&access_type=offline&response_type=code",
    }),
  ],

  secret: process.env.NEXT_AUTH_SECRET,

  session: {
    maxAge: 6 * 60 * 60, // 6 hours
  },

  callbacks: {
    async signIn(user: AuthenticatedUser, account, profile) {
      if (account.provider === "google") {
        const { accessToken, idToken, provider } = account;
        user.accessToken = accessToken;
        user.idToken = idToken;
        user.provider = provider;
        return true;
      }

      return false;
    },

    async session(session: CustomSessionObject, user: AuthenticatedUser) {
      session.accessToken = user.accessToken;
      session.idToken = user.idToken;
      session.provider = user.provider;
      return session;
    },

    async jwt(token, user: AuthenticatedUser, account, profile, isNewUser) {
      if (user) {
        token.accessToken = user.accessToken;
        token.idToken = user.idToken;
        token.provider = user.provider;
      }

      return token;
    },
  },
};

export default (req: NextApiRequest, res: NextApiResponse) => {
  return NextAuth(req, res, settings);
};

Feedback
Documentation refers to searching through online documentation, code comments and issue history. The example project refers to next-auth-example.

  • Found the documentation helpful
  • Found documentation but was incomplete
  • [*] Could not find relevant documentation
  • Found the example project helpful
  • Did not find the example project helpful
@mahieyin-rahmun mahieyin-rahmun added the question Ask how to do something or how something works label Feb 20, 2021
@nextauthjs nextauthjs locked and limited conversation to collaborators Feb 21, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
question Ask how to do something or how something works
Projects
None yet
Development

No branches or pull requests

2 participants