diff --git a/changelog/13871.txt b/changelog/13871.txt new file mode 100644 index 000000000..c8c5103bb --- /dev/null +++ b/changelog/13871.txt @@ -0,0 +1,3 @@ +```release-note:bug +identity/oidc: Adds support for port-agnostic validation of loopback IP redirect URIs. +``` diff --git a/vault/identity_store_oidc_provider.go b/vault/identity_store_oidc_provider.go index d3a7a260d..e3580a9e1 100644 --- a/vault/identity_store_oidc_provider.go +++ b/vault/identity_store_oidc_provider.go @@ -1513,7 +1513,8 @@ func (i *IdentityStore) pathOIDCAuthorize(ctx context.Context, req *logical.Requ if redirectURI == "" { return authResponse("", state, ErrAuthInvalidRequest, "redirect_uri parameter is required") } - if !strutil.StrListContains(client.RedirectURIs, redirectURI) { + + if !validRedirect(redirectURI, client.RedirectURIs) { return authResponse("", state, ErrAuthInvalidRedirectURI, "redirect_uri is not allowed for the client") } diff --git a/vault/identity_store_oidc_provider_util.go b/vault/identity_store_oidc_provider_util.go new file mode 100644 index 000000000..f5b0dadb8 --- /dev/null +++ b/vault/identity_store_oidc_provider_util.go @@ -0,0 +1,38 @@ +package vault + +import ( + "net/url" + + "github.com/hashicorp/go-secure-stdlib/strutil" +) + +// validRedirect checks whether uri is in allowed using special handling for loopback uris. +// Ref: https://tools.ietf.org/html/rfc8252#section-7.3 +func validRedirect(uri string, allowed []string) bool { + inputURI, err := url.Parse(uri) + if err != nil { + return false + } + + // if uri isn't a loopback, just string search the allowed list + if !strutil.StrListContains([]string{"localhost", "127.0.0.1", "::1"}, inputURI.Hostname()) { + return strutil.StrListContains(allowed, uri) + } + + // otherwise, search for a match in a port-agnostic manner, per the OAuth RFC. + inputURI.Host = inputURI.Hostname() + + for _, a := range allowed { + allowedURI, err := url.Parse(a) + if err != nil { + return false + } + allowedURI.Host = allowedURI.Hostname() + + if inputURI.String() == allowedURI.String() { + return true + } + } + + return false +}