Skip to content

Commit

Permalink
authn/cookie_session: Add extra_from modifier (#335)
Browse files Browse the repository at this point in the history
The extra_from modifier is a GJSON path that points to the extra field. Useful if the upstream API does not return a `{"subject": "...", "extra": "..."}` format.
  • Loading branch information
aeneasr authored Jan 13, 2020
1 parent eb82132 commit ee2b9e7
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 5 deletions.
6 changes: 6 additions & 0 deletions .schemas/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,12 @@
"title": "Preserve Path",
"type": "boolean",
"description": "When set to true, any path specified in `check_session_url` will be preserved instead of overwriting the path with the path from the original request"
},
"extra_from": {
"title": "Extra JSON Path",
"description": "The `extra` field in the ORY Oathkeeper authentication session is set using this JSON Path. Defaults to `extra`, and could be `@this` (for the root element), `foo.bar` (for key foo.bar), or any other valid GJSON path. See [GSJON Syntax](https://github.com/tidwall/gjson/blob/master/SYNTAX.md) for reference.",
"type": "string",
"default": "extra"
}
},
"required": [
Expand Down
30 changes: 25 additions & 5 deletions pipeline/authn/authenticator_cookie_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net/url"

"github.com/pkg/errors"
"github.com/tidwall/gjson"

"github.com/ory/herodot"

Expand All @@ -15,13 +16,20 @@ import (
"github.com/ory/oathkeeper/pipeline"
)

func init() {
gjson.AddModifier("this", func(json, arg string) string {
return json
})
}

type AuthenticatorCookieSessionFilter struct {
}

type AuthenticatorCookieSessionConfiguration struct {
Only []string `json:"only"`
CheckSessionURL string `json:"check_session_url"`
PreservePath bool `json:"preserve_path"`
ExtraFrom string `json:"extra_from"`
}

type AuthenticatorCookieSession struct {
Expand Down Expand Up @@ -53,6 +61,10 @@ func (a *AuthenticatorCookieSession) Config(config json.RawMessage) (*Authentica
return nil, NewErrAuthenticatorMisconfigured(a, err)
}

if len(c.ExtraFrom) == 0 {
c.ExtraFrom = "extra"
}

return &c, nil
}

Expand All @@ -74,17 +86,25 @@ func (a *AuthenticatorCookieSession) Authenticate(r *http.Request, config json.R
}

var session struct {
Subject string `json:"subject"`
Extra map[string]interface{} `json:"extra"`
Subject string `json:"subject"`
}
err = json.Unmarshal(body, &session)
if err != nil {
if err = json.Unmarshal(body, &session); err != nil {
return nil, helper.ErrForbidden.WithReason(err.Error()).WithTrace(err)
}

extra := map[string]interface{}{}
rawExtra := gjson.GetBytes(body, cf.ExtraFrom).Raw
if rawExtra == "" {
rawExtra = "null"
}

if err = json.Unmarshal([]byte(rawExtra), &extra); err != nil {
return nil, helper.ErrForbidden.WithReasonf("The configured GJSON path returned an error on JSON output: %s", err.Error()).WithDebugf("GJSON path: %s\nBody: %s\nResult: %s", cf.ExtraFrom, body, rawExtra).WithTrace(err)
}

return &AuthenticationSession{
Subject: session.Subject,
Extra: session.Extra,
Extra: extra,
}, nil
}

Expand Down
27 changes: 27 additions & 0 deletions pipeline/authn/authenticator_cookie_session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,33 @@ func TestAuthenticatorCookieSession(t *testing.T) {
require.NoError(t, err, "%#v", errors.Cause(err))
})

t.Run("description=should work with nested extra keys", func(t *testing.T) {
testServer, _ := makeServer(200, `{"subject": "123", "session": {"foo": "bar"}}`)
session, err := pipelineAuthenticator.Authenticate(
makeRequest("GET", "/", map[string]string{"sessionid": "zyx"}, ""),
json.RawMessage(fmt.Sprintf(`{"check_session_url": "%s", "extra_from": "session"}`, testServer.URL)),
nil,
)
require.NoError(t, err, "%#v", errors.Cause(err))
assert.Equal(t, &AuthenticationSession{
Subject: "123",
Extra: map[string]interface{}{"foo": "bar"},
}, session)
})

t.Run("description=should work with the root key", func(t *testing.T) {
testServer, _ := makeServer(200, `{"subject": "123", "session": {"foo": "bar"}}`)
session, err := pipelineAuthenticator.Authenticate(
makeRequest("GET", "/", map[string]string{"sessionid": "zyx"}, ""),
json.RawMessage(fmt.Sprintf(`{"check_session_url": "%s", "extra_from": "@this"}`, testServer.URL)),
nil,
)
require.NoError(t, err, "%#v", errors.Cause(err))
assert.Equal(t, &AuthenticationSession{
Subject: "123",
Extra: map[string]interface{}{"session": map[string]interface{}{"foo": "bar"}, "subject": "123"},
}, session)
})
})
}

Expand Down

0 comments on commit ee2b9e7

Please sign in to comment.