Skip to content

Commit

Permalink
handler/oauth2: use hmac strategy for jwt refresh tokens (ory#190)
Browse files Browse the repository at this point in the history
Closes ory#180
  • Loading branch information
budougumi0617 committed Jul 8, 2017
1 parent 02948d9 commit 79b8375
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 61 deletions.
50 changes: 50 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ bumps (`0.1.0` -> `0.2.0`).
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->


- [0.11.0](#0110)
- [WildcardScopeStrategy](#wildcardscopestrategy)
- [Refresh tokens and authorize codes are no longer JWTs](#refresh-tokens-and-authorize-codes-are-no-longer-jwts)
- [Delete access tokens when persisting refresh session](#delete-access-tokens-when-persisting-refresh-session)
- [0.10.0](#0100)
- [0.9.0](#090)
- [0.8.0](#080)
- [Breaking changes](#breaking-changes)
Expand All @@ -21,6 +27,10 @@ bumps (`0.1.0` -> `0.2.0`).

## 0.11.0

This release introduces breaking changes:

### WildcardScopeStrategy

A new [scope strategy](https://github.com/ory/fosite/pull/187) was introduced called `WildcardScopeStrategy`. This strategy is now the default when using
the composer. To set the HierarchicScopeStrategy strategy, do:

Expand All @@ -32,7 +42,47 @@ var config = &compose.Config{
}
```

### Refresh tokens and authorize codes are no longer JWTs

Using JWTs for refresh tokens and authorize codes did not make sense:

1. Refresh tokens are long-living credentials, JWTs require an expiry date.
2. Refresh tokens are never validated client-side, only server-side. Thus access to the store is available.
3. Authorize codes are never validated client-side, only server-side.

Also, one compose method changed due to this:

```go
package compose

// ..

- func NewOAuth2JWTStrategy(key *rsa.PrivateKey) *oauth2.RS256JWTStrategy
+ func NewOAuth2JWTStrategy(key *rsa.PrivateKey, strategy *oauth2.HMACSHAStrategy) *oauth2.RS256JWTStrategy
```

### Delete access tokens when persisting refresh session

Please delete access tokens in your store when you persist a refresh session. This increases security. Here
is an example of how to do that using only existing methods:

```go
func (s *MemoryStore) PersistRefreshTokenGrantSession(ctx context.Context, originalRefreshSignature, accessSignature, refreshSignature string, request fosite.Requester) error {
if ts, err := s.GetRefreshTokenSession(ctx, originalRefreshSignature, nil); err != nil {
return err
} else if err := s.RevokeAccessToken(ctx, ts.GetID()); err != nil {
return err
} else if err := s.RevokeRefreshToken(ctx, ts.GetID()); err != nil {
return err
} else if err := s.CreateAccessTokenSession(ctx, accessSignature, request); err != nil {
return err
} else if err := s.CreateRefreshTokenSession(ctx, refreshSignature, request); err != nil {
return err
}

return nil
}
```

## 0.10.0

Expand Down
3 changes: 2 additions & 1 deletion compose/compose_strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ func NewOAuth2HMACStrategy(config *Config, secret []byte) *oauth2.HMACSHAStrateg
}
}

func NewOAuth2JWTStrategy(key *rsa.PrivateKey) *oauth2.RS256JWTStrategy {
func NewOAuth2JWTStrategy(key *rsa.PrivateKey, strategy *oauth2.HMACSHAStrategy) *oauth2.RS256JWTStrategy {
return &oauth2.RS256JWTStrategy{
RS256JWTStrategy: &jwt.RS256JWTStrategy{
PrivateKey: key,
},
HMACSHAStrategy: strategy,
}
}

Expand Down
39 changes: 19 additions & 20 deletions handler/oauth2/strategy_jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import (
// RS256JWTStrategy is a JWT RS256 strategy.
type RS256JWTStrategy struct {
*jwt.RS256JWTStrategy
Issuer string
HMACSHAStrategy *HMACSHAStrategy
Issuer string
}

func (h RS256JWTStrategy) signature(token string) string {
Expand All @@ -31,12 +32,13 @@ func (h RS256JWTStrategy) AccessTokenSignature(token string) string {
return h.signature(token)
}

func (h RS256JWTStrategy) RefreshTokenSignature(token string) string {
return h.signature(token)
func (h *RS256JWTStrategy) GenerateAccessToken(_ context.Context, requester fosite.Requester) (token string, signature string, err error) {
return h.generate(fosite.AccessToken, requester)
}

func (h RS256JWTStrategy) AuthorizeCodeSignature(token string) string {
return h.signature(token)
func (h *RS256JWTStrategy) ValidateAccessToken(_ context.Context, _ fosite.Requester, token string) error {
_, err := h.validate(token)
return err
}

func (h *RS256JWTStrategy) ValidateJWT(tokenType fosite.TokenType, token string) (requester fosite.Requester, err error) {
Expand Down Expand Up @@ -68,31 +70,28 @@ func (h *RS256JWTStrategy) ValidateJWT(tokenType fosite.TokenType, token string)
return
}

func (h *RS256JWTStrategy) GenerateAccessToken(_ context.Context, requester fosite.Requester) (token string, signature string, err error) {
return h.generate(fosite.AccessToken, requester)
func (h RS256JWTStrategy) RefreshTokenSignature(token string) string {
return h.HMACSHAStrategy.RefreshTokenSignature(token)
}

func (h *RS256JWTStrategy) ValidateAccessToken(_ context.Context, _ fosite.Requester, token string) error {
_, err := h.validate(token)
return err
func (h RS256JWTStrategy) AuthorizeCodeSignature(token string) string {
return h.HMACSHAStrategy.AuthorizeCodeSignature(token)
}

func (h *RS256JWTStrategy) GenerateRefreshToken(_ context.Context, requester fosite.Requester) (token string, signature string, err error) {
return h.generate(fosite.RefreshToken, requester)
func (h *RS256JWTStrategy) GenerateRefreshToken(ctx context.Context, req fosite.Requester) (token string, signature string, err error) {
return h.HMACSHAStrategy.GenerateRefreshToken(ctx, req)
}

func (h *RS256JWTStrategy) ValidateRefreshToken(_ context.Context, _ fosite.Requester, token string) error {
_, err := h.validate(token)
return err
func (h *RS256JWTStrategy) ValidateRefreshToken(ctx context.Context, req fosite.Requester, token string) error {
return h.HMACSHAStrategy.ValidateRefreshToken(ctx, req, token)
}

func (h *RS256JWTStrategy) GenerateAuthorizeCode(_ context.Context, requester fosite.Requester) (token string, signature string, err error) {
return h.generate(fosite.AuthorizeCode, requester)
func (h *RS256JWTStrategy) GenerateAuthorizeCode(ctx context.Context, req fosite.Requester) (token string, signature string, err error) {
return h.HMACSHAStrategy.GenerateAuthorizeCode(ctx, req)
}

func (h *RS256JWTStrategy) ValidateAuthorizeCode(_ context.Context, requester fosite.Requester, token string) error {
_, err := h.validate(token)
return err
func (h *RS256JWTStrategy) ValidateAuthorizeCode(ctx context.Context, req fosite.Requester, token string) error {
return h.HMACSHAStrategy.ValidateAuthorizeCode(ctx, req, token)
}

func (h *RS256JWTStrategy) validate(token string) (t *jwtx.Token, err error) {
Expand Down
40 changes: 0 additions & 40 deletions handler/oauth2/strategy_jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,43 +99,3 @@ func TestAccessToken(t *testing.T) {
}
}
}

func TestRefreshToken(t *testing.T) {
token, signature, err := j.GenerateRefreshToken(nil, jwtValidCase(fosite.RefreshToken))
assert.Nil(t, err, "%s", err)
assert.Equal(t, strings.Split(token, ".")[2], signature)

validate := j.signature(token)
err = j.ValidateRefreshToken(nil, jwtValidCase(fosite.RefreshToken), token)
assert.Nil(t, err, "%s", err)
assert.Equal(t, signature, validate)
}

func TestGenerateAuthorizeCode(t *testing.T) {
for _, c := range []struct {
r *fosite.Request
pass bool
}{
{
r: jwtValidCase(fosite.AuthorizeCode),
pass: true,
},
{
r: jwtExpiredCase(fosite.AuthorizeCode),
pass: false,
},
} {
token, signature, err := j.GenerateAuthorizeCode(nil, c.r)
assert.Nil(t, err, "%s", err)
assert.Equal(t, strings.Split(token, ".")[2], signature)

validate := j.signature(token)
err = j.ValidateAuthorizeCode(nil, c.r, token)
if c.pass {
assert.Nil(t, err, "%s", err)
assert.Equal(t, signature, validate)
} else {
assert.NotNil(t, err, "%s", err)
}
}
}
1 change: 1 addition & 0 deletions integration/helper_setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ var jwtStrategy = &oauth2.RS256JWTStrategy{
RS256JWTStrategy: &jwt.RS256JWTStrategy{
PrivateKey: internal.MustRSAKey(),
},
HMACSHAStrategy: hmacStrategy,
}

func mockServer(t *testing.T, f fosite.OAuth2Provider, session fosite.Session) *httptest.Server {
Expand Down

0 comments on commit 79b8375

Please sign in to comment.