Skip to content

Commit

Permalink
handler: flatten packages closes #70
Browse files Browse the repository at this point in the history
openid: don't autogrant openid scope - closes #68

all: clean up scopes / arguments - closes #66

all: composable factories - closes #64

all: refactor token validation - closes #63

all: remove mandatory scope - closes #62
  • Loading branch information
Aeneas Rekkas (arekkas) committed Aug 6, 2016
1 parent 2429044 commit f41216c
Show file tree
Hide file tree
Showing 22 changed files with 78 additions and 55 deletions.
5 changes: 5 additions & 0 deletions compose/compose_oauth2.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func OAuth2AuthorizeExplicitFactory(config *Config, storage interface{}, strateg
CoreValidator: &oauth2.CoreValidator{
CoreStrategy: strategy.(oauth2.CoreStrategy),
CoreStorage: storage.(oauth2.CoreStorage),
ScopeStrategy: fosite.HierarchicScopeStrategy,
},
}
}
Expand All @@ -46,6 +47,7 @@ func OAuth2ClientCredentialsGrantFactory(config *Config, storage interface{}, st
CoreValidator: &oauth2.CoreValidator{
CoreStrategy: strategy.(oauth2.CoreStrategy),
CoreStorage: storage.(oauth2.CoreStorage),
ScopeStrategy: fosite.HierarchicScopeStrategy,
},
}
}
Expand All @@ -66,6 +68,7 @@ func OAuth2RefreshTokenGrantFactory(config *Config, storage interface{}, strateg
CoreValidator: &oauth2.CoreValidator{
CoreStrategy: strategy.(oauth2.CoreStrategy),
CoreStorage: storage.(oauth2.CoreStorage),
ScopeStrategy: fosite.HierarchicScopeStrategy,
},
}
}
Expand All @@ -86,6 +89,7 @@ func OAuth2AuthorizeImplicitFactory(config *Config, storage interface{}, strateg
CoreValidator: &oauth2.CoreValidator{
CoreStrategy: strategy.(oauth2.CoreStrategy),
CoreStorage: storage.(oauth2.CoreStorage),
ScopeStrategy: fosite.HierarchicScopeStrategy,
},
}
}
Expand All @@ -109,6 +113,7 @@ func OAuth2ResourceOwnerPasswordCredentialsFactory(config *Config, storage inter
CoreValidator: &oauth2.CoreValidator{
CoreStrategy: strategy.(oauth2.CoreStrategy),
CoreStorage: storage.(oauth2.CoreStorage),
ScopeStrategy: fosite.HierarchicScopeStrategy,
},
}
}
2 changes: 1 addition & 1 deletion errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var (
ErrInvalidClient = errors.New("Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method)")
ErrInvalidState = errors.Errorf("The state is missing or has less than %d characters and is therefore considered too weak", MinParameterEntropy)
ErrInsufficientEntropy = errors.Errorf("The request used a security parameter (e.g., anti-replay, anti-csrf) with insufficient entropy (minimum of %d characters)", MinParameterEntropy)
ErrMisconfiguration = errors.New("The request failed because of a misconfiguration")
ErrMisconfiguration = errors.New("The request failed because of an internal error that is probably caused by misconfiguration")
ErrNotFound = errors.New("Could not find the requested resource(s)")
)

Expand Down
30 changes: 19 additions & 11 deletions fosite-example/main.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package pkg
package main

import (
"crypto/rand"
Expand Down Expand Up @@ -160,27 +160,35 @@ func authEndpoint(rw http.ResponseWriter, req *http.Request) {
}
// You have now access to authorizeRequest, Code ResponseTypes, Scopes ...

var requestedScopes string
for _, this := range ar.GetRequestedScopes() {
requestedScopes += fmt.Sprintf(`<li><input type="checkbox" name="scopes" value="%s">%s</li>`, this, this)
}

// Normally, this would be the place where you would check if the user is logged in and gives his consent.
// We're simplifying things and just checking if the request includes a valid username and password
if req.Form.Get("username") != "peter" {
req.ParseForm()
if req.PostForm.Get("username") != "peter" {
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
rw.Write([]byte(`<h1>Login page</h1>`))
rw.Write([]byte(`
rw.Write([]byte(fmt.Sprintf(`
<p>Howdy! This is the log in page. For this example, it is enough to supply the username.</p>
<form method="post">
<p>
By logging in, you consent to grant these scopes:
<ul>%s</ul>
</p>
<input type="text" name="username" /> <small>try peter</small><br>
<input type="submit">
</form>
`))
`, requestedScopes)))
return
}

// we allow issuing of refresh tokens per default
if ar.GetRequestedScopes().Has("offline") {
ar.GrantScope("offline")
}
if ar.GetRequestedScopes().Has("photos") {
ar.GrantScope("photos")
// we allow issuing of refresh tokens, id tokens and access to "photos" per default
for _, scope := range req.PostForm["scopes"] {
ar.GrantScope(scope)

}

// Now that the user is authorized, we set up a session:
Expand Down Expand Up @@ -230,7 +238,7 @@ func validateEndpoint(rw http.ResponseWriter, req *http.Request) {
ctx := fosite.NewContext()
mySessionData := newSession("peter")

ar, err := oauth2.ValidateToken(ctx, req.URL.Query().Get("token"), fosite.AccessToken, mySessionData, "photos", "photos.create")
ar, err := oauth2.ValidateToken(ctx, req.URL.Query().Get("token"), fosite.AccessToken, mySessionData)
if err != nil {
fmt.Fprintf(rw, "<h1>An error occurred!</h1>%s", err.Error())
return
Expand Down
2 changes: 1 addition & 1 deletion fosite-example/pkg/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func NewExampleStore() *Store {
RedirectURIs: []string{"http://localhost:3846/callback"},
ResponseTypes: []string{"id_token", "code", "token"},
GrantTypes: []string{"implicit", "refresh_token", "authorization_code", "password", "client_credentials"},
Scopes: []string{"fosite", "photos", "offline"},
Scopes: []string{"fosite", "openid", "photos", "offline"},
},
},
Users: map[string]UserRelation{
Expand Down
3 changes: 2 additions & 1 deletion handler/oauth2/flow_authorize_code_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/ory-am/fosite"
"github.com/pkg/errors"
"golang.org/x/net/context"
"fmt"
)

// AuthorizeExplicitGrantTypeHandler is a response handler for the Authorize Code grant using the explicit grant type
Expand Down Expand Up @@ -46,7 +47,7 @@ func (c *AuthorizeExplicitGrantHandler) HandleAuthorizeEndpointRequest(ctx conte
client := ar.GetClient()
for _, scope := range ar.GetRequestedScopes() {
if !c.ScopeStrategy(client.GetScopes(), scope) {
return errors.Wrap(fosite.ErrInvalidScope, "")
return errors.Wrap(fosite.ErrInvalidScope, fmt.Sprintf("The client is not allowed to request scope %s", scope))
}
}

Expand Down
6 changes: 3 additions & 3 deletions handler/oauth2/flow_authorize_code_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func (c *AuthorizeExplicitGrantHandler) HandleTokenEndpointRequest(ctx context.C
}

if !request.GetClient().GetGrantTypes().Has("authorization_code") {
return errors.Wrap(fosite.ErrInvalidGrant, "")
return errors.Wrap(fosite.ErrInvalidGrant, "The client is not allowed to use grant type authorization_code")
}

code := r.PostForm.Get("code")
Expand All @@ -43,7 +43,7 @@ func (c *AuthorizeExplicitGrantHandler) HandleTokenEndpointRequest(ctx context.C
// confidential client, or if the client is public, ensure that the
// code was issued to "client_id" in the request,
if authorizeRequest.GetClient().GetID() != request.GetClient().GetID() {
return errors.Wrap(fosite.ErrInvalidRequest, "")
return errors.Wrap(fosite.ErrInvalidRequest, "Client ID mismatch")
}

// ensure that the "redirect_uri" parameter is present if the
Expand All @@ -52,7 +52,7 @@ func (c *AuthorizeExplicitGrantHandler) HandleTokenEndpointRequest(ctx context.C
// their values are identical.
forcedRedirectURI := authorizeRequest.GetRequestForm().Get("redirect_uri")
if forcedRedirectURI != "" && forcedRedirectURI != r.PostForm.Get("redirect_uri") {
return errors.Wrap(fosite.ErrInvalidRequest, "")
return errors.Wrap(fosite.ErrInvalidRequest, "Redirect URI mismatch")
}

// Checking of POST client_id skipped, because:
Expand Down
7 changes: 4 additions & 3 deletions handler/oauth2/flow_authorize_implicit.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/pkg/errors"
"golang.org/x/net/context"
"github.com/ory-am/fosite"
"fmt"
)

// AuthorizeImplicitGrantTypeHandler is a response handler for the Authorize Code grant using the implicit grant type
Expand All @@ -32,17 +33,17 @@ func (c *AuthorizeImplicitGrantTypeHandler) HandleAuthorizeEndpointRequest(ctx c
}

if !ar.GetClient().GetResponseTypes().Has("token") {
return errors.Wrap(fosite.ErrInvalidGrant, "")
return errors.Wrap(fosite.ErrInvalidGrant, "The client is not allowed to use response type token")
}

if !ar.GetClient().GetGrantTypes().Has("implicit") {
return errors.Wrap(fosite.ErrInvalidGrant, "")
return errors.Wrap(fosite.ErrInvalidGrant, "The client is not allowed to use grant type implicit")
}

client := ar.GetClient()
for _, scope := range ar.GetRequestedScopes() {
if !c.ScopeStrategy(client.GetScopes(), scope) {
return errors.Wrap(fosite.ErrInvalidScope, "")
return errors.Wrap(fosite.ErrInvalidScope, fmt.Sprintf("The client is not allowed to request scope %s", scope))
}
}

Expand Down
5 changes: 3 additions & 2 deletions handler/oauth2/flow_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/ory-am/fosite"
"github.com/pkg/errors"
"golang.org/x/net/context"
"fmt"
)

type ClientCredentialsGrantHandler struct {
Expand All @@ -24,7 +25,7 @@ func (c *ClientCredentialsGrantHandler) HandleTokenEndpointRequest(_ context.Con
client := request.GetClient()
for _, scope := range request.GetRequestedScopes() {
if !c.ScopeStrategy(client.GetScopes(), scope) {
return errors.Wrap(fosite.ErrInvalidScope, "")
return errors.Wrap(fosite.ErrInvalidScope, fmt.Sprintf("The client is not allowed to request scope %s", scope))
}
}

Expand All @@ -43,7 +44,7 @@ func (c *ClientCredentialsGrantHandler) PopulateTokenEndpointResponse(ctx contex
}

if !request.GetClient().GetGrantTypes().Has("client_credentials") {
return errors.Wrap(fosite.ErrInvalidGrant, "")
return errors.Wrap(fosite.ErrInvalidGrant, "The client is not allowed to use grant type client_credentials")
}

return c.IssueAccessToken(ctx, r, request, response)
Expand Down
4 changes: 2 additions & 2 deletions handler/oauth2/flow_refresh.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (c *RefreshTokenGrantHandler) HandleTokenEndpointRequest(ctx context.Contex
}

if !request.GetClient().GetGrantTypes().Has("refresh_token") {
return errors.Wrap(fosite.ErrInvalidGrant, "")
return errors.Wrap(fosite.ErrInvalidGrant, "The client is not allowed to use grant type refresh_token")
}

refresh := req.PostForm.Get("refresh_token")
Expand All @@ -54,7 +54,7 @@ func (c *RefreshTokenGrantHandler) HandleTokenEndpointRequest(ctx context.Contex

// The authorization server MUST ... and ensure that the refresh token was issued to the authenticated client
if accessRequest.GetClient().GetID() != request.GetClient().GetID() {
return errors.Wrap(fosite.ErrInvalidRequest, "")
return errors.Wrap(fosite.ErrInvalidRequest, "Client ID mismatch")
}
return nil
}
Expand Down
7 changes: 4 additions & 3 deletions handler/oauth2/flow_resource_owner.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/ory-am/fosite"
"github.com/pkg/errors"
"golang.org/x/net/context"
"fmt"
)

type ResourceOwnerPasswordCredentialsGrantHandler struct {
Expand All @@ -26,13 +27,13 @@ func (c *ResourceOwnerPasswordCredentialsGrantHandler) HandleTokenEndpointReques
}

if !request.GetClient().GetGrantTypes().Has("password") {
return errors.Wrap(fosite.ErrInvalidGrant, "")
return errors.Wrap(fosite.ErrInvalidGrant, "The client is not allowed to use grant type password")
}

username := req.PostForm.Get("username")
password := req.PostForm.Get("password")
if username == "" || password == "" {
return errors.Wrap(fosite.ErrInvalidRequest, "")
return errors.Wrap(fosite.ErrInvalidRequest, "Username or password missing")
} else if err := c.ResourceOwnerPasswordCredentialsGrantStorage.Authenticate(ctx, username, password); errors.Cause(err) == fosite.ErrNotFound {
return errors.Wrap(fosite.ErrInvalidRequest, err.Error())
} else if err != nil {
Expand All @@ -42,7 +43,7 @@ func (c *ResourceOwnerPasswordCredentialsGrantHandler) HandleTokenEndpointReques
client := request.GetClient()
for _, scope := range request.GetRequestedScopes() {
if !c.ScopeStrategy(client.GetScopes(), scope) {
return errors.Wrap(fosite.ErrInvalidScope, "")
return errors.Wrap(fosite.ErrInvalidScope, fmt.Sprintf("The client is not allowed to request scope %s", scope))
}
}

Expand Down
6 changes: 4 additions & 2 deletions handler/oauth2/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@ func (c *CoreValidator) validateAccessToken(ctx context.Context, token string, a
sig := c.CoreStrategy.AccessTokenSignature(token)
or, err := c.CoreStorage.GetAccessTokenSession(ctx, sig, accessRequest.GetSession())
if err != nil {
fmt.Printf("%s", err)
return errors.Wrap(fosite.ErrRequestUnauthorized, err.Error())
} else if err := c.CoreStrategy.ValidateAccessToken(ctx, or, token); err != nil {
fmt.Printf("%s", err)
return errors.Wrap(fosite.ErrRequestUnauthorized, err.Error())
}

for _, scope := range scopes {
if !c.ScopeStrategy(accessRequest.GetGrantedScopes(), scope) {
return errors.Wrap(fosite.ErrRequestForbidden, fmt.Sprintf("scope %s was not granted", scope))
if !c.ScopeStrategy(or.GetGrantedScopes(), scope) {
return errors.Wrap(fosite.ErrInvalidScope, "")
}
}

Expand Down
6 changes: 3 additions & 3 deletions handler/openid/flow_explicit_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ type OpenIDConnectExplicitHandler struct {
}

func (c *OpenIDConnectExplicitHandler) HandleAuthorizeEndpointRequest(ctx context.Context, req *http.Request, ar fosite.AuthorizeRequester, resp fosite.AuthorizeResponder) error {
if !(ar.GetRequestedScopes().Has("openid") && ar.GetResponseTypes().Exact("code")) {
if !(ar.GetGrantedScopes().Has("openid") && ar.GetResponseTypes().Exact("code")) {
return nil
}

if !ar.GetClient().GetResponseTypes().Has("id_token", "code") {
return errors.Wrap(fosite.ErrInvalidClient, "client is not allowed to use rseponse type id_token and code")
return errors.Wrap(fosite.ErrInvalidRequest, "The client is not allowed to use response type id_token and code")
}

if len(resp.GetCode()) == 0 {
return errors.Wrap(fosite.ErrMisconfiguration, "code is not set")
return errors.Wrap(fosite.ErrMisconfiguration, "Authorization code has not been issued yet")
}

if err := c.OpenIDConnectRequestStorage.CreateOpenIDConnectSession(ctx, resp.GetCode(), ar); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion handler/openid/flow_explicit_auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func TestExplicit_HandleAuthorizeEndpointRequest(t *testing.T) {
{
description: "should fail because no code set",
setup: func() {
areq.Scopes = fosite.Arguments{"openid"}
areq.GrantedScopes = fosite.Arguments{"openid"}
areq.Form.Set("nonce", "11111111111111111111111111111")
aresp.EXPECT().GetCode().Return("")
},
Expand Down
8 changes: 4 additions & 4 deletions handler/openid/flow_explicit_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ func (c *OpenIDConnectExplicitHandler) PopulateTokenEndpointResponse(ctx context
return errors.Wrap(fosite.ErrServerError, err.Error())
}

if !authorize.GetRequestedScopes().Has("openid") {
return errors.Wrap(fosite.ErrUnknownRequest, "")
if !authorize.GetGrantedScopes().Has("openid") {
return errors.Wrap(fosite.ErrMisconfiguration, "The an openid connect session was found but the openid scope is missing in it")
}

if !requester.GetClient().GetGrantTypes().Has("authorization_code") {
return errors.Wrap(fosite.ErrInvalidGrant, "")
return errors.Wrap(fosite.ErrInvalidGrant, "The client is not allowed to use the authorization_code grant type")
}

if !requester.GetClient().GetResponseTypes().Has("id_token") {
return errors.Wrap(fosite.ErrInvalidGrant, "")
return errors.Wrap(fosite.ErrInvalidGrant, "The client is not allowed to use response type id_token")
}

return c.IssueExplicitIDToken(ctx, req, authorize, responder)
Expand Down
4 changes: 2 additions & 2 deletions handler/openid/flow_explicit_token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,14 @@ func TestExplicit_PopulateTokenEndpointResponse(t *testing.T) {
areq.GrantTypes = fosite.Arguments{"authorization_code"}
store.EXPECT().GetOpenIDConnectSession(nil, "foobar", areq).Return(fosite.NewAuthorizeRequest(), nil)
},
expectErr: fosite.ErrUnknownRequest,
expectErr: fosite.ErrMisconfiguration,
},
{
description: "should pass",
setup: func() {
r := fosite.NewAuthorizeRequest()
r.Session = areq.Session
r.Scopes = fosite.Arguments{"openid"}
r.GrantedScopes = fosite.Arguments{"openid"}
r.Form.Set("nonce", "1111111111111111")
store.EXPECT().GetOpenIDConnectSession(nil, gomock.Any(), areq).AnyTimes().Return(r, nil)
},
Expand Down

0 comments on commit f41216c

Please sign in to comment.