Skip to content

Instantly share code, notes, and snippets.

@victorsteven
Created February 4, 2020 00:40
Show Gist options
  • Save victorsteven/f1c5154cd1d9b269876e00990ecaeed0 to your computer and use it in GitHub Desktop.
Save victorsteven/f1c5154cd1d9b269876e00990ecaeed0 to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/go-redis/redis/v7"
"github.com/twinj/uuid"
"net/http"
"os"
"strconv"
"strings"
"time"
)
var (
client *redis.Client
)
func init() {
//Initializing redis
dsn := os.Getenv("REDIS_DSN")
if len(dsn) == 0 {
dsn = "localhost:6379"
}
client = redis.NewClient(&redis.Options{
Addr: dsn, //redis port
})
_, err := client.Ping().Result()
if err != nil {
panic(err)
}
}
type AccessDetails struct {
AccessUuid string
UserId uint64
}
type TokenDetails struct {
AccessToken string
RefreshToken string
AccessUuid string
RefreshUuid string
AtExpires int64
RtExpires int64
}
func CreateToken(userid uint64) (*TokenDetails, error) {
td := &TokenDetails{}
td.AtExpires = time.Now().Add(time.Minute * 10).Unix()
td.AccessUuid = uuid.NewV4().String()
td.RtExpires = time.Now().Add(time.Minute * 60).Unix()
td.RefreshUuid = uuid.NewV4().String()
var err error
//Creating Access Token
os.Setenv("ACCESS_SECRET", "jdnfksdmfksd") //this should be in an env file
atClaims := jwt.MapClaims{}
atClaims["authorized"] = true
atClaims["access_uuid"] = td.AccessUuid
atClaims["user_id"] = userid
atClaims["exp"] = td.AtExpires
at := jwt.NewWithClaims(jwt.SigningMethodHS256, atClaims)
td.AccessToken, err = at.SignedString([]byte(os.Getenv("API_SECRET")))
if err != nil {
return nil, err
}
//Creating Refresh Token
os.Setenv("REFRESH_SECRET", "mcmvmkmsdnfsdmfdsjf") //this should be in an env file
rtClaims := jwt.MapClaims{}
rtClaims["refresh_uuid"] = td.RefreshUuid
rtClaims["user_id"] = userid
rtClaims["exp"] = td.RtExpires
rt := jwt.NewWithClaims(jwt.SigningMethodHS256, rtClaims)
td.RefreshToken, err = rt.SignedString([]byte(os.Getenv("API_SECRET_REFRESH")))
if err != nil {
return nil, err
}
return td, nil
}
func TokenValid(r *http.Request) error {
token, err := VerifyToken(r)
if err != nil {
return err
}
if _, ok := token.Claims.(jwt.Claims); !ok && !token.Valid {
return err
}
return nil
}
func VerifyToken(r *http.Request) (*jwt.Token, error) {
tokenString := ExtractToken(r)
token, err := jwt.Parse(tokenString, 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("API_SECRET")), nil
})
if err != nil {
return nil, err
}
return token, nil
}
//get the token from the request body
func ExtractToken(r *http.Request) string {
bearToken := r.Header.Get("Authorization")
//normally Authorization the_token_xxx
strArr := strings.Split(bearToken, " ")
if len(strArr) == 2 {
return strArr[1]
}
return ""
}
func ExtractTokenAuth(r *http.Request) (*AccessDetails, error) {
token, err := VerifyToken(r)
if err != nil {
return nil, err
}
claims, ok := token.Claims.(jwt.MapClaims) //the token claims should conform to MapClaims
if ok && token.Valid {
accessUuid, ok := claims["access_uuid"].(string) //convert the interface to string
if !ok {
return nil, err
}
userId, err := strconv.ParseUint(fmt.Sprintf("%.f", claims["user_id"]), 10, 64)
if err != nil {
return nil, err
}
return &AccessDetails{
AccessUuid: accessUuid,
UserId: userId,
}, nil
}
return nil, err
}
//Save token metadata to Redis
func CreateAuth(userid uint64, td *TokenDetails) error {
at := time.Unix(td.AtExpires, 0) //converting Unix to UTC(to Time object)
rt := time.Unix(td.RtExpires, 0)
now := time.Now()
errAccess := client.Set(td.AccessUuid, strconv.Itoa(int(userid)), at.Sub(now)).Err()
if errAccess != nil {
return errAccess
}
errRefresh := client.Set(td.RefreshUuid, strconv.Itoa(int(userid)), rt.Sub(now)).Err()
if errRefresh != nil {
return errRefresh
}
return nil
}
//Check the metadata saved
func FetchAuth(authD *AccessDetails) (uint64, error) {
userid, err := client.Get(authD.AccessUuid).Result()
if err != nil {
return 0, err
}
userID, _ := strconv.ParseUint(userid, 10, 64)
return userID, nil
}
//Once a user row in the auth table
func DeleteToken(givenUuid string) (int64,error) {
deleted, err := client.Del(givenUuid).Result()
if err != nil {
return 0, err
}
return deleted, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment