Skip to content

Commit 855f670

Browse files
final commit finishing the project
1 parent 4796f4c commit 855f670

File tree

7 files changed

+253
-57
lines changed

7 files changed

+253
-57
lines changed

README.md

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# Golang-JWT
2+
### A bullet-proof production ready authentication system.
3+
4+
## POST
5+
### SignUp
6+
- ```bash
7+
http://localhost:9000/users/signup
8+
```
9+
10+
### Body
11+
- `raw (json)`
12+
```javascript
13+
{
14+
"First_name": "Aayush",
15+
"Last_name":"Kumar",
16+
"Password":"aayush@01",
17+
"Email": "aayush@gmail.com",
18+
"Phone":"9999966666",
19+
"User_type": "ADMIN"
20+
}
21+
```
22+
## POST
23+
### Login
24+
- ```bash
25+
http://localhost:9000/users/login
26+
```
27+
- `Login User`
28+
> This endpoint allows users to log in by providing their email and password.
29+
30+
### Request Body
31+
- Type: `JSON`
32+
- `Email` (string): The email of the user.
33+
- `Password` (string): The password of the user.
34+
### Response
35+
> The response is in the form of a JSON schema representing the user's details upon successful login.
36+
37+
```javascript
38+
{
39+
"type": "object",
40+
"properties": {
41+
"ID": { "type": "string" },
42+
"first_name": { "type": "string" },
43+
"last_name": { "type": "string" },
44+
"password": { "type": "string" },
45+
"email": { "type": "string" },
46+
"phone": { "type": "string" },
47+
"token": { "type": "string" },
48+
"user_type": { "type": "string" },
49+
"refresh_token": { "type": "string" },
50+
"created_at": { "type": "string" },
51+
"update_at": { "type": "string" },
52+
"user_id": { "type": "string" }
53+
}
54+
}
55+
```
56+
57+
### Authorization
58+
## JWT Bearer
59+
- Body `raw (json)`
60+
```json
61+
{
62+
"Email": "aditya@gmail.com",
63+
"Password":"aditya@01"
64+
}
65+
```
66+
## GET
67+
### GetUsers(Only ADMIN)
68+
```bash
69+
http://localhost:9000/users?recordPerPage=10&page=2
70+
```
71+
> This endpoint makes an HTTP GET request to retrieve a list of users with pagination support. The request includes query parameters for the number of records per page and the page number.
72+
73+
### Response
74+
> The response is a JSON object with a status code of 200 and a content type of application/json. Below is the JSON schema for the response:
75+
76+
```javascript
77+
{
78+
"type": "object",
79+
"properties": {
80+
"total_count": {
81+
"type": "integer"
82+
},
83+
"user_items": {
84+
"type": "array",
85+
"items": {
86+
"type": "object",
87+
"properties": {
88+
"_id": {
89+
"type": "string"
90+
},
91+
"created_at": {
92+
"type": "string"
93+
},
94+
"email": {
95+
"type": "string"
96+
},
97+
"first_name": {
98+
"type": "string"
99+
},
100+
"last_name": {
101+
"type": "string"
102+
},
103+
"password": {
104+
"type": "string"
105+
},
106+
"phone": {
107+
"type": "string"
108+
},
109+
"refreshToken": {
110+
"type": "string"
111+
},
112+
"refresh_token": {
113+
"type": "string"
114+
},
115+
"token": {
116+
"type": "string"
117+
},
118+
"update_at": {
119+
"type": "string"
120+
},
121+
"updated_at": {
122+
"type": "string"
123+
},
124+
"user_id": {
125+
"type": "string"
126+
},
127+
"user_type": {
128+
"type": "string"
129+
}
130+
}
131+
}
132+
}
133+
}
134+
}
135+
```
136+
137+
### Authorization
138+
- Bearer Token
139+
- Token `<token>`
140+
### Request Headers
141+
- token `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJFbWFpbCI6ImFkaXR5YUBnbWFpbC5jb20iLCJGaXJzdF9uYW1lIjoiQWRpdHlhIiwiTGFzdF9uYW1lIjoiS3VtYXIiLCJVaWQiOiI2N2ZhYTE3N2RjMWU0ZDVmOGYyZTE0OGUiLCJVc2VyX3R5cGUiOiJBRE1JTiIsImV4cCI6MTc0NDU2NzcyMH0.CJikdZldJdVTKNXOH9yyq7-XIhcSTlZgVPzdjO1rxk4`
142+
### Query Params
143+
- `recordPerPage 10`
144+
- `page 2`
145+
## GET
146+
### GetUserById
147+
- `http://localhost:9000/users/67faa177dc1e4d5f8f2e148e`
148+
> This endpoint retrieves user information based on the provided user ID. The response returns a JSON object with the user's details, including ID, first name, last name, password, email, phone, token, user type, refresh token, creation timestamp, update timestamp, and user ID.
149+
150+
```javascript
151+
{
152+
"type": "object",
153+
"properties": {
154+
"ID": { "type": "string" },
155+
"first_name": { "type": "string" },
156+
"last_name": { "type": "string" },
157+
"password": { "type": "string" },
158+
"email": { "type": "string" },
159+
"phone": { "type": "string" },
160+
"token": { "type": "string" },
161+
"user_type": { "type": "string" },
162+
"refresh_token": { "type": "string" },
163+
"created_at": { "type": "string" },
164+
"update_at": { "type": "string" },
165+
"user_id": { "type": "string" }
166+
}
167+
}
168+
```
169+
170+
### Request Headers
171+
- token `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJFbWFpbCI6ImFkaXR5YUBnbWFpbC5jb20iLCJGaXJzdF9uYW1lIjoiQWRpdHlhIiwiTGFzdF9uYW1lIjoiS3VtYXIiLCJVaWQiOiI2N2ZhYTE3N2RjMWU0ZDVmOGYyZTE0OGUiLCJVc2VyX3R5cGUiOiJBRE1JTiIsImV4cCI6MTc0NDU2NzcyMH0.CJikdZldJdVTKNXOH9yyq7-XIhcSTlZgVPzdjO1rxk4`

controllers/userController.go

Lines changed: 18 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package controllers
22

33
import (
4-
"bytes"
54
"context"
65
"fmt"
76
"log"
@@ -10,7 +9,6 @@ import (
109
"time"
1110

1211
"github.com/adix/gojwt/database"
13-
"github.com/adix/gojwt/helpers"
1412
helpers "github.com/adix/gojwt/helpers"
1513
"github.com/adix/gojwt/models"
1614
"github.com/gin-gonic/gin"
@@ -80,8 +78,9 @@ func Signup() gin.HandlerFunc {
8078
user.Created_at, _ = time.Parse(time.RFC3339, time.Now().Format(time.RFC3339))
8179
user.Update_at, _ = time.Parse(time.RFC3339, time.Now().Format(time.RFC3339))
8280
user.ID = primitive.NewObjectID()
83-
user.User_id = user.ID.Hex()
84-
token, refreshToken, err := helpers.GenerateAllTokens(*user.Email, *user.First_name, *user.Last_name, *user.User_type, *&user.User_id)
81+
userID := user.ID.Hex()
82+
user.User_id = &userID
83+
token, refreshToken, err := helpers.GenerateAllTokens(*user.Email, *user.First_name, *user.Last_name, *user.User_type, *user.User_id)
8584
if err != nil {
8685
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
8786
return
@@ -174,65 +173,38 @@ func GetUsers() gin.HandlerFunc {
174173
}
175174
var ctx, cancel = context.WithTimeout(context.Background(), 100*time.Second)
176175

177-
//pagination
178176
recordPerPage, err := strconv.Atoi(c.Query("recordPerPage"))
179177
if err != nil || recordPerPage < 1 {
180178
recordPerPage = 10
181179
}
182-
183180
page, err1 := strconv.Atoi(c.Query("page"))
184181
if err1 != nil || page < 1 {
185182
page = 1
186183
}
187184

188-
//just like skip and limit in nodejs
189185
startIndex := (page - 1) * recordPerPage
190-
191186
startIndex, err = strconv.Atoi(c.Query("startIndex"))
192187

193188
matchStage := bson.D{{"$match", bson.D{{}}}}
194-
groupStage := bson.D{
195-
{"$group", bson.D{
196-
{"_id", bson.D{
197-
{"_id", "null"},
198-
{"total_count", bson.D{
199-
{"$sum", 1}},
200-
},
201-
{"data", bson.D{
202-
{"$push", "$$ROOT"}},
203-
},
204-
},
205-
},
206-
},
207-
},
208-
}
189+
groupStage := bson.D{{"$group", bson.D{
190+
{"_id", bson.D{{"_id", "null"}}},
191+
{"total_count", bson.D{{"$sum", 1}}},
192+
{"data", bson.D{{"$push", "$$ROOT"}}}}}}
209193
projectStage := bson.D{
210-
{"$project", {
211-
bson.D{
212-
{"_id", 0},
213-
{"total_count", 1},
214-
{"user_items", bson.D{
215-
{"$slice", []interface{
216-
"$data", startIndex, recordPerPage
217-
}},
218-
}}
219-
}
220-
}}
221-
}
222-
result ,err := userCollection.Aggregate(ctx, mongo.Pipeline{
223-
matchStage, groupStage, projectStage
224-
})
194+
{"$project", bson.D{
195+
{"_id", 0},
196+
{"total_count", 1},
197+
{"user_items", bson.D{{"$slice", []interface{}{"$data", startIndex, recordPerPage}}}}}}}
198+
result, err := userCollection.Aggregate(ctx, mongo.Pipeline{
199+
matchStage, groupStage, projectStage})
225200
defer cancel()
226-
227-
if err != nil{
228-
c.JSON(http.StatusInternalServerError, gin.H{"error": "error occured while listing users"})
229-
201+
if err != nil {
202+
c.JSON(http.StatusInternalServerError, gin.H{"error": "error occured while listing user items"})
230203
}
231-
var allUsers []bson.M
232-
233-
if err = result.All(ctx, &allUsers); err != nil{
204+
var allusers []bson.M
205+
if err = result.All(ctx, &allusers); err != nil {
234206
log.Fatal(err)
235207
}
236-
c.JSON(http.StatusOK, allUsers[0])
208+
c.JSON(http.StatusOK, allusers[0])
237209
}
238210
}

helpers/tokenHelper.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ package helpers
22

33
import (
44
"context"
5+
"fmt"
56
"log"
67
"os"
78
"time"
89

910
"github.com/adix/gojwt/database"
10-
"github.com/dgrijalva/jwt-go"
1111
jwt "github.com/dgrijalva/jwt-go"
1212
"go.mongodb.org/mongo-driver/bson"
1313
"go.mongodb.org/mongo-driver/bson/primitive"
@@ -91,3 +91,29 @@ func UpdateAllTokens(signedToken string, signedRefreshToken string, user_id stri
9191
}
9292
return
9393
}
94+
func ValidateToken(signedToken string) (claims *SignedDetails, msg string) {
95+
token, err := jwt.ParseWithClaims(
96+
signedToken,
97+
&SignedDetails{},
98+
func(t *jwt.Token) (interface{}, error) {
99+
return []byte(SECRET_KEY), nil
100+
},
101+
)
102+
if err != nil {
103+
msg = err.Error()
104+
return
105+
}
106+
claims, ok := token.Claims.(*SignedDetails)
107+
if !ok {
108+
msg = fmt.Sprintf("The token is invalid")
109+
msg = err.Error()
110+
return
111+
}
112+
113+
if claims.ExpiresAt < time.Now().Local().Unix() {
114+
msg = fmt.Sprintf("Token is expired")
115+
msg = err.Error()
116+
return
117+
}
118+
return claims, msg
119+
}

middlewares/authMiddleware.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,34 @@
11
package middlewares
22

3-
import ()
3+
import (
4+
"fmt"
5+
helpers "github.com/adix/gojwt/helpers"
6+
"github.com/gin-gonic/gin"
7+
"net/http"
8+
)
49

5-
func Authenticate() {
10+
func Authenticate() gin.HandlerFunc {
11+
return func(c *gin.Context) {
12+
clientToken := c.Request.Header.Get("token")
613

14+
if clientToken == "" {
15+
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("No authorization header provided")})
16+
c.Abort()
17+
return
18+
}
19+
20+
claims, err := helpers.ValidateToken(clientToken)
21+
if err != "" {
22+
c.JSON(http.StatusInternalServerError, gin.H{"error": err})
23+
c.Abort()
24+
return
25+
}
26+
27+
c.Set("email", claims.Email)
28+
c.Set("first_name", claims.First_name)
29+
c.Set("last_name", claims.Last_name)
30+
c.Set("uid", claims.Uid)
31+
c.Set("user_type", claims.User_type)
32+
c.Next()
33+
}
734
}

models/userModel.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ import (
88

99
type User struct {
1010
ID primitive.ObjectID `bson:"_id"`
11-
First_name *string `json:"first_name" validate:"required, min=2, max=100"`
12-
Last_name *string `json:"last_name" validate:"required, min=2, max=100"`
13-
Password *string `json:"password" validate:"required, min=6"`
14-
Email *string `json:"email" validate:"email, required"`
11+
First_name *string `json:"first_name" validate:"required,min=2,max=100"`
12+
Last_name *string `json:"last_name" validate:"required,min=2,max=100"`
13+
Password *string `json:"password" validate:"required,min=6"`
14+
Email *string `json:"email" validate:"email,required"`
1515
Phone *string `json:"phone" validate:"required"`
1616
Token *string `json:"token"`
17-
User_type *string `json:"user_type" validate:"required, eq=ADMIN|eq=USER"`
17+
User_type *string `json:"user_type" validate:"required,eq=ADMIN|eq=USER"`
1818
Refresh_token *string `json:"refresh_token"`
1919
Created_at time.Time `json:"created_at"`
2020
Update_at time.Time `json:"update_at"`

routes/authRouter.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ import (
66
)
77

88
func AuthRoutes(incomingRoutes *gin.Engine) {
9-
incomingRoutes.POST("/users/singup", controller.Signup())
10-
incomingRoutes.POST("users/login", controller.Login())
9+
incomingRoutes.POST("/users/signup", controller.Signup())
10+
incomingRoutes.POST("/users/login", controller.Login())
1111
}

routes/userRouter.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ import (
99
func UserRoutes(incomingRoutes *gin.Engine) {
1010
incomingRoutes.Use(middlewares.Authenticate())
1111
incomingRoutes.GET("/users", controller.GetUsers())
12-
incomingRoutes.GET("/users:id", controller.GetUser())
12+
incomingRoutes.GET("/users/:id", controller.GetUser())
1313
}

0 commit comments

Comments
 (0)