Last active
April 6, 2020 22:44
-
-
Save victorsteven/85728317207c4ef40941978a447f6e53 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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