mirror of
https://github.com/bubblecup-12/VogelSocialMedia.git
synced 2025-07-07 05:18:46 +00:00
added authentication and swagger
This commit is contained in:
parent
c311e3adda
commit
64c0d79438
8 changed files with 447 additions and 56 deletions
|
@ -3,42 +3,107 @@ import { PrismaClient } from "@prisma/client";
|
|||
import { UserLoginDto, userLoginSchema } from "../schemas/userSchemas";
|
||||
import jwt from "jsonwebtoken";
|
||||
import dotenv from "dotenv";
|
||||
import { string } from "zod";
|
||||
import bcrypt from "bcryptjs";
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
const prisma = new PrismaClient();
|
||||
// load environment variables from .env file
|
||||
dotenv.config();
|
||||
const JWT_SECRET: string = process.env.TOKEN_SECRET!;
|
||||
|
||||
function generateAccessToken(username: string) {
|
||||
return jwt.sign({ username }, JWT_SECRET, { expiresIn: "1800s" });
|
||||
const JWT_SECRET: string = process.env.TOKEN_SECRET!; // this secret is used to sign the JWT token
|
||||
// Generate a JWT token with the username as payload and a secret from the environment variables which expires in 1800 seconds (30 minutes)
|
||||
function generateAccessToken(username: string, userId: string) {
|
||||
return jwt.sign(
|
||||
{ username: username, role: "user", sub: userId },
|
||||
JWT_SECRET,
|
||||
{ expiresIn: "1800s", issuer: "VogelApi" }
|
||||
); //TODO: change role to user role
|
||||
}
|
||||
|
||||
// Endpoint to register a new user
|
||||
export const registerUser = async (req: Request, res: Response) => {
|
||||
const userRequest = await req.body;
|
||||
const user = await prisma.user.create({ data: userRequest });
|
||||
console.log(user.username);
|
||||
res.json({ message: "User registered successfully", data: req.body });
|
||||
const { username, password, email } = await req.body; //gets the data from the request body
|
||||
if (!username || !password || !email) {
|
||||
// check if username, password and email are provided and
|
||||
res
|
||||
.status(400)
|
||||
.json({ message: "Username, password and email are required" });
|
||||
}
|
||||
const existingUser = await prisma.user.findUnique({
|
||||
// check if the user already exists
|
||||
where: {
|
||||
username: username,
|
||||
},
|
||||
});
|
||||
if (existingUser) {
|
||||
// if the user already exists, return an error message
|
||||
res.status(400).json({ message: `User "${username}" already exists` });
|
||||
}
|
||||
const hashedPassword = await bcrypt.hash(password, 10); // hash the password with bcrypt
|
||||
if (!hashedPassword) {
|
||||
// check if the password was hashed successfully
|
||||
res.status(500).json({ message: "Server Error" });
|
||||
}
|
||||
const userData = {
|
||||
// create a new user object with the data from the request body and the hashed password
|
||||
username: username,
|
||||
email: email,
|
||||
password: hashedPassword,
|
||||
};
|
||||
const user = await prisma.user.create({ data: userData }); // create a new user in the database
|
||||
if (!user) {
|
||||
// check if the user was created successfully
|
||||
res.status(500).json({ message: "Server Error" });
|
||||
}
|
||||
res.json({ message: "User registered successfully" });
|
||||
res.status(201).json({
|
||||
message: "user created",
|
||||
data: { username: username, email: email },
|
||||
}); // return the user object with the username and email
|
||||
};
|
||||
|
||||
export const loginUser = (req: Request, res: Response) => {
|
||||
const token: string = generateAccessToken(req.body.username);
|
||||
res.json({ message: "User logged in successfully", data: req.body, token });
|
||||
// Endpoint to login a user (unfinished)
|
||||
export const loginUser = async (req: Request, res: Response) => {
|
||||
const { username, password } = req.body; // get the data from the request body
|
||||
if (!username || !password) {
|
||||
// check if username and password are provided
|
||||
res.status(400).json({ message: "Username and password are required" });
|
||||
}
|
||||
const user = await prisma.user.findUnique({
|
||||
// check if the user exists
|
||||
where: {
|
||||
username: username,
|
||||
},
|
||||
});
|
||||
if (!user) {
|
||||
// if the user does not exist, return an error message
|
||||
res.status(400).json({ message: `User "${username}" not found` });
|
||||
return;
|
||||
}
|
||||
const isPasswordValid = await bcrypt.compare(password, user.password); // compare the password with the hashed password in the database
|
||||
if (!isPasswordValid) {
|
||||
// if the password is not valid, return an error message
|
||||
res.status(401).json({ message: "Invalid password" });
|
||||
}
|
||||
const token: string = generateAccessToken(user.username, user.id); // generate a JWT token with the username and userId as payload
|
||||
res.set("Authorization", `Bearer ${token}`); // set the token in the response header
|
||||
res.json({ message: "User logged in successfully" });
|
||||
};
|
||||
|
||||
// Endpoint to get user data
|
||||
export const getUser = async (req: Request, res: Response) => {
|
||||
const username = req.body.username;
|
||||
console.log(username, req.body);
|
||||
const username: string = req.query.username as string;
|
||||
if (!username) {
|
||||
res.status(400).json({ message: "Username is required" });
|
||||
}
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
username: username,
|
||||
},
|
||||
});
|
||||
if (!user) {
|
||||
res.status(404).json({ message: "User not found" });
|
||||
res.status(404).json({ message: `User "${username}" not found` });
|
||||
return;
|
||||
}
|
||||
res.json({ message: "User found", data: user });
|
||||
res.json({
|
||||
message: "User found",
|
||||
data: { username: user.username, email: user.email },
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
import express, { NextFunction, Request, Response } from "express";
|
||||
import jwt from "jsonwebtoken";
|
||||
import jwt, { TokenExpiredError } from "jsonwebtoken";
|
||||
import dotenv from "dotenv";
|
||||
import { string } from "zod";
|
||||
dotenv.config();
|
||||
// imports the JWT secret
|
||||
const JWT_SECRET: string = process.env.TOKEN_SECRET!;
|
||||
|
||||
if (!JWT_SECRET) console.log("no JWT secret");
|
||||
// create an interface for the JWT payload
|
||||
// this interface is used to define the structure of the JWT payload
|
||||
interface JwtPayload {
|
||||
username: string;
|
||||
iat: number;
|
||||
exp: number;
|
||||
}
|
||||
|
||||
// extend the Express Request interface to include the user property
|
||||
// this is used to store the JWT payload in the request object
|
||||
declare global {
|
||||
namespace Express {
|
||||
interface Request {
|
||||
|
@ -17,27 +21,26 @@ declare global {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Middleware function to authenticate the JWT token
|
||||
export function authenticateToken() {
|
||||
return (req: Request, res: Response, next: NextFunction) => {
|
||||
const authHeader = req.headers["authorization"];
|
||||
const token = authHeader && authHeader.split(" ")[1];
|
||||
const authHeader = req.headers["authorization"]; // get the authorization header from the request
|
||||
const token = authHeader && authHeader.split(" ")[1]; // split the header to get the token
|
||||
|
||||
if (token == null) res.sendStatus(401);
|
||||
if (token == null)
|
||||
res.sendStatus(401); // if there is no token, return 401 Unauthorized
|
||||
else {
|
||||
jwt.verify(
|
||||
token,
|
||||
process.env.TOKEN_SECRET as string,
|
||||
(err: any, user: any) => {
|
||||
console.log(err);
|
||||
|
||||
if (err) res.sendStatus(403);
|
||||
|
||||
req.user = user;
|
||||
|
||||
next();
|
||||
},
|
||||
);
|
||||
jwt.verify(token, JWT_SECRET, (err: any, user: any) => {
|
||||
// verify the token with the secret
|
||||
console.log(err);
|
||||
if (err) {
|
||||
if (err instanceof TokenExpiredError)
|
||||
// check if the error is expired and return 401
|
||||
res.status(401).json({ message: "Token expired" });
|
||||
else res.status(403).json({ message: "Invalid token" });
|
||||
}
|
||||
next();
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -14,17 +14,100 @@ import {
|
|||
loginUser,
|
||||
getUser,
|
||||
} from "../controllers/userController";
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* UserRegistrationDto:
|
||||
* type: object
|
||||
* required:
|
||||
* - username
|
||||
* - email
|
||||
* - password
|
||||
* properties:
|
||||
* username:
|
||||
* type: string
|
||||
* email:
|
||||
* type: string
|
||||
* format: email
|
||||
* password:
|
||||
* type: string
|
||||
* format: password
|
||||
* UserLoginDto:
|
||||
* type: object
|
||||
* required:
|
||||
* - username
|
||||
* - password
|
||||
* properties:
|
||||
* username:
|
||||
* type: string
|
||||
* password:
|
||||
* type: string
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/user/register:
|
||||
* post:
|
||||
* summary: Register a new user
|
||||
* tags: [User]
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/UserRegistrationDto'
|
||||
* responses:
|
||||
* 201:
|
||||
* description: Benutzer erfolgreich registriert
|
||||
* 400:
|
||||
* description: Ungültige Eingabedaten
|
||||
*/
|
||||
userRouter.post(
|
||||
"/register",
|
||||
validateData(userRegistrationSchema),
|
||||
registerUser,
|
||||
registerUser
|
||||
);
|
||||
/**
|
||||
* @swagger
|
||||
* /api/user/login:
|
||||
* post:
|
||||
* summary: Log in a user
|
||||
* tags: [User]
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/UserLoginDto'
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Login erfolgreich
|
||||
* 401:
|
||||
* description: Ungültige Anmeldedaten
|
||||
*/
|
||||
userRouter.post("/login", validateData(userLoginSchema), loginUser);
|
||||
userRouter.get(
|
||||
"/getUser",
|
||||
authenticateToken(),
|
||||
validateData(userLoginSchema),
|
||||
getUser,
|
||||
);
|
||||
/**
|
||||
* @swagger
|
||||
* /api/user/getUser/{username}:
|
||||
* get:
|
||||
* summary: Get user data
|
||||
* tags: [User]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: username
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Der Benutzername, nach dem gesucht werden soll
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Login erfolgreich
|
||||
* 401:
|
||||
* description: Ungültige Anmeldedaten
|
||||
*/
|
||||
userRouter.get("/getUser/:username", authenticateToken(), getUser);
|
||||
|
||||
export default userRouter;
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export const userRegistrationSchema = z.object({
|
||||
username: z.string(),
|
||||
username: z.string().regex(/^\S*$/, "Username must not contain spaces"), // No whitespaces allowed,
|
||||
email: z.string().email(),
|
||||
password: z.string().min(8),
|
||||
});
|
||||
|
||||
export const userLoginSchema = z.object({
|
||||
username: z.string(),
|
||||
password: z.string().min(8),
|
||||
username: z.string().regex(/^\S*$/, "Username must not contain spaces"), // No whitespaces allowed,
|
||||
password: z.string(),
|
||||
});
|
||||
// DTO-Typen aus den Schemas ableiten
|
||||
export type UserRegistrationDto = z.infer<typeof userRegistrationSchema>;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import express, { Request, Response } from "express";
|
||||
import express, { Request, Response, Application } from "express";
|
||||
|
||||
import dotenv from "dotenv";
|
||||
import userRouter from "./routes/userRoutes";
|
||||
|
@ -9,9 +9,50 @@ dotenv.config();
|
|||
const app = express();
|
||||
const port = 3000;
|
||||
|
||||
//swagger configuration
|
||||
import swaggerJSDoc from "swagger-jsdoc";
|
||||
import swaggerUi from "swagger-ui-express";
|
||||
|
||||
const options = {
|
||||
definition: {
|
||||
openapi: "3.1.0",
|
||||
info: {
|
||||
title: "VogelApi",
|
||||
version: "0.0.1",
|
||||
description:
|
||||
"This is a simple CRUD API application made with Express and documented with Swagger",
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
url: "http://localhost:3000",
|
||||
},
|
||||
],
|
||||
components: {
|
||||
securitySchemes: {
|
||||
bearerAuth: {
|
||||
type: "http",
|
||||
scheme: "bearer",
|
||||
bearerFormat: "JWT", // Optional, for documentation purposes
|
||||
},
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{
|
||||
bearerAuth: [], // Apply globally if needed
|
||||
},
|
||||
],
|
||||
},
|
||||
apis: ["src/routes/*.ts"], // Hier werden alle Routen-Dateien mit Swagger-Kommentaren geladen
|
||||
};
|
||||
const specs = swaggerJSDoc(options);
|
||||
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(specs));
|
||||
|
||||
app.use(bodyParser.json());
|
||||
app.use("/api/user", userRouter);
|
||||
|
||||
// Sample route
|
||||
app.get("/api/hello", (req, res) => {
|
||||
res.send("Hello World!");
|
||||
});
|
||||
app.listen(port, () => {
|
||||
console.log(`Server läuft auf http://localhost:${port}`);
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue