create-lyrajs 1.0.15 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/package.json +2 -2
- package/template/src/controller/AuthController.ts +83 -5
- package/template/src/controller/UserController.ts +23 -2
- package/template/src/router/routes/authRoutes.ts +3 -0
- package/template/src/router/routes/userRoutes.ts +6 -5
- package/template/src/server.ts +1 -2
package/package.json
CHANGED
package/template/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"maestro": "maestro"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@lyra-js/core": "^1.0.
|
|
10
|
+
"@lyra-js/core": "^1.0.15",
|
|
11
11
|
"bcrypt": "^6.0.0",
|
|
12
12
|
"cookie-parser": "^1.4.7",
|
|
13
13
|
"cors": "^2.8.5",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"express": "^5.1.0",
|
|
16
16
|
"express-rate-limit": "^8.2.1",
|
|
17
17
|
"jsonwebtoken": "^9.0.2",
|
|
18
|
+
"md5": "^2.3.0",
|
|
18
19
|
"mysql2": "^3.14.1",
|
|
19
20
|
"nodemailer": "^7.0.7",
|
|
20
21
|
"reflect-metadata": "^0.2.2",
|
|
@@ -24,7 +25,6 @@
|
|
|
24
25
|
"@types/bcrypt": "^5.0.2",
|
|
25
26
|
"@types/cookie-parser": "^1.4.8",
|
|
26
27
|
"@types/cors": "^2.8.18",
|
|
27
|
-
"@types/dotenv": "^8.2.3",
|
|
28
28
|
"@types/express": "^5.0.1",
|
|
29
29
|
"@types/jsonwebtoken": "^9.0.9",
|
|
30
30
|
"@types/node": "^22.15.17",
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { AccessControl, ProtectedRouteType, SecurityConfig } from "@lyra-js/core"
|
|
2
|
+
import { AuthenticatedRequest, UnauthorizedException, Validator } from "@lyra-js/core"
|
|
1
3
|
import bcrypt from "bcrypt"
|
|
2
4
|
import { NextFunction, Request, Response } from "express"
|
|
3
5
|
import jwt from "jsonwebtoken"
|
|
6
|
+
import md5 from "md5"
|
|
4
7
|
|
|
5
|
-
import { SecurityConfig } from "@lyra-js/core"
|
|
6
|
-
import { AuthenticatedRequest, UnauthorizedException, Validator } from "@lyra-js/core"
|
|
7
8
|
import { User } from "@entity/User"
|
|
8
9
|
import { userRepository } from "@repository/UserRepository"
|
|
9
10
|
|
|
@@ -44,7 +45,7 @@ export class AuthController {
|
|
|
44
45
|
user.lastname = lastname
|
|
45
46
|
user.email = email
|
|
46
47
|
user.password = hashedPassword
|
|
47
|
-
user.role =
|
|
48
|
+
user.role = "ROLE_USER"
|
|
48
49
|
|
|
49
50
|
await userRepository.save(user)
|
|
50
51
|
|
|
@@ -77,11 +78,13 @@ export class AuthController {
|
|
|
77
78
|
expiresIn: securityConfig.jwt.token_expiration
|
|
78
79
|
})
|
|
79
80
|
|
|
80
|
-
|
|
81
|
+
const refreshToken = jwt.sign({ id: user.id }, securityConfig.jwt.secret_key_refresh as string, {
|
|
81
82
|
algorithm: securityConfig.jwt.algorithm as string,
|
|
82
83
|
expiresIn: securityConfig.jwt.refresh_token_expiration
|
|
83
84
|
})
|
|
84
85
|
|
|
86
|
+
user.refresh_token = md5(refreshToken)
|
|
87
|
+
|
|
85
88
|
await userRepository.save(user)
|
|
86
89
|
|
|
87
90
|
res.cookie("Token", token, {
|
|
@@ -92,9 +95,18 @@ export class AuthController {
|
|
|
92
95
|
partitioned: false
|
|
93
96
|
})
|
|
94
97
|
|
|
98
|
+
res.cookie("RefreshToken", refreshToken, {
|
|
99
|
+
sameSite: "Lax",
|
|
100
|
+
httpOnly: true,
|
|
101
|
+
secure: process.env.ENV === "production",
|
|
102
|
+
maxAge: 1000 * 60 * 60 * 24,
|
|
103
|
+
partitioned: false
|
|
104
|
+
})
|
|
105
|
+
|
|
95
106
|
delete user.password
|
|
107
|
+
delete user.refresh_token
|
|
96
108
|
|
|
97
|
-
res.status(200).json({ message: "User authenticated in successfully", user, token })
|
|
109
|
+
res.status(200).json({ message: "User authenticated in successfully", user, token, refreshToken })
|
|
98
110
|
} catch (error) {
|
|
99
111
|
next(error)
|
|
100
112
|
}
|
|
@@ -119,6 +131,72 @@ export class AuthController {
|
|
|
119
131
|
|
|
120
132
|
static signOut = async (_req: Request, res: Response) => {
|
|
121
133
|
res.clearCookie("Token")
|
|
134
|
+
res.clearCookie("RefreshToken")
|
|
122
135
|
res.status(200).json({ message: "Unauthenticated successfully" })
|
|
123
136
|
}
|
|
137
|
+
|
|
138
|
+
static updateProfile = async (req: AuthenticatedRequest<Request>, res: Response, next: NextFunction) => {
|
|
139
|
+
try {
|
|
140
|
+
const { data }: { data: User } = req.body
|
|
141
|
+
const user = req.user as User
|
|
142
|
+
if (!user) throw new UnauthorizedException()
|
|
143
|
+
if (data?.id && data.id !== user.id) throw new UnauthorizedException()
|
|
144
|
+
if (data.role) delete data.role
|
|
145
|
+
if (data.created_at) delete data.created_at
|
|
146
|
+
if (data.refresh_token) delete data.refresh_token
|
|
147
|
+
data.updated_at = new Date()
|
|
148
|
+
if (user && data.password) data.password = await bcrypt.hash(data.password, 10)
|
|
149
|
+
if (user) await userRepository.save(data)
|
|
150
|
+
res.status(200).json({ message: "Users updated successfully" })
|
|
151
|
+
} catch (error) {
|
|
152
|
+
next(error)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
static refreshToken = async (req: Request, res: Response, next: NextFunction) => {
|
|
157
|
+
try {
|
|
158
|
+
const securityConfig = new SecurityConfig().getConfig()
|
|
159
|
+
const refreshToken = req.cookies.RefreshToken
|
|
160
|
+
const decoded = await AccessControl.decodeToken(refreshToken)
|
|
161
|
+
|
|
162
|
+
if (!decoded || !decoded.id) throw new UnauthorizedException("Invalid refresh token")
|
|
163
|
+
|
|
164
|
+
const user = await userRepository.find(decoded.id)
|
|
165
|
+
|
|
166
|
+
if (!user || md5(refreshToken) !== user.refresh_token) throw new UnauthorizedException("Invalid refresh token")
|
|
167
|
+
|
|
168
|
+
AccessControl.checkRefreshTokenValid(refreshToken)
|
|
169
|
+
|
|
170
|
+
const newToken = await AccessControl.getNewToken(user)
|
|
171
|
+
|
|
172
|
+
await userRepository.save({ ...user })
|
|
173
|
+
|
|
174
|
+
res.cookie("Token", newToken, {
|
|
175
|
+
sameSite: "lax",
|
|
176
|
+
httpOnly: true,
|
|
177
|
+
secure: process.env.ENV === "production",
|
|
178
|
+
maxAge: securityConfig.jwt.token_expiration
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
delete user.password
|
|
182
|
+
delete user.refresh_token
|
|
183
|
+
|
|
184
|
+
res.status(200).json({ message: "User authenticated in successfully", user, refreshToken })
|
|
185
|
+
} catch (_refreshError) {
|
|
186
|
+
return res.redirect(securityConfig.auth_routes.sign_out)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
static removeUser = async (req: AuthenticatedRequest<Request>, res: Response, next: NextFunction) => {
|
|
191
|
+
const user = req.user
|
|
192
|
+
|
|
193
|
+
if (!user) throw new UnauthorizedException()
|
|
194
|
+
|
|
195
|
+
await userRepository.delete(user.id)
|
|
196
|
+
|
|
197
|
+
res.clearCookie("Token")
|
|
198
|
+
res.clearCookie("RefreshToken")
|
|
199
|
+
|
|
200
|
+
res.status(200).json({ message: "User deleted successfully" })
|
|
201
|
+
}
|
|
124
202
|
}
|
|
@@ -2,11 +2,12 @@ import { NextFunction, Request, Response } from "express"
|
|
|
2
2
|
import { Validator, ValidationException } from "@lyra-js/core"
|
|
3
3
|
import { User } from "@entity/User"
|
|
4
4
|
import { userRepository } from "@repository/UserRepository"
|
|
5
|
+
import bcrypt from "bcrypt";
|
|
5
6
|
|
|
6
7
|
export class UserController {
|
|
7
8
|
static list = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
|
8
9
|
try {
|
|
9
|
-
const users = await userRepository.findAll().map( (user: User) => {
|
|
10
|
+
const users = (await userRepository.findAll()).map( (user: User) => {
|
|
10
11
|
delete user.password
|
|
11
12
|
return user
|
|
12
13
|
} )
|
|
@@ -55,7 +56,25 @@ export class UserController {
|
|
|
55
56
|
new ValidationException("Password is to weak. I must be 10 characters long, including at least 1 lowercase, 1 uppercase, 1 number and 1 special character.")
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
data.
|
|
59
|
+
const isEmailUsed = await userRepository.findOneBy({ data.email })
|
|
60
|
+
|
|
61
|
+
if (isEmailUsed) {
|
|
62
|
+
throw new Error("Email already in use")
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!Validator.isPasswordValid(data.password)) {
|
|
66
|
+
throw new Error("Invalid password")
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const user = new User()
|
|
70
|
+
const hashedPassword = await bcrypt.hash(data.password, 10)
|
|
71
|
+
|
|
72
|
+
user.username = data.username
|
|
73
|
+
user.firstname = data.firstname
|
|
74
|
+
user.lastname = data.lastname
|
|
75
|
+
user.email = data.email
|
|
76
|
+
user.password = hashedPassword
|
|
77
|
+
user.role = 'ROLE_USER'
|
|
59
78
|
|
|
60
79
|
await userRepository.save(data)
|
|
61
80
|
res.status(201).json({ message: "User created successfully" })
|
|
@@ -69,6 +88,8 @@ export class UserController {
|
|
|
69
88
|
const { data }: {data: User} = req.body
|
|
70
89
|
const user = await userRepository.find(data.id)
|
|
71
90
|
if (!user) res.status(404).json({ message: "User not found" })
|
|
91
|
+
if (user && data.password) data.password = await bcrypt.hash(data.password, 10);
|
|
92
|
+
user.updated_at = new Date()
|
|
72
93
|
if (user) await userRepository.save(data)
|
|
73
94
|
res.status(200).json({ message: "Users updated successfully" })
|
|
74
95
|
} catch (error) {
|
|
@@ -8,3 +8,6 @@ authRoutes.post("/sign-up", AuthController.signUp)
|
|
|
8
8
|
authRoutes.post("/sign-in", rateLimiter, AuthController.signIn)
|
|
9
9
|
authRoutes.get("/user", AuthController.getAuthenticatedUser)
|
|
10
10
|
authRoutes.get("/sign-out", AuthController.signOut)
|
|
11
|
+
authRoutes.get("/refresh-token", AuthController.refreshToken)
|
|
12
|
+
authRoutes.post("/update-account", AuthController.updateProfile)
|
|
13
|
+
authRoutes.delete("/delete-account", AuthController.removeUser)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { UserController } from "@controller/UserController"
|
|
2
2
|
import { Router } from "express"
|
|
3
|
+
import { isAdmin } from "@lyrajs/core"
|
|
3
4
|
|
|
4
5
|
export const userRoutes = Router()
|
|
5
6
|
|
|
6
|
-
userRoutes.get("/all", UserController.list)
|
|
7
|
-
userRoutes.get("/:id", UserController.read)
|
|
8
|
-
userRoutes.
|
|
9
|
-
userRoutes.patch("/:id", UserController.update)
|
|
10
|
-
userRoutes.delete("/:id", UserController.delete)
|
|
7
|
+
userRoutes.get("/all", isAdmin, UserController.list)
|
|
8
|
+
userRoutes.get("/:id", isAdmin, UserController.read)
|
|
9
|
+
userRoutes.post("/", isAdmin, UserController.create)
|
|
10
|
+
userRoutes.patch("/:id", isAdmin, UserController.update)
|
|
11
|
+
userRoutes.delete("/:id", isAdmin, UserController.delete)
|
package/template/src/server.ts
CHANGED
|
@@ -15,14 +15,13 @@ const securityConfig = new SecurityConfig().getConfig()
|
|
|
15
15
|
const port = process.env.PORT
|
|
16
16
|
const app = express()
|
|
17
17
|
|
|
18
|
-
app.set("trust proxy",
|
|
18
|
+
app.set("trust proxy", false)
|
|
19
19
|
app.use(cookieParser())
|
|
20
20
|
app.use(express.json({ limit: securityConfig.limits.request_max_size || "10mb" }))
|
|
21
21
|
app.use(express.urlencoded({ limit: securityConfig.limits.request_max_size || "10mb", extended: true }))
|
|
22
22
|
app.use(
|
|
23
23
|
cors({
|
|
24
24
|
origin: `${process.env.CLIENT_APP_URL}`,
|
|
25
|
-
methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"],
|
|
26
25
|
credentials: true
|
|
27
26
|
})
|
|
28
27
|
)
|