package restutility import ( "errors" "time" "crypto/sha256" "crypto/rand" "encoding/hex" "strings" "github.com/dgrijalva/jwt-go" ) const OneDay = time.Hour*24 const OneWeek = OneDay*7 const saltSize = 32 const appName = "korisnicki-centar" const secret = "korisnicki-centar-api" type TokenClaims struct { Username string `json:"username"` Role string `json:"role"` jwt.StandardClaims } type CredentialsStruct struct { Username string `json:"username"` Password string `json:"password"` } func generateSalt() (salt string, err error) { rawsalt := make([]byte, saltSize) _, err = rand.Read(rawsalt) if err != nil { return "", err } salt = hex.EncodeToString(rawsalt) return salt, nil } func HashString(str string, presalt string) (hash, salt string, err error) { // chech if message is presalted if presalt == "" { salt, err = generateSalt() if err != nil { return "", "", err } } else { salt = presalt } // convert strings to raw byte slices rawstr := []byte(str) rawsalt, err := hex.DecodeString(salt) if err != nil { return "", "", err } rawdata := make([]byte, len(rawstr) + len(rawsalt)) rawdata = append(rawdata, rawstr...) rawdata = append(rawdata, rawsalt...) // hash message + salt hasher := sha256.New() hasher.Write(rawdata) rawhash := hasher.Sum(nil) hash = hex.EncodeToString(rawhash) return hash, salt, nil } func CreateAPIToken(username, role string) (string, error) { var apiToken string var err error if err != nil { return "", err } claims := TokenClaims{ username, role, jwt.StandardClaims{ ExpiresAt: (time.Now().Add(OneWeek)).Unix(), Issuer: appName, }, } jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) apiToken, err = jwtToken.SignedString([]byte(secret)) if err != nil { return "", err } return apiToken, nil } func RefreshAPIToken(tokenString string) (string, error) { var newToken string tokenString = strings.TrimPrefix(tokenString, "Bearer ") token, err := parseTokenFunc(tokenString) if err != nil { return "", err } // type assertion claims, ok := token.Claims.(*TokenClaims) if !ok || !token.Valid { return "", errors.New("token is not valid") } claims.ExpiresAt = (time.Now().Add(OneWeek)).Unix() jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) newToken, err = jwtToken.SignedString([]byte(secret)) if err != nil { return "", err } return newToken, nil } func ParseAPIToken(tokenString string) (*TokenClaims, error) { if ok := strings.HasPrefix(tokenString, "Bearer"); ok { tokenString = strings.TrimPrefix(tokenString, "Bearer ") } else { return &TokenClaims{}, errors.New("Authorization header is incomplete") } token, err := parseTokenFunc(tokenString) if err != nil { return &TokenClaims{}, err } // type assertion claims, ok := token.Claims.(*TokenClaims) if !ok || !token.Valid { return &TokenClaims{}, errors.New("token is not valid") } return claims, nil } func parseTokenFunc(tokenString string) (*jwt.Token, error) { token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, func(token *jwt.Token) (interface{}, error) { return []byte(secret), nil }, ) return token, err }