Skip to content

Commit

Permalink
proxy: Use scp,scope,scopes in jwt authenticator
Browse files Browse the repository at this point in the history
Previously, the JWT authenticator only used the "scope" claim to retrieve scope values from a JWT. Now, "scp", "scope", "scopes" are supported as string arrays and strings separated by spaces.

Closes #138

Signed-off-by: aeneasr <aeneas@ory.sh>
  • Loading branch information
aeneasr committed Apr 5, 2019
1 parent 6fb7151 commit 99817c4
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 2 deletions.
37 changes: 35 additions & 2 deletions proxy/authenticator_jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"net/url"
"strings"

"github.com/ory/x/stringsx"

"github.com/dgrijalva/jwt-go"
"github.com/pkg/errors"
"gopkg.in/square/go-jose.v2"
Expand Down Expand Up @@ -148,9 +150,8 @@ func (a *AuthenticatorJWT) Authenticate(r *http.Request, config json.RawMessage,
}

if a.scopeStrategy != nil {
tokenScope := mapx.GetStringSliceDefault(map[interface{}]interface{}{"scope": claims["scope"]}, "scope", []string{})
for _, scope := range cf.Scopes {
if !a.scopeStrategy(tokenScope, scope) {
if !a.scopeStrategy(getScopeClaim(claims), scope) {
return nil, errors.WithStack(helper.ErrForbidden.WithReason(fmt.Sprintf("Token is missing required scope %s.", scope)))
}
}
Expand All @@ -166,6 +167,38 @@ func (a *AuthenticatorJWT) Authenticate(r *http.Request, config json.RawMessage,
}, nil
}

func getScopeClaim(claims map[string]interface{}) []string {
var ok bool
var interim interface{}

for _, k := range []string{"scp", "scope", "scopes"} {
if interim, ok = claims[k]; ok {
break
}
}

if !ok {
return []string{}
}

switch i := interim.(type) {
case []string:
return i
case []interface{}:
vs := make([]string, len(i))
for k, v := range i {
if vv, ok := v.(string); ok {
vs[k] = vv
}
}
return vs
case string:
return stringsx.Splitx(i, " ")
default:
return []string{}
}
}

func (a *AuthenticatorJWT) findRSAPublicKey(t *jwt.Token) (*rsa.PublicKey, error) {
keys, err := a.fetcher.Resolve(a.jwksURL, false)
if err != nil {
Expand Down
22 changes: 22 additions & 0 deletions proxy/authenticator_jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,3 +280,25 @@ func TestAuthenticatorJWT(t *testing.T) {
})
}
}

func TestGetScopeClaim(t *testing.T) {
for k, tc := range []struct {
i map[string]interface{}
e []string
}{
{i: map[string]interface{}{}, e: []string{}},
{i: map[string]interface{}{"scp": "foo bar"}, e: []string{"foo", "bar"}},
{i: map[string]interface{}{"scope": "foo bar"}, e: []string{"foo", "bar"}},
{i: map[string]interface{}{"scopes": "foo bar"}, e: []string{"foo", "bar"}},
{i: map[string]interface{}{"scp": []string{"foo", "bar"}}, e: []string{"foo", "bar"}},
{i: map[string]interface{}{"scope": []string{"foo", "bar"}}, e: []string{"foo", "bar"}},
{i: map[string]interface{}{"scopes": []string{"foo", "bar"}}, e: []string{"foo", "bar"}},
{i: map[string]interface{}{"scp": []interface{}{"foo", "bar"}}, e: []string{"foo", "bar"}},
{i: map[string]interface{}{"scope": []interface{}{"foo", "bar"}}, e: []string{"foo", "bar"}},
{i: map[string]interface{}{"scopes": []interface{}{"foo", "bar"}}, e: []string{"foo", "bar"}},
} {
t.Run(fmt.Sprintf("case=%d", k), func(t *testing.T) {
assert.EqualValues(t, tc.e, getScopeClaim(tc.i))
})
}
}

0 comments on commit 99817c4

Please sign in to comment.