Harden compliance portal auth and TLS

Align console references and OAuth branding with the
compliance-page model, and fix certificate cache eviction,
portal OAuth handlers, and magic-link edge cases left after
the trust-center rename.

Signed-off-by: Bryan Frimin <bryan@probo.com>
This commit is contained in:
Bryan Frimin
2026-07-20 09:59:25 +02:00
parent b03acbd029
commit 43ce3a7c53
51 changed files with 626 additions and 458 deletions

View File

@@ -580,6 +580,19 @@ func (s AuthService) SendMagicLink(ctx context.Context, req *SendMagicLinkReques
return fmt.Errorf("cannot generate magic link token: %w", err)
}
senderName := magicLinkDefaultSenderName
if req.OAuth2ClientIDRaw != nil && *req.OAuth2ClientIDRaw != "" {
branding, err := s.OAuth2ServerService.ClientBranding(ctx, *req.OAuth2ClientIDRaw)
if err != nil {
return fmt.Errorf("cannot load oauth2 client branding: %w", err)
}
if branding != nil {
senderName = branding.Name
}
}
return s.pg.WithTx(
ctx,
func(ctx context.Context, tx pg.Tx) error {
@@ -596,7 +609,6 @@ func (s AuthService) SendMagicLink(ctx context.Context, req *SendMagicLinkReques
fullName := req.Email.Username()
identity := &coredata.Identity{}
senderName := magicLinkDefaultSenderName
if err := identity.LoadByEmail(ctx, tx, req.Email); err == nil {
if identity.FullName != "" {
@@ -608,17 +620,6 @@ func (s AuthService) SendMagicLink(ctx context.Context, req *SendMagicLinkReques
}
}
if req.OAuth2ClientIDRaw != nil && *req.OAuth2ClientIDRaw != "" {
branding, err := s.OAuth2ServerService.ClientBranding(ctx, *req.OAuth2ClientIDRaw)
if err != nil {
return fmt.Errorf("cannot load oauth2 client branding: %w", err)
}
if branding != nil {
senderName = branding.Name
}
}
emailPresenterCfg := emails.DefaultPresenterConfig(s.baseURL)
if req.MagicLinkBaseURL != nil {

View File

@@ -26,7 +26,6 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"strings"
"time"
"go.probo.inc/probo/pkg/coredata"
@@ -117,25 +116,6 @@ func NewIDTokenClaims(
return claims
}
func ParseIDTokenClaims(raw string) (*IDTokenClaims, error) {
parts := strings.Split(raw, ".")
if len(parts) != 3 {
return nil, fmt.Errorf("cannot parse id token: invalid format")
}
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return nil, fmt.Errorf("cannot parse id token payload: %w", err)
}
var claims IDTokenClaims
if err := json.Unmarshal(payload, &claims); err != nil {
return nil, fmt.Errorf("cannot decode id token claims: %w", err)
}
return &claims, nil
}
func ParseIDTokenIdentity(
raw string,
jwks *jose.JWKS,

View File

@@ -756,28 +756,34 @@ func (s *OrganizationService) CreateOrganization(
return fmt.Errorf("cannot insert mailing list: %w", err)
}
defaultDomainHostname := trustCenter.Slug + "." + s.trustCenterBaseDomain
// Self-managed installs without a configured base domain don't get
// a default managed domain: there is no suffix to mint a
// "{slug}." hostname from, so the compliance page stays without
// a domain until the organization adds a custom one.
if s.trustCenterBaseDomain != "" {
defaultDomainHostname := trustCenter.Slug + "." + s.trustCenterBaseDomain
defaultDomain := coredata.NewCustomDomain(
tenantID,
organization.ID,
defaultDomainHostname,
true,
)
defaultDomain := coredata.NewCustomDomain(
tenantID,
organization.ID,
defaultDomainHostname,
true,
)
certificate, err := s.certManager.EnsureCertificate(ctx, tx, scope, defaultDomainHostname)
if err != nil {
return fmt.Errorf("cannot ensure certificate for default custom domain: %w", err)
certificate, err := s.certManager.EnsureCertificate(ctx, tx, scope, defaultDomainHostname)
if err != nil {
return fmt.Errorf("cannot ensure certificate for default custom domain: %w", err)
}
defaultDomain.CertificateID = &certificate.ID
if err := defaultDomain.Insert(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot insert default custom domain: %w", err)
}
trustCenter.DefaultDomainID = &defaultDomain.ID
}
defaultDomain.CertificateID = &certificate.ID
if err := defaultDomain.Insert(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot insert default custom domain: %w", err)
}
trustCenter.DefaultDomainID = &defaultDomain.ID
if err := trustCenter.Insert(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot insert trust center: %w", err)
}

View File

@@ -62,7 +62,6 @@ type (
magicLinkTokenValidity time.Duration
sessionDuration time.Duration
bucket string
encryptionKey cipher.EncryptionKey
trustCenterBaseDomain string
certManager *certmanager.Service
certificate *x509.Certificate
@@ -150,6 +149,10 @@ func NewService(
return nil, fmt.Errorf("oauth2 scope registry is required")
}
if cfg.CertManager == nil {
return nil, fmt.Errorf("cert manager is required")
}
svc := &Service{
pg: pgClient,
fm: fm,
@@ -163,7 +166,6 @@ func NewService(
magicLinkTokenValidity: cfg.MagicLinkTokenValidity,
sessionDuration: cfg.SessionDuration,
bucket: cfg.Bucket,
encryptionKey: cfg.EncryptionKey,
trustCenterBaseDomain: cfg.TrustCenterBaseDomain,
certManager: cfg.CertManager,
certificate: cfg.Certificate,