adonisjs / auth

Official Authentication package for AdonisJS
https://docs.adonisjs.com/guides/auth/introduction
MIT License
196 stars 64 forks source link

Keep getting "E_INVALID_SESSION: Invalid session" after switch from "jwt" to "session" authentication scheme in api-only app #142

Closed nperegrine closed 4 years ago

nperegrine commented 4 years ago

Hello @RomainLanz @thetutlage ,

I keep getting the error "E_INVALID_SESSION: Invalid session" after I switch from "jwt" to "session" authentication scheme in api-only app.

Initially, I wanted to use the default "jwt" authentication scheme with my Nuxt front-end app, but due to security concerns about token theft and CSRF which are not handled by Nuxt.js at all, I decided I will switch back to basic "sessions" authentication.

Is it not possible to switch from "jwt" to "session" authentication with the api-only boilerplate? Or must I install the adonis-fullstack-app before I can use sessions? I have installed all the respective adonis sessions and shield modules but to no avail.

I don't know what I might be doing wrong and I think this may be a bug with AdonisJs

Steps to Reproduce

1.) Install adonis project using api-only flag 2.) Switch from "jwt" to "session" authentication scheme 3.) Make sure adonis "Session" and "Shield" modules are installed and configured properly 4.) Setup login and the register controller 5.) On successful login, when I try to access http://localhost:3333/v1/me to get the currently authenticated user, I get the error "E_INVALID_SESSION: Invalid session"

Here are my files

a.) auth.js

`/* @type {import('@adonisjs/framework/src/Env')} / const Env = use('Env')

module.exports = { authenticator: 'session',

session: { serializer: 'lucid', model: 'App/Models/User', scheme: 'session', uid: 'email', password: 'password' },

basic: { serializer: 'lucid', model: 'App/Models/User', scheme: 'basic', uid: 'email', password: 'password' }, jwt: { serializer: 'lucid', model: 'App/Models/User', scheme: 'jwt', uid: 'email', password: 'password', options: { secret: Env.get('APP_KEY') } },

api: { serializer: 'lucid', model: 'App/Models/User', scheme: 'api', uid: 'email', password: 'password' } }`

b.) session.js

`"use strict";

/* @type {import('@adonisjs/framework/src/Env')} / const Env = use("Env");

module.exports = {

driver: Env.get("SESSION_DRIVER", "cookie"),

cookieName: "keeb-session",

clearWithBrowser: true,

age: "2h",

cookie: { httpOnly: true, sameSite: false, path: "/" },

file: { location: "sessions" },

redis: { host: "127.0.0.1", port: 6379, password: null, db: 0, keyPrefix: "" } };`

c.) shield.js

`"use strict";

module.exports = {

csp: {

reportOnly: false,

setAllHeaders: false,

disableAndroid: true

},

xss: { enabled: true, enableOnOldIE: false },

xframe: "DENY",

nosniff: true,

noopen: true,

csrf: { enable: false, methods: ["POST", "PUT", "DELETE"], filterUris: [], cookieOptions: { httpOnly: false, sameSite: true, path: "/", maxAge: 7200 } } };`

d.) start/app.js

`"use strict";

const providers = [ "@adonisjs/framework/providers/AppProvider", "@adonisjs/auth/providers/AuthProvider", "@adonisjs/bodyparser/providers/BodyParserProvider", "@adonisjs/cors/providers/CorsProvider", "@adonisjs/lucid/providers/LucidProvider", "@adonisjs/session/providers/SessionProvider", "adonis-lucid-polymorphic/providers/PolymorphicProvider", "adonis-bumblebee/providers/BumblebeeProvider", "@adonisjs/validator/providers/ValidatorProvider", "@adonisjs/mail/providers/MailProvider", "@adonisjs/framework/providers/ViewProvider", "@adonisjs/shield/providers/ShieldProvider" ];

const aceProviders = ["@adonisjs/lucid/providers/MigrationsProvider"];

const aliases = {};

const commands = [];

module.exports = { providers, aceProviders, aliases, commands };`

e.) start/kernel.js

`'use strict'

/* @type {import('@adonisjs/framework/src/Server')} / const Server = use('Server')

const globalMiddleware = [ 'Adonis/Middleware/BodyParser', 'Adonis/Middleware/Session', 'Adonis/Middleware/Shield', 'Adonis/Middleware/AuthInit', 'App/Middleware/ConvertEmptyStringsToNull', ]

const namedMiddleware = { auth: 'Adonis/Middleware/Auth', guest: 'Adonis/Middleware/AllowGuestOnly' }

const serverMiddleware = [ 'Adonis/Middleware/Static', 'Adonis/Middleware/Cors' ]

Server .registerGlobal(globalMiddleware) .registerNamed(namedMiddleware) .use(serverMiddleware)`

f.) Login controller

`"use strict";

class LoginController {` async login({ request, auth, response }) { const { email, password } = request.only(["email", "password"]);

    await auth.attempt(email, password);

return response.json({ status: "Login successful." }); }`

Package.json

{ "name": "adonis-api-app", "version": "4.1.0", "adonis-version": "4.1.0", "description": "Adonisjs boilerplate for API server with pre-configured JWT", "main": "index.js", "scripts": { "start": "node server.js", "test": "node ace test" }, "keywords": [ "adonisjs", "adonis-app" ], "author": "", "license": "UNLICENSED", "private": true, "dependencies": { "@adonisjs/ace": "^5.0.8", "@adonisjs/auth": "^3.1.0", "@adonisjs/bodyparser": "^2.0.5", "@adonisjs/cors": "^1.0.7", "@adonisjs/fold": "^4.0.9", "@adonisjs/framework": "^5.0.9", "@adonisjs/ignitor": "^2.0.8", "@adonisjs/lucid": "^6.1.3", "@adonisjs/mail": "^3.0.10", "@adonisjs/session": "^1.0.28", "@adonisjs/shield": "^1.0.8", "@adonisjs/validator": "^5.0.6", "@google-cloud/projectify": "^1.0.4", "adonis-bumblebee": "^2.1.0", "adonis-lucid-polymorphic": "^1.0.1", "aws-sdk": "^2.597.0", "axios": "^0.19.0", "device-detector-js": "^1.1.2", "formidable": "^1.2.1", "moment": "^2.24.0", "mysql": "^2.17.1", "nanoid": "^2.1.8", "pkgcloud": "^1.7.0", "sharp": "^0.23.4", "twilio": "^3.39.1" }, "devDependencies": { "babel-eslint": "^10.0.2", "eslint": "^6.8.0", "eslint-config-prettier": "^6.9.0", "eslint-loader": "^2.2.1", "eslint-plugin-prettier": "^3.1.2", "prettier": "^1.19.1" }, "autoload": { "App": "./app" } }

My experience with Adonis has been great. I personally don't like posting issues often but after a lot of research, I can't seem to figure out the cause of this error. I am currently rounding up with my project (nuxt.js frontend + adonisjs backend) and this is the main issue that's preventing us from going live.

Please any help or insight will be greatly appreciated 🙏🙏

rishabhpoddar commented 4 years ago

Hi, This is not a direct answer to your question, however, if by session you mean opaque tokens (random string as an access token), then just using that will not solve your concerns about token theft or CSRF.

At best, you cannot prevent token theft, all you can do is detect it and then take action. As per RFC 6749 (https://tools.ietf.org/html/rfc6749#section-10.4) using rotating refresh tokens is a great (and only reliable) method to detect token theft. Others use changes in IP address and device fingerprints as a proxy for token theft, but those lead to many false negatives/positives..

There is an article that talks about all the security considerations with sessions (including token theft and CSRF protection) and how you can solve them. Here is the link: https://supertokens.io/blog/all-you-need-to-know-about-user-session-security

I hope this helps.

RomainLanz commented 4 years ago

Hi @nperegrine! :wave:

@rishabhpoddar Using the session scheme means using a HTTP Only Secure Cookie configured with Same-Site. So it solves the issue of CSRF & XSS attacks.

Concerning your issue, ensure that you are sending the request from Nuxt using credentials: true.

rishabhpoddar commented 4 years ago

@RomainLanz thanks for the clarification. While same-site does solve CSRF for most cases, there may be a problem in using it (the point below is an edge use cases):

I'm not too sure about my opinion above, please correct me if I am wrong. That being said, I would still be most comfortable using anti-csrf tokens for CSRF protection.

RomainLanz commented 4 years ago

@rishabhpoddar I'm not quite sure to have understood your first point.

Using Same-Site: LAX is the way recommended by OWASP to avoid CSRF vulnerability. The primarily issue with this header is that IE11 do not support it.

Concerning your second point, this would be the result of a very bad API design. 😄 I believe it shouldn't not be used as example.

rishabhpoddar commented 4 years ago

@RomainLanz Please allow me to rephrase my first point: same-site LAX, for website a.com will prevent its iframe on website b.com to work, assuming a.com is a service that needs to be embedded in other sites. That being said, this is a very nice case.

Also, I have edited my previous answer to remove the second point. Thanks!

nperegrine commented 4 years ago

Thank you very much @RomainLanz ... configuring credentials: true with Axios and also setting credentials: true in config/cors.js resolved the issue.

Thank you all for your assistance.

nperegrine commented 4 years ago

@rishabhpoddar I'm not quite sure to have understood your first point.

Using Same-Site: LAX is the way recommended by OWASP to avoid CSRF vulnerability. The primarily issue with this header is that IE11 do not support it.

Concerning your second point, this would be the result of a very bad API design. 😄 I believe it shouldn't not be used as example.

@RomainLanz please how can I set a cookie in AdonisJs as Same-site: LAX to help prevent CSRF ? Here's how I'm creating my adonisjs cookie:

response.cookie('token', token, { secure: true, httpOnly: true, domain: 'domain.com', path: '/', sameSite: true, maxAge: 2592000 })

If I change sameSite: false does that mean the same as Same-Site: LAX or is there another way of doing this?

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.