forked from mystiq/dex
fix: return invalid_grant error on claiming token of another client
Signed-off-by: m.nabokikh <maksim.nabokikh@flant.com>
This commit is contained in:
parent
c319983ecc
commit
578cb05f7b
2 changed files with 58 additions and 1 deletions
|
@ -76,7 +76,9 @@ func (s *Server) getRefreshTokenFromStorage(clientID string, token *internal.Ref
|
||||||
|
|
||||||
if refresh.ClientID != clientID {
|
if refresh.ClientID != clientID {
|
||||||
s.logger.Errorf("client %s trying to claim token for client %s", clientID, refresh.ClientID)
|
s.logger.Errorf("client %s trying to claim token for client %s", clientID, refresh.ClientID)
|
||||||
return nil, invalidErr
|
// According to https://datatracker.ietf.org/doc/html/rfc6749#section-5.2 Dex should response with the
|
||||||
|
// invalid grant error if token has already been claimed by another client.
|
||||||
|
return nil, &refreshError{msg: errInvalidGrant, desc: invalidErr.desc, code: http.StatusBadRequest}
|
||||||
}
|
}
|
||||||
|
|
||||||
if refresh.Token != token.Token {
|
if refresh.Token != token.Token {
|
||||||
|
|
|
@ -481,6 +481,47 @@ func makeOAuth2Tests(clientID string, clientSecret string, now func() time.Time)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "refresh with different client id",
|
||||||
|
scopes: []string{"openid", "email"},
|
||||||
|
handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token, conn *mock.Callback) error {
|
||||||
|
v := url.Values{}
|
||||||
|
v.Add("client_id", clientID)
|
||||||
|
v.Add("client_secret", clientSecret)
|
||||||
|
v.Add("grant_type", "refresh_token")
|
||||||
|
v.Add("refresh_token", "existedrefrestoken")
|
||||||
|
v.Add("scope", "oidc email")
|
||||||
|
resp, err := http.PostForm(p.Endpoint().TokenURL, v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusBadRequest {
|
||||||
|
return fmt.Errorf("expected status code %d, got %d", http.StatusBadRequest, resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var respErr struct {
|
||||||
|
Error string `json:"error"`
|
||||||
|
Description string `json:"error_description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = json.NewDecoder(resp.Body).Decode(&respErr); err != nil {
|
||||||
|
return fmt.Errorf("cannot decode token response: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if respErr.Error != errInvalidGrant {
|
||||||
|
return fmt.Errorf("expected error %q, got %q", errInvalidGrant, respErr.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedMsg := "Refresh token is invalid or has already been claimed by another client."
|
||||||
|
if respErr.Description != expectedMsg {
|
||||||
|
return fmt.Errorf("expected error description %q, got %q", expectedMsg, respErr.Description)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// This test ensures that the connector.RefreshConnector interface is being
|
// This test ensures that the connector.RefreshConnector interface is being
|
||||||
// used when clients request a refresh token.
|
// used when clients request a refresh token.
|
||||||
|
@ -792,6 +833,13 @@ func TestOAuth2CodeFlow(t *testing.T) {
|
||||||
t.Fatalf("failed to create client: %v", err)
|
t.Fatalf("failed to create client: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := s.storage.CreateRefresh(storage.RefreshToken{
|
||||||
|
ID: "existedrefrestoken",
|
||||||
|
ClientID: "unexcistedclientid",
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("failed to create existed refresh token: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create the OAuth2 config.
|
// Create the OAuth2 config.
|
||||||
oauth2Config = &oauth2.Config{
|
oauth2Config = &oauth2.Config{
|
||||||
ClientID: client.ID,
|
ClientID: client.ID,
|
||||||
|
@ -1570,6 +1618,13 @@ func TestOAuth2DeviceFlow(t *testing.T) {
|
||||||
t.Fatalf("failed to create client: %v", err)
|
t.Fatalf("failed to create client: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := s.storage.CreateRefresh(storage.RefreshToken{
|
||||||
|
ID: "existedrefrestoken",
|
||||||
|
ClientID: "unexcistedclientid",
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("failed to create existed refresh token: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Grab the issuer that we'll reuse for the different endpoints to hit
|
// Grab the issuer that we'll reuse for the different endpoints to hit
|
||||||
issuer, err := url.Parse(s.issuerURL.String())
|
issuer, err := url.Parse(s.issuerURL.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in a new issue