Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
NEXT_PUBLIC_API_TOKEN=""
JWT_SECRET=""
NODE_ENV=""
NEXT_PUBLIC_API_URL=""
NEXT_PUBLIC_API_URL=""
NEXT_PUBLIC_JWT_SECRET=""
2 changes: 1 addition & 1 deletion backend/src/entities/user.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ export class User {

@Field()
isPasswordChange: boolean;
}
}
23 changes: 16 additions & 7 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,26 @@ import { loadedLogos, loadLogos } from './lib/logoLoader';
const prisma = new PrismaClient();

export interface JwtPayload {
userId: number;
id: number;
}

export interface MyContext {
req: express.Request;
res: express.Response;
apiKey: string | undefined;
cookies: Cookies;
token : string | undefined | null;
user: User | null;
}

// export interface JwtPayload {
// userId: number;
// email?: string;
// role?: string;
// iat?: number;
// exp?: number;
// }

const app = express();
const httpServer = http.createServer(app);

Expand Down Expand Up @@ -170,13 +179,13 @@ async function main() {
expressMiddleware(server, {
context: async ({ req, res }) => {
const cookies = new Cookies(req, res);
// console.log("cookies:", cookies.get("jwt"));

let user: User | null = null;

const token = cookies.get("jwt");
// console.log("Token du cookie:", token ? "Présent" : "Absent");
const token = cookies.get("token");

if (token && process.env.JWT_SECRET) {
// console.log("token ---->", token)
try {
const { payload } = await jwtVerify<JwtPayload>(
token,
Expand All @@ -186,7 +195,7 @@ async function main() {
// console.log("Payload du token décodé:", payload);

const prismaUser = await prisma.user.findUnique({
where: { id: payload.userId }
where: { id: payload.id }
});

if (prismaUser) {
Expand All @@ -202,7 +211,7 @@ async function main() {

} catch (err) {
console.error("Erreur de vérification JWT:", err); // Log l'erreur complète
cookies.set("jwt", "", { expires: new Date(0), httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax' as const });
cookies.set("token", "", { expires: new Date(0), httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax' as const });
}
}

Expand All @@ -219,7 +228,7 @@ async function main() {
await checkApiKey(apiKey);
}

return { req, res, apiKey, cookies, user };
return { req, res, apiKey, cookies, token, user };
},
})
);
Expand Down
3 changes: 2 additions & 1 deletion backend/src/lib/generateSecurePassword.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ export function generateSecurePassword(): string {
const uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const lowercase = "abcdefghijklmnopqrstuvwxyz";
const numbers = "0123456789";
const symbols = "!@#$%^&*()_+[]{}|;:,.<>?";
// const symbols = "!@#$%^&*()_+[]{}|;:,.<>?";
const symbols = "!@#$%^*-_=+";

const all = uppercase + lowercase + numbers + symbols;

Expand Down
42 changes: 36 additions & 6 deletions backend/src/resolvers/user.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import argon2 from "argon2";
import { structureMessageCreatedAccountHTML, structureMessageCreatedAccountTEXT } from "../mail/structureMail.service";
import { Response } from "../entities/response.types";
import { emailRegex, passwordRegex, checkRegex } from "../regex";
import jwt from "jsonwebtoken";
import { jwtVerify, SignJWT } from "jose";
import { TextEncoder } from "util";
import { MyContext } from "..";

// const prisma = new PrismaClient();
const secret = new TextEncoder().encode(process.env.JWT_SECRET);

@Resolver(() => User)
export class UserResolver {
Expand Down Expand Up @@ -164,15 +166,21 @@ export class UserResolver {
}

const tokenPayload = {
userId: user.id,
id: user.id,
email: user.email,
role: user.role,
};

if (!process.env.JWT_SECRET) {
return { code: 500, message: "Please check your JWT configuration !" };
}

const token = jwt.sign(tokenPayload, process.env.JWT_SECRET , { expiresIn: "7d" });

const token = await new SignJWT(tokenPayload)
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt()
.setExpirationTime("7d")
.sign(secret);

const cookieOptions = {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
Expand All @@ -181,7 +189,7 @@ export class UserResolver {
path: '/',
};

ctx.cookies.set("jwt", token, cookieOptions);
ctx.cookies.set("token", token, cookieOptions);

return {
code: 200,
Expand All @@ -197,6 +205,28 @@ export class UserResolver {
}
}

@Query(() => User, { nullable: true })
async me(@Ctx() { req, token }: MyContext): Promise<User | null> {
// console.log("refresh FRONTcdodddof")
// console.log("req.cookies", req.cookies)
// console.log(cookies)
// const token = req.cookies.token;
console.log("token", token)
if (!token) return null;

try {
const payload = await jwtVerify(token, new TextEncoder().encode(process.env.JWT_SECRET));
const user = await this.db.user.findUnique({ where: { id: payload.payload.id as number } });
if (!user) return null;
return {
...user,
role: user.role as UserRole,
};
} catch {
return null;
}
}

@Mutation(() => Response)
async logout(
@Ctx() ctx: MyContext
Expand All @@ -208,7 +238,7 @@ export class UserResolver {
return { code: 401, message: "Authentication required." };
}

ctx.cookies.set("jwt", "", {
ctx.cookies.set("token", "", {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax' as const,
Expand Down
1 change: 1 addition & 0 deletions backend/src/types/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ export type Query = {
generateCaptcha: CaptchaResponse;
getGlobalStats: GlobalStatsResponse;
listBackupFiles: BackupFilesResponse;
me?: Maybe<User>;
projectById: ProjectResponse;
projectList: ProjectsResponse;
skillList: CategoryResponse;
Expand Down
4 changes: 4 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ services:
args:
NEXT_PUBLIC_API_TOKEN: ${NEXT_PUBLIC_API_TOKEN}
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
NEXT_PUBLIC_JWT_SECRET: ${NEXT_PUBLIC_JWT_SECRET}
JWT_SECRET: ${JWT_SECRET}
ports:
- 3000:3000
volumes:
Expand All @@ -32,6 +34,8 @@ services:
- NEXT_PUBLIC_IMAGE_URL=http://localhost:8000
- NEXT_PUBLIC_API_TOKEN=${NEXT_PUBLIC_API_TOKEN}
- NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
- NEXT_PUBLIC_JWT_SECRET=${NEXT_PUBLIC_JWT_SECRET}
- JWT_SECRET=${JWT_SECRET}
- API_URL=${API_URL}
- NODE_ENV=development
env_file:
Expand Down
4 changes: 3 additions & 1 deletion frontend/.env.sample
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
isProduction="true and false"
NEXT_DISABLE_HMR="true and false"
NEXT_PUBLIC_API_TOKEN=""
NEXT_PUBLIC_API_URL=""
NEXT_PUBLIC_API_URL=""
NEXT_PUBLIC_JWT_SECRET=""
JWT_SECRET=""
12 changes: 9 additions & 3 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,16 @@ ENV NEXT_PUBLIC_API_TOKEN=${NEXT_PUBLIC_API_TOKEN}
ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}

# CMD npm run dev
ARG NEXT_PUBLIC_JWT_SECRET
ENV NEXT_PUBLIC_JWT_SECRET=${NEXT_PUBLIC_JWT_SECRET}

RUN npm run build
CMD npm run start
ARG JWT_SECRET
ENV JWT_SECRET=${JWT_SECRET}

CMD npm run dev

# RUN npm run build
# CMD npm run start

# RUN if [ "$NODE_ENV" = "production" ]; then npm run build; fi
# CMD if [ "$NODE_ENV" = "production" ]; then npm run start; else npm run dev; fi
Loading