diff --git a/clase-7/.gitignore b/clase-7/.gitignore new file mode 100644 index 0000000..2226022 --- /dev/null +++ b/clase-7/.gitignore @@ -0,0 +1,2 @@ +node_modules +db \ No newline at end of file diff --git a/clase-7/config.js b/clase-7/config.js new file mode 100644 index 0000000..e7eabf0 --- /dev/null +++ b/clase-7/config.js @@ -0,0 +1,5 @@ +export const { + PORT = 3000, + SALT_ROUND = 10, + SECRET_JWT_KEY = 'this-is-an-awesome-secret-key-mucho-mas-largo-y-muy-seguro' +} = process.env diff --git a/clase-7/index.js b/clase-7/index.js new file mode 100644 index 0000000..711476f --- /dev/null +++ b/clase-7/index.js @@ -0,0 +1,81 @@ +import express from 'express' +import { PORT, SECRET_JWT_KEY } from './config.js' +import cookieParser from 'cookie-parser' +import jwt from 'jsonwebtoken' +import { UserRepository } from './user-repository.js' + +const app = express() + +app.set('view engine', 'ejs') + +app.use(express.json()) +app.use(cookieParser()) + +app.use((req, res, next) => { + const token = req.cookies.access_token + req.session = { user: null } + + try { + const data = jwt.verify(token, SECRET_JWT_KEY) + req.session.user = data + } catch (error) { + req.session.user = null + } + + next() +}) + +app.get('/', (req, res) => { + const { user } = req.session + res.render('index', user) +}) + +app.post('/login', async (req, res) => { + const { username, password } = req.body + try { + const user = await UserRepository.login({ username, password }) + const token = jwt.sign( + { id: user._id, username: user.username }, + SECRET_JWT_KEY, + { + expiresIn: '1h' + } + ) + res + .cookie('access_token', token, { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'strict', + maxAge: 1000 * 60 * 60 + }) + .status(200).send({ user, token }) + } catch (error) { + res.status(401).send({ error: error.message }) + } +}) + +app.post('/register', async (req, res) => { + const { username, password } = req.body + try { + const id = await UserRepository.create({ + username, + password + }) + res.send({ id }) + } catch (error) { + res.status(400).send({ error: error.message }) + } +}) + +app.post('/logout', (req, res) => { + res + .clearCookie('access_token') + .json({ message: 'Logout successful' }) +}) + +app.get('/protected', (req, res) => { + const { user } = req.session + if (!user) return res.status(403).send('Access not authorized') + res.render('protected', user) +}) +app.listen(PORT, () => { console.log(`Server is listen on port ${PORT}`) }) diff --git a/clase-7/package.json b/clase-7/package.json new file mode 100644 index 0000000..c01a7ab --- /dev/null +++ b/clase-7/package.json @@ -0,0 +1,28 @@ +{ + "name": "6.-user-aouth-midu", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "dev": "node --watch index.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "bcrypt": "^5.1.1", + "cookie-parser": "^1.4.7", + "db-local": "^3.1.0", + "ejs": "^3.1.10", + "express": "^4.21.2", + "jsonwebtoken": "^9.0.2" + }, + "devDependencies": { + "standard": "^17.1.2" + }, + "eslintConfig": { + "extends": "standard" + } +} diff --git a/clase-7/readme.md b/clase-7/readme.md new file mode 100644 index 0000000..c3bdd96 --- /dev/null +++ b/clase-7/readme.md @@ -0,0 +1,9 @@ +## To run this app download it and run the command npm i to install all denpendencies (Is required have node on you computer). + +```bash +npm i +``` +## screenshots: +![Image](https://github.com/user-attachments/assets/f91a1f7a-ba1c-418f-ade7-7b91264c5b67) +![Image](https://github.com/user-attachments/assets/0a65335a-33bb-41d6-8db6-25af541d8f04) +![Image](https://github.com/user-attachments/assets/eb071544-6c44-4e68-b6d4-7f1606278d8c) \ No newline at end of file diff --git a/clase-7/user-repository.js b/clase-7/user-repository.js new file mode 100644 index 0000000..dc57c79 --- /dev/null +++ b/clase-7/user-repository.js @@ -0,0 +1,54 @@ +import DBLocal from 'db-local' +import crypto from 'crypto' +import bcrypt from 'bcrypt' +import { SALT_ROUND } from './config.js' +const { Schema } = new DBLocal({ path: './db' }) + +const User = Schema('User', { + _id: { type: String, required: true }, + username: { type: String, required: true }, + password: { type: String, required: true } +}) + +export class UserRepository { + static async create ({ username, password }) { + Validatiton.password(password) + Validatiton.username(username) + const user = User.findOne({ username }) + if (user) throw new Error(`The username: ${username} is already taken`) + const id = crypto.randomUUID() + const hashedPassword = await bcrypt.hash(password, SALT_ROUND) + User.create({ + id, + username, + password: hashedPassword + }).save() + + return id + } + + static async login ({ username, password }) { + Validatiton.username(username) + Validatiton.password(password) + + const user = User.findOne({ username }) + if (!user) throw new Error('This user does not exist in our database') + const isValid = await bcrypt.compare(password, user.password) + if (!isValid) throw new Error('Incorrect password') + const { password: _, ...publicUser } = user + console.log(publicUser) + return publicUser + } +} + +class Validatiton { + static password (password) { + if (typeof password !== 'string') throw new Error('Password must be a string') + if (password.length < 6) throw new Error('Password at leat must have 6 characters long') + } + + static username (username) { + if (typeof username !== 'string') throw new Error('Username must be a string') + if (username.length < 3) throw new Error('Username at least must be have 3 characters long') + } +} diff --git a/clase-7/views/index.ejs b/clase-7/views/index.ejs new file mode 100644 index 0000000..f649bcd --- /dev/null +++ b/clase-7/views/index.ejs @@ -0,0 +1,220 @@ + + + + + + Login & registration Forms` + + + + + + + +
+ <% if (typeof username !== 'undefined') { %> +
+

Hola <%= username %>!

+

Estas en el panel de administracion

+ +
+ <% } %> + + <% if (typeof username === 'undefined') { %> +
+
+

Login

+ + + + + + + +   +
+
+ +
+
+

Register

+ + + + + + + + +   + +
+
+ <% } %> +
+ + + \ No newline at end of file diff --git a/clase-7/views/protected.ejs b/clase-7/views/protected.ejs new file mode 100644 index 0000000..5b10123 --- /dev/null +++ b/clase-7/views/protected.ejs @@ -0,0 +1,12 @@ + + + + + + Document + + +

Hello <%= username %>

+

Este contenido esta protegido

+ + \ No newline at end of file