1
\$\begingroup\$

so I have a Nextjs frontend that is using Next Auth for authentication. This will be using a variety of Oauth providers but for this example and to prove the concept I am just using the github provider.

First, the next app is barebones and uses the default config for Nextjs and Next auth.

It has one special route

/pages/api/protected/[...requests].js

The purpose of this route is to act as a middleman to wrap up requests with the JWT provided by Next Auth and sent to the server API. It passes through any server errors returned to the client.

EG:

http://localhost:3000/api/protected/users -> http://localhost:4000/users http://localhost:3000/api/protected/widgets -> http://localhost:4000/widgets http://localhost:3000/api/protected/users/1/widgets -> http://localhost:4000/users/1/widgets

source:

// Catch all that basically authorizes any request to the backend
// api and adds the JWT token to the request headers. No Special 
// magic is done here besides adding the token to the request.

import { getToken } from 'next-auth/jwt'
import QueryString from 'qs'
import getAccessToken from '@util/getAccessToken'
import fetchWithStatus from '@apiWrapper/fetchWithStatus'

const getAccessToken = ({req}) => {
  const secret = process.env.NEXTAUTH_SECRET
  const info = await getToken({ req, secret })
  if (info === null) {
    return null
  } else {
    return (info.accessToken)
  }
}

export default async (req, res) => {
  try {
    let accessToken = await getAccessToken({ req })
    const { request, ...qs } = req.query
    const path = request.join('/')
    let opts = {
      headers: {
        'Authorization': `Bearer: ${accessToken}`
      },
      method: req.method
    }
    if (req.method === 'POST' || req.method === 'PUT') {
      opts.body = JSON.stringify(req.body)
      opts.headers['Content-Type'] = 'application/json'
    }
    const { data, status } = await fetchWithStatus(`${process.env.API_SERVER}/${path}?${QueryString.stringify(qs)}`, opts)
    res.status(status).json({ status: status, data: data })
  } catch (error) {
    console.error(error)
    res.status(error.status || 500).end(error.message)
  }
}

Then the request is authenticated on the Rails API by providing a current_user based on the decoded JWT.

class ApplicationController < Rails::ApiController

  before_action :authenticate!

  private

  def authenticate!
    begin
      @user = JwtService::Decode(request.headers['Authorization'])
    rescue JwtService::AuthorizationError # Catches Decode errors, etc...
      head 401
    end
  end

  def current_user
    @user
  end
end

As you can see, I believe making what in a normal application would be 1 api request has now turned into 3 + any external calls that need to be made to decode the JWT.

The upside to this approach in my (uneducated) opinion, is I can offload almost all of the user authentication logic to Next Auth and not worry about it too much.

Is there a better or different way I should be approaching this?

\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.