Blame view

auth_utility.go 3.53 KB
ea858b8a7   Marko Tikvić   refactoring
1
  package webutility
90fd36e9b   Marko Tikvić   resolved some dep...
2
3
  
  import (
90fd36e9b   Marko Tikvić   resolved some dep...
4
  	"errors"
90fd36e9b   Marko Tikvić   resolved some dep...
5
6
7
8
9
10
  	"time"
  	"crypto/sha256"
  	"crypto/rand"
  	"encoding/hex"
  	"strings"
  	"github.com/dgrijalva/jwt-go"
90fd36e9b   Marko Tikvić   resolved some dep...
11
  )
7d3deb50d   Marko Tikvić   modified list_con...
12
13
  const OneDay   = time.Hour*24
  const OneWeek  = OneDay*7
90fd36e9b   Marko Tikvić   resolved some dep...
14
15
16
  const saltSize = 32
  const appName  = "korisnicki-centar"
  const secret   = "korisnicki-centar-api"
90fd36e9b   Marko Tikvić   resolved some dep...
17
18
  type TokenClaims struct {
  	Username string `json:"username"`
7d3deb50d   Marko Tikvić   modified list_con...
19
  	Role     string `json:"role"`
90fd36e9b   Marko Tikvić   resolved some dep...
20
21
22
23
24
25
26
  	jwt.StandardClaims
  }
  
  type CredentialsStruct struct {
  	Username string `json:"username"`
  	Password string `json:"password"`
  }
e1fbb41f9   Marko Tikvić   added comments
27
  // generateSalt returns a random string of 'saltSize' length to be used for hashing.
4b4ea384f   Marko Tikvić   hmm
28
  func generateSalt() (salt string, err error) {
90fd36e9b   Marko Tikvić   resolved some dep...
29
  	rawsalt := make([]byte, saltSize)
33fd58161   markotikvic   minor changes, sh...
30

4b4ea384f   Marko Tikvić   hmm
31
  	_, err = rand.Read(rawsalt)
90fd36e9b   Marko Tikvić   resolved some dep...
32
33
34
  	if err != nil {
  		return "", err
  	}
33fd58161   markotikvic   minor changes, sh...
35

90fd36e9b   Marko Tikvić   resolved some dep...
36
37
38
  	salt = hex.EncodeToString(rawsalt)
  	return salt, nil
  }
e1fbb41f9   Marko Tikvić   added comments
39
40
  // HashString hashes input string with SHA256 algorithm.
  // If the presalt parameter is not provided HashString will generate new salt string.
33fd58161   markotikvic   minor changes, sh...
41
  func HashString(str string, presalt string) (hash, salt string, err error) {
90fd36e9b   Marko Tikvić   resolved some dep...
42
43
  	// chech if message is presalted
  	if presalt == "" {
33fd58161   markotikvic   minor changes, sh...
44
  		salt, err = generateSalt()
90fd36e9b   Marko Tikvić   resolved some dep...
45
46
47
48
49
50
51
52
  		if err != nil {
  			return "", "", err
  		}
  	} else {
  		salt = presalt
  	}
  
  	// convert strings to raw byte slices
33fd58161   markotikvic   minor changes, sh...
53
  	rawstr := []byte(str)
90fd36e9b   Marko Tikvić   resolved some dep...
54
55
56
57
  	rawsalt, err := hex.DecodeString(salt)
  	if err != nil {
  		return "", "", err
  	}
33fd58161   markotikvic   minor changes, sh...
58
59
60
  
  	rawdata := make([]byte, len(rawstr) + len(rawsalt))
  	rawdata = append(rawdata, rawstr...)
90fd36e9b   Marko Tikvić   resolved some dep...
61
62
63
64
65
66
  	rawdata = append(rawdata, rawsalt...)
  
  	// hash message + salt
  	hasher := sha256.New()
  	hasher.Write(rawdata)
  	rawhash := hasher.Sum(nil)
33fd58161   markotikvic   minor changes, sh...
67

90fd36e9b   Marko Tikvić   resolved some dep...
68
69
70
  	hash = hex.EncodeToString(rawhash)
  	return hash, salt, nil
  }
e1fbb41f9   Marko Tikvić   added comments
71
72
  // CreateAPIToken creates JWT token encoding username, role,
  // expiration date and issuer claims in it.
33fd58161   markotikvic   minor changes, sh...
73
  func CreateAPIToken(username, role string) (string, error) {
7d3deb50d   Marko Tikvić   modified list_con...
74
  	var apiToken string
90fd36e9b   Marko Tikvić   resolved some dep...
75
76
77
  	var err error
  
  	if err != nil {
6f4b8a711   Marko Tikvić   token response ch...
78
  		return "", err
90fd36e9b   Marko Tikvić   resolved some dep...
79
80
81
82
83
84
85
86
87
88
89
90
  	}
  
  	claims := TokenClaims{
  		username,
  		role,
  		jwt.StandardClaims{
  			ExpiresAt: (time.Now().Add(OneWeek)).Unix(),
  			Issuer:    appName,
  		},
  	}
  
  	jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
6f4b8a711   Marko Tikvić   token response ch...
91
  	apiToken, err = jwtToken.SignedString([]byte(secret))
90fd36e9b   Marko Tikvić   resolved some dep...
92
  	if err != nil {
6f4b8a711   Marko Tikvić   token response ch...
93
  		return "", err
90fd36e9b   Marko Tikvić   resolved some dep...
94
95
96
  	}
  	return apiToken, nil
  }
e1fbb41f9   Marko Tikvić   added comments
97
  // RefreshAPIToken prolongs JWT token's expiration date.
6f4b8a711   Marko Tikvić   token response ch...
98
  func RefreshAPIToken(tokenString string) (string, error) {
7d3deb50d   Marko Tikvić   modified list_con...
99
  	var newToken string
90fd36e9b   Marko Tikvić   resolved some dep...
100
  	tokenString = strings.TrimPrefix(tokenString, "Bearer ")
e1fbb41f9   Marko Tikvić   added comments
101
  	token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc)
90fd36e9b   Marko Tikvić   resolved some dep...
102
  	if err != nil {
6f4b8a711   Marko Tikvić   token response ch...
103
  		return "", err
90fd36e9b   Marko Tikvić   resolved some dep...
104
105
106
107
108
  	}
  
  	// type assertion
  	claims, ok := token.Claims.(*TokenClaims)
  	if !ok || !token.Valid {
6f4b8a711   Marko Tikvić   token response ch...
109
  		return "", errors.New("token is not valid")
90fd36e9b   Marko Tikvić   resolved some dep...
110
111
112
113
  	}
  
  	claims.ExpiresAt = (time.Now().Add(OneWeek)).Unix()
  	jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
6f4b8a711   Marko Tikvić   token response ch...
114
  	newToken, err = jwtToken.SignedString([]byte(secret))
90fd36e9b   Marko Tikvić   resolved some dep...
115
  	if err != nil {
6f4b8a711   Marko Tikvić   token response ch...
116
  		return "", err
90fd36e9b   Marko Tikvić   resolved some dep...
117
118
119
120
  	}
  
  	return newToken, nil
  }
e1fbb41f9   Marko Tikvić   added comments
121
  // ParseAPIToken parses JWT token claims.
b291ac8c4   Marko Tikvić   clened up
122
  func ParseAPIToken(tokenString string) (*TokenClaims, error) {
e1fbb41f9   Marko Tikvić   added comments
123
  	if ok := strings.HasPrefix(tokenString, "Bearer "); ok {
90fd36e9b   Marko Tikvić   resolved some dep...
124
125
126
127
  		tokenString = strings.TrimPrefix(tokenString, "Bearer ")
  	} else {
  		return &TokenClaims{}, errors.New("Authorization header is incomplete")
  	}
e1fbb41f9   Marko Tikvić   added comments
128
  	token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc)
90fd36e9b   Marko Tikvić   resolved some dep...
129
130
131
132
133
134
135
136
137
138
139
  	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
  }
e1fbb41f9   Marko Tikvić   added comments
140
141
142
  // secretFunc returns byte slice of 'secret'.
  func secretFunc(token *jwt.Token) (interface{}, error) {
  	return []byte(secret), nil
90fd36e9b   Marko Tikvić   resolved some dep...
143
  }