feat: sync Telegram Login OIDC provider
This commit is contained in:
parent
30774f8c39
commit
ebead9e98c
63 changed files with 11374 additions and 37 deletions
115
internal/app/telegramlogin/url_test.go
Normal file
115
internal/app/telegramlogin/url_test.go
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
package telegramlogin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
func TestNormalizeRedirectURIIsExactAndRejectsOpenRedirectShapes(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
raw string
|
||||
allowHTTP bool
|
||||
want string
|
||||
valid bool
|
||||
}{
|
||||
{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: "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"},
|
||||
{name: "leading whitespace", raw: " https://example.com/callback"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got, _, err := NormalizeRedirectURI(test.raw, test.allowHTTP)
|
||||
if test.valid {
|
||||
if err != nil || got != test.want {
|
||||
t.Fatalf("NormalizeRedirectURI() = %q,%v, want %q,nil", got, err, test.want)
|
||||
}
|
||||
} else if !errors.Is(err, domain.ErrTelegramLoginURLInvalid) {
|
||||
t.Fatalf("NormalizeRedirectURI() error = %v, want URL invalid", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendAuthorizationErrorPreservesState(t *testing.T) {
|
||||
got, err := AppendAuthorizationError("https://example.com/callback?tenant=one", "access_denied", "opaque")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
u, _ := url.Parse(got)
|
||||
if u.Query().Get("tenant") != "one" || u.Query().Get("error") != "access_denied" || u.Query().Get("state") != "opaque" {
|
||||
t.Fatalf("error redirect = %q", got)
|
||||
}
|
||||
if _, err := AppendAuthorizationError("https://example.com/callback", "invalid_client", ""); err == nil {
|
||||
t.Fatal("unsafe authorization error unexpectedly accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeWebOriginRejectsPathAndQuery(t *testing.T) {
|
||||
if got, err := NormalizeWebOrigin("https://Example.com/", false); err != nil || got != "https://example.com" {
|
||||
t.Fatalf("NormalizeWebOrigin = %q,%v", got, err)
|
||||
}
|
||||
for _, raw := range []string{"https://example.com/path", "https://example.com/?x=1", "https://example.com/#x"} {
|
||||
if _, err := NormalizeWebOrigin(raw, false); !errors.Is(err, domain.ErrTelegramLoginURLInvalid) {
|
||||
t.Fatalf("NormalizeWebOrigin(%q) error = %v, want URL invalid", raw, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeLoopbackIPv6PreservesURLBrackets(t *testing.T) {
|
||||
origin, err := NormalizeWebOrigin("http://[0:0:0:0:0:0:0:1]:80/", true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if origin != "http://[0:0:0:0:0:0:0:1]" {
|
||||
t.Fatalf("origin=%q", origin)
|
||||
}
|
||||
redirect, domainName, err := NormalizeRedirectURI("http://[::1]/callback", true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if redirect != "http://[::1]/callback" || domainName != "::1" {
|
||||
t.Fatalf("redirect=%q domain=%q", redirect, domainName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPKCERFC7636Vector(t *testing.T) {
|
||||
const verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
|
||||
const want = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
|
||||
got, err := PKCEChallenge(verifier)
|
||||
if err != nil || got != want {
|
||||
t.Fatalf("PKCEChallenge = %q,%v, want %q,nil", got, err, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodeSealerUsesAADAndRetiringKeys(t *testing.T) {
|
||||
oldKey := make([]byte, 32)
|
||||
newKey := make([]byte, 32)
|
||||
oldKey[0], newKey[0] = 1, 2
|
||||
old, err := NewCodeSealer("old", map[string][]byte{"old": oldKey})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sealed, nonce, keyID, err := old.Seal("authorization-code", []byte("request-1"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rotated, err := NewCodeSealer("new", map[string][]byte{"old": oldKey, "new": newKey})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, err := rotated.Open(sealed, nonce, keyID, []byte("request-1")); err != nil || got != "authorization-code" {
|
||||
t.Fatalf("Open after rotation = %q,%v", got, err)
|
||||
}
|
||||
if _, err := rotated.Open(sealed, nonce, keyID, []byte("request-2")); !errors.Is(err, domain.ErrTelegramLoginCodeInvalid) {
|
||||
t.Fatalf("Open with wrong AAD error = %v", err)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue