├── .gitignore ├── README.md ├── app.vue ├── layouts └── default.vue ├── nuxt.config.ts ├── package-lock.json ├── package.json ├── pages ├── dashboard.vue └── index.vue ├── public ├── favicon.ico └── robots.txt ├── scripts ├── add-posts-table.js └── create-db.js ├── server └── tsconfig.json ├── tsconfig.json └── utils └── db.ts /.gitignore: -------------------------------------------------------------------------------- 1 | db.sqlite 2 | 3 | # Nuxt dev/build outputs 4 | .output 5 | .data 6 | .nuxt 7 | .nitro 8 | .cache 9 | dist 10 | 11 | # Node dependencies 12 | node_modules 13 | 14 | # Logs 15 | logs 16 | *.log 17 | 18 | # Misc 19 | .DS_Store 20 | .fleet 21 | .idea 22 | 23 | # Local env files 24 | .env 25 | .env.* 26 | !.env.example 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Nuxt Authentication Course Sample Project 2 | 3 | This repo is for education purposes only, not for production. (many things are missing for production use) 4 | 5 | Install the dependencies: 6 | 7 | ```bash 8 | npm install 9 | ``` 10 | 11 | Run the dev server: 12 | 13 | ```bash 14 | npm run dev 15 | ``` 16 | -------------------------------------------------------------------------------- /app.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 44 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 26 | -------------------------------------------------------------------------------- /nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | export default defineNuxtConfig({ 3 | compatibilityDate: '2024-04-03', 4 | devtools: { enabled: true } 5 | }) 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-app", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "build": "nuxt build", 7 | "dev": "nuxt dev", 8 | "generate": "nuxt generate", 9 | "preview": "nuxt preview", 10 | "postinstall": "nuxt prepare" 11 | }, 12 | "dependencies": { 13 | "bcrypt": "^5.1.1", 14 | "better-sqlite3": "^9.6.0", 15 | "crypto-random-string": "^5.0.0", 16 | "nuxt": "^3.12.4", 17 | "nuxt-auth-utils": "^0.3.4", 18 | "vue": "latest" 19 | }, 20 | "devDependencies": { 21 | "@types/bcrypt": "^5.0.2", 22 | "@types/better-sqlite3": "^7.6.11" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /pages/dashboard.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-Pop/Nuxt_Authentication/3e3c20ea03893ae3c50ff14b87ab3b85f84eafb6/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /scripts/add-posts-table.js: -------------------------------------------------------------------------------- 1 | import Database from 'better-sqlite3' 2 | 3 | const db = new Database('db.sqlite') 4 | 5 | db.prepare(`CREATE TABLE IF NOT EXISTS posts ( 6 | id TEXT PRIMARY KEY NOT NULL, 7 | content TEXT NOT NULL, 8 | userId TEXT NOT NULL 9 | )`).run() 10 | 11 | db.close() 12 | -------------------------------------------------------------------------------- /scripts/create-db.js: -------------------------------------------------------------------------------- 1 | import Database from 'better-sqlite3' 2 | 3 | const db = new Database('db.sqlite') 4 | 5 | db.prepare(`CREATE TABLE IF NOT EXISTS users ( 6 | id TEXT PRIMARY KEY NOT NULL, 7 | username TEXT UNIQUE NOT NULL, 8 | passwordHash TEXT NOT NULL, 9 | sessionToken TEXT, 10 | csrfToken TEXT 11 | )`).run() 12 | 13 | db.close() 14 | -------------------------------------------------------------------------------- /server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /utils/db.ts: -------------------------------------------------------------------------------- 1 | import Database from 'better-sqlite3' 2 | import cryptoRandomString from 'crypto-random-string' 3 | import bcrypt from 'bcrypt' 4 | 5 | export const db = new Database('db.sqlite', { verbose: console.log }) 6 | 7 | export const createSession = (username: string) => { 8 | const sessionToken = cryptoRandomString({length: 21, type: 'base64'}) 9 | const csrfToken = cryptoRandomString({length: 21, type: 'base64'}) 10 | const user = db 11 | .prepare( 12 | 'SELECT id FROM users WHERE username=?' 13 | ) 14 | .get(username) 15 | if (user) { 16 | const info = db 17 | .prepare('UPDATE users SET sessionToken=?, csrfToken=? WHERE id=?') 18 | .run(sessionToken, csrfToken, user.id) 19 | if (info.changes === 1) { 20 | return { sessionToken, userId: user.id } 21 | } 22 | } 23 | } 24 | 25 | export const createUser = async (username: string, password: string) => { 26 | const id = cryptoRandomString({length: 21, type: 'base64'}) 27 | const passwordHash = await bcrypt.hash(password, 10) 28 | try { 29 | const info = db 30 | .prepare('INSERT INTO users (id, username, passwordHash, sessionToken, csrfToken) VALUES (?, ?, ?, NULL, NULL)') 31 | .run(id, username, passwordHash) 32 | return info.changes === 1 33 | } catch { 34 | return false 35 | } 36 | } 37 | 38 | export const deleteSession = (userId: string) => { 39 | db 40 | .prepare('UPDATE users SET sessionToken=NULL, csrfToken=NULL WHERE id=?') 41 | .run(userId) 42 | } 43 | 44 | export const verifyPassword = async (username: string, password: string) => { 45 | const result = db 46 | .prepare<[string], { passwordHash: string }>( 47 | 'SELECT passwordHash FROM users WHERE username=?' 48 | ) 49 | .get(username) 50 | if (result !== undefined) { 51 | return await bcrypt.compare(password, result.passwordHash) 52 | } 53 | return false 54 | } 55 | 56 | export const verifySession = (userId: string, sessionToken: string) => { 57 | const result = db 58 | .prepare<[string, string], { id: string }>( 59 | 'SELECT id FROM users WHERE id=? AND sessionToken=?' 60 | ) 61 | .get(userId, sessionToken) 62 | return result !== undefined && result.id === userId 63 | } 64 | 65 | 66 | export const getPostsByUser = (userId: string) => { 67 | const result = db 68 | .prepare<[string], { id: string, content: string }>( 69 | `SELECT id, content FROM posts WHERE userId=?` 70 | ) 71 | .all(userId) 72 | return Array.isArray(result) ? result : [] 73 | } 74 | 75 | export const createPost = (userId: string, content: string) => { 76 | const id = cryptoRandomString({length: 21, type: 'base64'}) 77 | const info = db 78 | .prepare('INSERT INTO posts (id, content, userId) VALUES (?, ?, ?)') 79 | .run(id, content, userId) 80 | return info.changes === 1 81 | } 82 | 83 | export const getCsrfToken = (userId: string) => { 84 | const result = db 85 | .prepare<[string], { csrfToken: string }>( 86 | `SELECT csrfToken FROM users WHERE id=?` 87 | ) 88 | .get(userId) 89 | return result?.csrfToken 90 | } 91 | 92 | export const verifyCsrfToken = (userId: string, token: string) => { 93 | const result = db 94 | .prepare<[string], { csrfToken: string }>( 95 | `SELECT csrfToken FROM users WHERE id=?` 96 | ) 97 | .get(userId) 98 | return result !== undefined ? result.csrfToken === token : false 99 | } 100 | --------------------------------------------------------------------------------