Skip to content

Instantly share code, notes, and snippets.

@victorsteven
Last active April 6, 2020 22:44
Show Gist options
  • Save victorsteven/85728317207c4ef40941978a447f6e53 to your computer and use it in GitHub Desktop.
Save victorsteven/85728317207c4ef40941978a447f6e53 to your computer and use it in GitHub Desktop.
package interfaces
import (
"fmt"
"food-app/application"
"food-app/domain/entity"
"food-app/infrastructure/auth"
"github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
"net/http"
"os"
"strconv"
)
type Authenticate struct {
us application.UserAppInterface
rd auth.AuthInterface
tk auth.TokenInterface
}
//Authenticate constructor
func NewAuthenticate(uApp application.UserAppInterface, rd auth.AuthInterface, tk auth.TokenInterface) *Authenticate {
return &Authenticate{
us: uApp,
rd: rd,
tk: tk,
}
}
func (au *Authenticate) Login(c *gin.Context) {
var user *entity.User
var tokenErr = map[string]string{}
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusUnprocessableEntity, "Invalid json provided")
return
}
//validate request:
validateUser := user.Validate("login")
if len(validateUser) > 0 {
c.JSON(http.StatusUnprocessableEntity, validateUser)
return
}
u, userErr := au.us.GetUserByEmailAndPassword(user)
if userErr != nil {
c.JSON(http.StatusInternalServerError, userErr)
return
}
ts, tErr := au.tk.CreateToken(u.ID)
if tErr != nil {
tokenErr["token_error"] = tErr.Error()
c.JSON(http.StatusUnprocessableEntity, tErr.Error())
return
}
saveErr := au.rd.CreateAuth(u.ID, ts)
if saveErr != nil {
c.JSON(http.StatusInternalServerError, saveErr.Error())
return
}
userData := make(map[string]interface{})
userData["access_token"] = ts.AccessToken
userData["refresh_token"] = ts.RefreshToken
userData["id"] = u.ID
userData["first_name"] = u.FirstName
userData["last_name"] = u.LastName
c.JSON(http.StatusOK, userData)
}
func (au *Authenticate) Logout(c *gin.Context) {
//check is the user is authenticated first
metadata, err := au.tk.ExtractTokenMetadata(c.Request)
if err != nil {
c.JSON(http.StatusUnauthorized, "Unauthorized")
return
}
//if the access token exist and it is still valid, then delete both the access token and the refresh token
deleteErr := au.rd.DeleteTokens(metadata)
if deleteErr != nil {
c.JSON(http.StatusUnauthorized, deleteErr.Error())
return
}
c.JSON(http.StatusOK, "Successfully logged out")
}
//Refresh is the function that uses the refresh_token to generate new pairs of refresh and access tokens.
func (au *Authenticate) Refresh(c *gin.Context) {
mapToken := map[string]string{}
if err := c.ShouldBindJSON(&mapToken); err != nil {
c.JSON(http.StatusUnprocessableEntity, err.Error())
return
}
refreshToken := mapToken["refresh_token"]
//verify the token
token, err := jwt.Parse(refreshToken, func(token *jwt.Token) (interface{}, error) {
//Make sure that the token method conform to "SigningMethodHMAC"
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(os.Getenv("REFRESH_SECRET")), nil
})
//any error may be due to token expiration
if err != nil {
c.JSON(http.StatusUnauthorized, err.Error())
return
}
//is token valid?
if _, ok := token.Claims.(jwt.Claims); !ok && !token.Valid {
c.JSON(http.StatusUnauthorized, err)
return
}
//Since token is valid, get the uuid:
claims, ok := token.Claims.(jwt.MapClaims)
if ok && token.Valid {
refreshUuid, ok := claims["refresh_uuid"].(string) //convert the interface to string
if !ok {
c.JSON(http.StatusUnprocessableEntity, "Cannot get uuid")
return
}
userId, err := strconv.ParseUint(fmt.Sprintf("%.f", claims["user_id"]), 10, 64)
if err != nil {
c.JSON(http.StatusUnprocessableEntity, "Error occurred")
return
}
//Delete the previous Refresh Token
delErr := au.rd.DeleteRefresh(refreshUuid)
if delErr != nil { //if any goes wrong
c.JSON(http.StatusUnauthorized, "unauthorized")
return
}
//Create new pairs of refresh and access tokens
ts, createErr := au.tk.CreateToken(userId)
if createErr != nil {
c.JSON(http.StatusForbidden, createErr.Error())
return
}
//save the tokens metadata to redis
saveErr := au.rd.CreateAuth(userId, ts)
if saveErr != nil {
c.JSON(http.StatusForbidden, saveErr.Error())
return
}
tokens := map[string]string{
"access_token": ts.AccessToken,
"refresh_token": ts.RefreshToken,
}
c.JSON(http.StatusCreated, tokens)
} else {
c.JSON(http.StatusUnauthorized, "refresh token expired")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment