♻️ Refactored token verification
This commit is contained in:
parent
451da06bc6
commit
53098497a5
|
@ -1,6 +1,7 @@
|
||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fasthttp/router"
|
"github.com/fasthttp/router"
|
||||||
|
@ -22,6 +23,12 @@ type (
|
||||||
Token string
|
Token string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VerificationResponse struct {
|
||||||
|
Me string `json:"me"`
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
Scope string `json:"scope"`
|
||||||
|
}
|
||||||
|
|
||||||
RevocationResponse struct{}
|
RevocationResponse struct{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,12 +44,42 @@ func NewRequestHandler(useCase token.UseCase) *RequestHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *RequestHandler) Register(r *router.Router) {
|
func (h *RequestHandler) Register(r *router.Router) {
|
||||||
|
r.GET("/token", h.Read)
|
||||||
r.POST("/token", h.Update)
|
r.POST("/token", h.Update)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *RequestHandler) Update(ctx *http.RequestCtx) {
|
func (h *RequestHandler) Read(ctx *http.RequestCtx) {
|
||||||
ctx.SetStatusCode(http.StatusBadRequest)
|
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)) {
|
switch string(ctx.FormValue(Action)) {
|
||||||
case ActionRevoke:
|
case ActionRevoke:
|
||||||
h.Revocation(ctx)
|
h.Revocation(ctx)
|
||||||
|
@ -70,11 +107,9 @@ func (h *RequestHandler) Revocation(ctx *http.RequestCtx) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := encoder.Encode(RevocationResponse{}); err != nil {
|
if err := encoder.Encode(&RevocationResponse{}); err != nil {
|
||||||
ctx.SetStatusCode(http.StatusInternalServerError)
|
ctx.SetStatusCode(http.StatusInternalServerError)
|
||||||
encoder.Encode(err)
|
encoder.Encode(err)
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/brianvoe/gofakeit"
|
"github.com/brianvoe/gofakeit"
|
||||||
|
"github.com/goccy/go-json"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
http "github.com/valyala/fasthttp"
|
http "github.com/valyala/fasthttp"
|
||||||
|
@ -18,7 +19,48 @@ import (
|
||||||
"source.toby3d.me/website/oauth/internal/util"
|
"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) {
|
func TestRevocation(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
repo := repository.NewMemoryTokenRepository()
|
repo := repository.NewMemoryTokenRepository()
|
||||||
|
@ -45,7 +87,7 @@ func TestRevocation(t *testing.T) {
|
||||||
resp := http.AcquireResponse()
|
resp := http.AcquireResponse()
|
||||||
defer http.ReleaseResponse(resp)
|
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(http.StatusOK, resp.StatusCode())
|
||||||
assert.Equal(`{}`, strings.TrimSpace(string(resp.Body())))
|
assert.Equal(`{}`, strings.TrimSpace(string(resp.Body())))
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,11 @@ package token
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"source.toby3d.me/website/oauth/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UseCase interface {
|
type UseCase interface {
|
||||||
|
Verify(ctx context.Context, token string) (*model.Token, error)
|
||||||
Revoke(ctx context.Context, token string) error
|
Revoke(ctx context.Context, token string) error
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package usecase
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"source.toby3d.me/website/oauth/internal/model"
|
||||||
"source.toby3d.me/website/oauth/internal/token"
|
"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 {
|
func (useCase *tokenUseCase) Revoke(ctx context.Context, token string) error {
|
||||||
return useCase.tokens.Delete(ctx, token)
|
return useCase.tokens.Delete(ctx, token)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,28 @@ import (
|
||||||
"source.toby3d.me/website/oauth/internal/token/usecase"
|
"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) {
|
func TestRevoke(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user