so to provide better context to my problem, my crudapi is about simulating a bikeshop.
I create an account by passing an account obj. to POST “localhost:8080/users”, which if successful adds the info to the usernames, usercontacts and passwords table respectively. usernames hold the user’s username, usercontacts stores their sensitive data(addres, fname, lname) and passwords stored their passwords as an encrypted hash. When an account is created the response {user_id: #, msg: registered }
is sent back.
My current login route is a Post ‘localhost:8080/user/login’.
It hashes user provided pswd and checks its against
the currently hashed pswd stored in the database.
However, I want to return a token after authenticating.
Let me know if there’s anything missing.
JSON OBJECTS
Account
{
"username": string,
"fname": string,
"lname": string,
"address": string,
"password": string
}
usernames
{
"id": int,
"user_id": int,
"username": string,
}
usercontacts
{
"id": int,
"user_id": int,
"fname": string,
"lname": string,
"address": string
}
passwords
{
"id": int,
"user_id": int,
"password": string,
}
Login-Creds
{
"username": string,
"pswd": []byte
}
ROUTES
My Implmenetation
- Routes > main.go
- Account info & Authentication > accts.go
- Field Error handling > fields.go
MAIN FILE
Create Account Route
func createAcct(r *gin.Engine) *gin.Engine {
r.POST("/users/", func(c *gin.Context) {
var acct accts.Account
var acctErr fields.GrammarError
var acctStatus *accts.Registered
err := c.ShouldBind(&acct)
if err != nil {
acctErr.AddMsg(fields.BadRequest,
"Binding Error: failed to bind fields to account object, mismatched data-types")
c.JSON(fields.ErrorCode, acctErr)
return
}
// validate account info
acctStatus, acctErr = accts.AddAccount(&acct)
// send response back
errMsgSize := len(acctErr.ErrMsgs)
switch {
case errMsgSize > 0:
c.JSON(fields.ErrorCode, acctErr)
default:
c.JSON(statusOK, *acctStatus)
}
//log.Println("Account: ", acct)
})
return r
}
Login Route
func logIn(r *gin.Engine) *gin.Engine {
r.POST("/user/login", func(c *gin.Context) {
var loginErr fields.GrammarError
var rqstData respBodyData
var userCred accts.LoginCred
loginErr = rqstData.FieldErr
err := c.ShouldBind(&userCred)
if err != nil {
loginErr.AddMsg(fields.BadRequest,
"Binding Error: failed to bind fields to account object, mismatched data-types")
c.JSON(fields.ErrorCode, loginErr)
return
}
// validate account info
authStatus, loginErr := accts.LogIntoAcct(userCred)
if err != nil {
loginErr.AddMsg(fields.BadRequest,
"Binding Error: failed to bind fields to account object, mismatched data-types")
c.JSON(fields.ErrorCode, loginErr)
return
}
// send response back
errMsgSize := len(loginErr.ErrMsgs)
switch {
case errMsgSize > 0:
c.JSON(fields.ErrorCode, loginErr)
default:
c.JSON(statusOK, authStatus)
}
})
return r
}
ACCTS FILE
Add Account
// adds the account info to the appropiate tables w/ the database
func AddAccount(acct *Account) (*Registered, fields.GrammarError) {
acctErr := &fields.GrammarError{}
validateAccount(acct, acctErr)
if acctErr.ErrMsgs != nil {
return nil, *acctErr
}
// if no errors add info to appropiate tables
addUsername(acct, acctErr)
if acctErr.ErrMsgs != nil {
return nil, *acctErr
}
addUserContact(acct, acctErr)
if acctErr.ErrMsgs != nil {
return nil, *acctErr
}
// add passwords to table, don't if err existf
addPassword(acct, acctErr)
if acctErr.ErrMsgs != nil {
// fmt.Printf("Errors in AddAccount Func, %v\n", acctErr.ErrMsgs)
return nil, *acctErr
}
return &Registered{acct.ID, "registered"}, *acctErr
}
Login
// matches the client's username and pswd against the database
// if there's a match the user is logged in, otherwise
// there's an issue with username or password
func LogIntoAcct(userCred LoginCred) (*LoginStatus, fields.GrammarError) {
// get the user struct check if it exists
usrs, fieldErr := readUserByUsername(userCred.UserName)
if fieldErr.ErrMsgs != nil {
return nil, fieldErr
}
// get the stored pswd hash
usr := usrs[0]
pswds, fieldErr := ReadHashByID(usr.ID)
if fieldErr.ErrMsgs != nil {
return nil, fieldErr
}
// hash the given pswd and compare it to whats
// stored in the databse
hashedPswd := pswds[0].Password
err := bcrypt.CompareHashAndPassword(hashedPswd, []byte(userCred.Password))
if err != nil {
// log.Printf("The Password Hash Comparison Failed: %v\n", err.Error())
fieldErr.AddMsg(BadRequest, "Error: password is incorrect")
return nil, fieldErr
}
return &LoginStatus{usr.ID, "LoggedIn"}, fieldErr
}