Skip to content

Commit

Permalink
setup of access token
Browse files Browse the repository at this point in the history
Signed-off-by: PratikforCoding <kotalpratik@gmail.com>
  • Loading branch information
PratikforCoding committed Oct 3, 2023
1 parent 4d7656a commit db3be15
Show file tree
Hide file tree
Showing 10 changed files with 210 additions and 61 deletions.
4 changes: 2 additions & 2 deletions auth/auth.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package auth

import (

"golang.org/x/crypto/bcrypt"
)

Expand All @@ -15,4 +14,5 @@ func HashedPassword(password string) (string, error) {

func CheckPasswordHash(password, hash string) error {
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
}
}

80 changes: 80 additions & 0 deletions auth/token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package auth

import (
"errors"
"net/http"
"time"

"github.com/golang-jwt/jwt/v5"
)

var ErrorNoAuthHeaderIncluded = errors.New("not auth header included in request")

func MakeAccessToken(id string, jwtSecret string, expiresIn time.Duration) (string, error) {
signingKey := []byte(jwtSecret)

token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
IssuedAt: jwt.NewNumericDate(time.Now().UTC()),
ExpiresAt: jwt.NewNumericDate(time.Now().UTC().Add(expiresIn)),
Subject: id,
Issuer: "busofact-access", // Match the issuer here
})

return token.SignedString(signingKey)
}

func MakeRefreshToken(id string, jwtSecret string, expiresIn time.Duration) (string, error) {
signingKey := []byte(jwtSecret)

token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
IssuedAt: jwt.NewNumericDate(time.Now().UTC()),
ExpiresAt: jwt.NewNumericDate(time.Now().UTC().Add(expiresIn)),
Subject: id,
Issuer: "busofact-refresh", // Match the issuer here
})

return token.SignedString(signingKey)
}

func GetTokenFromCookie(r *http.Request) (string, error) {
// Get access_token from cookie
cookie, err := r.Cookie("access_token")
if err != nil {
if err == http.ErrNoCookie {
// If the cookie is not set, return an unauthorized status
return "", errors.New("no cookie included in request")
}

return "", errors.New("couldn't get cookie from request")
}

return cookie.Value, nil
}

func ValidateJWT(tokenString, tokenSecret string) (string, error) {
claimsStruct := jwt.RegisteredClaims{}
token, err := jwt.ParseWithClaims(
tokenString,
&claimsStruct,
func(token *jwt.Token) (interface{}, error) {return []byte(tokenSecret), nil},
)
if err != nil {
return "", err
}

issuer, err := token.Claims.GetIssuer()
if err != nil {
return "", err
}

if issuer != "busofact-access" {
return "", errors.New("token is refresh token")
}

userIDString, err := token.Claims.GetSubject()
if err != nil {
return "", err
}
return userIDString, nil
}

42 changes: 24 additions & 18 deletions controllers/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ import (
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"github.com/PratikforCoding/BusoFact.git/models"
)

type APIConfig struct {
jwtSecret string
BusCollection *mongo.Collection
UserCollection *mongo.Collection
}

func NewAPIConfig(busCol, usrCol *mongo.Collection) *APIConfig {
return &APIConfig{BusCollection: busCol, UserCollection: usrCol}
func NewAPIConfig(busCol, usrCol *mongo.Collection, jwtSecret string) *APIConfig {
return &APIConfig{BusCollection: busCol, UserCollection: usrCol, jwtSecret: jwtSecret}
}

func (apiCfg *APIConfig)getBuses(source string, destination string) []primitive.M {
Expand Down Expand Up @@ -131,56 +133,58 @@ func (apiCfg *APIConfig)getBusByName(name string) (bson.M, error) {
return bus, nil
}

func (apiCfg *APIConfig)createUser(username, email, password string) (bson.M, error) {
func (apiCfg *APIConfig)createUser(firstName, lastName, email, password string) (model.User, error) {
foundUser, err := apiCfg.getUser(email)
if err != nil {
hash, err := auth.HashedPassword(password)
if err != nil {
return nil, err
return model.User{}, err
}
user := bson.M{
"username": username,
"email": email,
"password": hash,
user := model.User{
FristName: firstName,
LastName: lastName,
Email: email,
Password: hash,
}

inserted, err := apiCfg.UserCollection.InsertOne(context.Background(), user)
if err != nil {
log.Fatal(err)
}
fmt.Println("Inserted user id:", inserted.InsertedID)
log.Println("Inserted user id:", inserted.InsertedID)
createdUser, err := apiCfg.getUser(email)
if err != nil {
log.Println(err)
return nil, err
return model.User{}, err
}
return createdUser, nil
}
return foundUser, errors.New("user already exists")
}

func (apiCfg *APIConfig)userLogin(email, password string) (bson.M, error) {
func (apiCfg *APIConfig)userLogin(email, password string) (model.User, error) {
var user model.User
user, err := apiCfg.getUser(email)
if err != nil {
return nil, errors.New("user doesn't exist")
return model.User{}, errors.New("user doesn't exist")
}
userHash := user["password"].(string)
userHash := user.Password
err = auth.CheckPasswordHash(password, userHash)
if err != nil {
fmt.Println(err)
return nil, errors.New("wrong password")
log.Println(err)
return model.User{}, errors.New("wrong password")
}
return user, nil
}

func (apiCfg *APIConfig)getUser(email string) (bson.M, error) {
func (apiCfg *APIConfig)getUser(email string) (model.User, error) {
filter := bson.M{"email":email}
var user bson.M
var user model.User
err := apiCfg.UserCollection.FindOne(context.TODO(), filter).Decode(&user)
if err != nil {
if err == mongo.ErrNoDocuments {
fmt.Println("user not found")
return nil , errors.New("user not found")
return model.User{} , errors.New("user not found")
} else {
log.Fatal(err)
}
Expand All @@ -189,3 +193,5 @@ func (apiCfg *APIConfig)getUser(email string) (bson.M, error) {
return user, nil
}



98 changes: 70 additions & 28 deletions controllers/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,23 @@ package controller

import (
"encoding/json"
"fmt"
"net/http"
"strings"
"time"

"github.com/PratikforCoding/BusoFact.git/auth"
reply "github.com/PratikforCoding/BusoFact.git/json"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
model "github.com/PratikforCoding/BusoFact.git/models"
)

func (apiCfg *APIConfig)HandlerGetBuses(w http.ResponseWriter, r *http.Request) {
type parameters struct {
Source string `json:"source"`
Destination string `json:"destination"`
}
decoder := json.NewDecoder(r.Body)
params := parameters{}
err := decoder.Decode(&params)
if err != nil {
reply.RespondWtihError(w, http.StatusInternalServerError, "Couldn't decode parameters")
if r.Method != http.MethodGet {
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
return
}
source := strings.ToLower(params.Source)
destination := strings.ToLower(params.Destination)

source := r.URL.Query().Get("source")
destination := r.URL.Query().Get("destination")

buses := apiCfg.getBuses(source, destination)
reply.RespondWithJson(w, http.StatusFound, buses)
Expand All @@ -34,9 +29,22 @@ func (apiCfg *APIConfig)HandlerAddBuses(w http.ResponseWriter, r *http.Request)
Name string `json:"name"`
StopageName string `json:"stopageName"`
}

token, err := auth.GetTokenFromCookie(r)
if err != nil {
reply.RespondWtihError(w, http.StatusUnauthorized, "Couldn't get token from request")
return
}

claims, err := auth.ValidateJWT(token, apiCfg.jwtSecret)
if err != nil {
reply.RespondWtihError(w, http.StatusUnauthorized, "Couldn't validate token")
return
}
fmt.Println(claims)
decoder := json.NewDecoder(r.Body)
params := parameters{}
err := decoder.Decode(&params)
err = decoder.Decode(&params)
if err != nil {
reply.RespondWtihError(w, http.StatusInternalServerError, "Couldn't decode parameters")
return
Expand Down Expand Up @@ -73,7 +81,8 @@ func (apiCfg *APIConfig)HandlerGetBusByName(w http.ResponseWriter, r *http.Reque

func (apiCfg *APIConfig)HandlerCreateAccount(w http.ResponseWriter, r *http.Request) {
type parameters struct {
Username string `json:"username"`
FristName string `json:"firstName"`
LastName string `json:"lastName"`
Email string `json:"email"`
Password string `json:"password"`
ConfirmPassword string `json:"confpassword"`
Expand All @@ -92,16 +101,17 @@ func (apiCfg *APIConfig)HandlerCreateAccount(w http.ResponseWriter, r *http.Requ
return
}

user, err := apiCfg.createUser(params.Username, params.Email, params.Password)
user, err := apiCfg.createUser(params.FristName, params.LastName, params.Email, params.Password)

if err != nil {
reply.RespondWtihError(w, http.StatusConflict, "User already exists")
reply.RespondWtihError(w, http.StatusInternalServerError, "error creating user")
return
}
idStr := user["_id"].(primitive.ObjectID).Hex()
retUser := bson.M {
"username": user["username"].(string),
"email": user["email"].(string),
"id": idStr,
retUser := model.User{
ID: user.ID,
FristName: user.FristName,
LastName: user.LastName,
Email: user.Email,
}
reply.RespondWithJson(w, http.StatusCreated, retUser)
}
Expand Down Expand Up @@ -131,11 +141,43 @@ func (apiCfg *APIConfig)HandlerLogin(w http.ResponseWriter, r *http.Request) {
reply.RespondWtihError(w, http.StatusNotFound, errorMsg)
return
}
idStr := user["_id"].(primitive.ObjectID).Hex()
retUser := bson.M {
"username": user["username"].(string),
"email": user["email"].(string),
"id": idStr,

accessTokenTime := 60 * 60
refreshTokenTime := 60 *24 * 60 * 60
accessToken, err := auth.MakeAccessToken(user.ID.Hex(), apiCfg.jwtSecret, time.Duration(accessTokenTime) * time.Second)
if err != nil {
reply.RespondWtihError(w, http.StatusInternalServerError, "Couldn't not create Access Token")
return
}

refreshToken, err := auth.MakeRefreshToken(user.ID.Hex(), apiCfg.jwtSecret, time.Duration(refreshTokenTime) * time.Second)
if err != nil {
reply.RespondWtihError(w, http.StatusInternalServerError, "Couldn't not create Access Token")
return
}

http.SetCookie(w, &http.Cookie{
Name: "access_token",
Value: accessToken,
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteLaxMode,
})

http.SetCookie(w, &http.Cookie{
Name: "refresh_token",
Value: refreshToken,
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteLaxMode,
})


retUser := model.User{
ID: user.ID,
FristName: user.FristName,
LastName: user.LastName,
Email: user.Email,
}
reply.RespondWithJson(w, http.StatusOK, retUser)
}
Expand Down
12 changes: 8 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@ module github.com/PratikforCoding/BusoFact.git
go 1.21.0

require (
github.com/go-chi/chi/v5 v5.0.10 // indirect
github.com/go-chi/chi/v5 v5.0.10
github.com/golang-jwt/jwt/v5 v5.0.0
github.com/joho/godotenv v1.5.1
go.mongodb.org/mongo-driver v1.12.1
golang.org/x/crypto v0.13.0
)

require (
github.com/golang/snappy v0.0.1 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
go.mongodb.org/mongo-driver v1.12.1 // indirect
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
golang.org/x/text v0.13.0 // indirect
)
Loading

0 comments on commit db3be15

Please sign in to comment.