Nextjs-auth0: Don't get IdToken nor AccessToken

Created on 18 Jan 2020  路  14Comments  路  Source: auth0/nextjs-auth0

I'm able to login to Auth0. But whenever I switch on any of the tokens, I don't get a valid session any more. I never get an IdToken nor an AccessToken into my session for using them with Apollo authenticating in Hasura:

    // Store the id_token in the session. Defaults to false.
    storeIdToken: true,
    // Store the access_token in the session. Defaults to false.
    storeAccessToken: true,
    "@apollo/react-hooks": "3.1.3",
    "@apollo/react-ssr": "3.1.3",
    "@auth0/nextjs-auth0": "^0.10.0",
    "apollo-cache-inmemory": "1.6.5",
    "apollo-client": "2.6.8",
    "apollo-link-context": "1.0.19",
    "apollo-link-http": "1.5.16",
    "dotenv": "^8.2.0",
    "graphql": "14.5.8",
    "graphql-tag": "2.10.1",
    "isomorphic-unfetch": "^3.0.0",
    "js-cookie": "2.2.1",
    "next": "9.1.7",
    "react": "16.12.0",
    "react-dom": "16.12.0"

Tried also the example from https://github.com/vgrafe/nextjs-auth0-hasura
But never got it working with Authorization Header only with anonymous, which is not what I require.

Most helpful comment

@tobkle @shansmith01 maybe this can help you:

lib/auth0.js

import { initAuth0 } from '@auth0/nextjs-auth0'

export default initAuth0({
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
  scope: process.env.AUTH0_SCOPE,
  redirectUri: process.env.REDIRECT_URI,
  postLogoutRedirectUri: process.env.POST_LOGOUT_REDIRECT_URI,
  audience: 'myaudience',
  session: {
    cookieSecret: process.env.SESSION_COOKIE_SECRET,
    cookieLifetime: process.env.SESSION_COOKIE_LIFETIME,
    storeIdToken: false,
    storeRefreshToken: false,
    storeAccessToken: true,
  },
})

apolloClient.js

import { ApolloClient } from 'apollo-client'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { HttpLink } from 'apollo-link-http'
import { setContext } from 'apollo-link-context'
import { onError } from 'apollo-link-error'
import fetch from 'isomorphic-unfetch'

let accessToken = null

const requestAccessToken = async () => {
  if (!!accessToken) return

  const res = await fetch('/api/session')
  if (res.ok) {
    const json = await res.json()
    accessToken = json.accessToken
  } else {
    accessToken = 'public'
  }
}

// return the headers to the context so httpLink can read them
const authLink = setContext(async (req, { headers }) => {
  await requestAccessToken()
  if (!accessToken || accessToken === 'public') {
    return {
      headers,
    }
  } else {
    return {
      headers: {
        ...headers,
        Authorization: `Bearer ${accessToken}`,
      },
    }
  }
})

// remove cached token on 401 from the server
const resetTokenLink = onError(({ networkError }) => {
  if (networkError && networkError.name === 'ServerError' && networkError.statusCode === 401) {
    accessToken = null
  }
})

const httpLink = new HttpLink({
  uri: process.env.API_URL,
  credentials: 'include',
  fetch,
})

export default function createApolloClient(initialState, ctx) {
  // The `ctx` (NextPageContext) will only be present on the server.
  // use it to extract auth headers (ctx.req) or similar.
  accessToken = null
  return new ApolloClient({
    ssrMode: Boolean(ctx),
    link: authLink.concat(resetTokenLink).concat(httpLink),
    cache: new InMemoryCache().restore(initialState),
  })
}

api/session.js

import auth0 from '../../lib/auth0'

export default async function session(req, res) {
  try {
    const s = await auth0.getSession(req)

    if (s) res.send(s)
    res.status(500).end(s)
  } catch (error) {
    res.status(error.status || 500).end(error.message)
  }
}

Steps

Auth0
  • Generate Auth0 Regular Web App (Next.js)
  • Generate Auth0 API with audience ('myaudience')
  • Add rules to set tokens and sync with hasura backend; docs
Hasura
  • Add HASURA_GRAPHQL_JWT_SECRET; config-generator
  • Add HASURA_GRAPHQL_UNAUTHORIZED_ROLE (e.g. role 'anonymous')
  • Add HASURA_GRAPHQL_ADMIN_SECRET
Next.js App
  • Install Auth0 and enable storeAccessToken and add audience from Auth0-API (without audience no jwt token) (e.g. lib/auth0.js)
  • Install Apollo to Next.js
  • Add link-context with jwt (e.g. apolloClient.js)
  • Add api/session-route to get session for client and server (e.g. api/session.js)

Hope that's it :-)

All 14 comments

Same here. I got some short string like 1h4NJkvu_HOM7O2y4OXUwH5xgphmjgYP, which is definitely not an access token.

Also the examples in the folders examples/api-call-example in combination with examples/sample-api aren't working. As soon as I set storeIdToken: true, I don't get a session any more. If I have it on false, I get a session, but without token.
Do I need to setup a special configuration in the Auth0 Dashboard somewhere in order to get that working?

Something is off indeed. The auth on the live demo is not working. I'll look into why this weekend.

I have the same issue. I am a noob, but with some console.log-ing, I found out, that at this point: https://github.com/auth0/nextjs-auth0/blob/7a4a33c4e603b8f2a9e4db8bd05850ddb15e95bb/src/handlers/callback.ts#L47

session still has accessToken, so something after that might screw it up.

I don't know if it is helpful information, just wanted to share.

UPDATE:
When setting storeIdToken or storeAccessToken to true, on the /api/callback call I get

image

in the Chrome console on the network tab, Set-Cookie response header.

Also, it takes over 2s to resolve the request to /api/callback.

UPDATE2:
Doing a lot of research here, I really hope something of it is useful. (Still a noob though). I looked it up, and appearantly a cookie can only be 4096bytes. Although, when setting storeIdToken or storeAccessToken, the size increases to around 9000bytes (setting both to true). Don't know if that can be the problem.

Has this been investigated?

About this issue, can you make an example repository?. Because I have a project with Hasura, Apollo and Auth0, with SSR, working without problems.

The only way to retrieve the idToken remember is in the API and using the method getSession. You have a small explanation in this comment I made in another issue https://github.com/auth0/nextjs-auth0/issues/21#issuecomment-578383434

@Afsoon We can use also the example you've already checked. It is the very same problem there. I tried also https://github.com/BjoernRave/Auth0-Urql-Nextjs-Prisma2 and read your comment. I have the very same problem:

If I set in lib/auth0.ts

storeIdToken: true,

The login isn't successfully processed any more.
In console I see a message:

has accessToken gip false

I also changed /pages/api/getToken.ts in order to see the session content:

import auth0 from '../../lib/auth0';

export default async function session(req, res) {
    try {
        // const { accessToken } = await auth0.getSession(req);
        // if (accessToken) res.send(accessToken);
                // res.status(500).end(accessToken);
                // did instead...
        const session = await auth0.getSession(req);
        if (session) res.send(session);
        res.status(500).end(session);
                 ...
}

But as Login doesn't succeed with the storeIdToken flag, I don't get any idToken in my session if I run

http://localhost:3000/api/getToken

@Afsoon However, I would be also interested in your Hasura example, I haven't worked out yet, how to proxy the Hasura call within /pages/api/hasuracall.ts

I forked the project from the other issue and made the changes I said in the comment, https://github.com/Afsoon/Auth0-Urql-Nextjs-Prisma2, in the following screenshot can you notice in the first load the idToken is null, because I never logged but after the login you can see the token in the console:

imagen

And you can see in the React Dev Tools, you notice the props idToken, that contains the value of the JWT:

imagen

I can't publish yet the example because I still didn't launched the application and I am not totally sure it's working at 100%, but this solution is based on how I have structure the project.

Interesting. Copied your code and still don't get the idToken. Anything wrong with my Auth0 setup?

[ ready ] compiled successfully - ready on http://localhost:3000
Warning: You're using a string directly inside <Link>. This usage has been deprecated. Please add an <a> tag as child of <Link>
has idToken gip false
idToken value: null
has idToken gip false
idToken value: null
has idToken gip false
idToken value: null
has idToken gip false
idToken value: null
has idToken gip false
idToken value: null
has idToken gip false
idToken value: null
[ info ]  bundled successfully, waiting for typecheck results...
[ ready ] compiled successfully - ready on http://localhost:3000
[ event ] build page: /api/getToken
[ wait ]  compiling ...
[ ready ] compiled successfully - ready on http://localhost:3000
TypeError: Cannot destructure property `idToken` of 'undefined' or 'null'.
    at session (/Users/toby/Code/next-auth0/frontend/.next/server/static/development/pages/api/getToken.js:158:11)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at async Object.apiResolver (/Users/toby/Code/next-auth0/frontend/node_modules/next/dist/next-server/server/api-utils.js:42:9)
    at async DevServer.handleApiRequest (/Users/toby/Code/next-auth0/frontend/node_modules/next/dist/next-server/server/next-server.js:427:9)
    at async Object.fn (/Users/toby/Code/next-auth0/frontend/node_modules/next/dist/next-server/server/next-server.js:350:37)
    at async Router.execute (/Users/toby/Code/next-auth0/frontend/node_modules/next/dist/next-server/server/router.js:42:32)
    at async DevServer.run (/Users/toby/Code/next-auth0/frontend/node_modules/next/dist/next-server/server/next-server.js:468:29)
[ info ]  bundled successfully, waiting for typecheck results...

Then my Auth0 settings must be somewhere wrong and I don't understand the logic.

I did the following:

  1. Created an Application within Auth0.
    Used the domain, clientId and clientSecret from the Application in the env variables
  2. Created an API with identifier 'inventhora.com/api/graphql' used RS256 for it.
  3. Run it.

Hey @Afsoon. Any chance you can share your repo please? I am also working on a project with Hasura, Apollo and Auth0, with SSR. I am going nuts figuring out a good way to pass the idToken from the backend into the apolloClient headers

Thanks

Still don't get any idToken. Tried a lot meanwhile. But it looks like there is no support. So I no longer consider Auth0 for my development.

@tobkle @shansmith01 maybe this can help you:

lib/auth0.js

import { initAuth0 } from '@auth0/nextjs-auth0'

export default initAuth0({
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
  scope: process.env.AUTH0_SCOPE,
  redirectUri: process.env.REDIRECT_URI,
  postLogoutRedirectUri: process.env.POST_LOGOUT_REDIRECT_URI,
  audience: 'myaudience',
  session: {
    cookieSecret: process.env.SESSION_COOKIE_SECRET,
    cookieLifetime: process.env.SESSION_COOKIE_LIFETIME,
    storeIdToken: false,
    storeRefreshToken: false,
    storeAccessToken: true,
  },
})

apolloClient.js

import { ApolloClient } from 'apollo-client'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { HttpLink } from 'apollo-link-http'
import { setContext } from 'apollo-link-context'
import { onError } from 'apollo-link-error'
import fetch from 'isomorphic-unfetch'

let accessToken = null

const requestAccessToken = async () => {
  if (!!accessToken) return

  const res = await fetch('/api/session')
  if (res.ok) {
    const json = await res.json()
    accessToken = json.accessToken
  } else {
    accessToken = 'public'
  }
}

// return the headers to the context so httpLink can read them
const authLink = setContext(async (req, { headers }) => {
  await requestAccessToken()
  if (!accessToken || accessToken === 'public') {
    return {
      headers,
    }
  } else {
    return {
      headers: {
        ...headers,
        Authorization: `Bearer ${accessToken}`,
      },
    }
  }
})

// remove cached token on 401 from the server
const resetTokenLink = onError(({ networkError }) => {
  if (networkError && networkError.name === 'ServerError' && networkError.statusCode === 401) {
    accessToken = null
  }
})

const httpLink = new HttpLink({
  uri: process.env.API_URL,
  credentials: 'include',
  fetch,
})

export default function createApolloClient(initialState, ctx) {
  // The `ctx` (NextPageContext) will only be present on the server.
  // use it to extract auth headers (ctx.req) or similar.
  accessToken = null
  return new ApolloClient({
    ssrMode: Boolean(ctx),
    link: authLink.concat(resetTokenLink).concat(httpLink),
    cache: new InMemoryCache().restore(initialState),
  })
}

api/session.js

import auth0 from '../../lib/auth0'

export default async function session(req, res) {
  try {
    const s = await auth0.getSession(req)

    if (s) res.send(s)
    res.status(500).end(s)
  } catch (error) {
    res.status(error.status || 500).end(error.message)
  }
}

Steps

Auth0
  • Generate Auth0 Regular Web App (Next.js)
  • Generate Auth0 API with audience ('myaudience')
  • Add rules to set tokens and sync with hasura backend; docs
Hasura
  • Add HASURA_GRAPHQL_JWT_SECRET; config-generator
  • Add HASURA_GRAPHQL_UNAUTHORIZED_ROLE (e.g. role 'anonymous')
  • Add HASURA_GRAPHQL_ADMIN_SECRET
Next.js App
  • Install Auth0 and enable storeAccessToken and add audience from Auth0-API (without audience no jwt token) (e.g. lib/auth0.js)
  • Install Apollo to Next.js
  • Add link-context with jwt (e.g. apolloClient.js)
  • Add api/session-route to get session for client and server (e.g. api/session.js)

Hope that's it :-)

Interesting. I could only get this to work by:
storeIdToken: false,
storeRefreshToken: true,
storeAccessToken: true,

if i set storeIdToken to true then cookie isnt set.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

RyanPayso13 picture RyanPayso13  路  4Comments

pasenidis picture pasenidis  路  4Comments

jvorcak picture jvorcak  路  4Comments

flapjackfritz picture flapjackfritz  路  6Comments

platocrat picture platocrat  路  3Comments