Blame view

auth_utility.go 3.85 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"
6ec91280b   Marko Tikvić   working on docume...
17
  // TokenClaims are JWT token claims.
90fd36e9b   Marko Tikvić   resolved some dep...
18
19
  type TokenClaims struct {
  	Username string `json:"username"`
7d3deb50d   Marko Tikvić   modified list_con...
20
  	Role     string `json:"role"`
90fd36e9b   Marko Tikvić   resolved some dep...
21
22
  	jwt.StandardClaims
  }
6ec91280b   Marko Tikvić   working on docume...
23
  // CredentialsStruct is an instace of username/password values. 
90fd36e9b   Marko Tikvić   resolved some dep...
24
25
26
27
  type CredentialsStruct struct {
  	Username string `json:"username"`
  	Password string `json:"password"`
  }
6ec91280b   Marko Tikvić   working on docume...
28
  // generateSalt returns a string of random characters of 'saltSize' length.
4b4ea384f   Marko Tikvić   hmm
29
  func generateSalt() (salt string, err error) {
90fd36e9b   Marko Tikvić   resolved some dep...
30
  	rawsalt := make([]byte, saltSize)
33fd58161   markotikvic   minor changes, sh...
31

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

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

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