# HG changeset patch # User Peter Sanchez # Date 1682708020 21600 # Fri Apr 28 12:53:40 2023 -0600 # Node ID c85b38ec8fac52f710d56447f4e96a32b475470d # Parent ea3aaf68773d5c1bdb88a4116d597b086e17b220 Adding introspect handler diff --git a/bearer.go b/bearer.go --- a/bearer.go +++ b/bearer.go @@ -31,6 +31,7 @@ // BearerToken ... type BearerToken struct { Version uint + Issued Timestamp Expires Timestamp Grants string ClientID string @@ -171,7 +172,8 @@ // 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 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ 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 --- a/go.sum +++ b/go.sum @@ -627,8 +627,8 @@ 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 --- a/logic.go +++ b/logic.go @@ -43,10 +43,12 @@ 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 --- a/middleware.go +++ b/middleware.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "fmt" "strings" @@ -27,7 +26,7 @@ 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 --- a/routes.go +++ b/routes.go @@ -21,6 +21,8 @@ // 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 @@ 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)