Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: fix the refresh token issue (#14)
Browse files Browse the repository at this point in the history
* fix: fix the refresh token issue

* fix: fix the OIDC token missing issue
wood-push-melon authored and nsklikas committed Dec 18, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 4cba133 commit a56d930
Showing 8 changed files with 411 additions and 216 deletions.
274 changes: 198 additions & 76 deletions handler/oauth2/flow_authorize_code_token_test.go

Large diffs are not rendered by default.

11 changes: 7 additions & 4 deletions handler/oauth2/flow_generic_code_token.go
Original file line number Diff line number Diff line change
@@ -90,7 +90,7 @@ func (c *GenericCodeTokenEndpointHandler) PopulateTokenEndpointResponse(ctx cont
return errorsx.WithStack(err)
}

for _, scope := range ar.GetRequestedScopes() {
for _, scope := range ar.GetGrantedScopes() {
requester.GrantScope(scope)
}

@@ -105,7 +105,7 @@ func (c *GenericCodeTokenEndpointHandler) PopulateTokenEndpointResponse(ctx cont
}

var refreshToken, refreshTokenSignature string
if c.canIssueRefreshToken(ctx, ar) {
if c.canIssueRefreshToken(ctx, requester) {
refreshToken, refreshTokenSignature, err = c.RefreshTokenStrategy.GenerateRefreshToken(ctx, requester)
if err != nil {
return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
@@ -229,11 +229,14 @@ func (c *GenericCodeTokenEndpointHandler) CanHandleTokenEndpointRequest(ctx cont
}

func (c *GenericCodeTokenEndpointHandler) canIssueRefreshToken(ctx context.Context, requester fosite.Requester) bool {
scope := c.Config.GetRefreshTokenScopes(ctx)
if len(scope) > 0 && !requester.GetGrantedScopes().HasOneOf(scope...) {
scopes := c.Config.GetRefreshTokenScopes(ctx)

// Require one of the refresh token scopes, if set.
if len(scopes) > 0 && !requester.GetGrantedScopes().HasOneOf(scopes...) {
return false
}

// Do not issue a refresh token to clients that cannot use the refresh token grant type.
if !requester.GetClient().GetGrantTypes().Has("refresh_token") {
return false
}
4 changes: 2 additions & 2 deletions handler/openid/flow_device_auth.go
Original file line number Diff line number Diff line change
@@ -25,15 +25,15 @@ type OpenIDConnectDeviceHandler struct {
}

func (c *OpenIDConnectDeviceHandler) HandleDeviceEndpointRequest(ctx context.Context, dar fosite.DeviceRequester, resp fosite.DeviceResponder) error {
if !(dar.GetGrantedScopes().Has("openid")) {
if !(dar.GetRequestedScopes().Has("openid")) {
return nil
}

if !dar.GetClient().GetGrantTypes().Has(string(fosite.GrantTypeDeviceCode)) {
return nil
}

if len(resp.GetDeviceCode()) == 0 {
if resp.GetDeviceCode() == "" {
return errorsx.WithStack(fosite.ErrMisconfiguration.WithDebug("The device code has not been issued yet, indicating a broken code configuration."))
}

24 changes: 12 additions & 12 deletions handler/openid/flow_device_auth_test.go
Original file line number Diff line number Diff line change
@@ -64,7 +64,7 @@ func TestDeviceAuth_HandleDeviceEndpointRequest(t *testing.T) {

client := &fosite.DefaultClient{
ID: "foo",
GrantTypes: fosite.Arguments{string(fosite.GrantTypeDeviceCode)},
GrantTypes: fosite.Arguments{"urn:ietf:params:oauth:grant-type:device_code"},
}

testCases := []struct {
@@ -78,17 +78,17 @@ func TestDeviceAuth_HandleDeviceEndpointRequest(t *testing.T) {
description: "should ignore because scope openid is not set",
authreq: &fosite.DeviceRequest{
Request: fosite.Request{
GrantedScope: fosite.Arguments{"email"},
RequestedScope: fosite.Arguments{"email"},
},
},
},
{
description: "should ignore because client grant type is invalid",
authreq: &fosite.DeviceRequest{
Request: fosite.Request{
GrantedScope: fosite.Arguments{"openid", "email"},
RequestedScope: fosite.Arguments{"openid", "email"},
Client: &fosite.DefaultClient{
GrantTypes: []string{string(fosite.GrantTypeAuthorizationCode)},
GrantTypes: []string{"authorization_code"},
},
},
},
@@ -97,8 +97,8 @@ func TestDeviceAuth_HandleDeviceEndpointRequest(t *testing.T) {
description: "should fail because device code is not issued",
authreq: &fosite.DeviceRequest{
Request: fosite.Request{
GrantedScope: fosite.Arguments{"openid", "email"},
Client: client,
RequestedScope: fosite.Arguments{"openid", "email"},
Client: client,
},
},
authresp: &fosite.DeviceResponse{},
@@ -108,9 +108,9 @@ func TestDeviceAuth_HandleDeviceEndpointRequest(t *testing.T) {
description: "should fail because cannot create session",
authreq: &fosite.DeviceRequest{
Request: fosite.Request{
GrantedScope: fosite.Arguments{"openid", "email"},
Client: client,
Session: session,
RequestedScope: fosite.Arguments{"openid", "email"},
Client: client,
Session: session,
},
},
authresp: &fosite.DeviceResponse{
@@ -128,9 +128,9 @@ func TestDeviceAuth_HandleDeviceEndpointRequest(t *testing.T) {
description: "should pass",
authreq: &fosite.DeviceRequest{
Request: fosite.Request{
GrantedScope: fosite.Arguments{"openid", "email"},
Client: client,
Session: session,
RequestedScope: fosite.Arguments{"openid", "email"},
Client: client,
Session: session,
},
},
authresp: &fosite.DeviceResponse{
8 changes: 4 additions & 4 deletions handler/openid/flow_device_token.go
Original file line number Diff line number Diff line change
@@ -21,6 +21,10 @@ func (c *OpenIDConnectDeviceHandler) PopulateTokenEndpointResponse(ctx context.C
return errorsx.WithStack(fosite.ErrUnknownRequest)
}

if !requester.GetClient().GetGrantTypes().Has(string(fosite.GrantTypeDeviceCode)) {
return errorsx.WithStack(fosite.ErrUnauthorizedClient.WithHint("The OAuth 2.0 Client is not allowed to use the authorization grant \"urn:ietf:params:oauth:grant-type:device_code\"."))
}

deviceCode := requester.GetRequestForm().Get("device_code")
signature, err := c.DeviceCodeStrategy.DeviceCodeSignature(ctx, deviceCode)
ar, err := c.OpenIDConnectRequestStorage.GetOpenIDConnectSession(ctx, signature, requester)
@@ -35,10 +39,6 @@ func (c *OpenIDConnectDeviceHandler) PopulateTokenEndpointResponse(ctx context.C
return errorsx.WithStack(fosite.ErrMisconfiguration.WithDebug("An OpenID Connect session was found but the openid scope is missing, probably due to a broken code configuration."))
}

if !requester.GetClient().GetGrantTypes().Has(string(fosite.GrantTypeDeviceCode)) {
return errorsx.WithStack(fosite.ErrUnauthorizedClient.WithHint("The OAuth 2.0 Client is not allowed to use the authorization grant \"urn:ietf:params:oauth:grant-type:device_code\"."))
}

session, ok := ar.GetSession().(Session)
if !ok {
return errorsx.WithStack(fosite.ErrServerError.WithDebug("Failed to generate id token because session must be of type fosite/handler/openid.Session."))
46 changes: 15 additions & 31 deletions handler/openid/flow_device_token_test.go
Original file line number Diff line number Diff line change
@@ -81,7 +81,7 @@ func TestDeviceToken_PopulateTokenEndpointResponse(t *testing.T) {

client := &fosite.DefaultClient{
ID: "foo",
GrantTypes: fosite.Arguments{string(fosite.GrantTypeDeviceCode)},
GrantTypes: fosite.Arguments{"urn:ietf:params:oauth:grant-type:device_code"},
}

testCases := []struct {
@@ -95,8 +95,9 @@ func TestDeviceToken_PopulateTokenEndpointResponse(t *testing.T) {
{
description: "should fail because the grant type is invalid",
areq: &fosite.AccessRequest{
GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)},
GrantTypes: fosite.Arguments{"authorization_code"},
Request: fosite.Request{
Client: client,
Form: url.Values{"device_code": []string{"device_code"}},
Session: session,
},
@@ -107,8 +108,9 @@ func TestDeviceToken_PopulateTokenEndpointResponse(t *testing.T) {
{
description: "should fail because session not found",
areq: &fosite.AccessRequest{
GrantTypes: fosite.Arguments{string(fosite.GrantTypeDeviceCode)},
GrantTypes: fosite.Arguments{"urn:ietf:params:oauth:grant-type:device_code"},
Request: fosite.Request{
Client: client,
Form: url.Values{"device_code": []string{"device_code"}},
Session: session,
},
@@ -122,8 +124,9 @@ func TestDeviceToken_PopulateTokenEndpointResponse(t *testing.T) {
{
description: "should fail because session lookup fails",
areq: &fosite.AccessRequest{
GrantTypes: fosite.Arguments{string(fosite.GrantTypeDeviceCode)},
GrantTypes: fosite.Arguments{"urn:ietf:params:oauth:grant-type:device_code"},
Request: fosite.Request{
Client: client,
Form: url.Values{"device_code": []string{"device_code"}},
Session: session,
},
@@ -136,15 +139,17 @@ func TestDeviceToken_PopulateTokenEndpointResponse(t *testing.T) {
{
description: "should fail because auth request grant scope is invalid",
areq: &fosite.AccessRequest{
GrantTypes: fosite.Arguments{string(fosite.GrantTypeDeviceCode)},
GrantTypes: fosite.Arguments{"urn:ietf:params:oauth:grant-type:device_code"},
Request: fosite.Request{
Client: client,
Form: url.Values{"device_code": []string{"device_code"}},
Session: session,
},
},
setup: func(areq *fosite.AccessRequest) {
authreq := &fosite.DeviceRequest{
Request: fosite.Request{
Client: client,
GrantedScope: fosite.Arguments{"email"},
Session: session,
},
@@ -153,33 +158,10 @@ func TestDeviceToken_PopulateTokenEndpointResponse(t *testing.T) {
},
expectErr: fosite.ErrMisconfiguration,
},
{
description: "should fail because auth request's client grant type is invalid",
areq: &fosite.AccessRequest{
GrantTypes: fosite.Arguments{string(fosite.GrantTypeDeviceCode)},
Request: fosite.Request{
Client: &fosite.DefaultClient{
GrantTypes: fosite.Arguments{string(fosite.GrantTypeAuthorizationCode)},
},
Form: url.Values{"device_code": []string{"device_code"}},
Session: session,
},
},
setup: func(areq *fosite.AccessRequest) {
authreq := &fosite.DeviceRequest{
Request: fosite.Request{
GrantedScope: fosite.Arguments{"openid", "email"},
Session: session,
},
}
store.EXPECT().GetOpenIDConnectSession(gomock.Any(), gomock.Any(), areq).Return(authreq, nil)
},
expectErr: fosite.ErrUnauthorizedClient,
},
{
description: "should fail because auth request is missing session",
areq: &fosite.AccessRequest{
GrantTypes: fosite.Arguments{string(fosite.GrantTypeDeviceCode)},
GrantTypes: fosite.Arguments{"urn:ietf:params:oauth:grant-type:device_code"},
Request: fosite.Request{
Client: client,
Form: url.Values{"device_code": []string{"device_code"}},
@@ -189,6 +171,7 @@ func TestDeviceToken_PopulateTokenEndpointResponse(t *testing.T) {
setup: func(areq *fosite.AccessRequest) {
authreq := &fosite.DeviceRequest{
Request: fosite.Request{
Client: client,
GrantedScope: fosite.Arguments{"openid", "email"},
},
}
@@ -199,7 +182,7 @@ func TestDeviceToken_PopulateTokenEndpointResponse(t *testing.T) {
{
description: "should fail because auth request session is missing subject claims",
areq: &fosite.AccessRequest{
GrantTypes: fosite.Arguments{string(fosite.GrantTypeDeviceCode)},
GrantTypes: fosite.Arguments{"urn:ietf:params:oauth:grant-type:device_code"},
Request: fosite.Request{
Client: client,
Form: url.Values{"device_code": []string{"device_code"}},
@@ -209,6 +192,7 @@ func TestDeviceToken_PopulateTokenEndpointResponse(t *testing.T) {
setup: func(areq *fosite.AccessRequest) {
authreq := &fosite.DeviceRequest{
Request: fosite.Request{
Client: client,
GrantedScope: fosite.Arguments{"openid", "email"},
Session: NewDefaultSession(),
},
@@ -220,7 +204,7 @@ func TestDeviceToken_PopulateTokenEndpointResponse(t *testing.T) {
{
description: "should pass",
areq: &fosite.AccessRequest{
GrantTypes: fosite.Arguments{string(fosite.GrantTypeDeviceCode)},
GrantTypes: fosite.Arguments{"urn:ietf:params:oauth:grant-type:device_code"},
Request: fosite.Request{
Client: client,
Form: url.Values{"device_code": []string{"device_code"}},
234 changes: 162 additions & 72 deletions handler/rfc8628/token_handler_test.go

Large diffs are not rendered by default.

26 changes: 11 additions & 15 deletions integration/authorize_device_grant_request_test.go
Original file line number Diff line number Diff line change
@@ -53,14 +53,14 @@ func runDeviceFlowTest(t *testing.T) {
{
description: "should fail with invalid_grant",
setup: func() {
fositeStore.Clients["device-client"].(*fosite.DefaultClient).GrantTypes = []string{string(fosite.GrantTypeAuthorizationCode)}
fositeStore.Clients["device-client"].(*fosite.DefaultClient).GrantTypes = []string{"authorization_code"}
},
err: true,
check: func(t *testing.T, token *goauth.DeviceAuthResponse, err error) {
assert.ErrorContains(t, err, "invalid_grant")
},
cleanUp: func() {
fositeStore.Clients["device-client"].(*fosite.DefaultClient).GrantTypes = []string{string(fosite.GrantTypeDeviceCode)}
fositeStore.Clients["device-client"].(*fosite.DefaultClient).GrantTypes = []string{"urn:ietf:params:oauth:grant-type:device_code"}
},
},
{
@@ -152,7 +152,6 @@ func runDeviceFlowAccessTokenTest(t *testing.T) {
description string
setup func()
params []goauth.AuthCodeOption
err bool
check func(t *testing.T, token *goauth.Token, err error)
cleanUp func()
}{
@@ -161,30 +160,27 @@ func runDeviceFlowAccessTokenTest(t *testing.T) {
setup: func() {
},
params: []goauth.AuthCodeOption{goauth.SetAuthURLParam("grant_type", "invalid_grant_type")},
err: true,
check: func(t *testing.T, token *goauth.Token, err error) {
assert.ErrorContains(t, err, "invalid_request")
},
},
{
description: "should fail with unauthorized client",
setup: func() {
fositeStore.Clients["device-client"].(*fosite.DefaultClient).GrantTypes = []string{string(fosite.GrantTypeAuthorizationCode)}
fositeStore.Clients["device-client"].(*fosite.DefaultClient).GrantTypes = []string{"authorization_code"}
},
params: []goauth.AuthCodeOption{},
err: true,
check: func(t *testing.T, token *goauth.Token, err error) {
assert.ErrorContains(t, err, "unauthorized_client")
},
cleanUp: func() {
fositeStore.Clients["device-client"].(*fosite.DefaultClient).GrantTypes = []string{string(fosite.GrantTypeDeviceCode)}
fositeStore.Clients["device-client"].(*fosite.DefaultClient).GrantTypes = []string{"urn:ietf:params:oauth:grant-type:device_code"}
},
},
{
description: "should fail with invalid device code",
setup: func() {},
params: []goauth.AuthCodeOption{goauth.SetAuthURLParam("device_code", "invalid_device_code")},
err: true,
check: func(t *testing.T, token *goauth.Token, err error) {
assert.ErrorContains(t, err, "invalid_grant")
},
@@ -194,7 +190,6 @@ func runDeviceFlowAccessTokenTest(t *testing.T) {
setup: func() {
oauthClient.ClientID = "invalid_client_id"
},
err: true,
check: func(t *testing.T, token *goauth.Token, err error) {
assert.ErrorContains(t, err, "invalid_client")
},
@@ -204,17 +199,18 @@ func runDeviceFlowAccessTokenTest(t *testing.T) {
},
{
description: "should pass",
setup: func() {},
err: false,
check: func(t *testing.T, token *goauth.Token, err error) {
assert.Equal(t, "bearer", token.TokenType)
assert.NotEmpty(t, token.AccessToken)
},
},
} {
t.Run(fmt.Sprintf("case=%d description=%s", k, c.description), func(t *testing.T) {
c.setup()
if c.setup != nil {
c.setup()
}

token, err := oauthClient.DeviceAccessToken(context.Background(), resp, c.params...)
if !c.err {
assert.NotEmpty(t, token.AccessToken)
}

if c.check != nil {
c.check(t, token, err)

0 comments on commit a56d930

Please sign in to comment.