services/users/api/rest/auth.go
package rest
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v4/request"
"github.com/go-chi/chi"
"github.com/pkg/errors"
"go.kicksware.com/api/services/users/core/meta"
"go.kicksware.com/api/services/users/core/model"
"go.kicksware.com/api/services/users/core/service"
"go.kicksware.com/api/services/users/usecase/business"
)
var (
ErrInvalidTokenClaims = errors.New("invalid token claims")
)
func (h *Handler) SingUp(w http.ResponseWriter, r *http.Request) {
user, err := h.getRequestBody(r); if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
token, err := h.auth.SingUp(user); if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
h.setupAuthCookie(w, token)
h.setupResponse(w, token, http.StatusOK)
}
func (h *Handler) Login(w http.ResponseWriter, r *http.Request) {
user, err := h.getRequestBody(r); if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
token, err := h.auth.Login(user); if err != nil {
if errors.Cause(err) == service.ErrPasswordInvalid ||
errors.Cause(err) == service.ErrNotConfirmed {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
h.setupAuthCookie(w, token)
h.setupResponse(w, token, http.StatusOK)
}
func (h *Handler) Remote(w http.ResponseWriter, r *http.Request) {
user, err := h.getRequestBody(r); if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
token, err := h.auth.Remote(user); if err != nil {
if errors.Cause(err) == service.ErrInvalidRemoteID {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
} else if errors.Cause(err) == service.ErrInvalidRemoteProvider {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
h.setupAuthCookie(w, token)
h.setupResponse(w, token, http.StatusOK)
}
func (h *Handler) Guest(w http.ResponseWriter, r *http.Request) {
access := r.URL.Query().Get("access")
if !h.auth.VerifyAccessKey([]byte(access)) {
http.Error(w, business.ErrInvalidAccessKey.Error(), http.StatusUnauthorized)
return
}
token, err := h.auth.Guest(); if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
h.setupAuthCookie(w, token)
h.setupResponse(w, token, http.StatusOK)
}
func (h *Handler) RefreshToken(w http.ResponseWriter, r *http.Request) {
token, err := h.auth.Refresh(chi.URLParam(r,"token")); if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
h.setupAuthCookie(w, token)
h.setupResponse(w, token, http.StatusOK)
}
func (h *Handler) Logout(w http.ResponseWriter, r *http.Request) {
token := chi.URLParam(r,"token")
if err := h.auth.Logout(token); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
h.recallAuthCookie(w)
h.setupResponse(w, token, http.StatusOK)
}
func (h *Handler) Authenticator(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token, err := h.getRequestToken(r); if err != nil {
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
fmt.Println()
return
}
if token == nil || !token.Valid {
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
return
}
// Token is authenticated, pass it through
next.ServeHTTP(w, r)
})
}
func (h *Handler) Authorizer(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token, err := h.getRequestToken(r); if err != nil {
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
fmt.Println()
return
}
claims, err := business.GetClaims(token); if err != nil {
http.Error(w, ErrInvalidTokenClaims.Error(), http.StatusInternalServerError)
fmt.Println()
return
}
if claims != nil && claims.Role != string(model.Guest) {
r.URL.User = url.UserPassword(claims.UniqueID, token.Raw)
} else {
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
fmt.Println()
return
}
next.ServeHTTP(w, r)
})
}
func (h *Handler) getRequestToken(r *http.Request) (token *jwt.Token, err error) {
token, err = request.ParseFromRequest(r, request.OAuth2Extractor, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodRSA); ok {
return h.auth.PublicKey(), nil
}
return nil, fmt.Errorf("authenticator: unexpected signing method: %q", token.Header["alg"])
})
return
}
func getClaims(token *jwt.Token) (*meta.AuthClaims, error) {
payload, err := json.Marshal(token.Claims); if err != nil {
return nil, err
}
claims := &meta.AuthClaims{}
if err = json.Unmarshal(payload, claims); err != nil {
return nil, err
}
return claims, nil
}