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!
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.
I think I found the issue.
I think there needs to be a separate function to fetch the session for the server-side.
The error is because it looks like the server requests the session object through a http request, instead of using the DB.
I have this behind an nginx ingress and inside a docker container.
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
Yeah, that sound about right, I think it's probably a consequence of the configuration here.
The NextAuth.js client uses the build in fetch() in Next.js to make the call, I'm not sure how to tell that globally to use certificate / disable SSL. (If it turns out there isn't a way, we can always try and figure out how to set that at runtime in NextAuth.js for folks that need it).
@geraldnolan might be able to help with this, he wrote the documentation for apple which needs HTTPS to run locally and might have solved this :-)
PS: The session() method works both client and server (normally) but I am thinking of adding a server-only method to that talks directly to the DB (or reads JWT cookie if that is enabled); it would need to be passed a DB connection though, so would be more awkward to use.
Hey this MIGHT work for testing locally:
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
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, to fix this, I have some options:
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?
(Note in production everything should be fine; the example site has server side calls to session; it should just work normally when everything has a 'real' cert.)
Thanks!
By setting NEXTAUTH_SITE I now get this error as a response:
400 Bad Request
The plain HTTP request was sent to HTTPS port
nginx/1.19.0
So it looks like the best solution is to have a server-side method
I see that is server/session.js there is already a usage of getSession from the adapter.
Would it be sane to write a wrapper around that to get the session?
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.
Thanks @iaincollins !
Yeah, I'll try to get the redirect working before doing this.
@TwoAbove Can you share your nginx configuration?
@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.
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")
PS: The session() method works both client and server (normally) but I am thinking of adding a server-only method to that talks directly to the DB (or reads JWT cookie if that is enabled).
Is there any update on this server-only method @iaincollins ? It would be super useful for us, as it would effectively half our Lambda invocations (every single API endpoint always needs to call the "/session" endoint, resulting in two Lambda invocations). Thank you! ❤️
Most helpful comment
@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.
HAPROXY Config The configuration below gets an A+ on https://www.ssllabs.com/
Dockerfile
haproxy.cfg
Error when running Next-Auth-Example
To get around this error on development.