feat(api): surface effective Authentik public paths in specs

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-02 13:51:54 +00:00
parent a07896d88e
commit a6693e1656
3 changed files with 35 additions and 10 deletions

View file

@ -398,8 +398,8 @@ func TestAPISpecCmd_Good_AuthentikPublicPathsAreNormalised(t *testing.T) {
if !ok {
t.Fatalf("expected x-authentik-public-paths array, got %T", spec["x-authentik-public-paths"])
}
if len(paths) != 2 || paths[0] != "/public" || paths[1] != "/docs" {
t.Fatalf("expected normalised public paths [/public /docs], got %v", paths)
if len(paths) != 4 || paths[0] != "/health" || paths[1] != "/swagger" || paths[2] != "/public" || paths[3] != "/docs" {
t.Fatalf("expected normalised public paths [/health /swagger /public /docs], got %v", paths)
}
}
@ -867,8 +867,8 @@ func TestAPISpecCmd_Good_AuthentikFlagsPopulateSpecMetadata(t *testing.T) {
if !ok {
t.Fatalf("expected x-authentik-public-paths array, got %T", spec["x-authentik-public-paths"])
}
if len(publicPaths) != 2 || publicPaths[0] != "/public" || publicPaths[1] != "/docs" {
t.Fatalf("expected public paths [/public /docs], got %v", publicPaths)
if len(publicPaths) != 4 || publicPaths[0] != "/health" || publicPaths[1] != "/swagger" || publicPaths[2] != "/public" || publicPaths[3] != "/docs" {
t.Fatalf("expected public paths [/health /swagger /public /docs], got %v", publicPaths)
}
}
@ -1151,8 +1151,8 @@ func TestAPISDKCmd_Good_TempSpecUsesMetadataFlags(t *testing.T) {
if !ok {
t.Fatalf("expected x-authentik-public-paths array, got %T", spec["x-authentik-public-paths"])
}
if len(publicPaths) != 2 || publicPaths[0] != "/public" || publicPaths[1] != "/docs" {
t.Fatalf("expected public paths [/public /docs], got %v", publicPaths)
if len(publicPaths) != 4 || publicPaths[0] != "/health" || publicPaths[1] != "/swagger" || publicPaths[2] != "/docs" || publicPaths[3] != "/public" {
t.Fatalf("expected public paths [/health /swagger /docs /public], got %v", publicPaths)
}
if info["termsOfService"] != "https://example.com/terms" {

View file

@ -169,8 +169,8 @@ func (sb *SpecBuilder) Build(groups []RouteGroup) ([]byte, error) {
if sb.AuthentikTrustedProxy {
spec["x-authentik-trusted-proxy"] = true
}
if len(sb.AuthentikPublicPaths) > 0 {
spec["x-authentik-public-paths"] = slices.Clone(sb.AuthentikPublicPaths)
if paths := sb.effectiveAuthentikPublicPaths(); len(paths) > 0 {
spec["x-authentik-public-paths"] = paths
}
if sb.TermsOfService != "" {
@ -1905,6 +1905,31 @@ func (sb *SpecBuilder) effectiveSSEPath() string {
return ssePath
}
// effectiveAuthentikPublicPaths returns the public paths that Authentik skips
// in practice, including the always-public health and Swagger endpoints.
func (sb *SpecBuilder) effectiveAuthentikPublicPaths() []string {
if !sb.hasAuthentikMetadata() {
return nil
}
paths := []string{"/health", "/swagger", resolveSwaggerPath(sb.SwaggerPath)}
paths = append(paths, sb.AuthentikPublicPaths...)
return normalisePublicPaths(paths)
}
// hasAuthentikMetadata reports whether the spec carries any Authentik-related
// configuration worth surfacing.
func (sb *SpecBuilder) hasAuthentikMetadata() bool {
if sb == nil {
return false
}
return strings.TrimSpace(sb.AuthentikIssuer) != "" ||
strings.TrimSpace(sb.AuthentikClientID) != "" ||
sb.AuthentikTrustedProxy ||
len(sb.AuthentikPublicPaths) > 0
}
// documentedResponseHeaders converts route-specific response header metadata
// into OpenAPI header objects.
func documentedResponseHeaders(headers map[string]string) map[string]any {

View file

@ -152,8 +152,8 @@ func TestEngine_Good_OpenAPISpecBuilderCarriesEngineMetadata(t *testing.T) {
if !ok {
t.Fatalf("expected x-authentik-public-paths array, got %T", spec["x-authentik-public-paths"])
}
if len(publicPaths) != 2 || publicPaths[0] != "/public" || publicPaths[1] != "/docs" {
t.Fatalf("expected public paths [/public /docs], got %v", publicPaths)
if len(publicPaths) != 4 || publicPaths[0] != "/health" || publicPaths[1] != "/swagger" || publicPaths[2] != "/docs" || publicPaths[3] != "/public" {
t.Fatalf("expected public paths [/health /swagger /docs /public], got %v", publicPaths)
}
contact, ok := info["contact"].(map[string]any)