Skip to content

Commit

Permalink
all: oauth and guard endpoints now accept basic auth instead of token…
Browse files Browse the repository at this point in the history
… auth.
  • Loading branch information
Aeneas Rekkas committed Dec 20, 2015
1 parent 57d5427 commit 7d6b191
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 205 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ node_modules/
*.iml
*.exe
.vagrant
*.log
*.log
*.stackdump
2 changes: 1 addition & 1 deletion cli/hydra-host/handler/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (c *Core) Start(ctx *cli.Context) error {
c.clientHandler = clients.NewHandler(c.Ctx.Osins, m)
c.connectionHandler = connections.NewHandler(c.Ctx.Connections, m)
c.providers = provider.NewRegistry(providers)
c.policyHandler = policies.NewHandler(c.Ctx.Policies, m, c.guard, j)
c.policyHandler = policies.NewHandler(c.Ctx.Policies, m, c.guard, j, c.Ctx.Osins)
c.oauthHandler = &oauth.Handler{
Accounts: c.Ctx.Accounts,
Policies: c.Ctx.Policies,
Expand Down
47 changes: 8 additions & 39 deletions client/http/client.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package http

import (
"bytes"
"encoding/json"
"fmt"
"github.com/RangelReale/osin"
Expand All @@ -14,7 +13,6 @@ import (
"golang.org/x/oauth2/clientcredentials"
"net/http"
"net/url"
"strconv"
)

var isAllowed struct {
Expand Down Expand Up @@ -64,33 +62,15 @@ func (c *HTTPClient) IsAuthenticated(token string) (bool, error) {
}

func isValidAuthenticationRequest(c *HTTPClient, token string, retry bool) (bool, error) {
if c.clientToken == nil {
if ct, err := c.clientConfig.Token(oauth2.NoContext); err != nil {
return false, errors.New(err)
} else if ct == nil {
return false, errors.New("Access token could not be retrieved")
} else {
c.clientToken = ct
}
return isValidAuthenticationRequest(c, token, false)
}

data := url.Values{}
client := &http.Client{}
data.Set("token", token)
r, err := http.NewRequest("POST", pkg.JoinURL(c.ep, "/oauth2/introspect"), bytes.NewBufferString(data.Encode()))
if err != nil {
return false, err
}

r.Header.Add("Authorization", c.clientToken.Type()+" "+c.clientToken.AccessToken)
r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
r.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
resp, err := client.Do(r)
if err != nil {
return false, err
request := gorequest.New()
resp, body, errs := request.Post(pkg.JoinURL(c.ep, "/oauth2/introspect")).Type("form").SetBasicAuth(c.clientConfig.ClientID, c.clientConfig.ClientSecret).SendString(data.Encode()).End()
if len(errs) > 0 {
return false, errors.Errorf("Got errors: %v", errs)
} else if resp.StatusCode != http.StatusOK {
return false, errors.Errorf("Status code %d is not 200: %s", resp.StatusCode, body)
}
defer resp.Body.Close()

if retry && resp.StatusCode == http.StatusUnauthorized {
var err error
Expand All @@ -108,7 +88,7 @@ func isValidAuthenticationRequest(c *HTTPClient, token string, retry bool) (bool
Active bool `json:"active"`
}

if err := json.NewDecoder(resp.Body).Decode(&introspect); err != nil {
if err := json.Unmarshal([]byte(body), &introspect); err != nil {
return false, err
} else if !introspect.Active {
return false, errors.New("Authentication denied")
Expand All @@ -117,19 +97,8 @@ func isValidAuthenticationRequest(c *HTTPClient, token string, retry bool) (bool
}

func isValidAuthorizeRequest(c *HTTPClient, ar *AuthorizeRequest, retry bool) (bool, error) {
if c.clientToken == nil {
if ct, err := c.clientConfig.Token(oauth2.NoContext); err != nil {
return false, errors.New(err)
} else if ct == nil {
return false, errors.New("Access token could not be retrieved")
} else {
c.clientToken = ct
}
return isValidAuthorizeRequest(c, ar, false)
}

request := gorequest.New()
resp, body, errs := request.Post(pkg.JoinURL(c.ep, "/guard/allowed")).Set("Authorization", c.clientToken.Type()+" "+c.clientToken.AccessToken).Set("Content-Type", "application/json").Send(*ar).End()
resp, body, errs := request.Post(pkg.JoinURL(c.ep, "/guard/allowed")).SetBasicAuth(c.clientConfig.ClientID, c.clientConfig.ClientSecret).Set("Content-Type", "application/json").Send(*ar).End()
if len(errs) > 0 {
return false, errors.Errorf("Got errors: %v", errs)
} else if retry && resp.StatusCode == http.StatusUnauthorized {
Expand Down
131 changes: 12 additions & 119 deletions client/http/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package http_test

import (
"encoding/json"
"fmt"
"github.com/go-errors/errors"
"github.com/gorilla/mux"
"github.com/ory-am/common/pkg"
Expand Down Expand Up @@ -33,8 +32,8 @@ func tokenHandler(rw http.ResponseWriter, req *http.Request) {
func TestIsRequestAllowed(t *testing.T) {
router := mux.NewRouter()
router.HandleFunc("/guard/allowed", func(rw http.ResponseWriter, req *http.Request) {
if req.Header.Get("Authorization") != "Bearer fetch-token-ok" {
http.Error(rw, "", http.StatusUnauthorized)
if req.Header.Get("Authorization") != "Basic YXBwOmtleQ==" {
http.Error(rw, req.Header.Get("Authorization"), http.StatusUnauthorized)
return
}
pkg.WriteJSON(rw, struct {
Expand All @@ -45,125 +44,19 @@ func TestIsRequestAllowed(t *testing.T) {
ts := httptest.NewServer(router)
defer ts.Close()

c := New(ts.URL, "irrelevant", "irrelephant")
c := New(ts.URL, "app", "key")
c.SetClientToken(&oauth2.Token{TokenType: "bearer", AccessToken: "foobar"})
allowed, err := c.IsRequestAllowed(&http.Request{Header: http.Header{"Authorization": []string{"Bearer token"}}}, "", "", "")
assert.Nil(t, err)
assert.True(t, allowed)
}

func TestIsAllowedRetriesOnlyOnceWhenTokenIsInvalid(t *testing.T) {
var count int
router := mux.NewRouter()
router.HandleFunc("/oauth2/token", tokenHandler).Methods("POST")
router.HandleFunc("/guard/allowed", func(rw http.ResponseWriter, req *http.Request) {
http.Error(rw, fmt.Sprintf("token invalid try ", count), http.StatusUnauthorized)
count++
}).Methods("POST")
ts := httptest.NewServer(router)
defer ts.Close()
c := New(ts.URL, "irrelevant", "irrelephant")
c.SetClientToken(&oauth2.Token{TokenType: "bearer", AccessToken: "foobar"})

allowed, err := c.IsAllowed(&AuthorizeRequest{
Permission: "foo",
Token: "bar",
Resource: "res",
Context: &operator.Context{
Owner: "foo",
},
})
assert.NotNil(t, err)
assert.False(t, allowed)
assert.Equal(t, 2, count)
}

func TestIsAllowedRetriesWhenTokenIsExpired(t *testing.T) {
var try int
router := mux.NewRouter()
router.HandleFunc("/guard/allowed", func(rw http.ResponseWriter, req *http.Request) {
try++
if try == 1 {
t.Logf("token invalid try ", try)
http.Error(rw, fmt.Sprintf("token invalid try ", try), http.StatusUnauthorized)
return
}

pkg.WriteJSON(rw, struct {
Allowed bool `json:"allowed"`
}{Allowed: true})
}).Methods("POST")
router.HandleFunc("/oauth2/token", tokenHandler).Methods("POST")
ts := httptest.NewServer(router)
defer ts.Close()

c := New(ts.URL, "irrelevant", "irrelephant")
c.SetClientToken(&oauth2.Token{TokenType: "bearer", AccessToken: "foobar"})

allowed, err := c.IsAllowed(&AuthorizeRequest{
Permission: "foo",
Token: "bar",
Resource: "res",
Context: &operator.Context{
Owner: "foo",
},
})
assert.Nil(t, err, "%s", err)
assert.True(t, allowed)
assert.Equal(t, 2, try)
}

func TestIsAuthenticatedRetriesWhenTokenIsExpired(t *testing.T) {
var try int
router := mux.NewRouter()
router.HandleFunc("/oauth2/introspect", func(rw http.ResponseWriter, req *http.Request) {
try++
if try == 1 {
http.Error(rw, fmt.Sprintf("token invalid try ", try), http.StatusUnauthorized)
return
}

pkg.WriteJSON(rw, struct {
Active bool `json:"active"`
}{Active: true})
}).Methods("POST")
router.HandleFunc("/oauth2/token", tokenHandler).Methods("POST")
ts := httptest.NewServer(router)
defer ts.Close()

c := New(ts.URL, "irrelevant", "irrelephant")
c.SetClientToken(&oauth2.Token{TokenType: "bearer", AccessToken: "client"})
active, err := c.IsAuthenticated("federated.token")
assert.Nil(t, err, "%s", err)
assert.True(t, active)
assert.Equal(t, 2, try)
}

func TestIsAuthenticatedRetriesOnlyOnceWhenTokenIsExpired(t *testing.T) {
var count int
router := mux.NewRouter()
router.HandleFunc("/oauth2/introspect", func(rw http.ResponseWriter, req *http.Request) {
count++
http.Error(rw, fmt.Sprintf("token invalid try ", count), http.StatusUnauthorized)
}).Methods("POST")
router.HandleFunc("/oauth2/token", tokenHandler).Methods("POST")
ts := httptest.NewServer(router)
defer ts.Close()

c := New(ts.URL, "irrelevant", "irrelephant")
c.SetClientToken(&oauth2.Token{TokenType: "bearer", AccessToken: "client"})
active, err := c.IsAuthenticated("federated.token")
assert.NotNil(t, err)
assert.False(t, active)
assert.Equal(t, 2, count)
}

func TestIsAllowed(t *testing.T) {
router := mux.NewRouter()
router.HandleFunc("/oauth2/token", tokenHandler).Methods("POST")
router.HandleFunc("/guard/allowed", func(rw http.ResponseWriter, req *http.Request) {
if req.Header.Get("Authorization") != "Bearer fetch-token-ok" {
http.Error(rw, "", http.StatusUnauthorized)
if req.Header.Get("Authorization") != "Basic YXBwOmtleQ==" {
http.Error(rw, req.Header.Get("Authorization"), http.StatusUnauthorized)
return
}
var p handler.GrantedPayload
Expand All @@ -185,9 +78,9 @@ func TestIsAllowed(t *testing.T) {
ts := httptest.NewServer(router)
defer ts.Close()

c := New(ts.URL, "irrelevant", "irrelephant")
c := New(ts.URL, "app", "key")
allowed, err := c.IsAllowed(&AuthorizeRequest{Permission: "foo", Token: "bar", Resource: "res", Context: &operator.Context{Owner: "foo"}})
assert.Nil(t, err)
assert.Nil(t, err, "%s", err)
assert.True(t, allowed)
}

Expand All @@ -196,8 +89,8 @@ func TestIsAuthenticated(t *testing.T) {
called := false
router.HandleFunc("/oauth2/token", tokenHandler).Methods("POST")
router.HandleFunc("/oauth2/introspect", func(rw http.ResponseWriter, req *http.Request) {
if req.Header.Get("Authorization") != "Bearer fetch-token-ok" {
http.Error(rw, "", http.StatusUnauthorized)
if req.Header.Get("Authorization") != "Basic YXBwOmtleQ==" {
http.Error(rw, req.Header.Get("Authorization"), http.StatusUnauthorized)
return
}
req.ParseForm()
Expand All @@ -210,9 +103,9 @@ func TestIsAuthenticated(t *testing.T) {
ts := httptest.NewServer(router)
defer ts.Close()

c := New(ts.URL, "irrelevant", "irrelephant")
active, err := c.IsAuthenticated("federated.token")
assert.Nil(t, err)
c := New(ts.URL, "app", "key")
active, err := c.IsAuthenticated("some.token")
assert.Nil(t, err, "%s", err)
assert.True(t, active)
assert.True(t, called)
}
28 changes: 19 additions & 9 deletions oauth/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package handler

import (
"encoding/json"
"errors"
"fmt"
"github.com/RangelReale/osin"
log "github.com/Sirupsen/logrus"
"github.com/go-errors/errors"
"github.com/gorilla/mux"
hctx "github.com/ory-am/common/handler"
"github.com/ory-am/common/pkg"
Expand Down Expand Up @@ -77,7 +77,7 @@ func (h *Handler) SetRoutes(r *mux.Router, extractor func(h hctx.ContextHandler)
func (h *Handler) RevokeHandler(w http.ResponseWriter, r *http.Request) {
auth, err := osin.CheckBasicAuth(r)
if err != nil {
pkg.HttpError(w, errors.New("Unauthorized"), http.StatusServiceUnavailable)
pkg.HttpError(w, errors.New("Unauthorized"), http.StatusUnauthorized)
return
} else if auth == nil {
pkg.HttpError(w, errors.New("Unauthorized"), http.StatusUnauthorized)
Expand All @@ -86,7 +86,10 @@ func (h *Handler) RevokeHandler(w http.ResponseWriter, r *http.Request) {

client, err := h.OAuthStore.GetClient(auth.Username)
if err != nil {
pkg.HttpError(w, errors.New("Unauthorized"), http.StatusServiceUnavailable)
pkg.HttpError(w, errors.New("Unauthorized"), http.StatusUnauthorized)
return
} else if client.GetSecret() != auth.Password {
pkg.HttpError(w, errors.New("Unauthorized"), http.StatusUnauthorized)
return
}

Expand Down Expand Up @@ -161,6 +164,15 @@ func (h *Handler) IntrospectHandler(w http.ResponseWriter, r *http.Request) {
return
}

client, err := h.OAuthStore.GetClient(auth.Username)
if err != nil {
pkg.HttpError(w, errors.New("Unauthorized"), http.StatusUnauthorized)
return
} else if client.GetSecret() != auth.Password {
pkg.HttpError(w, errors.New("Unauthorized"), http.StatusUnauthorized)
return
}

result := make(map[string]interface{})
result["active"] = false

Expand All @@ -180,11 +192,11 @@ func (h *Handler) IntrospectHandler(w http.ResponseWriter, r *http.Request) {
}

claims := jwt.ClaimsCarrier(token.Claims)
if claims.GetAudience() != h.Audience {
if claims.GetAudience() != auth.Username {
log.WithFields(log.Fields{
"introspect": "fail",
"expted": h.Audience,
"actual": claims.GetAudience(),
"introspect": "fail",
"actualAudience": auth.Username,
"expectedAudience": claims.GetAudience(),
}).Warn(`Token audience mismatch.`)
pkg.WriteJSON(w, result)
return
Expand All @@ -193,8 +205,6 @@ func (h *Handler) IntrospectHandler(w http.ResponseWriter, r *http.Request) {
if claims.GetSubject() == "" {
log.WithFields(log.Fields{
"introspect": "fail",
"expted": h.Audience,
"actual": claims.GetAudience(),
}).Warn(`Token claims no subject.`)
pkg.WriteJSON(w, result)
return
Expand Down
Loading

0 comments on commit 7d6b191

Please sign in to comment.