♻️ Refactored token verification
This commit is contained in:
parent
451da06bc6
commit
53098497a5
|
@ -1,6 +1,7 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"github.com/fasthttp/router"
|
||||
|
@ -22,6 +23,12 @@ type (
|
|||
Token string
|
||||
}
|
||||
|
||||
VerificationResponse struct {
|
||||
Me string `json:"me"`
|
||||
ClientID string `json:"client_id"`
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
RevocationResponse struct{}
|
||||
)
|
||||
|
||||
|
@ -37,12 +44,42 @@ func NewRequestHandler(useCase token.UseCase) *RequestHandler {
|
|||
}
|
||||
|
||||
func (h *RequestHandler) Register(r *router.Router) {
|
||||
r.GET("/token", h.Read)
|
||||
r.POST("/token", h.Update)
|
||||
}
|
||||
|
||||
func (h *RequestHandler) Update(ctx *http.RequestCtx) {
|
||||
ctx.SetStatusCode(http.StatusBadRequest)
|
||||
func (h *RequestHandler) Read(ctx *http.RequestCtx) {
|
||||
ctx.SetContentType(common.MIMEApplicationJSON)
|
||||
ctx.SetStatusCode(http.StatusOK)
|
||||
|
||||
encoder := json.NewEncoder(ctx)
|
||||
rawToken := ctx.Request.Header.Peek(http.HeaderAuthorization)
|
||||
|
||||
token, err := h.useCase.Verify(ctx, string(bytes.TrimSpace(bytes.TrimPrefix(rawToken, []byte("Bearer")))))
|
||||
if err != nil {
|
||||
ctx.SetStatusCode(http.StatusBadRequest)
|
||||
encoder.Encode(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if token == nil {
|
||||
ctx.SetStatusCode(http.StatusUnauthorized)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err := encoder.Encode(&VerificationResponse{
|
||||
Me: token.Me,
|
||||
ClientID: token.ClientID,
|
||||
Scope: strings.Join(token.Scopes, " "),
|
||||
}); err != nil {
|
||||
ctx.SetStatusCode(http.StatusInternalServerError)
|
||||
encoder.Encode(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *RequestHandler) Update(ctx *http.RequestCtx) {
|
||||
switch string(ctx.FormValue(Action)) {
|
||||
case ActionRevoke:
|
||||
h.Revocation(ctx)
|
||||
|
@ -70,11 +107,9 @@ func (h *RequestHandler) Revocation(ctx *http.RequestCtx) {
|
|||
return
|
||||
}
|
||||
|
||||
if err := encoder.Encode(RevocationResponse{}); err != nil {
|
||||
if err := encoder.Encode(&RevocationResponse{}); err != nil {
|
||||
ctx.SetStatusCode(http.StatusInternalServerError)
|
||||
encoder.Encode(err)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/brianvoe/gofakeit"
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
http "github.com/valyala/fasthttp"
|
||||
|
@ -18,7 +19,48 @@ import (
|
|||
"source.toby3d.me/website/oauth/internal/util"
|
||||
)
|
||||
|
||||
func TestVerification(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require := require.New(t)
|
||||
assert := assert.New(t)
|
||||
repo := repository.NewMemoryTokenRepository()
|
||||
accessToken := model.Token{
|
||||
AccessToken: gofakeit.Password(true, true, true, true, false, 32),
|
||||
Type: "Bearer",
|
||||
ClientID: "https://app.example.com/",
|
||||
Me: "https://user.example.net/",
|
||||
Scopes: []string{"create", "update", "delete"},
|
||||
}
|
||||
|
||||
require.NoError(repo.Create(context.TODO(), &accessToken))
|
||||
|
||||
req := http.AcquireRequest()
|
||||
defer http.ReleaseRequest(req)
|
||||
|
||||
req.Header.SetMethod(http.MethodGet)
|
||||
req.SetRequestURI("http://localhost/token")
|
||||
req.Header.Set(http.HeaderAccept, common.MIMEApplicationJSON)
|
||||
req.Header.Set(http.HeaderAuthorization, "Bearer "+accessToken.AccessToken)
|
||||
|
||||
resp := http.AcquireResponse()
|
||||
defer http.ReleaseResponse(resp)
|
||||
|
||||
require.NoError(util.Serve(delivery.NewRequestHandler(usecase.NewTokenUseCase(repo)).Read, req, resp))
|
||||
assert.Equal(http.StatusOK, resp.StatusCode())
|
||||
|
||||
token := new(delivery.VerificationResponse)
|
||||
require.NoError(json.Unmarshal(resp.Body(), token))
|
||||
assert.Equal(&delivery.VerificationResponse{
|
||||
Me: accessToken.Me,
|
||||
ClientID: accessToken.ClientID,
|
||||
Scope: strings.Join(accessToken.Scopes, " "),
|
||||
}, token)
|
||||
}
|
||||
|
||||
func TestRevocation(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require := require.New(t)
|
||||
assert := assert.New(t)
|
||||
repo := repository.NewMemoryTokenRepository()
|
||||
|
@ -45,7 +87,7 @@ func TestRevocation(t *testing.T) {
|
|||
resp := http.AcquireResponse()
|
||||
defer http.ReleaseResponse(resp)
|
||||
|
||||
require.NoError(util.Serve(delivery.NewRequestHandler(usecase.NewTokenUseCase(repo)).Revocation, req, resp))
|
||||
require.NoError(util.Serve(delivery.NewRequestHandler(usecase.NewTokenUseCase(repo)).Update, req, resp))
|
||||
assert.Equal(http.StatusOK, resp.StatusCode())
|
||||
assert.Equal(`{}`, strings.TrimSpace(string(resp.Body())))
|
||||
|
||||
|
|
|
@ -2,8 +2,11 @@ package token
|
|||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
)
|
||||
|
||||
type UseCase interface {
|
||||
Verify(ctx context.Context, token string) (*model.Token, error)
|
||||
Revoke(ctx context.Context, token string) error
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package usecase
|
|||
import (
|
||||
"context"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
"source.toby3d.me/website/oauth/internal/token"
|
||||
)
|
||||
|
||||
|
@ -16,6 +17,10 @@ func NewTokenUseCase(tokens token.Repository) token.UseCase {
|
|||
}
|
||||
}
|
||||
|
||||
func (useCase *tokenUseCase) Verify(ctx context.Context, token string) (*model.Token, error) {
|
||||
return useCase.tokens.Get(ctx, token)
|
||||
}
|
||||
|
||||
func (useCase *tokenUseCase) Revoke(ctx context.Context, token string) error {
|
||||
return useCase.tokens.Delete(ctx, token)
|
||||
}
|
||||
|
|
|
@ -13,6 +13,28 @@ import (
|
|||
"source.toby3d.me/website/oauth/internal/token/usecase"
|
||||
)
|
||||
|
||||
func TestVerify(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require := require.New(t)
|
||||
assert := assert.New(t)
|
||||
repo := repository.NewMemoryTokenRepository()
|
||||
ucase := usecase.NewTokenUseCase(repo)
|
||||
accessToken := &model.Token{
|
||||
AccessToken: gofakeit.Password(true, true, true, true, false, 32),
|
||||
Type: "Bearer",
|
||||
ClientID: "https://app.example.com/",
|
||||
Me: "https://user.example.net/",
|
||||
Scopes: []string{"create", "update", "delete"},
|
||||
}
|
||||
|
||||
require.NoError(repo.Create(context.TODO(), accessToken))
|
||||
|
||||
token, err := ucase.Verify(context.TODO(), accessToken.AccessToken)
|
||||
require.NoError(err)
|
||||
assert.Equal(accessToken, token)
|
||||
}
|
||||
|
||||
func TestRevoke(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
Loading…
Reference in New Issue