Skip to content

Commit

Permalink
* warden: move IntrospectToken from warden sdk to oauth2 - closes #201
Browse files Browse the repository at this point in the history
* warden: rename InspectToken to IntrospectToken - closes #200
  • Loading branch information
Aeneas Rekkas (arekkas) committed Aug 17, 2016
1 parent 9896fc0 commit 90ed0e2
Show file tree
Hide file tree
Showing 11 changed files with 349 additions and 186 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
[![HTTP API Documentation](https://img.shields.io/badge/docs-http%20api-blue.svg)](http://docs.hdyra.apiary.io/)
[![Code Documentation](https://img.shields.io/badge/docs-godoc-blue.svg)](https://godoc.org/github.com/ory-am/hydra)

Hydra is being developed by german-based company [Ory](https://ory.am). Join our [newsletter](http://eepurl.com/bKT3N9) to stay on top of new developments. We respond to *basic support requests in our free time* on [Google Groups](https://groups.google.com/forum/#!forum/ory-hydra/new) and [Gitter](https://gitter.im/ory-am/hydra).

If you are looking for 24/7 enterprise support or SLAs, [contact us now](mailto:[email protected]).
Hydra is being developed by german-based company [Ory](https://ory.am).
Join our [newsletter](http://eepurl.com/bKT3N9) to stay on top of new developments.
We offer basic support requests on [Google Groups](https://groups.google.com/forum/#!forum/ory-hydra/new) and [Gitter](https://gitter.im/ory-am/hydra)
as well as [consulting](https://ory.am/products/hydra/enterprise) around integrating Hydra into
your particular environment and [24/7 support](https://ory.am/products/hydra/enterprise).

Hydra uses the security first OAuth2 and OpenID Connect SDK [Fosite](https://github.com/ory-am/fosite) and [Ladon](https://github.com/ory-am/ladon) for policy-based access control.

Expand Down
67 changes: 0 additions & 67 deletions firewall/introspector.go

This file was deleted.

19 changes: 13 additions & 6 deletions oauth2/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/ory-am/fosite"
"github.com/ory-am/hydra/herodot"
"github.com/ory-am/hydra/pkg"
"github.com/ory-am/hydra/firewall"
)

const (
Expand All @@ -23,11 +24,15 @@ const (
)

type Handler struct {
OAuth2 fosite.OAuth2Provider
Consent ConsentStrategy
ForcedHTTP bool
OAuth2 fosite.OAuth2Provider
Consent ConsentStrategy

ConsentURL url.URL
Introspector Introspector
Firewall firewall.Firewall
H herodot.Herodot

ForcedHTTP bool
ConsentURL url.URL
}

func (this *Handler) SetRoutes(r *httprouter.Router) {
Expand All @@ -39,8 +44,10 @@ func (this *Handler) SetRoutes(r *httprouter.Router) {
}

func (this *Handler) Introspect(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
var inactive = map[string]bool{"active": false}

ctx := herodot.NewContext()
clientCtx, err := this.Warden.TokenValid(ctx, TokenFromRequest(r))
clientCtx, err := this.Firewall.TokenValid(ctx, this.Firewall.TokenFromRequest(r))
if err != nil {
this.H.WriteError(ctx, w, r, err)
return
Expand All @@ -51,7 +58,7 @@ func (this *Handler) Introspect(w http.ResponseWriter, r *http.Request, _ httpro
return
}

auth, err := this.Warden.IntrospectToken(ctx, r.PostForm.Get("token"))
auth, err := this.Introspector.IntrospectToken(ctx, r.PostForm.Get("token"))
if err != nil {
this.H.Write(ctx, w, r, &inactive)
return
Expand Down
67 changes: 67 additions & 0 deletions oauth2/introspector.go
Original file line number Diff line number Diff line change
@@ -1 +1,68 @@
package oauth2

import "golang.org/x/net/context"

// Introspection contains an access token's session data as specified by IETF RFC 7662, see:
// https://tools.ietf.org/html/rfc7662
type Introspection struct {
// Active is a boolean indicator of whether or not the presented token
// is currently active. The specifics of a token's "active" state
// will vary depending on the implementation of the authorization
// server and the information it keeps about its tokens, but a "true"
// value return for the "active" property will generally indicate
// that a given token has been issued by this authorization server,
// has not been revoked by the resource owner, and is within its
// given time window of validity (e.g., after its issuance time and
// before its expiration time).
Active bool `json:"active"`

// Scope is a JSON string containing a space-separated list of
// scopes associated with this token
Scope string `json:"scope,omitempty"`

// ClientID is aclient identifier for the OAuth 2.0 client that
// requested this token.
ClientID string `json:"client_id,omitempty"`

// Subject of the token, as defined in JWT [RFC7519].
// Usually a machine-readable identifier of the resource owner who
// authorized this token.
Subject string `json:"sub,omitempty"`

// Expires at is an integer timestamp, measured in the number of seconds
// since January 1 1970 UTC, indicating when this token will expire
ExpiresAt int64 `json:"exp,omitempty"`

// Issued at is an integer timestamp, measured in the number of seconds
// since January 1 1970 UTC, indicating when this token was
// originally issued
IssuedAt int64 `json:"iat,omitempty"`

// NotBefore is an integer timestamp, measured in the number of seconds
// since January 1 1970 UTC, indicating when this token is not to be
// used before
NotBefore int64 `json:"nbf,omitempty"`

// Username is a human-readable identifier for the resource owner who
// authorized this token.
Username int64 `json:"username,omitempty"`

// Audience is a service-specific string identifier or list of string
// identifiers representing the intended audience for this token
Audience string `json:"aud,omitempty"`

// Issuer is a string representing the issuer of this token
Issuer string `json:"iss,omitempty"`
}

// Introspector is capable of introspecting an access token according to IETF RFC 7662, see:
// https://tools.ietf.org/html/rfc7662
type Introspector interface {
// IntrospectToken performs a token introspection according to IETF RFC 7662, see: https://tools.ietf.org/html/rfc7662
//
// func anyHttpHandler(w http.ResponseWriter, r *http.Request) {
// ctx, err := introspector.IntrospectToken(context.Background(), introspector.TokenFromRequest(r), "photos", "files")
// fmt.Sprintf("%s", ctx.Subject)
// }
IntrospectToken(ctx context.Context, token string) (*Introspection, error)
}
64 changes: 64 additions & 0 deletions oauth2/introspector_http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package oauth2

import (
"net/url"
"bytes"
"io/ioutil"
"net/http"
"golang.org/x/net/context"
"strconv"
"encoding/json"
"github.com/ory-am/fosite"
"golang.org/x/oauth2/clientcredentials"
"golang.org/x/oauth2"
"github.com/go-errors/errors"
)

type HTTPIntrospector struct {
Client *http.Client
Dry bool
Endpoint *url.URL
}

func (this *HTTPIntrospector) TokenFromRequest(r *http.Request) string {
return fosite.AccessTokenFromRequest(r)
}

func (this *HTTPIntrospector) SetClient(c *clientcredentials.Config) {
this.Client = c.Client(oauth2.NoContext)
}

// IntrospectToken is capable of introspecting tokens according to https://tools.ietf.org/html/rfc7662
//
// The HTTP API is documented at http://docs.hdyra.apiary.io/#reference/oauth2/oauth2-token-introspection
func (this *HTTPIntrospector) IntrospectToken(ctx context.Context, token string) (*Introspection, error) {
var resp = new(Introspection)
var ep = *this.Endpoint
ep.Path = IntrospectPath

data := url.Values{"token": []string{token}}
hreq, err := http.NewRequest("POST", ep.String(), bytes.NewBufferString(data.Encode()))
if err != nil {
return nil, errors.New(err)
}

hreq.Header.Add("Content-Type", "application/x-www-form-urlencoded")
hreq.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
hres, err := this.Client.Do(hreq)
if err != nil {
return nil, errors.New(err)
}
defer hres.Body.Close()

if hres.StatusCode < 200 || hres.StatusCode >= 300 {
body, _ := ioutil.ReadAll(hres.Body)
return nil, errors.Errorf("Expected 2xx status code but got %d.\n%s", hres.StatusCode, body)
} else if err := json.NewDecoder(hres.Body).Decode(resp); err != nil {
body, _ := ioutil.ReadAll(hres.Body)
return nil, errors.Errorf("%s: %s", err, body)
} else if !resp.Active {
return nil, errors.New("Token is malformed, expired or otherwise invalid")
}

return resp, nil
}
44 changes: 44 additions & 0 deletions oauth2/introspector_local.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package oauth2

import (
"github.com/ory-am/fosite"
"time"
"strings"
"net/http"
"golang.org/x/net/context"
"github.com/Sirupsen/logrus"
)

type LocalIntrospector struct {
OAuth2 fosite.OAuth2Provider

AccessTokenLifespan time.Duration
Issuer string
}

func (w *LocalIntrospector) TokenFromRequest(r *http.Request) string {
return fosite.AccessTokenFromRequest(r)
}

func (w *LocalIntrospector) IntrospectToken(ctx context.Context, token string) (*Introspection, error) {
var session = new(Session)
var auth, err = w.OAuth2.ValidateToken(ctx, token, fosite.AccessToken, session)
if err != nil {
logrus.WithError(err).Infof("Token introspection failed")
return &Introspection{
Active: false,
}, err
}

session = auth.GetSession().(*Session)
return &Introspection{
Active: true,
Subject: session.Subject,
Audience: auth.GetClient().GetID(),
Scope: strings.Join(auth.GetGrantedScopes(), " "),
Issuer: w.Issuer,
IssuedAt: auth.GetRequestedAt().Unix(),
NotBefore: auth.GetRequestedAt().Unix(),
ExpiresAt: session.AccessTokenExpiresAt(auth.GetRequestedAt().Add(w.AccessTokenLifespan)).Unix(),
}, nil
}
Loading

0 comments on commit 90ed0e2

Please sign in to comment.