Skip to content

Commit

Permalink
fix: native clients must be able to request a dynamic port rfc8252#se…
Browse files Browse the repository at this point in the history
…ction-7.3

fixes ory#284
  • Loading branch information
raffis committed Mar 3, 2020
1 parent 67c081c commit d8a93d5
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 1 deletion.
37 changes: 36 additions & 1 deletion authorize_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ package fosite

import (
"net/url"
"regexp"
"strings"

"github.com/asaskevich/govalidator"
Expand Down Expand Up @@ -83,7 +84,7 @@ func MatchRedirectURIWithClientRedirectURIs(rawurl string, client Client) (*url.
// If no redirect_uri was given and the client has exactly one valid redirect_uri registered, use that instead
return redirectURIFromClient, nil
}
} else if rawurl != "" && StringInSlice(rawurl, client.GetRedirectURIs()) {
} else if rawurl != "" && isMatchingRedirectURI(rawurl, client.GetRedirectURIs()) {
// If a redirect_uri was given and the clients knows it (simple string comparison!)
// return it.
if parsed, err := url.Parse(rawurl); err == nil && IsValidRedirectURI(parsed) {
Expand All @@ -95,6 +96,40 @@ func MatchRedirectURIWithClientRedirectURIs(rawurl string, client Client) (*url.
return nil, errors.WithStack(ErrInvalidRequest.WithHint(`The "redirect_uri" parameter does not match any of the OAuth 2.0 Client's pre-registered redirect urls.`))
}

// Match a requested redirect URI against a pool of registered client URIs
//
// Test a given redirect URI against a pool of URIs provided by a registered client.
// If the OAuth 2.0 Client has loopback URIs registered either an IPv4 URI http://127.0.0.1 or
// an IPv6 URI http://[::1] a client is allowed to request a dynamic port and the server MUST accept
// it as a valid redirection uri.
//
// https://tools.ietf.org/html/rfc8252#section-7.3
// Native apps that are able to open a port on the loopback network
// interface without needing special permissions (typically, those on
// desktop operating systems) can use the loopback interface to receive
// the OAuth redirect.
//
// Loopback redirect URIs use the "http" scheme and are constructed with
// the loopback IP literal and whatever port the client is listening on.
func isMatchingRedirectURI(uri string, haystack []string) bool {
for _, b := range haystack {
l := strings.ToLower(b)
if l == strings.ToLower(uri) || isLoopbackURI(uri, l) {
return true
}
}
return false
}

func isLoopbackURI(uri string, registeredURI string) bool {
if registeredURI != "http://127.0.0.1" && registeredURI != "http://[::1]" {
return false
}

match, _ := regexp.MatchString("http://(127.0.0.1|\\[::1\\]):?(\\d+)?", uri)
return match
}

// IsValidRedirectURI validates a redirect_uri as specified in:
//
// * https://tools.ietf.org/html/rfc6749#section-3.1.2
Expand Down
40 changes: 40 additions & 0 deletions authorize_helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,46 @@ func TestDoesClientWhiteListRedirect(t *testing.T) {
url: "https://bar.com/cb123",
isError: true,
},
{
client: &DefaultClient{RedirectURIs: []string{"http://[::1]"}},
url: "http://[::1]:1024",
isError: false,
expected: "http://[::1]:1024",
},
{
client: &DefaultClient{RedirectURIs: []string{"http://[::1]"}},
url: "http://[::1]:1024/cb",
isError: false,
expected: "http://[::1]:1024/cb",
},
{
client: &DefaultClient{RedirectURIs: []string{"http://[::1]"}},
url: "http://foo.bar/bar",
isError: true,
},
{
client: &DefaultClient{RedirectURIs: []string{"http://127.0.0.1"}},
url: "http://127.0.0.1:1024",
isError: false,
expected: "http://127.0.0.1:1024",
},
{
client: &DefaultClient{RedirectURIs: []string{"http://127.0.0.1"}},
url: "http://127.0.0.1:64000/cb",
isError: false,
expected: "http://127.0.0.1:64000/cb",
},
{
client: &DefaultClient{RedirectURIs: []string{"http://127.0.0.1"}},
url: "http://127.0.0.1",
isError: false,
expected: "http://127.0.0.1",
},
{
client: &DefaultClient{RedirectURIs: []string{"http://127.0.0.1"}},
url: "http://foo.bar/bar",
isError: true,
},
} {
redir, err := MatchRedirectURIWithClientRedirectURIs(c.url, c.client)
assert.Equal(t, c.isError, err != nil, "%d: %s", k, err)
Expand Down

0 comments on commit d8a93d5

Please sign in to comment.