🐛 Fixed parsing actions in token HTTP delivery

This commit is contained in:
Maxim Lebedev 2024-05-08 19:24:23 +05:00
parent 0f99ae6dcb
commit 645daae946
Signed by: toby3d
GPG Key ID: 1F14E25B7C119FC5
2 changed files with 65 additions and 52 deletions

View File

@ -3,20 +3,18 @@ package http
import (
"encoding/json"
"net/http"
"github.com/lestrrat-go/jwx/v2/jwa"
"strings"
"source.toby3d.me/toby3d/auth/internal/common"
"source.toby3d.me/toby3d/auth/internal/domain"
"source.toby3d.me/toby3d/auth/internal/domain/action"
"source.toby3d.me/toby3d/auth/internal/middleware"
"source.toby3d.me/toby3d/auth/internal/token"
pathutil "source.toby3d.me/toby3d/auth/internal/util/path"
)
type Handler struct {
config domain.Config
tokens token.UseCase
config domain.Config
}
func NewHandler(tokens token.UseCase, config domain.Config) *Handler {
@ -27,22 +25,6 @@ func NewHandler(tokens token.UseCase, config domain.Config) *Handler {
}
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
chain := middleware.Chain{
//nolint:exhaustivestruct
middleware.JWTWithConfig(middleware.JWTConfig{
Skipper: func(_ http.ResponseWriter, r *http.Request) bool {
head, _ := pathutil.Shift(r.URL.Path)
return head == "token"
},
SigningKey: []byte(h.config.JWT.Secret),
SigningMethod: jwa.SignatureAlgorithm(h.config.JWT.Algorithm),
ContextKey: "token",
TokenLookup: "form:token," + "header:" + common.HeaderAuthorization + ":Bearer ",
AuthScheme: "Bearer",
}),
}
if r.Method != http.MethodPost {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
@ -56,11 +38,11 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
default:
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
case "token":
chain.Handler(h.handleAction).ServeHTTP(w, r)
h.handleAction(w, r)
case "introspect":
chain.Handler(h.handleIntrospect).ServeHTTP(w, r)
h.handleIntrospect(w, r)
case "revocation":
chain.Handler(h.handleRevokation).ServeHTTP(w, r)
h.handleRevocation(w, r)
}
}
@ -95,14 +77,7 @@ func (h *Handler) handleIntrospect(w http.ResponseWriter, r *http.Request) {
return
}
_ = encoder.Encode(&TokenIntrospectResponse{
Active: true,
ClientID: tkn.ClientID.String(),
Exp: tkn.Expiry.Unix(),
Iat: tkn.CreatedAt.Unix(),
Me: tkn.Me.String(),
Scope: tkn.Scope.String(),
})
_ = encoder.Encode(NewTokenIntrospectResponse(tkn))
w.WriteHeader(http.StatusOK)
}
@ -114,6 +89,12 @@ func (h *Handler) handleAction(w http.ResponseWriter, r *http.Request) {
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set(common.HeaderContentType, common.MIMEApplicationJSONCharsetUTF8)
encoder := json.NewEncoder(w)
@ -130,7 +111,7 @@ func (h *Handler) handleAction(w http.ResponseWriter, r *http.Request) {
return
}
a, err := action.Parse(r.PostForm.Get("action"))
a, err := action.Parse(strings.ToLower(r.PostForm.Get("action")))
if err != nil {
w.WriteHeader(http.StatusBadRequest)
@ -141,7 +122,7 @@ func (h *Handler) handleAction(w http.ResponseWriter, r *http.Request) {
switch a {
case action.Revoke:
h.handleRevokation(w, r)
h.handleRevocation(w, r)
}
}
}
@ -181,20 +162,12 @@ func (h *Handler) handleExchange(w http.ResponseWriter, r *http.Request) {
return
}
resp := &TokenExchangeResponse{
AccessToken: token.AccessToken,
ExpiresIn: token.Expiry.Unix(),
Me: token.Me.String(),
Profile: NewTokenProfileResponse(profile),
RefreshToken: "", // TODO(toby3d)
}
_ = encoder.Encode(resp)
_ = encoder.Encode(NewTokenExchangeResponse(*token, profile))
w.WriteHeader(http.StatusOK)
}
func (h *Handler) handleRevokation(w http.ResponseWriter, r *http.Request) {
func (h *Handler) handleRevocation(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)

View File

@ -2,6 +2,7 @@ package http
import (
"errors"
"fmt"
"net/http"
"source.toby3d.me/toby3d/auth/internal/domain"
@ -124,16 +125,16 @@ func NewTokenProfileResponse(in *domain.Profile) *TokenProfileResponse {
out.Name = in.Name
if in.URL != nil {
out.URL = in.URL.String()
}
for dst, src := range map[*string]fmt.Stringer{
&out.Email: in.Email,
&out.Photo: in.Photo,
&out.URL: in.URL,
} {
if src == nil {
continue
}
if in.Photo != nil {
out.Photo = in.Photo.String()
}
if in.Email != nil {
out.Email = in.Email.String()
*dst = src.String()
}
return out
@ -216,3 +217,42 @@ func (r *TokenIntrospectRequest) bind(req *http.Request) error {
return nil
}
func NewTokenExchangeResponse(token domain.Token, profile *domain.Profile) *TokenExchangeResponse {
out := &TokenExchangeResponse{
Profile: NewTokenProfileResponse(profile),
AccessToken: token.AccessToken,
Me: token.Me.String(),
RefreshToken: token.RefreshToken,
ExpiresIn: 0,
}
if !token.Expiry.IsZero() {
out.ExpiresIn = token.Expiry.Unix()
}
return out
}
func NewTokenIntrospectResponse(token *domain.Token) *TokenIntrospectResponse {
out := new(TokenIntrospectResponse)
if token == nil {
return out
}
out.Active = true
out.Me = token.Me.String()
out.ClientID = token.ClientID.String()
out.Scope = token.Scope.String()
if !token.CreatedAt.IsZero() {
out.Iat = token.CreatedAt.Unix()
}
if !token.Expiry.IsZero() {
out.Exp = token.Expiry.Unix()
}
return out
}