Basic Access & Refresh Token
This commit is contained in:
@@ -1,13 +1,16 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"net/http"
|
||||
"encoding/json"
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
|
||||
"reichard.io/imagini/internal/models"
|
||||
// "github.com/lestrrat-go/jwx/jwt"
|
||||
// "github.com/lestrrat-go/jwx/jwa"
|
||||
// log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// https://www.calhoun.io/pitfalls-of-context-values-and-how-to-avoid-or-mitigate-them/
|
||||
@@ -35,20 +38,51 @@ func (api *API) loginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Verify Device Name Exists
|
||||
deviceHeader := r.Header.Get("X-Imagini-DeviceName")
|
||||
if deviceHeader == "" {
|
||||
errorJSON(w, "Missing 'X-Imagini-DeviceName' header.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Derive Device Type
|
||||
var deviceType string
|
||||
userAgent := strings.ToLower(r.Header.Get("User-Agent"))
|
||||
if strings.HasPrefix(userAgent, "ios-imagini"){
|
||||
deviceType = "iOS"
|
||||
} else if strings.HasPrefix(userAgent, "android-imagini"){
|
||||
deviceType = "Android"
|
||||
} else if strings.HasPrefix(userAgent, "chrome"){
|
||||
deviceType = "Chrome"
|
||||
} else if strings.HasPrefix(userAgent, "firefox"){
|
||||
deviceType = "Firefox"
|
||||
} else if strings.HasPrefix(userAgent, "msie"){
|
||||
deviceType = "Internet Explorer"
|
||||
} else if strings.HasPrefix(userAgent, "edge"){
|
||||
deviceType = "Edge"
|
||||
} else if strings.HasPrefix(userAgent, "safari"){
|
||||
deviceType = "Safari"
|
||||
}else {
|
||||
deviceType = "Unknown"
|
||||
}
|
||||
|
||||
// Do login
|
||||
resp := api.Auth.AuthenticateUser(creds)
|
||||
resp, user := api.Auth.AuthenticateUser(creds)
|
||||
if !resp {
|
||||
errorJSON(w, "Invalid credentials.", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Create tokens
|
||||
accessToken := api.Auth.CreateJWTAccessToken()
|
||||
refreshToken := api.Auth.CreateRefreshToken()
|
||||
// Create New Device
|
||||
device, err := api.DB.CreateDevice(models.Device{Name: deviceHeader, Type: deviceType})
|
||||
|
||||
// Create Tokens
|
||||
accessToken, err := api.Auth.CreateJWTAccessToken(user, device)
|
||||
refreshToken, err := api.Auth.CreateJWTRefreshToken(user, device)
|
||||
|
||||
// Set appropriate cookies
|
||||
accessCookie := http.Cookie{Name: "AccessToken", Value: accessToken}
|
||||
refreshCookie := http.Cookie{Name: "RefreshToken", Value: refreshToken}
|
||||
accessCookie := http.Cookie{Name: "AccessToken", Value: accessToken, HttpOnly: true}
|
||||
refreshCookie := http.Cookie{Name: "RefreshToken", Value: refreshToken, HttpOnly: true}
|
||||
http.SetCookie(w, &accessCookie)
|
||||
http.SetCookie(w, &refreshCookie)
|
||||
|
||||
@@ -78,15 +112,50 @@ func (api *API) logoutHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (api *API) refreshLoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ok := api.Auth.ValidateRefreshToken()
|
||||
refreshCookie, err := r.Cookie("RefreshToken")
|
||||
if err != nil {
|
||||
log.Warn("[middleware] Cookie not found")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate Refresh Token
|
||||
refreshToken, ok := api.Auth.ValidateJWTRefreshToken(refreshCookie.Value)
|
||||
if !ok {
|
||||
// TODO: Clear Access & Refresh Cookies
|
||||
http.SetCookie(w, &http.Cookie{Name: "AccessToken", Expires: time.Unix(0, 0)})
|
||||
http.SetCookie(w, &http.Cookie{Name: "RefreshToken", Expires: time.Unix(0, 0)})
|
||||
errorJSON(w, "Invalid credentials.", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Acquire User & Device (Trusted)
|
||||
did, ok := refreshToken.Get("did")
|
||||
if !ok {
|
||||
errorJSON(w, "Invalid credentials.", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
uid, ok := refreshToken.Get(jwt.SubjectKey)
|
||||
if !ok {
|
||||
errorJSON(w, "Invalid credentials.", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
deviceID, err := uuid.Parse(fmt.Sprintf("%v", did))
|
||||
if err != nil {
|
||||
errorJSON(w, "Invalid credentials.", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
userID, err := uuid.Parse(fmt.Sprintf("%v", uid))
|
||||
if err != nil {
|
||||
errorJSON(w, "Invalid credentials.", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Device Skeleton
|
||||
user := models.User{Base: models.Base{UUID: userID}}
|
||||
device := models.Device{Base: models.Base{UUID: deviceID}}
|
||||
|
||||
// Update token
|
||||
accessToken := api.Auth.CreateJWTAccessToken()
|
||||
accessToken, err := api.Auth.CreateJWTAccessToken(user, device)
|
||||
accessCookie := http.Cookie{Name: "AccessToken", Value: accessToken}
|
||||
http.SetCookie(w, &accessCookie)
|
||||
|
||||
|
||||
@@ -22,27 +22,22 @@ func multipleMiddleware(h http.HandlerFunc, m ...Middleware) http.HandlerFunc {
|
||||
|
||||
func (api *API) authMiddleware(next http.Handler) http.HandlerFunc {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
cookie, err := r.Cookie("Token")
|
||||
// Acquire Token
|
||||
accessCookie, err := r.Cookie("AccessToken")
|
||||
if err != nil {
|
||||
log.Warn("[middleware] Cookie not found")
|
||||
log.Warn("[middleware] AccessToken not found")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate cookie.Value JWT with
|
||||
api.Auth.ValidateJWTToken(cookie.Value)
|
||||
// Validate JWT Tokens
|
||||
_, accessOK := api.Auth.ValidateJWTAccessToken(accessCookie.Value)
|
||||
|
||||
|
||||
log.Info("[middleware] Cookie Name: ", cookie.Name)
|
||||
log.Info("[middleware] Cookie Value: ", cookie.Value)
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
|
||||
// if true {
|
||||
// next.ServeHTTP(w, r)
|
||||
// } else {
|
||||
// w.WriteHeader(http.StatusUnauthorized)
|
||||
// }
|
||||
if accessOK {
|
||||
next.ServeHTTP(w, r)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -27,13 +27,4 @@ func (api *API) meHandler(w http.ResponseWriter, r *http.Request) {
|
||||
errorJSON(w, "Method is not supported.", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
// Get Authenticated User & Return Object
|
||||
authCookie, err := r.Cookie("Token")
|
||||
if err != nil {
|
||||
log.Error("[api] ", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("[api] Auth Cookie: ", authCookie)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user