Skip to content

Commit

Permalink
Merge pull request #48 from ory-am/explicit-offline
Browse files Browse the repository at this point in the history
explicit: issue refresh token only when 'offline' scope is set
  • Loading branch information
Aeneas committed Jun 9, 2016
2 parents c68a3e9 + 8b9816c commit 85a732d
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 14 deletions.
2 changes: 1 addition & 1 deletion errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ var (
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")
ErrNotFound = errors.New("Could not find the requested resource(s)")
ErrNotFound = errors.New("Could not find the requested resource(s)")
)

const (
Expand Down
8 changes: 7 additions & 1 deletion fosite-example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ var store = &exampleStore.Store{
RedirectURIs: []string{"http://localhost:3846/callback"},
ResponseTypes: []string{"id_token", "code", "token"},
GrantTypes: []string{"implicit", "refresh_token", "authorization_code", "password", "client_credentials"},
GrantedScopes: []string{"fosite", "offline"},
},
},
Users: map[string]exampleStore.UserRelation{
Expand All @@ -64,7 +65,7 @@ var clientConf = goauth.Config{
ClientID: "my-client",
ClientSecret: "foobar",
RedirectURL: "http://localhost:3846/callback",
Scopes: []string{"fosite", "openid"},
Scopes: []string{"fosite", "openid", "offline"},
Endpoint: goauth.Endpoint{
TokenURL: "http://localhost:3846/token",
AuthURL: "http://localhost:3846/auth",
Expand Down Expand Up @@ -335,6 +336,11 @@ func authEndpoint(rw http.ResponseWriter, req *http.Request) {
return
}

// we allow issuing of refresh tokens per default
if ar.GetScopes().Has("offline") {
ar.GrantScope("offline")
}

// Now that the user is authorized, we set up a session:
mySessionData := newSession("peter")

Expand Down
19 changes: 15 additions & 4 deletions handler/core/explicit/explicit_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"time"

"github.com/go-errors/errors"
"golang.org/x/net/context"
"github.com/ory-am/fosite"
"golang.org/x/net/context"
)

// HandleTokenEndpointRequest implements
Expand Down Expand Up @@ -87,24 +87,35 @@ func (c *AuthorizeExplicitGrantTypeHandler) PopulateTokenEndpointResponse(ctx co
return errors.New(fosite.ErrInvalidRequest)
}

access, accessSignature, err := c.AccessTokenStrategy.GenerateAccessToken(ctx, requester)
authorizeRequest, err := c.AuthorizeCodeGrantStorage.GetAuthorizeCodeSession(ctx, signature, requester.GetSession())
if err != nil {
return errors.New(fosite.ErrServerError)
}

refresh, refreshSignature, err := c.RefreshTokenStrategy.GenerateRefreshToken(ctx, requester)
access, accessSignature, err := c.AccessTokenStrategy.GenerateAccessToken(ctx, requester)
if err != nil {
return errors.New(fosite.ErrServerError)
}

var refresh, refreshSignature string
if authorizeRequest.GetGrantedScopes().Has("offline") {
refresh, refreshSignature, err = c.RefreshTokenStrategy.GenerateRefreshToken(ctx, requester)
if err != nil {
return errors.New(fosite.ErrServerError)
}
}

if err := c.AuthorizeCodeGrantStorage.PersistAuthorizeCodeGrantSession(ctx, signature, accessSignature, refreshSignature, requester); err != nil {
return errors.New(fosite.ErrServerError)
}

responder.SetAccessToken(access)
responder.SetTokenType("bearer")
responder.SetExpiresIn(c.AccessTokenLifespan / time.Second)
responder.SetExtra("refresh_token", refresh)
responder.SetScopes(requester.GetGrantedScopes())
if refresh != "" {
responder.SetExtra("refresh_token", refresh)
}

return nil
}
18 changes: 14 additions & 4 deletions handler/core/explicit/explicit_token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func TestPopulateTokenEndpointResponse(t *testing.T) {

areq := fosite.NewAccessRequest(nil)
httpreq := &http.Request{PostForm: url.Values{}}
authreq := fosite.NewAuthorizeRequest()

h := AuthorizeExplicitGrantTypeHandler{
AuthorizeCodeGrantStorage: store,
Expand All @@ -48,18 +49,27 @@ func TestPopulateTokenEndpointResponse(t *testing.T) {
description: "should fail because authcode validation failed",
setup: func() {
areq.GrantTypes = fosite.Arguments{"authorization_code"}
areq.Client = &fosite.DefaultClient{GrantTypes: fosite.Arguments{"authorization_code"}}
areq.Client = &fosite.DefaultClient{
GrantTypes: fosite.Arguments{"authorization_code"},
}
httpreq.PostForm.Add("code", "authcode")
auch.EXPECT().ValidateAuthorizeCode(nil, areq, "authcode").Return("", errors.New(""))
},
expectErr: fosite.ErrInvalidRequest,
},
{
description: "should fail because access token generation failed",
description: "should fail because lookup failed",
setup: func() {
areq.GrantTypes = fosite.Arguments{"authorization_code"}
httpreq.PostForm.Add("code", "authcode")
auch.EXPECT().ValidateAuthorizeCode(nil, areq, "authcode").AnyTimes().Return("authsig", nil)
store.EXPECT().GetAuthorizeCodeSession(nil, "authsig", nil).Return(nil, fosite.ErrNotFound)
},
expectErr: fosite.ErrServerError,
},
{
description: "should fail because access token generation failed",
setup: func() {
authreq.GrantedScopes = []string{"offline"}
store.EXPECT().GetAuthorizeCodeSession(nil, "authsig", nil).AnyTimes().Return(authreq, nil)
ach.EXPECT().GenerateAccessToken(nil, areq).Return("", "", errors.New("error"))
},
expectErr: fosite.ErrServerError,
Expand Down
6 changes: 3 additions & 3 deletions handler/core/refresh/refresh.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ import (
)

type RefreshTokenGrantHandler struct {
AccessTokenStrategy core.AccessTokenStrategy
AccessTokenStrategy core.AccessTokenStrategy

RefreshTokenStrategy core.RefreshTokenStrategy
RefreshTokenStrategy core.RefreshTokenStrategy

// RefreshTokenGrantStorage is used to persist session data across requests.
RefreshTokenGrantStorage RefreshTokenGrantStorage

// AccessTokenLifespan defines the lifetime of an access token.
AccessTokenLifespan time.Duration
AccessTokenLifespan time.Duration
}

// HandleTokenEndpointRequest implements https://tools.ietf.org/html/rfc6749#section-6
Expand Down
4 changes: 4 additions & 0 deletions integration/helper_endpoints_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ func authEndpointHandler(t *testing.T, oauth2 fosite.OAuth2Provider, session int
return
}

if ar.GetScopes().Has("offline") {
ar.GrantScope("offline")
}

// Normally, this would be the place where you would check if the user is logged in and gives his consent.
// For this test, let's assume that the user exists, is logged in, and gives his consent...

Expand Down
1 change: 1 addition & 0 deletions integration/helper_setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var fositeStore = &store.Store{
RedirectURIs: []string{"http://localhost:3846/callback"},
ResponseTypes: []string{"id_token", "code", "token"},
GrantTypes: []string{"implicit", "refresh_token", "authorization_code", "password", "client_credentials"},
GrantedScopes: []string{"fosite", "offline"},
},
},
Users: map[string]store.UserRelation{
Expand Down
9 changes: 8 additions & 1 deletion integration/refresh_token_grant_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func runRefreshTokenGrantTest(t *testing.T, strategy interface{}) {
pass: false,
},
{
description: "should pass",
description: "should fail because scope missing",
setup: func() {
handler := &refresh.RefreshTokenGrantHandler{
AccessTokenStrategy: strategy.(core.AccessTokenStrategy),
Expand All @@ -67,6 +67,13 @@ func runRefreshTokenGrantTest(t *testing.T, strategy interface{}) {
}
f.TokenEndpointHandlers.Append(handler)
},
pass: false,
},
{
description: "should pass",
setup: func() {
oauthClient.Scopes = []string{"fosite", "offline"}
},
pass: true,
},
} {
Expand Down
1 change: 1 addition & 0 deletions rand/bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package rand
import (
"crypto/rand"
"io"

"github.com/go-errors/errors"
)

Expand Down

0 comments on commit 85a732d

Please sign in to comment.