From 53098497a51574a74c9d106f77c4c0ffce1d2a93 Mon Sep 17 00:00:00 2001 From: Maxim Lebedev Date: Mon, 20 Sep 2021 23:25:08 +0500 Subject: [PATCH] :recycle: Refactored token verification --- internal/token/delivery/http/token_http.go | 45 ++++++++++++++++--- .../token/delivery/http/token_http_test.go | 44 +++++++++++++++++- internal/token/usecase.go | 3 ++ internal/token/usecase/token_usecase.go | 5 +++ internal/token/usecase/token_usecase_test.go | 22 +++++++++ 5 files changed, 113 insertions(+), 6 deletions(-) diff --git a/internal/token/delivery/http/token_http.go b/internal/token/delivery/http/token_http.go index ec5a903..94d3a2e 100644 --- a/internal/token/delivery/http/token_http.go +++ b/internal/token/delivery/http/token_http.go @@ -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 } } diff --git a/internal/token/delivery/http/token_http_test.go b/internal/token/delivery/http/token_http_test.go index 234747d..21acbec 100644 --- a/internal/token/delivery/http/token_http_test.go +++ b/internal/token/delivery/http/token_http_test.go @@ -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()))) diff --git a/internal/token/usecase.go b/internal/token/usecase.go index f7e4ae7..12ae9fb 100644 --- a/internal/token/usecase.go +++ b/internal/token/usecase.go @@ -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 } diff --git a/internal/token/usecase/token_usecase.go b/internal/token/usecase/token_usecase.go index e266447..4a00030 100644 --- a/internal/token/usecase/token_usecase.go +++ b/internal/token/usecase/token_usecase.go @@ -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) } diff --git a/internal/token/usecase/token_usecase_test.go b/internal/token/usecase/token_usecase_test.go index e50f595..54a7d68 100644 --- a/internal/token/usecase/token_usecase_test.go +++ b/internal/token/usecase/token_usecase_test.go @@ -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()