mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-30 01:17:16 +00:00
Update auth-routes example
This commit is contained in:
parent
57997e294e
commit
bcaf91a278
176
examples/auth-routes/README.md
Normal file
176
examples/auth-routes/README.md
Normal file
@ -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.
|
@ -3,12 +3,14 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"body-parser": "^1.15.2",
|
"body-parser": "^1.15.2",
|
||||||
"cookie-session": "^2.0.0-alpha.2",
|
|
||||||
"express": "^4.14.0",
|
"express": "^4.14.0",
|
||||||
|
"express-session": "^1.14.2",
|
||||||
"nuxt": "latest",
|
"nuxt": "latest",
|
||||||
"whatwg-fetch": "^2.0.1"
|
"whatwg-fetch": "^2.0.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node server.js"
|
"dev": "node server.js",
|
||||||
|
"build": "nuxt build",
|
||||||
|
"start": "NODE_ENV=production node server.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Please login to see the secret content</h1>
|
<h1>Please login to see the secret content</h1>
|
||||||
<form v-if="!authUser" @submit.prevent="login">
|
<form v-if="!$store.state.authUser" @submit.prevent="login">
|
||||||
<p class="error" v-if="formError">{{ formError }}</p>
|
<p class="error" v-if="formError">{{ formError }}</p>
|
||||||
<p><i>To login, use <b>demo</b> as username and <b>demo</b> as password.</i></p>
|
<p><i>To login, use <b>demo</b> as username and <b>demo</b> as password.</i></p>
|
||||||
<p>Username: <input type="text" v-model="formUsername" name="username" /></p>
|
<p>Username: <input type="text" v-model="formUsername" name="username" /></p>
|
||||||
@ -9,21 +9,19 @@
|
|||||||
<button type="submit">Login</button>
|
<button type="submit">Login</button>
|
||||||
</form>
|
</form>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
Hello {{ authUser.username }}!
|
Hello {{ $store.state.authUser.username }}!
|
||||||
<pre>I am the secret content, I am shown only when the use is connected.</pre>
|
<pre>I am the secret content, I am shown only when the use is connected.</pre>
|
||||||
|
<p><i>You can also refresh this page, you'll still be connected!</i></p>
|
||||||
|
<button @click="logout">Logout</button>
|
||||||
</div>
|
</div>
|
||||||
|
<p><router-link to="/secret">Super secret page</router-link></p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Polyfill for window.fetch()
|
|
||||||
require('whatwg-fetch')
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data ({ req }) {
|
data () {
|
||||||
console.log(req && req.session)
|
|
||||||
return {
|
return {
|
||||||
authUser: (req && req.session.authUser) || null,
|
|
||||||
formError: null,
|
formError: null,
|
||||||
formUsername: '',
|
formUsername: '',
|
||||||
formPassword: ''
|
formPassword: ''
|
||||||
@ -31,26 +29,21 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
login () {
|
login () {
|
||||||
fetch('/api/login', {
|
this.$store.dispatch('login', {
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
username: this.formUsername,
|
username: this.formUsername,
|
||||||
password: this.formPassword
|
password: this.formPassword
|
||||||
})
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.formUsername = ''
|
||||||
|
this.formPassword = ''
|
||||||
|
this.formError = null
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.catch((e) => {
|
||||||
if (res.status === 401) {
|
this.formError = e.message
|
||||||
this.formError = 'Bad credentials'
|
|
||||||
} else {
|
|
||||||
return res.json()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then((authUser) => {
|
|
||||||
this.authUser = authUser
|
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
logout () {
|
||||||
|
this.$store.dispatch('logout')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
18
examples/auth-routes/pages/secret.vue
Normal file
18
examples/auth-routes/pages/secret.vue
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>Super secret page</h1>
|
||||||
|
<p>If you try to access this URL not conntected, you will be redirected to the home page (server-side or client-side)</p>
|
||||||
|
<router-link to="/">Back to the home page</router-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
// we use fetch() because we do not need to set data to this component
|
||||||
|
fetch ({ store, redirect }) {
|
||||||
|
if (!store.state.authUser) {
|
||||||
|
return redirect('/')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -1,16 +1,17 @@
|
|||||||
const Nuxt = require('nuxt')
|
const Nuxt = require('nuxt')
|
||||||
const bodyParser = require('body-parser')
|
const bodyParser = require('body-parser')
|
||||||
const cookieSession = require('cookie-session')
|
const session = require('express-session')
|
||||||
const app = require('express')()
|
const app = require('express')()
|
||||||
|
|
||||||
// Body parser, to access req.body
|
// Body parser, to access req.body
|
||||||
app.use(bodyParser.json())
|
app.use(bodyParser.json())
|
||||||
|
|
||||||
// Sessions with cookies to have req.session
|
// Sessions to create req.session
|
||||||
app.use(cookieSession({
|
app.use(session({
|
||||||
name: 'nuxt-session',
|
secret: 'super-secret-key',
|
||||||
keys: ['nuxt-key-1', 'nuxt-key-2'],
|
resave: false,
|
||||||
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
saveUninitialized: false,
|
||||||
|
cookie: { maxAge: 60000 }
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// POST /api/login to log in the user and add him to the req.session.authUser
|
// 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' })
|
res.status(401).json({ error: 'Bad credentials' })
|
||||||
})
|
})
|
||||||
|
|
||||||
app.use(function (req, res, next) {
|
// POST /api/logout to log out the user and remove it from the req.session
|
||||||
req.session.views = (req.session.views || 0) + 1
|
app.post('/api/logout', function (req, res) {
|
||||||
next()
|
delete req.session.authUser
|
||||||
|
res.json({ ok: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
app.all('/', function (req, res) {
|
// We instantiate Nuxt.js with the options
|
||||||
console.log(req.session)
|
new Nuxt({
|
||||||
res.send('Hello')
|
dev: process.env.NODE_ENV !== 'production'
|
||||||
})
|
})
|
||||||
|
|
||||||
new Nuxt()
|
|
||||||
.then((nuxt) => {
|
.then((nuxt) => {
|
||||||
app.use(nuxt.render)
|
app.use(nuxt.render)
|
||||||
// app.use(function (req, res) {
|
|
||||||
// nuxt.render(req, res)
|
|
||||||
// })
|
|
||||||
// .then((stream) => {
|
|
||||||
// stream.pipe(fs.createFile)
|
|
||||||
// })
|
|
||||||
app.listen(3000)
|
app.listen(3000)
|
||||||
console.log('Server is listening on http://localhost:3000')
|
console.log('Server is listening on http://localhost:3000')
|
||||||
})
|
})
|
||||||
|
69
examples/auth-routes/store/index.js
Normal file
69
examples/auth-routes/store/index.js
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user