Ignore port for loopback redirect URIs (#21293)
Following https://datatracker.ietf.org/doc/html/rfc8252#section-7.3 Fixes #21285
This commit is contained in:
parent
0e83ab8df7
commit
6a45a691c1
2 changed files with 33 additions and 0 deletions
|
@ -10,6 +10,7 @@ import (
|
||||||
"encoding/base32"
|
"encoding/base32"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -56,6 +57,18 @@ func (app *OAuth2Application) PrimaryRedirectURI() string {
|
||||||
|
|
||||||
// ContainsRedirectURI checks if redirectURI is allowed for app
|
// ContainsRedirectURI checks if redirectURI is allowed for app
|
||||||
func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool {
|
func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool {
|
||||||
|
uri, err := url.Parse(redirectURI)
|
||||||
|
// ignore port for http loopback uris following https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
|
||||||
|
if err == nil && uri.Scheme == "http" && uri.Port() != "" {
|
||||||
|
ip := net.ParseIP(uri.Hostname())
|
||||||
|
if ip != nil && ip.IsLoopback() {
|
||||||
|
// strip port
|
||||||
|
uri.Host = uri.Hostname()
|
||||||
|
if util.IsStringInSlice(uri.String(), app.RedirectURIs, true) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return util.IsStringInSlice(redirectURI, app.RedirectURIs, true)
|
return util.IsStringInSlice(redirectURI, app.RedirectURIs, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,26 @@ func TestOAuth2Application_ContainsRedirectURI(t *testing.T) {
|
||||||
assert.False(t, app.ContainsRedirectURI("d"))
|
assert.False(t, app.ContainsRedirectURI("d"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOAuth2Application_ContainsRedirectURI_WithPort(t *testing.T) {
|
||||||
|
app := &auth_model.OAuth2Application{
|
||||||
|
RedirectURIs: []string{"http://127.0.0.1/", "http://::1/", "http://192.168.0.1/", "http://intranet/", "https://127.0.0.1/"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// http loopback uris should ignore port
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
|
||||||
|
assert.True(t, app.ContainsRedirectURI("http://127.0.0.1:3456/"))
|
||||||
|
assert.True(t, app.ContainsRedirectURI("http://127.0.0.1/"))
|
||||||
|
assert.True(t, app.ContainsRedirectURI("http://[::1]:3456/"))
|
||||||
|
|
||||||
|
// not http
|
||||||
|
assert.False(t, app.ContainsRedirectURI("https://127.0.0.1:3456/"))
|
||||||
|
// not loopback
|
||||||
|
assert.False(t, app.ContainsRedirectURI("http://192.168.0.1:9954/"))
|
||||||
|
assert.False(t, app.ContainsRedirectURI("http://intranet:3456/"))
|
||||||
|
// unparseable
|
||||||
|
assert.False(t, app.ContainsRedirectURI(":"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestOAuth2Application_ValidateClientSecret(t *testing.T) {
|
func TestOAuth2Application_ValidateClientSecret(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
|
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
|
||||||
|
|
Reference in a new issue