Skip to content

Commit

Permalink
oauth2: Requires firewall check for introspecting access tokens (#678)
Browse files Browse the repository at this point in the history
  • Loading branch information
arekkas authored Dec 7, 2017
1 parent c2c9c84 commit f5b6558
Show file tree
Hide file tree
Showing 24 changed files with 216 additions and 114 deletions.
6 changes: 3 additions & 3 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@

[[constraint]]
name = "github.com/ory/fosite"
version = "0.13.1"
version = "0.14.1"

[[constraint]]
name = "github.com/ory/graceful"
Expand Down
2 changes: 2 additions & 0 deletions cmd/server/handler_oauth2_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ func newOAuth2Handler(c *config.Config, router *httprouter.Router, cm oauth2.Con
CookieStore: sessions.NewCookieStore(c.GetCookieSecret()),
Issuer: c.Issuer,
L: c.GetLogger(),
W: c.Context().Warden,
ResourcePrefix: c.AccessControlResourcePrefix,
}

handler.SetRoutes(router)
Expand Down
11 changes: 9 additions & 2 deletions compose/firewall.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

"github.com/ory/fosite"
foauth2 "github.com/ory/fosite/handler/oauth2"
"github.com/ory/fosite/storage"
"github.com/ory/hydra/firewall"
. "github.com/ory/hydra/oauth2"
"github.com/ory/hydra/pkg"
Expand All @@ -31,9 +32,13 @@ import (
)

func NewMockFirewall(issuer string, subject string, scopes fosite.Arguments, p ...ladon.Policy) (firewall.Firewall, *http.Client) {
return NewMockFirewallWithStore(issuer, subject, scopes, pkg.FositeStore(), p...)
}

func NewMockFirewallWithStore(issuer string, subject string, scopes fosite.Arguments, storage *storage.MemoryStore, p ...ladon.Policy) (firewall.Firewall, *http.Client) {
tokens := pkg.Tokens(1)

fositeStore := pkg.FositeStore()
fositeStore := storage
ps := map[string]ladon.Policy{}

for _, x := range p {
Expand All @@ -46,6 +51,8 @@ func NewMockFirewall(issuer string, subject string, scopes fosite.Arguments, p .
fositeStore.CreateAccessTokenSession(nil, tokens[0][0], ar)

conf := &oauth2.Config{Scopes: scopes, Endpoint: oauth2.Endpoint{}}
l := logrus.New()
l.Level = logrus.DebugLevel

return &warden.LocalWarden{
Warden: ladonWarden,
Expand All @@ -63,7 +70,7 @@ func NewMockFirewall(issuer string, subject string, scopes fosite.Arguments, p .
Issuer: issuer,
AccessTokenLifespan: time.Hour,
Groups: group.NewMemoryManager(),
L: logrus.New(),
L: l,
}, conf.Client(oauth2.NoContext, &oauth2.Token{
AccessToken: tokens[0][1],
Expiry: time.Now().Add(time.Hour),
Expand Down
23 changes: 14 additions & 9 deletions docs/api.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -1014,7 +1014,7 @@
},
"/oauth2/introspect": {
"post": {
"description": "The introspection endpoint allows to check if a token (both refresh and access) is active or not. An active token\nis neither expired nor revoked. If a token is active, additional information on the token will be included. You can\nset additional data for a token by setting `accessTokenExtra` during the consent flow.",
"description": "The introspection endpoint allows to check if a token (both refresh and access) is active or not. An active token\nis neither expired nor revoked. If a token is active, additional information on the token will be included. You can\nset additional data for a token by setting `accessTokenExtra` during the consent flow.\n\n```\n{\n\"resources\": [\"rn:hydra:oauth2:tokens\"],\n\"actions\": [\"introspect\"],\n\"effect\": \"allow\"\n}\n```",
"consumes": [
"application/x-www-form-urlencoded"
],
Expand All @@ -1035,7 +1035,9 @@
"basic": []
},
{
"oauth2": []
"oauth2": [
"hydra.introspect"
]
}
],
"parameters": [
Expand Down Expand Up @@ -1847,8 +1849,6 @@
},
"expiresAt": {
"description": "ExpiresAt is the time where the access request will expire.",
"type": "string",
"format": "date-time",
"x-go-name": "ExpiresAt"
},
"id": {
Expand Down Expand Up @@ -1895,8 +1895,6 @@
},
"expiresAt": {
"description": "ExpiresAt is the expiry timestamp.",
"type": "string",
"format": "date-time",
"x-go-name": "ExpiresAt"
},
"grantedScopes": {
Expand All @@ -1909,8 +1907,6 @@
},
"issuedAt": {
"description": "IssuedAt is the token creation time stamp.",
"type": "string",
"format": "date-time",
"x-go-name": "IssuedAt"
},
"issuer": {
Expand All @@ -1934,17 +1930,26 @@
"Handler": {
"type": "object",
"properties": {
"Generators": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/KeyGenerator"
}
},
"H": {
"$ref": "#/definitions/Writer"
},
"Manager": {
"$ref": "#/definitions/Manager"
},
"ResourcePrefix": {
"type": "string"
},
"W": {
"$ref": "#/definitions/Firewall"
}
},
"x-go-package": "github.com/ory/hydra/warden/group"
"x-go-package": "github.com/ory/hydra/jwk"
},
"KeyGenerator": {
"type": "object",
Expand Down
42 changes: 38 additions & 4 deletions oauth2/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ package oauth2

import (
"encoding/json"
"fmt"
"net"
"net/http"
"net/url"
"strings"

"github.com/julienschmidt/httprouter"
"github.com/ory/fosite"
"github.com/ory/hydra/firewall"
"github.com/ory/hydra/pkg"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
Expand All @@ -42,6 +44,8 @@ const (
IntrospectPath = "/oauth2/introspect"
RevocationPath = "/oauth2/revoke"

IntrospectScope = "hydra.introspect"

consentCookieName = "consent_session"
)

Expand Down Expand Up @@ -175,6 +179,14 @@ func (h *Handler) RevocationHandler(w http.ResponseWriter, r *http.Request, _ ht
// is neither expired nor revoked. If a token is active, additional information on the token will be included. You can
// set additional data for a token by setting `accessTokenExtra` during the consent flow.
//
// ```
// {
// "resources": ["rn:hydra:oauth2:tokens"],
// "actions": ["introspect"],
// "effect": "allow"
// }
// ```
//
// Consumes:
// - application/x-www-form-urlencoded
//
Expand All @@ -185,13 +197,36 @@ func (h *Handler) RevocationHandler(w http.ResponseWriter, r *http.Request, _ ht
//
// Security:
// basic:
// oauth2:
// oauth2: hydra.introspect
//
// Responses:
// 200: introspectOAuth2TokenResponse
// 401: genericError
// 500: genericError
func (h *Handler) IntrospectHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
if token := h.W.TokenFromRequest(r); token != "" {
if _, err := h.W.TokenAllowed(r.Context(), token, &firewall.TokenAccessRequest{
Resource: fmt.Sprintf(h.PrefixResource("oauth2:tokens")),
Action: "introspect",
}, IntrospectScope); err != nil {
h.H.WriteError(w, r, err)
return
}
} else if client, _, ok := r.BasicAuth(); ok {
// If no token is given, we do not need a scope.
if err := h.W.IsAllowed(r.Context(), &firewall.AccessRequest{
Subject: client,
Resource: fmt.Sprintf(h.PrefixResource("oauth2:tokens")),
Action: "introspect",
}); err != nil {
h.H.WriteError(w, r, err)
return
}
} else {
h.H.WriteError(w, r, errors.WithStack(fosite.ErrRequestUnauthorized))
return
}

var session = NewSession("")

var ctx = fosite.NewContext()
Expand All @@ -208,7 +243,7 @@ func (h *Handler) IntrospectHandler(w http.ResponseWriter, r *http.Request, _ ht
}

w.Header().Set("Content-Type", "application/json;charset=UTF-8")
err = json.NewEncoder(w).Encode(&Introspection{
if err = json.NewEncoder(w).Encode(&Introspection{
Active: true,
ClientID: resp.GetAccessRequester().GetClient().GetID(),
Scope: strings.Join(resp.GetAccessRequester().GetGrantedScopes(), " "),
Expand All @@ -219,8 +254,7 @@ func (h *Handler) IntrospectHandler(w http.ResponseWriter, r *http.Request, _ ht
Extra: resp.GetAccessRequester().GetSession().(*Session).Extra,
Audience: resp.GetAccessRequester().GetClient().GetID(),
Issuer: h.Issuer,
})
if err != nil {
}); err != nil {
pkg.LogError(err, h.L)
}
}
Expand Down
17 changes: 17 additions & 0 deletions oauth2/handler_struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/gorilla/sessions"
"github.com/ory/fosite"
"github.com/ory/herodot"
"github.com/ory/hydra/firewall"
"github.com/sirupsen/logrus"
)

Expand All @@ -41,4 +42,20 @@ type Handler struct {
ScopeStrategy fosite.ScopeStrategy

Issuer string

W firewall.Firewall

ResourcePrefix string
}

func (h *Handler) PrefixResource(resource string) string {
if h.ResourcePrefix == "" {
h.ResourcePrefix = "rn:hydra"
}

if h.ResourcePrefix[len(h.ResourcePrefix)-1] == ':' {
h.ResourcePrefix = h.ResourcePrefix[:len(h.ResourcePrefix)-1]
}

return h.ResourcePrefix + ":" + resource
}
Loading

0 comments on commit f5b6558

Please sign in to comment.