Skip to content

Commit

Permalink
JWT signing allow setting typ header (#616)
Browse files Browse the repository at this point in the history
* fixed test to allow setting 'typ' JWT header

* allow setting 'typ' JWT header

* changelog entry

* fix documentation

* simplified config check

* added missing headers check for 'inline' jwt_signing_profile
  • Loading branch information
johakoch authored Nov 15, 2022
1 parent 8e44bfd commit e64cd32
Show file tree
Hide file tree
Showing 8 changed files with 36 additions and 32 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Unreleased changes are available as `avenga/couper:edge` container.
* Aligned the evaluation of [`beta_oauth2`](https://docs.couper.io/configuration/block/oauth2_ac)/[`oidc`](https://docs.couper.io/configuration/block/oidc) `redirect_uri` to `saml` `sp_acs_url` ([#589](https://github.com/avenga/couper/pull/589))
* Proper handling of empty [`beta_oauth2`](https://docs.couper.io/configuration/block/oauth2_ac)/[`oidc`](https://docs.couper.io/configuration/block/oidc) `scope` ([#593](https://github.com/avenga/couper/pull/593))
* Throwing [sequence errors](https://docs.couper.io/configuration/error-handling#endpoint-error-types) and selecting appropriate [error handlers](https://docs.couper.io/configuration/error-handling) ([#595](https://github.com/avenga/couper/pull/595))
* Allow setting of the `typ` JWT header in [`jwt_signing_profile`s](https://docs.couper.io/configuration/block/jwt_signing_profile) ([#616](https://github.com/avenga/couper/pull/616))

---

Expand Down
9 changes: 1 addition & 8 deletions config/configload/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,15 +331,8 @@ func LoadConfig(body *hclsyntax.Body, src [][]byte, environment string) (*config
expression, _ := profile.Headers.Value(nil)
headers := seetie.ValueToMap(expression)

var errorMessage string
if _, exists := headers["alg"]; exists {
errorMessage = `"alg" cannot be set via "headers"`
} else if _, exists = headers["typ"]; exists {
errorMessage = `"typ" cannot be set via "headers"`
}

if errorMessage != "" {
return nil, errors.Configuration.Label(profile.Name).With(fmt.Errorf(errorMessage))
return nil, errors.Configuration.Label(profile.Name).With(fmt.Errorf(`"alg" cannot be set via "headers"`))
}
}

Expand Down
2 changes: 1 addition & 1 deletion config/jwt_signing_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import "github.com/hashicorp/hcl/v2"

type JWTSigningProfile struct {
Claims Claims `hcl:"claims,optional" docs:"claims for the JWT payload, claim values are evaluated per request"`
Headers hcl.Expression `hcl:"headers,optional" docs:"additional header fields for the JWT, {alg} and {typ} cannot be set"`
Headers hcl.Expression `hcl:"headers,optional" docs:"additional header fields for the JWT, {typ} has the default value {JWT}, {alg} cannot be set"`
Key string `hcl:"key,optional" docs:"private key (in PEM format) for {RS*} and {ES*} variants or the secret for {HS*} algorithms. Mutually exclusive with {key_file}."`
KeyFile string `hcl:"key_file,optional" docs:"reference to file containing signing key. Mutually exclusive with {key}. See {key} for more information."`
Name string `hcl:"name,label,optional"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ values: [
},
{
"default": "",
"description": "additional header fields for the JWT, `alg` and `typ` cannot be set",
"description": "additional header fields for the JWT, `typ` has the default value `JWT`, `alg` cannot be set",
"name": "headers",
"type": "object"
},
Expand Down
4 changes: 3 additions & 1 deletion eval/lib/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,9 @@ func CreateJWT(signatureAlgorithm string, key interface{}, mapClaims jwt.MapClai
headers = map[string]interface{}{}
}

headers["typ"] = "JWT"
if _, set := headers["typ"]; !set {
headers["typ"] = "JWT"
}
headers["alg"] = signingMethod.Alg()

// create token
Expand Down
23 changes: 2 additions & 21 deletions eval/lib/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,7 @@ func TestJwtSignDynamic(t *testing.T) {
headers = {
kid = "key-id"
foo = [request.method, backend_responses.default.status]
typ = "at+jwt"
}
claims = {
x-method = "GET"
Expand All @@ -607,7 +608,7 @@ func TestJwtSignDynamic(t *testing.T) {
}
`,
"MyToken",
map[string]interface{}{"alg": "HS256", "typ": "JWT", "kid": "key-id", "foo": []interface{}{"GET", 200}},
map[string]interface{}{"alg": "HS256", "typ": "at+jwt", "kid": "key-id", "foo": []interface{}{"GET", 200}},
`{"sub": "12345"}`,
3600,
http.MethodGet,
Expand Down Expand Up @@ -834,26 +835,6 @@ func TestJwtSignConfigError(t *testing.T) {
`{"sub": "12345"}`,
`configuration error: MyToken: "alg" cannot be set via "headers"`,
},
{
"user-defined typ header",
`
server "test" {
}
definitions {
jwt_signing_profile "MyToken" {
signature_algorithm = "HS256"
key = "$3cRe4"
ttl = "1h"
headers = {
typ = "JET"
}
}
}
`,
"MyToken",
`{"sub": "12345"}`,
`configuration error: MyToken: "typ" cannot be set via "headers"`,
},
}

for _, tt := range tests {
Expand Down
3 changes: 3 additions & 0 deletions oauth2/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ func NewClient(grantType string, asConfig config.OAuth2AS, clientConfig config.O
return nil, err
}
headers = seetie.ValueToMap(v)
if _, exists := headers["alg"]; exists {
return nil, fmt.Errorf(`"alg" cannot be set via "headers"`)
}
}

tokenEndpoint, err := asConfig.GetTokenEndpoint()
Expand Down
24 changes: 24 additions & 0 deletions server/http_oauth2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,30 @@ definitions {
`,
"configuration error: be: client authentication key: read error: open ",
},
{
"alg header with client_secret_jwt",
`server {}
definitions {
backend "be" {
oauth2 {
token_endpoint = "https://authorization.server/token"
client_id = "my_client"
client_secret = "my_client_secret"
grant_type = "client_credentials"
token_endpoint_auth_method = "client_secret_jwt"
jwt_signing_profile {
signature_algorithm = "HS256"
ttl = "10s"
headers = {
alg = "some value"
}
}
}
}
}
`,
"configuration error: be: \"alg\" cannot be set via \"headers\"",
},
} {
var errMsg string
conf, err := configload.LoadBytes([]byte(tc.hcl), "couper.hcl")
Expand Down

0 comments on commit e64cd32

Please sign in to comment.