feat: sync HTTP callback OIDC setup
This commit is contained in:
parent
bf72c246b6
commit
f53579416e
26 changed files with 557 additions and 133 deletions
|
|
@ -22,7 +22,7 @@ func newBotFatherTelegramLoginService(t *testing.T) *telegramloginapp.Service {
|
|||
pepper := make([]byte, 32)
|
||||
pepper[0] = 2
|
||||
service, err := telegramloginapp.NewService(memory.NewTelegramLoginStore(nil), sealer, telegramloginapp.Config{
|
||||
Issuer: "http://localhost:2404", AppScheme: "telesrv", AllowLoopbackHTTP: true,
|
||||
Issuer: "http://192.0.2.25:2404", AppScheme: "telesrv", AllowHTTP: true,
|
||||
ClientSecretPepper: pepper, Now: func() time.Time { return time.Unix(1_780_000_000, 0).UTC() },
|
||||
})
|
||||
if err != nil {
|
||||
|
|
@ -52,13 +52,13 @@ func TestBotFatherTelegramLoginConfigurationFlow(t *testing.T) {
|
|||
if len(secret) < 32 {
|
||||
t.Fatalf("client secret is unexpectedly short: %q", secret)
|
||||
}
|
||||
if reply := sendToBotFather(t, svc, messages, owner, "add origin http://localhost:3000"); !strings.Contains(reply, "Success!") {
|
||||
if reply := sendToBotFather(t, svc, messages, owner, "add origin http://rp.example.test:3000"); !strings.Contains(reply, "Success!") {
|
||||
t.Fatalf("add origin reply = %q", reply)
|
||||
}
|
||||
|
||||
sendToBotFather(t, svc, messages, owner, "/setlogin")
|
||||
sendToBotFather(t, svc, messages, owner, "login_demo_bot")
|
||||
if reply := sendToBotFather(t, svc, messages, owner, "add redirect http://localhost:3000/auth/callback"); !strings.Contains(reply, "Success!") {
|
||||
if reply := sendToBotFather(t, svc, messages, owner, "add redirect http://192.0.2.26:3000/auth/callback"); !strings.Contains(reply, "Success!") {
|
||||
t.Fatalf("add redirect reply = %q", reply)
|
||||
}
|
||||
|
||||
|
|
@ -83,7 +83,7 @@ func TestBotFatherTelegramLoginConfigurationFlow(t *testing.T) {
|
|||
|
||||
sendToBotFather(t, svc, messages, owner, "/logininfo")
|
||||
info := sendToBotFather(t, svc, messages, owner, "login_demo_bot")
|
||||
for _, want := range []string{"Signing algorithm: ES256", "web_origin http://localhost:3000", "redirect_uri http://localhost:3000/auth/callback", "dev.bedolaga.demo", "Bedolaga iOS Demo", "Bedolaga Android Demo"} {
|
||||
for _, want := range []string{"Signing algorithm: ES256", "web_origin http://rp.example.test:3000", "redirect_uri http://192.0.2.26:3000/auth/callback", "dev.bedolaga.demo", "Bedolaga iOS Demo", "Bedolaga Android Demo"} {
|
||||
if !strings.Contains(info, want) {
|
||||
t.Fatalf("login info = %q, missing %q", info, want)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -293,9 +293,10 @@ func (r *SigningKeyRing) sign(algorithm domain.TelegramLoginSigningAlgorithm, to
|
|||
}
|
||||
|
||||
type IDTokenIssuerConfig struct {
|
||||
Issuer string
|
||||
TTL time.Duration
|
||||
Now func() time.Time
|
||||
Issuer string
|
||||
TTL time.Duration
|
||||
Now func() time.Time
|
||||
AllowHTTP bool
|
||||
}
|
||||
|
||||
type IDTokenIssuer struct {
|
||||
|
|
@ -337,7 +338,7 @@ func NewIDTokenIssuer(keys *SigningKeyRing, cfg IDTokenIssuerConfig) (*IDTokenIs
|
|||
if keys == nil {
|
||||
return nil, errors.New("telegram login signing key ring is required")
|
||||
}
|
||||
issuer, err := NormalizeWebOrigin(cfg.Issuer, true)
|
||||
issuer, err := NormalizeWebOrigin(cfg.Issuer, cfg.AllowHTTP)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("telegram login ID token issuer: %w", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,6 +167,21 @@ func TestIDTokenIssuerScopeProjectionAndVerification(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestIDTokenIssuerAcceptsHTTPIPOnlyWhenEnabled(t *testing.T) {
|
||||
now := time.Date(2026, 7, 21, 10, 0, 0, 0, time.UTC)
|
||||
ring := telegramLoginTestSigningKeys(t, &now)
|
||||
if _, err := NewIDTokenIssuer(ring, IDTokenIssuerConfig{Issuer: "http://192.0.2.25:2401"}); err == nil {
|
||||
t.Fatal("HTTP issuer was accepted while AllowHTTP was false")
|
||||
}
|
||||
issuer, err := NewIDTokenIssuer(ring, IDTokenIssuerConfig{Issuer: "http://192.0.2.25:2401", AllowHTTP: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if issuer.Issuer() != "http://192.0.2.25:2401" {
|
||||
t.Fatalf("issuer=%q", issuer.Issuer())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSigningKeyRingRejectsWrongCurveAndDuplicateActiveKey(t *testing.T) {
|
||||
p384, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func normalizeNativeVerificationID(platform domain.TelegramLoginNativePlatform,
|
|||
// non-web custom scheme registered for a native application. Query and
|
||||
// fragment components are forbidden because OAuth response fields are
|
||||
// appended by the provider and must not collide with application input.
|
||||
func NormalizeNativeCallbackURI(raw string, allowLoopbackHTTP bool) (string, error) {
|
||||
func NormalizeNativeCallbackURI(raw string, allowHTTP bool) (string, error) {
|
||||
if raw == "" || len(raw) > maxTelegramLoginURLLength || raw != strings.TrimSpace(raw) || strings.IndexFunc(raw, unicode.IsControl) >= 0 {
|
||||
return "", domain.ErrTelegramLoginURLInvalid
|
||||
}
|
||||
|
|
@ -54,7 +54,7 @@ func NormalizeNativeCallbackURI(raw string, allowLoopbackHTTP bool) (string, err
|
|||
return "", domain.ErrTelegramLoginURLInvalid
|
||||
}
|
||||
if strings.EqualFold(u.Scheme, "http") || strings.EqualFold(u.Scheme, "https") {
|
||||
normalized, _, err := NormalizeRedirectURI(raw, allowLoopbackHTTP)
|
||||
normalized, _, err := NormalizeRedirectURI(raw, allowHTTP)
|
||||
return normalized, err
|
||||
}
|
||||
scheme := strings.ToLower(u.Scheme)
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ var telegramLoginMatchCodePool = []string{
|
|||
type Config struct {
|
||||
Issuer string
|
||||
AppScheme string
|
||||
AllowLoopbackHTTP bool
|
||||
AllowHTTP bool
|
||||
ClientSecretPepper []byte
|
||||
SupportedSigningAlgorithms []domain.TelegramLoginSigningAlgorithm
|
||||
RequestTTL time.Duration
|
||||
|
|
@ -49,7 +49,7 @@ type Service struct {
|
|||
sealer *CodeSealer
|
||||
issuer string
|
||||
appScheme string
|
||||
allowLoopbackHTTP bool
|
||||
allowHTTP bool
|
||||
clientSecretPepper []byte
|
||||
signingAlgorithms []domain.TelegramLoginSigningAlgorithm
|
||||
signingAlgorithmSet map[domain.TelegramLoginSigningAlgorithm]struct{}
|
||||
|
|
@ -62,7 +62,7 @@ func NewService(loginStore store.TelegramLoginStore, sealer *CodeSealer, cfg Con
|
|||
if loginStore == nil || sealer == nil || len(cfg.ClientSecretPepper) < 32 {
|
||||
return nil, fmt.Errorf("telegram login dependencies are incomplete")
|
||||
}
|
||||
issuer, err := NormalizeWebOrigin(cfg.Issuer, cfg.AllowLoopbackHTTP)
|
||||
issuer, err := NormalizeWebOrigin(cfg.Issuer, cfg.AllowHTTP)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("telegram login issuer: %w", err)
|
||||
}
|
||||
|
|
@ -93,7 +93,7 @@ func NewService(loginStore store.TelegramLoginStore, sealer *CodeSealer, cfg Con
|
|||
}
|
||||
return &Service{
|
||||
store: loginStore, sealer: sealer, issuer: issuer, appScheme: strings.ToLower(cfg.AppScheme),
|
||||
allowLoopbackHTTP: cfg.AllowLoopbackHTTP,
|
||||
allowHTTP: cfg.AllowHTTP,
|
||||
clientSecretPepper: append([]byte(nil), cfg.ClientSecretPepper...),
|
||||
signingAlgorithms: append([]domain.TelegramLoginSigningAlgorithm(nil), cfg.SupportedSigningAlgorithms...),
|
||||
signingAlgorithmSet: signingAlgorithmSet,
|
||||
|
|
@ -248,9 +248,9 @@ func (s *Service) AddAllowedURL(ctx context.Context, botUserID int64, kind domai
|
|||
var err error
|
||||
switch kind {
|
||||
case domain.TelegramLoginAllowedWebOrigin:
|
||||
normalized, err = NormalizeWebOrigin(raw, s.allowLoopbackHTTP)
|
||||
normalized, err = NormalizeWebOrigin(raw, s.allowHTTP)
|
||||
case domain.TelegramLoginAllowedRedirectURI:
|
||||
normalized, _, err = NormalizeRedirectURI(raw, s.allowLoopbackHTTP)
|
||||
normalized, _, err = NormalizeRedirectURI(raw, s.allowHTTP)
|
||||
default:
|
||||
err = domain.ErrTelegramLoginURLInvalid
|
||||
}
|
||||
|
|
@ -267,9 +267,9 @@ func (s *Service) DeleteAllowedURL(ctx context.Context, botUserID int64, kind do
|
|||
var err error
|
||||
switch kind {
|
||||
case domain.TelegramLoginAllowedWebOrigin:
|
||||
normalized, err = NormalizeWebOrigin(raw, s.allowLoopbackHTTP)
|
||||
normalized, err = NormalizeWebOrigin(raw, s.allowHTTP)
|
||||
case domain.TelegramLoginAllowedRedirectURI:
|
||||
normalized, _, err = NormalizeRedirectURI(raw, s.allowLoopbackHTTP)
|
||||
normalized, _, err = NormalizeRedirectURI(raw, s.allowHTTP)
|
||||
default:
|
||||
err = domain.ErrTelegramLoginURLInvalid
|
||||
}
|
||||
|
|
@ -311,7 +311,7 @@ func (s *Service) AddNativeApp(ctx context.Context, botUserID int64, platform do
|
|||
if err != nil {
|
||||
return domain.TelegramLoginNativeApp{}, err
|
||||
}
|
||||
callbackURI, err = NormalizeNativeCallbackURI(callbackURI, s.allowLoopbackHTTP)
|
||||
callbackURI, err = NormalizeNativeCallbackURI(callbackURI, s.allowHTTP)
|
||||
if err != nil {
|
||||
return domain.TelegramLoginNativeApp{}, err
|
||||
}
|
||||
|
|
@ -335,7 +335,7 @@ func (s *Service) DeleteNativeApp(ctx context.Context, botUserID, appID int64) (
|
|||
}
|
||||
|
||||
func (s *Service) matchNativeApp(ctx context.Context, botUserID int64, platform domain.TelegramLoginNativePlatform, rawCallbackURI string) (domain.TelegramLoginNativeApp, string, bool, error) {
|
||||
callbackURI, err := NormalizeNativeCallbackURI(rawCallbackURI, s.allowLoopbackHTTP)
|
||||
callbackURI, err := NormalizeNativeCallbackURI(rawCallbackURI, s.allowHTTP)
|
||||
if err != nil {
|
||||
return domain.TelegramLoginNativeApp{}, "", false, nil
|
||||
}
|
||||
|
|
@ -362,7 +362,7 @@ func (s *Service) ValidateMessageButton(ctx context.Context, botUserID int64, ra
|
|||
if !found || !client.Enabled {
|
||||
return "", "", domain.ErrTelegramLoginClientDisabled
|
||||
}
|
||||
normalizedURL, domainName, err = NormalizeRedirectURI(rawURL, s.allowLoopbackHTTP)
|
||||
normalizedURL, domainName, err = NormalizeRedirectURI(rawURL, s.allowHTTP)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
|
@ -370,7 +370,7 @@ func (s *Service) ValidateMessageButton(ctx context.Context, botUserID int64, ra
|
|||
if err != nil {
|
||||
return "", "", domain.ErrTelegramLoginURLInvalid
|
||||
}
|
||||
origin, err := NormalizeWebOrigin(u.Scheme+"://"+u.Host, s.allowLoopbackHTTP)
|
||||
origin, err := NormalizeWebOrigin(u.Scheme+"://"+u.Host, s.allowHTTP)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
|
@ -415,7 +415,7 @@ func (s *Service) AuthorizeMessageButton(ctx context.Context, params domain.Tele
|
|||
if err != nil {
|
||||
return domain.TelegramLoginMessageButtonResult{}, domain.ErrTelegramLoginURLInvalid
|
||||
}
|
||||
origin, err := NormalizeWebOrigin(u.Scheme+"://"+u.Host, s.allowLoopbackHTTP)
|
||||
origin, err := NormalizeWebOrigin(u.Scheme+"://"+u.Host, s.allowHTTP)
|
||||
if err != nil {
|
||||
return domain.TelegramLoginMessageButtonResult{}, err
|
||||
}
|
||||
|
|
@ -552,7 +552,7 @@ func (s *Service) CreateAuthorization(ctx context.Context, params CreateAuthoriz
|
|||
}
|
||||
var allowed, isApp bool
|
||||
var nativeApp domain.TelegramLoginNativeApp
|
||||
redirectURI, domainName, redirectErr := NormalizeRedirectURI(params.RedirectURI, s.allowLoopbackHTTP)
|
||||
redirectURI, domainName, redirectErr := NormalizeRedirectURI(params.RedirectURI, s.allowHTTP)
|
||||
if params.ResponseType == "code" && redirectErr == nil && !params.NativePlatform.Valid() {
|
||||
allowed, err = s.store.IsTelegramLoginURLAllowed(ctx, client.BotUserID, domain.TelegramLoginAllowedRedirectURI, redirectURI)
|
||||
if err != nil {
|
||||
|
|
@ -603,14 +603,14 @@ func (s *Service) CreateAuthorization(ctx context.Context, params CreateAuthoriz
|
|||
u, _ := url.Parse(redirectURI)
|
||||
origin = u.Scheme + "://" + u.Host
|
||||
}
|
||||
origin, err = NormalizeWebOrigin(origin, s.allowLoopbackHTTP)
|
||||
origin, err = NormalizeWebOrigin(origin, s.allowHTTP)
|
||||
if err != nil {
|
||||
return CreatedAuthorization{}, err
|
||||
}
|
||||
}
|
||||
if params.ResponseType == "post_message" {
|
||||
redirectURL, _ := url.Parse(redirectURI)
|
||||
redirectOrigin, redirectOriginErr := NormalizeWebOrigin(redirectURL.Scheme+"://"+redirectURL.Host, s.allowLoopbackHTTP)
|
||||
redirectOrigin, redirectOriginErr := NormalizeWebOrigin(redirectURL.Scheme+"://"+redirectURL.Host, s.allowHTTP)
|
||||
if redirectOriginErr != nil || redirectOrigin != origin {
|
||||
return CreatedAuthorization{}, domain.ErrTelegramLoginOriginNotAllowed
|
||||
}
|
||||
|
|
@ -627,7 +627,7 @@ func (s *Service) CreateAuthorization(ctx context.Context, params CreateAuthoriz
|
|||
if isApp {
|
||||
return CreatedAuthorization{}, domain.ErrTelegramLoginOriginNotAllowed
|
||||
}
|
||||
inAppOrigin, err = NormalizeWebOrigin(params.InAppOrigin, s.allowLoopbackHTTP)
|
||||
inAppOrigin, err = NormalizeWebOrigin(params.InAppOrigin, s.allowHTTP)
|
||||
if err != nil {
|
||||
return CreatedAuthorization{}, err
|
||||
}
|
||||
|
|
@ -693,7 +693,7 @@ func (s *Service) ResolveAuthorizationErrorTarget(ctx context.Context, clientID,
|
|||
redirectURI, safe, err := s.safeCodeRedirect(ctx, client.BotUserID, rawRedirectURI)
|
||||
return AuthorizationErrorTarget{ResponseType: responseType, RedirectURI: redirectURI}, safe, err
|
||||
case "post_message":
|
||||
redirectURI, _, err := NormalizeRedirectURI(rawRedirectURI, s.allowLoopbackHTTP)
|
||||
redirectURI, _, err := NormalizeRedirectURI(rawRedirectURI, s.allowHTTP)
|
||||
if err != nil {
|
||||
return AuthorizationErrorTarget{}, false, nil
|
||||
}
|
||||
|
|
@ -702,12 +702,12 @@ func (s *Service) ResolveAuthorizationErrorTarget(ctx context.Context, clientID,
|
|||
redirect, _ := url.Parse(redirectURI)
|
||||
origin = redirect.Scheme + "://" + redirect.Host
|
||||
}
|
||||
origin, err = NormalizeWebOrigin(origin, s.allowLoopbackHTTP)
|
||||
origin, err = NormalizeWebOrigin(origin, s.allowHTTP)
|
||||
if err != nil {
|
||||
return AuthorizationErrorTarget{}, false, nil
|
||||
}
|
||||
redirect, _ := url.Parse(redirectURI)
|
||||
redirectOrigin, err := NormalizeWebOrigin(redirect.Scheme+"://"+redirect.Host, s.allowLoopbackHTTP)
|
||||
redirectOrigin, err := NormalizeWebOrigin(redirect.Scheme+"://"+redirect.Host, s.allowHTTP)
|
||||
if err != nil || redirectOrigin != origin {
|
||||
return AuthorizationErrorTarget{}, false, nil
|
||||
}
|
||||
|
|
@ -725,7 +725,7 @@ func (s *Service) ResolveAuthorizationErrorTarget(ctx context.Context, clientID,
|
|||
}
|
||||
|
||||
func (s *Service) safeCodeRedirect(ctx context.Context, botUserID int64, raw string) (string, bool, error) {
|
||||
if redirectURI, _, err := NormalizeRedirectURI(raw, s.allowLoopbackHTTP); err == nil {
|
||||
if redirectURI, _, err := NormalizeRedirectURI(raw, s.allowHTTP); err == nil {
|
||||
allowed, err := s.store.IsTelegramLoginURLAllowed(ctx, botUserID, domain.TelegramLoginAllowedRedirectURI, redirectURI)
|
||||
if err != nil || allowed {
|
||||
return redirectURI, allowed, err
|
||||
|
|
@ -856,7 +856,7 @@ func (s *Service) RequestByDeepLinkForOrigin(ctx context.Context, rawURL, rawOri
|
|||
if rawOrigin == "" || request.InAppOrigin == "" {
|
||||
return domain.TelegramLoginRequest{}, domain.ErrTelegramLoginOriginNotAllowed
|
||||
}
|
||||
origin, err := NormalizeWebOrigin(rawOrigin, s.allowLoopbackHTTP)
|
||||
origin, err := NormalizeWebOrigin(rawOrigin, s.allowHTTP)
|
||||
if err != nil {
|
||||
return domain.TelegramLoginRequest{}, err
|
||||
}
|
||||
|
|
@ -1067,7 +1067,7 @@ func (s *Service) ExchangeInAppTokenAndIssue(ctx context.Context, token, rawOrig
|
|||
if issuer == nil || len(token) < 16 || len(token) > 1024 || strings.IndexFunc(token, func(r rune) bool { return r <= 0x20 || r == 0x7f }) >= 0 {
|
||||
return IssuedAuthorization{}, domain.ErrTelegramLoginCodeInvalid
|
||||
}
|
||||
origin, err := NormalizeWebOrigin(rawOrigin, s.allowLoopbackHTTP)
|
||||
origin, err := NormalizeWebOrigin(rawOrigin, s.allowHTTP)
|
||||
if err != nil {
|
||||
return IssuedAuthorization{}, domain.ErrTelegramLoginOriginNotAllowed
|
||||
}
|
||||
|
|
@ -1294,7 +1294,7 @@ func (s *Service) exchangeAuthorizationCode(ctx context.Context, params Exchange
|
|||
return ExchangedAuthorization{}, "", domain.ErrTelegramLoginCodeInvalid
|
||||
}
|
||||
} else {
|
||||
redirectURI, _, err = NormalizeRedirectURI(params.RedirectURI, s.allowLoopbackHTTP)
|
||||
redirectURI, _, err = NormalizeRedirectURI(params.RedirectURI, s.allowHTTP)
|
||||
if err != nil {
|
||||
return ExchangedAuthorization{}, "", err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ func newTelegramLoginTestServiceWithAlgorithms(t *testing.T, now *time.Time, alg
|
|||
pepper[0] = 9
|
||||
service, err := NewService(loginStore, sealer, Config{
|
||||
Issuer: "https://oauth.telesrv.test", AppScheme: "telesrv",
|
||||
AllowLoopbackHTTP: true, ClientSecretPepper: pepper,
|
||||
AllowHTTP: true, ClientSecretPepper: pepper,
|
||||
SupportedSigningAlgorithms: algorithms,
|
||||
Now: func() time.Time { return *now },
|
||||
})
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ import (
|
|||
|
||||
const maxTelegramLoginURLLength = 4096
|
||||
|
||||
func NormalizeRedirectURI(raw string, allowLoopbackHTTP bool) (normalized, domainName string, err error) {
|
||||
u, err := parseWebURL(raw, allowLoopbackHTTP)
|
||||
func NormalizeRedirectURI(raw string, allowHTTP bool) (normalized, domainName string, err error) {
|
||||
u, err := parseWebURL(raw, allowHTTP)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
|
@ -34,8 +34,8 @@ func NormalizeRedirectURI(raw string, allowLoopbackHTTP bool) (normalized, domai
|
|||
return u.String(), u.Hostname(), nil
|
||||
}
|
||||
|
||||
func NormalizeWebOrigin(raw string, allowLoopbackHTTP bool) (string, error) {
|
||||
u, err := parseWebURL(raw, allowLoopbackHTTP)
|
||||
func NormalizeWebOrigin(raw string, allowHTTP bool) (string, error) {
|
||||
u, err := parseWebURL(raw, allowHTTP)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
@ -46,7 +46,7 @@ func NormalizeWebOrigin(raw string, allowLoopbackHTTP bool) (string, error) {
|
|||
return u.String(), nil
|
||||
}
|
||||
|
||||
func parseWebURL(raw string, allowLoopbackHTTP bool) (*url.URL, error) {
|
||||
func parseWebURL(raw string, allowHTTP bool) (*url.URL, error) {
|
||||
if raw == "" || len(raw) > maxTelegramLoginURLLength || raw != strings.TrimSpace(raw) || strings.IndexFunc(raw, unicode.IsControl) >= 0 {
|
||||
return nil, domain.ErrTelegramLoginURLInvalid
|
||||
}
|
||||
|
|
@ -78,7 +78,7 @@ func parseWebURL(raw string, allowLoopbackHTTP bool) (*url.URL, error) {
|
|||
port = ""
|
||||
}
|
||||
case "http":
|
||||
if !allowLoopbackHTTP || !isLoopbackHost(host) {
|
||||
if !allowHTTP {
|
||||
return nil, domain.ErrTelegramLoginURLInvalid
|
||||
}
|
||||
if port == "80" {
|
||||
|
|
@ -99,14 +99,6 @@ func parseWebURL(raw string, allowLoopbackHTTP bool) (*url.URL, error) {
|
|||
return u, nil
|
||||
}
|
||||
|
||||
func isLoopbackHost(host string) bool {
|
||||
if host == "localhost" {
|
||||
return true
|
||||
}
|
||||
ip := net.ParseIP(host)
|
||||
return ip != nil && ip.IsLoopback()
|
||||
}
|
||||
|
||||
func AppendAuthorizationResult(redirectURI, code, state string) (string, error) {
|
||||
u, err := url.Parse(redirectURI)
|
||||
if err != nil || !u.IsAbs() || code == "" {
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@ func TestNormalizeRedirectURIIsExactAndRejectsOpenRedirectShapes(t *testing.T) {
|
|||
}{
|
||||
{name: "https canonical", raw: "https://EXAMPLE.com:443/callback?tenant=one", want: "https://example.com/callback?tenant=one", valid: true},
|
||||
{name: "idna", raw: "https://例子.测试/callback", want: "https://xn--fsqu00a.xn--0zwm56d/callback", valid: true},
|
||||
{name: "loopback dev", raw: "http://127.0.0.1:8080/callback", allowHTTP: true, want: "http://127.0.0.1:8080/callback", valid: true},
|
||||
{name: "http production", raw: "http://example.com/callback"},
|
||||
{name: "http hostname enabled", raw: "http://example.com:8080/callback", allowHTTP: true, want: "http://example.com:8080/callback", valid: true},
|
||||
{name: "http ipv4 enabled", raw: "http://192.0.2.25:3000/callback", allowHTTP: true, want: "http://192.0.2.25:3000/callback", valid: true},
|
||||
{name: "http disabled", raw: "http://example.com/callback"},
|
||||
{name: "userinfo", raw: "https://user@example.com/callback"},
|
||||
{name: "fragment", raw: "https://example.com/callback#token"},
|
||||
{name: "reserved code", raw: "https://example.com/callback?code=attacker"},
|
||||
|
|
@ -64,19 +65,19 @@ func TestNormalizeWebOriginRejectsPathAndQuery(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNormalizeLoopbackIPv6PreservesURLBrackets(t *testing.T) {
|
||||
origin, err := NormalizeWebOrigin("http://[0:0:0:0:0:0:0:1]:80/", true)
|
||||
func TestNormalizeHTTPIPv6PreservesURLBrackets(t *testing.T) {
|
||||
origin, err := NormalizeWebOrigin("http://[2001:db8::25]:80/", true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if origin != "http://[0:0:0:0:0:0:0:1]" {
|
||||
if origin != "http://[2001:db8::25]" {
|
||||
t.Fatalf("origin=%q", origin)
|
||||
}
|
||||
redirect, domainName, err := NormalizeRedirectURI("http://[::1]/callback", true)
|
||||
redirect, domainName, err := NormalizeRedirectURI("http://[2001:db8::26]:3000/callback", true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if redirect != "http://[::1]/callback" || domainName != "::1" {
|
||||
if redirect != "http://[2001:db8::26]:3000/callback" || domainName != "2001:db8::26" {
|
||||
t.Fatalf("redirect=%q domain=%q", redirect, domainName)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue