hydra
hydra copied to clipboard
[JWT Bearer Grant Type Issuers] Scopes are validated against first valid issuer for a specified subject in v2.X.X
Preflight checklist
- [X] I could not find a solution in the existing issues, docs, nor discussions.
- [X] I agree to follow this project's Code of Conduct.
- [X] I have read and am following this repository's Contribution Guidelines.
- [ ] This issue affects my Ory Network project.
- [X] I have joined the Ory Community Slack.
- [ ] I am signed up to the Ory Security Patch Newsletter.
Describe the bug
In Hydra v1.X.X if we issue two JWT Bearer Grant Type Issuers with 2 different keys for the same subject and same issuer but with scope "a" for the first key and scope "b", key 1 was allowed to grant scope "a" and key 2 was allowed to grant scope "b".
In Hydra v2.X.X if we do the same, key 1 is allowed to grant scope "a" and key 2 is not allowed to grant scope "b". If we delete Grant Issuer linked to key 1, then key 2 is allowed to grant scope "b".
Reproducing the bug
- Generate two JWT Bearer Grant Type Issuers with the same issuer, subject and scope a for Grant 1 and scope b for Grant 2.
- Request tokens with the scope specified in the JWT for each JWT with the client_id you created.
- Request with second JWT will fail saying scope b is not allowed.
Relevant log output
No response
Relevant configuration
No response
Version
v2.1.1
On which operating system are you observing this issue?
Linux
In which environment are you deploying?
Kubernetes
Additional Context
No response
Found the culprit `func (p *Persister) GetPublicKeyScopes(ctx context.Context, issuer string, subject string, keyId string) ([]string, error) { ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.GetPublicKeyScopes") defer span.End()
var data trust.SQLData
query := p.QueryWithNetwork(ctx).
Where("issuer = ?", issuer).
Where("subject = ? OR allow_any_subject IS TRUE", subject).
Where("key_id = ?", keyId).
Where("nid = ?", p.NetworkID(ctx))
if err := query.First(&data); err != nil {
return nil, sqlcon.HandleError(err)
}
return p.jwtGrantFromSQlData(data).Scope, nil
}`
Where("subject = ? OR allow_any_subject IS TRUE", subject). should be Where("(subject = ? OR allow_any_subject IS TRUE)", subject).
Small demo code :
package main
import (
"context"
"fmt"
"time"
"github.com/gobuffalo/pop/v6"
"github.com/xtgo/uuid"
)
type SQLData struct {
ID string `db:"id"`
NID uuid.UUID `db:"nid"`
Issuer string `db:"issuer"`
Subject string `db:"subject"`
AllowAnySubject bool `db:"allow_any_subject"`
Scope string `db:"scope"`
KeySet string `db:"key_set"`
KeyID string `db:"key_id"`
CreatedAt time.Time `db:"created_at"`
ExpiresAt time.Time `db:"expires_at"`
}
func main() {
con, err := pop.Connect("development")
if err != nil {
panic(err)
}
model := pop.NewModel(new(SQLData), context.Background())
query := con.Q().Where("issuer = ?", "abc").
Where("subject = ? OR allow_any_subject IS TRUE", "def").
Where("key_id = ?", "ghi").
Where("nid = ?", "klm")
fmt.Println(query.Exec())
str, _ := query.ToSQL(model)
fmt.Println(str)
}
Output:
SELECT sql_data.allow_any_subject, sql_data.created_at, sql_data.expires_at, sql_data.id, sql_data.issuer, sql_data.key_id, sql_data.key_set, sql_data.nid, sql_data.scope, sql_data.subject FROM sql_data AS sql_data WHERE issuer = ? AND subject = ? OR allow_any_subject IS TRUE AND key_id = ? AND nid = ?