From a54d2dc13b2acb8baf44a19439a958f0caeb130e Mon Sep 17 00:00:00 2001 From: Peter Sanchez Date: Fri, 28 Apr 2023 12:53:40 -0600 Subject: [PATCH] Adding introspect handler --- bearer.go | 8 +++++--- go.mod | 2 +- go.sum | 4 ++-- logic.go | 8 +++++--- middleware.go | 3 +-- routes.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 63 insertions(+), 11 deletions(-) diff --git a/bearer.go b/bearer.go index 9e20d19..5e51d9f 100644 --- a/bearer.go +++ b/bearer.go @@ -31,6 +31,7 @@ func ToTimestamp(t time.Time) Timestamp { // BearerToken ... type BearerToken struct { Version uint + Issued Timestamp Expires Timestamp Grants string ClientID string @@ -171,7 +172,8 @@ func (g *Grants) Encode() string { // TokenUser wrapper for gobwebs.User and token grants type TokenUser struct { - User gobwebs.User - Token *BearerToken - Grants *Grants + User gobwebs.User + Token *BearerToken + Grants *Grants + TokenHash [64]byte } diff --git a/go.mod b/go.mod index be970c6..9369cbd 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/Masterminds/squirrel v1.5.4 github.com/labstack/echo/v4 v4.10.2 github.com/segmentio/ksuid v1.0.4 - hg.code.netlandish.com/~netlandish/gobwebs v0.0.0-20230426140251-ec705d3e3fa6 + hg.code.netlandish.com/~netlandish/gobwebs v0.0.0-20230428122420-009ed0d86738 ) require ( diff --git a/go.sum b/go.sum index 6b40d1f..a079068 100644 --- a/go.sum +++ b/go.sum @@ -627,8 +627,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -hg.code.netlandish.com/~netlandish/gobwebs v0.0.0-20230426140251-ec705d3e3fa6 h1:pd2wi4TjKWcPb13/SgTi8FSHSZjeVnPxbLC/F4k3I/4= -hg.code.netlandish.com/~netlandish/gobwebs v0.0.0-20230426140251-ec705d3e3fa6/go.mod h1:+gStIFMqfW/W36PtW25x86MFS0FtXzbhnltOcB6hNf4= +hg.code.netlandish.com/~netlandish/gobwebs v0.0.0-20230428122420-009ed0d86738 h1:y3ywBE43YyBWjV32gHyfJArK6Xj89damOXDzWUTZ8AQ= +hg.code.netlandish.com/~netlandish/gobwebs v0.0.0-20230428122420-009ed0d86738/go.mod h1:+gStIFMqfW/W36PtW25x86MFS0FtXzbhnltOcB6hNf4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/logic.go b/logic.go index 1cfb22b..158d091 100644 --- a/logic.go +++ b/logic.go @@ -43,10 +43,12 @@ func OAuth2(ctx context.Context, token string, fetch gobwebs.UserFetch) (*TokenU return nil, fmt.Errorf("Invalid or expired OAuth 2.0 bearer token") } + bt.Issued = ToTimestamp(grant.Issued) gt := DecodeGrants(bt.Grants) return &TokenUser{ - User: user, - Token: bt, - Grants: >, + User: user, + Token: bt, + Grants: >, + TokenHash: hash, }, nil } diff --git a/middleware.go b/middleware.go index fd5afca..dd3497f 100644 --- a/middleware.go +++ b/middleware.go @@ -2,7 +2,6 @@ package oauth2 import ( "context" - "errors" "fmt" "strings" @@ -27,7 +26,7 @@ func Context(ctx context.Context, tuser *TokenUser) context.Context { func ForContext(ctx context.Context) *TokenUser { tuser, ok := ctx.Value(tuserCtxKey).(*TokenUser) if !ok { - panic(errors.New("invalid user context")) + return nil } return tuser } diff --git a/routes.go b/routes.go index 36efd7c..35f7e05 100644 --- a/routes.go +++ b/routes.go @@ -21,6 +21,8 @@ type Service struct { // RegisterRoutes ... func (s *Service) RegisterRoutes() { + s.eg.POST("/introspect", s.Introspect).Name = s.RouteName("introspect_post") + s.eg.Use(auth.AuthRequired()) s.eg.GET("/clients", s.ListClients).Name = s.RouteName("list_clients") s.eg.GET("/clients/add", s.AddClient).Name = s.RouteName("add_client") @@ -79,6 +81,53 @@ func (s *Service) AddClient(c echo.Context) error { return gctx.Render(http.StatusOK, "oauth2_add_client.html", gmap) } +// Introspect ... +func (s *Service) Introspect(c echo.Context) error { + req := c.Request() + ctype := req.Header.Get("Content-Type") + if ctype != "application/x-www-form-urlencoded" { + retErr := struct { + err string `json:"error"` + desc string `json:"error_description"` + uri string `json:"error_url"` // TODO Make this customizable + }{ + err: "invalid request", + desc: "Content-Type must be application/x-www-form-urlencoded", + } + return c.JSON(http.StatusBadRequest, &retErr) + } + + retFalse := struct { + active bool `json:"active"` + }{false} + + token := ForContext(req.Context()) + if token == nil { + return c.JSON(http.StatusOK, retFalse) + } + + if token.Token.ClientID == "" { + return c.JSON(http.StatusOK, retFalse) + } + + ret := struct { + active bool `json:"active"` + clientID string `json:"client_id"` + username string `json:"username"` + tokenType string `json:"token_type"` + exp int `json:"exp"` + iat int `json:"iat"` + }{ + active: true, + clientID: token.Token.ClientID, + username: token.User.GetEmail(), + tokenType: "bearer", + exp: int(token.Token.Expires), + iat: int(token.Token.Issued), + } + return c.JSON(http.StatusOK, ret) +} + // RouteName ... func (s *Service) RouteName(value string) string { return fmt.Sprintf("%s:%s", s.name, value) -- 2.45.2