-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathmain.go
163 lines (150 loc) · 4.44 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package main
import (
"fmt"
"log"
"net/http"
"os"
"strings"
"time"
"github.com/dgrijalva/jwt-go"
"github.com/garyburd/redigo/redis"
"github.com/gorilla/mux"
"golang.org/x/crypto/bcrypt"
)
// server type is for sharing dependencies with handlers
type server struct {
pool *redis.Pool
router *mux.Router
}
// POOL - Declare a global variable to store the Redis connection pool
var POOL *redis.Pool
// PORT - The running port for this service
var PORT = ""
// signKey - The JWT Sign key
var signKey = []byte("secret")
// Loads the default variables
func init() {
POOL = newPool("localhost:6379")
PORT = os.Getenv("AUTH_PORT")
if PORT == "" {
PORT = "4000"
}
signKey = []byte(os.Getenv("signKey"))
}
// Returns a pointer to a redis pool
func newPool(addr string) *redis.Pool {
return &redis.Pool{
MaxIdle: 5,
IdleTimeout: 240 * time.Second,
Dial: func() (redis.Conn, error) { return redis.Dial("tcp", addr) },
}
}
func (s *server) routes() {
s.router.HandleFunc("/login", s.handleLogin())
s.router.HandleFunc("/register", s.handleRegister())
}
// Gets you a token if you pass the right credentials
func (s *server) handleLogin() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var err error
setHeaders(w)
if r.Method != "POST" {
http.Error(w, fmt.Sprintf("{ \"error\": \"%s\" }", "Forbidden request"), 403)
return
}
conn := s.pool.Get()
defer conn.Close()
email := strings.ToLower(r.FormValue("email"))
password, err := redis.Bytes(conn.Do("GET", email))
if err == nil {
// compare passwords
err = bcrypt.CompareHashAndPassword(password, []byte(r.FormValue("password")))
// if it doesn't match
if err != nil {
http.Error(w, fmt.Sprintf("{ \"error\": \"%s\" }", "Wrong password"), 401)
return
}
token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims)
claims["admin"] = false
claims["email"] = email
// 24 hour token
claims["exp"] = time.Now().Add(time.Hour * 24).Unix()
tokenString, _ := token.SignedString(signKey)
w.Write([]byte(fmt.Sprintf("{ \"access_token\": \"%s\" }", tokenString)))
} else {
// email not found
http.Error(w, fmt.Sprintf("{ \"error\": \"%s\" }", "Email not found"), 401)
}
}
}
// Register a new user, gives you a token and sets the email -> password
// in redis if the email doesn't exist
func (s *server) handleRegister() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var err error
setHeaders(w)
if r.Method != "POST" {
http.Error(w, fmt.Sprintf("{ \"error\": \"%s\" }", "Forbidden request"), 403)
return
}
conn := s.pool.Get()
defer conn.Close()
email := strings.ToLower(r.FormValue("email"))
// check if the user is already registered
exists, err := redis.Bool(conn.Do("EXISTS", email))
if exists {
w.Write([]byte(fmt.Sprintf("{ \"error\": \"%s\" }", "Email taken")))
return
}
// get password from the post request form value
password := r.FormValue("password")
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password),
bcrypt.DefaultCost)
if err != nil {
panic(err)
}
// Set email -> password in redis
_, err = conn.Do("SET", email, string(hashedPassword[:]))
if err != nil {
log.Println(err)
}
token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims)
claims["admin"] = false
claims["email"] = email
// 24 hour token
claims["exp"] = time.Now().Add(time.Hour * 24).Unix()
tokenString, _ := token.SignedString(signKey)
w.Write([]byte(fmt.Sprintf("{ \"access_token\": \"%s\" }", tokenString)))
}
}
func setHeaders(w http.ResponseWriter) http.ResponseWriter {
w.Header().Set("Content-Type", "application/json")
// Uncomment the following lines if you are having CORS issues,
// optionally, replace "*" with your preferred address
//
// w.Header().Set("Access-Control-Allow-Origin", "*")
// w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
// w.Header().Set("Access-Control-Allow-Headers",
// "Accept, 0, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
return w
}
func main() {
sr := &server{
router: mux.NewRouter(),
pool: POOL,
}
sr.routes()
srv := &http.Server{
Handler: sr.router,
Addr: ":" + PORT,
WriteTimeout: 5 * time.Second,
ReadTimeout: 5 * time.Second,
}
log.Println("Auth server running at", PORT)
err := srv.ListenAndServe()
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}