next-auth: Error when fetching session in API

Hello again!

I’m having a problem when fetching a session in an api route. I get this error:

app_1          | CLIENT_FETCH_ERROR https://localhost:3000/api/auth/session FetchError: request to https://localhost:3000/api/auth/session failed, reason: write EPROTO 140431569815360:error:1408F10B:SSL routines:ssl3_get_record:wrong version number:../deps/openssl/openssl/ssl/record/ssl3_record.c:332:
app_1          | 
app_1          |     at ClientRequest.<anonymous> (/app/node_modules/next/dist/compiled/node-fetch/index.js:1:147710)
app_1          |     at ClientRequest.emit (events.js:310:20)
app_1          |     at TLSSocket.socketErrorListener (_http_client.js:426:9)
app_1          |     at TLSSocket.emit (events.js:310:20)
app_1          |     at errorOrDestroy (internal/streams/destroy.js:108:12)
app_1          |     at onwriteError (_stream_writable.js:424:5)
app_1          |     at onwrite (_stream_writable.js:445:5)
app_1          |     at internal/streams/destroy.js:50:7
app_1          |     at TLSSocket.Socket._destroy (net.js:677:5)
app_1          |     at TLSSocket.destroy (internal/streams/destroy.js:38:8)
app_1          |     at WriteWrap.onWriteComplete [as oncomplete] (internal/stream_base_commons.js:93:12) {
app_1          |   type: 'system',
app_1          |   errno: 'EPROTO',
app_1          |   code: 'EPROTO'
app_1          | }
app_1          | session null

The code is simple:

import { session as getSession } from 'next-auth/client'

export default async (req: NextApiRequest, res: NextApiResponse) => {
  const session = await getSession({ req });
  console.log('session', session);
  ...

Any pointers for how I can debug this? I’m not sure where to start.

Thanks!

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 24 (17 by maintainers)

Most upvoted comments

I had to do add this

NEXTAUTH_URL_INTERNAL='http://127.0.0.1:3000'

Note that it it only seemed to work with the IP address. It did not work with ‘localhost’

Hmm I think you might want to use http://localhost instead of https://localhost unless you have HTTPS set up (but it does work if you do have HTTPS setup).

If you DO have HTTPS set up it might be that your browser isn’t able to validate your locally signed certificate.

Hey this MIGHT work for testing locally:

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

@iaincollins and @iaincollins I was doing some additional testing to see if I could mimic the error @TwoAbove was getting and the same was happening using HAPROXY.

version: '3'

services:
  next-auth-example:
    build: 
     context:  ./
     dockerfile: Dockerfile
    restart: unless-stopped
    ports:
      - '3000:3000'
  haproxy:
    build: 
     context:  ./haproxy
     dockerfile: Dockerfile
    links:
      - next-auth-example 
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
      - 1936:1936


HAPROXY Config The configuration below gets an A+ on https://www.ssllabs.com/

Dockerfile

FROM haproxy:1.7
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
COPY /configs/ssl/private/localhost.pem  /usr/local/etc/private/localhost.pem

haproxy.cfg

global
		log 127.0.0.1 local0 notice
		maxconn 2000
 
defaults
		log global
		mode    http
		option  httplog
		option  dontlognull
		option   forwardfor
		option   http-server-close
		timeout connect 5000
		timeout client  50000
		timeout server  50000
 
listen  stats
        bind *:1936
        mode http
        stats enable
        stats hide-version
        stats realm Haproxy\ Statistics
        stats uri /

frontend www_frontend
		bind *:80     # Bind to port 80 (www) on the container
		reqadd X-Forwarded-Proto:\ http
		default_backend www-backend

frontend www-https
   bind *:443 ssl crt /usr/local/etc/private/localhost.pem no-sslv3 no-tlsv10 no-tls-tickets ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
   reqadd X-Forwarded-Proto:\ https
   default_backend www-backend

backend www-backend
   redirect scheme https if !{ ssl_fc }
   http-request redirect prefix http://%[hdr(host),regsub(^www\.,,i)] code 301 if { hdr_beg(host) -i www. }
   server www-1 next-auth-example:3000 check
   #server www-2 DOCKERHOST02:3000 check
   #server www-3 DOCKERHOST03:3000 check
   compression algo gzip



Error when running Next-Auth-Example

next-auth-example_1  | CLIENT_FETCH_ERROR https://localhost/api/auth/session FetchError: request to https://localhost/api/auth/session failed, reason: connect ECONNREFUSED 127.0.0.1:443
next-auth-example_1  |     at ClientRequest.<anonymous> (/usr/src/app/node_modules/next/dist/compiled/node-fetch/index.js:1:147710)
next-auth-example_1  |     at ClientRequest.emit (events.js:310:20)
next-auth-example_1  |     at TLSSocket.socketErrorListener (_http_client.js:426:9)
next-auth-example_1  |     at TLSSocket.emit (events.js:310:20)
next-auth-example_1  |     at emitErrorNT (internal/streams/destroy.js:92:8)
next-auth-example_1  |     at emitErrorAndCloseNT (internal/streams/destroy.js:60:3)
next-auth-example_1  |     at processTicksAndRejections (internal/process/task_queues.js:84:21) {
next-auth-example_1  |   type: 'system',
next-auth-example_1  |   errno: 'ECONNREFUSED',
next-auth-example_1  |   co

To get around this error on development.

  1. Create Certificate
openssl req -x509 -out localhost.crt -keyout localhost.key \
  -newkey rsa:2048 -nodes -sha256 \
  -subj '/CN=localhost' -extensions EXT -config <( \
   printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")
  1. Open up Keychain Access.
  2. Drag your certificate into Keychain Access.
  3. Go into the Certificates section and locate the certificate you just added
  4. Double click on it, enter the trust section and under “When using this certificate” select “Always Trust

Oh neato!

So you can set process.env.NEXTAUTH_SITE to http://localhost:3000 URL and the session() method will use that.

When calling session() on the server, this env var just needs to be set before you call it, like this:

process.env.NEXTAUTH_SITE = "http://localhost:3000"

You can also set it in nextauth.config.js as a client env if you want the browser to do that, but in this case I don’t think you need to do that, as it only needs to be set when called server side in your setup, I think?

Anything here @iaincollins? We could really use a server-side getSession utility. It makes no sense to make another api request from our api just to get the session. We just need to load it directly from the db.

Oh sure, I appreciate that is kludgy but you can totally just load the database provider directly and call methods from it.

e.g.

import Adapters from `next-auth/adapters`
const adapter = Adapters.Default(process.env.DATABASE_URL)
const db = await adapter.getAdapter({ /* options */ } )
const { getSession } = db

const sessionTokenCookieName = '__Secure-next-auth.session-token'
const sessionToken = req.cookies[sessionTokenCookieName]

// getSession(sessionToken)

BTW: Just checking if you are using JWT or database sessions?

If you are using JWT instead of database sessions, then you can get the session from the cookie, like this:

const jwtSecret = 'your secret' //  // see docs for basic options
const sessionTokenCookieName = '__Secure-next-auth.session-token' 
const sessionMaxAge =  30 * 24 * 60 * 60 * 1000 // see docs for basic options
const sessionToken = req.cookies[sessionTokenCookieName]
const token = jwt.verify(sessionToken, jwtSecret, { maxAge: sessionMaxAge })

However I think I would see if there is a way you can set NEXTAUTH_SITE to something other endpoint (just HTTP but on a different port) that the server can call?

Maybe there there an internal IP or hostname you could set it to that would mean it doesn’t try to connect to the nginx instance?

I think as it only needs to be set for local development (and maybe in a test environment, if you have one) that would be easier.

So, to fix this, I have some options:

  1. add https to app
  2. handle session verification without calling the https url
  3. Find a workaround

Okay, found what the issue is.

Here’s how I have things set up:

docker-compose has 2 containers (will have more once I set everything up): app nginx

only nginx is exposed and routes all requests wherever, and handles ssl. app receives http requests, but all the https parts work thanks to nginx.

When the session gets requested by app itself, it uses baseUrl, which is https://localhost:3000. Since node doesn’t have ssl configured, it errors

So the cert authority that I’m using is on my machine (so ssl works in browser), while node can’t verify the certs?

I’ll dig a bit deeper and check the certs