You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
quartermaster/pkg/api/users/views.go

144 lines
3.4 KiB

package users
import (
"encoding/json"
"log"
"net/http"
"os"
"time"
"github.com/dustinpianalto/quartermaster"
"github.com/dustinpianalto/quartermaster/pkg/services"
"github.com/golang-jwt/jwt"
"golang.org/x/crypto/bcrypt"
)
var jwtKey = []byte(os.Getenv("JWT_KEY"))
func login(w http.ResponseWriter, r *http.Request) {
var userReq quartermaster.User
err := json.NewDecoder(r.Body).Decode(&userReq)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
log.Println(err)
return
}
user, err := services.UserService.User(userReq.Username)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
log.Println(err)
return
}
if bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(userReq.Password)) != nil {
w.WriteHeader(http.StatusUnauthorized)
log.Println(err)
return
}
expires := time.Now().Add(10 * time.Minute)
claims := &quartermaster.Claims{
ID: user.ID,
Username: user.Username,
StandardClaims: jwt.StandardClaims{
ExpiresAt: expires.Unix(),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS512, claims)
tokenString, err := token.SignedString(jwtKey)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
log.Println(err)
return
}
http.SetCookie(w, &http.Cookie{
Name: "token",
Value: tokenString,
Expires: expires,
})
http.Redirect(w, r, "/", http.StatusFound) // Redirect with 302
}
func register(w http.ResponseWriter, r *http.Request) {
var user *quartermaster.User
err := json.NewDecoder(r.Body).Decode(&user)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
p, err := bcrypt.GenerateFromPassword([]byte(user.Password), 5)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
user.Password = string(p)
user, err = services.UserService.AddUser(user)
if err != nil {
w.WriteHeader(http.StatusConflict)
log.Println(err)
return
}
http.Redirect(w, r, "/login", http.StatusFound)
}
func refresh(w http.ResponseWriter, r *http.Request) {
c, err := r.Cookie("token")
if err != nil {
if err == http.ErrNoCookie {
w.WriteHeader(http.StatusUnauthorized)
return
}
w.WriteHeader(http.StatusBadRequest)
return
}
tknStr := c.Value
claims := &quartermaster.Claims{}
tkn, err := jwt.ParseWithClaims(tknStr, claims, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil
})
if err != nil {
if err == jwt.ErrSignatureInvalid {
w.WriteHeader(http.StatusUnauthorized)
return
}
w.WriteHeader(http.StatusBadRequest)
return
}
if !tkn.Valid {
w.WriteHeader(http.StatusUnauthorized)
return
}
// We ensure that a new token is not issued until enough time has elapsed
// In this case, a new token will only be issued if the old token is within
// 2 minutes of expiry. Otherwise, return a bad request status
if time.Until(time.Unix(claims.StandardClaims.ExpiresAt, 0)) >= 2*time.Minute {
w.WriteHeader(http.StatusBadRequest)
return
}
// Now, create a new token for the current use, with a renewed expiration time
expirationTime := time.Now().Add(10 * time.Minute)
claims.StandardClaims.ExpiresAt = expirationTime.Unix()
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString(jwtKey)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
// Set the new token as the users `token` cookie
http.SetCookie(w, &http.Cookie{
Name: "token",
Value: tokenString,
Expires: expirationTime,
})
}