diff --git a/examples/auth-routes/README.md b/examples/auth-routes/README.md new file mode 100644 index 0000000000..c98601e3e0 --- /dev/null +++ b/examples/auth-routes/README.md @@ -0,0 +1,176 @@ +# Authenticated Routes + +> Nuxt.js can be used to create authenticated routes easily. + +## Using Express and Sessions + +To add the sessions feature in our application, we will use `express` and `express-session`, for this, we need to use Nuxt.js programmatically. + +First, we install the depedencies: +```bash +yarn add express express-session body-parser whatwg-fetch +``` + +*We will talk about `whatwg-fetch` later.* + +Then we create our `server.js`: +```js +const Nuxt = require('nuxt') +const bodyParser = require('body-parser') +const session = require('express-session') +const app = require('express')() + +// Body parser, to access req.body +app.use(bodyParser.json()) + +// Sessions to create req.session +app.use(session({ + secret: 'super-secret-key', + resave: false, + saveUninitialized: false, + cookie: { maxAge: 60000 } +})) + +// POST /api/login to log in the user and add him to the req.session.authUser +app.post('/api/login', function (req, res) { + if (req.body.username === 'demo' && req.body.password === 'demo') { + req.session.authUser = { username: 'demo' } + return res.json({ username: 'demo' }) + } + res.status(401).json({ error: 'Bad credentials' }) +}) + +// POST /api/logout to log out the user and remove it from the req.session +app.post('/api/logout', function (req, res) { + delete req.session.authUser + res.json({ ok: true }) +}) + +// We instantiate Nuxt.js with the options +new Nuxt({ + dev: process.env.NODE_ENV !== 'production' +}) +.then((nuxt) => { + app.use(nuxt.render) + app.listen(3000) + console.log('Server is listening on http://localhost:3000') +}) +.catch((error) => { + console.error(error) + process.exit(1) +}) +``` + +And we update our `package.json` scripts: +```json +// ... +"scripts": { + "dev": "node server.js", + "build": "nuxt build", + "start": "NODE_ENV=production node server.js" +} +// ... +``` + +## Using the store + +We need a global state to let our application if the user is connected **across the pages**. + +To let Nuxt.js use Vuex, we create a `store/index.js` file: + +```js +import Vue from 'vue' +import Vuex from 'vuex' + +Vue.use(Vuex) + +// Polyfill for window.fetch() +require('whatwg-fetch') + +const store = new Vuex.Store({ + + state: { + authUser: null + }, + + mutations: { + SET_USER: function (state, user) { + state.authUser = user + } + }, + + actions: { + // ... + } + +}) + +export default store +``` + +1. We import `Vue` and `Vuex` (included in Nuxt.js) and we tell Vue to use Vuex to let us use `$store` in our components +2. We `require('whatwg-fetch')` to polyfill the `fetch()` method across all browsers (see [fetch repo](https://github.com/github/fetch)) +3. We create our `SET_USER` mutation which will set the `state.authUser` to the conntected user +4. We export our store instance to Nuxt.js can inject it to our main application + +### nuxtServerInit() action + +Nuxt.js will call a specific action called `nuxtServerInit` with the context in argument, so when the app will be loaded, the store will be already filled with some data we can get from the server. + +In our `store/index.js`, we can add the `nuxtServerInit` action: +```js +nuxtServerInit ({ commit }, { req }) { + if (req.session && req.session.authUser) { + commit('SET_USER', req.session.authUser) + } +} +``` + +### login() action + +We add a `login` action which will be called from our pages component to log in the user: +```js +login ({ commit }, { username, password }) { + return fetch('/api/login', { + // Send the client cookies to the server + credentials: 'same-origin', + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + username, + password + }) + }) + .then((res) => { + if (res.status === 401) { + throw new Error('Bad credentials') + } else { + return res.json() + } + }) + .then((authUser) => { + commit('SET_USER', authUser) + }) +} +``` + +### logout() method + +```js +logout ({ commit }) { + return fetch('/api/logout', { + // Send the client cookies to the server + credentials: 'same-origin', + method: 'POST' + }) + .then(() => { + commit('SET_USER', null) + }) +} +``` + +## Pages components + +Then we can use the `$store.state.authUser` data to check if the user is connected in our application. diff --git a/examples/auth-routes/package.json b/examples/auth-routes/package.json index 2546c1b8aa..b286e52738 100644 --- a/examples/auth-routes/package.json +++ b/examples/auth-routes/package.json @@ -3,12 +3,14 @@ "description": "", "dependencies": { "body-parser": "^1.15.2", - "cookie-session": "^2.0.0-alpha.2", "express": "^4.14.0", + "express-session": "^1.14.2", "nuxt": "latest", "whatwg-fetch": "^2.0.1" }, "scripts": { - "start": "node server.js" + "dev": "node server.js", + "build": "nuxt build", + "start": "NODE_ENV=production node server.js" } } diff --git a/examples/auth-routes/pages/index.vue b/examples/auth-routes/pages/index.vue index 7c0c1f00a9..7ff1293bda 100644 --- a/examples/auth-routes/pages/index.vue +++ b/examples/auth-routes/pages/index.vue @@ -1,7 +1,7 @@ diff --git a/examples/auth-routes/server.js b/examples/auth-routes/server.js index 199bd0893d..f4af24986f 100644 --- a/examples/auth-routes/server.js +++ b/examples/auth-routes/server.js @@ -1,16 +1,17 @@ const Nuxt = require('nuxt') const bodyParser = require('body-parser') -const cookieSession = require('cookie-session') +const session = require('express-session') const app = require('express')() // Body parser, to access req.body app.use(bodyParser.json()) -// Sessions with cookies to have req.session -app.use(cookieSession({ - name: 'nuxt-session', - keys: ['nuxt-key-1', 'nuxt-key-2'], - maxAge: 24 * 60 * 60 * 1000 // 24 hours +// Sessions to create req.session +app.use(session({ + secret: 'super-secret-key', + resave: false, + saveUninitialized: false, + cookie: { maxAge: 60000 } })) // POST /api/login to log in the user and add him to the req.session.authUser @@ -22,25 +23,18 @@ app.post('/api/login', function (req, res) { res.status(401).json({ error: 'Bad credentials' }) }) -app.use(function (req, res, next) { - req.session.views = (req.session.views || 0) + 1 - next() +// POST /api/logout to log out the user and remove it from the req.session +app.post('/api/logout', function (req, res) { + delete req.session.authUser + res.json({ ok: true }) }) -app.all('/', function (req, res) { - console.log(req.session) - res.send('Hello') +// We instantiate Nuxt.js with the options +new Nuxt({ + dev: process.env.NODE_ENV !== 'production' }) - -new Nuxt() .then((nuxt) => { app.use(nuxt.render) - // app.use(function (req, res) { - // nuxt.render(req, res) - // }) - // .then((stream) => { - // stream.pipe(fs.createFile) - // }) app.listen(3000) console.log('Server is listening on http://localhost:3000') }) diff --git a/examples/auth-routes/store/index.js b/examples/auth-routes/store/index.js new file mode 100644 index 0000000000..6f2a38f1a0 --- /dev/null +++ b/examples/auth-routes/store/index.js @@ -0,0 +1,69 @@ +import Vue from 'vue' +import Vuex from 'vuex' + +Vue.use(Vuex) + +// Polyfill for window.fetch() +require('whatwg-fetch') + +const store = new Vuex.Store({ + + state: { + authUser: null + }, + + mutations: { + SET_USER: function (state, user) { + state.authUser = user + } + }, + + actions: { + + nuxtServerInit ({ commit }, { req }) { + if (req.session && req.session.authUser) { + commit('SET_USER', req.session.authUser) + } + }, + + login ({ commit }, { username, password }) { + return fetch('/api/login', { + // Send the client cookies to the server + credentials: 'same-origin', + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + username, + password + }) + }) + .then((res) => { + if (res.status === 401) { + throw new Error('Bad credentials') + } else { + return res.json() + } + }) + .then((authUser) => { + commit('SET_USER', authUser) + }) + }, + + logout ({ commit }) { + return fetch('/api/logout', { + // Send the client cookies to the server + credentials: 'same-origin', + method: 'POST' + }) + .then(() => { + commit('SET_USER', null) + }) + } + + } + +}) + +export default store