[object Object]

← back to Cli Printing Press

fix(cli): generate usable oauth auth config (#617)

5b489da7bc09991d53251be1e847a348b408b016 · 2026-05-05 11:43:05 -0500 · Cathryn Lavery

Files touched

Diff

commit 5b489da7bc09991d53251be1e847a348b408b016
Author: Cathryn Lavery <50469282+cathrynlavery@users.noreply.github.com>
Date:   Tue May 5 11:43:05 2026 -0500

    fix(cli): generate usable oauth auth config (#617)
---
 internal/generator/auth_env_precedence_test.go | 56 ++++++++++++++++++++++++++
 internal/generator/templates/auth.go.tmpl      |  4 +-
 internal/generator/templates/config.go.tmpl    |  2 +-
 3 files changed, 59 insertions(+), 3 deletions(-)

diff --git a/internal/generator/auth_env_precedence_test.go b/internal/generator/auth_env_precedence_test.go
index 18650b0d..381fba78 100644
--- a/internal/generator/auth_env_precedence_test.go
+++ b/internal/generator/auth_env_precedence_test.go
@@ -48,6 +48,62 @@ func TestAuthHeader_ClientCredentialsAccessTokenWinsOverEnv(t *testing.T) {
 		"AccessToken check must appear BEFORE env-var fallback under OAuth2 client_credentials")
 }
 
+func TestAuthHeader_OAuth2AuthorizationCodeUsesToken(t *testing.T) {
+	t.Parallel()
+
+	apiSpec := minimalSpec("oauth-precedence")
+	apiSpec.Auth = spec.AuthConfig{
+		Type:             "oauth2",
+		Header:           "Authorization",
+		Format:           "Bearer {token}",
+		EnvVars:          []string{"OAUTH_AUTH_TEST_TOKEN"},
+		AuthorizationURL: "https://example.com/auth",
+		TokenURL:         "https://example.com/token",
+	}
+
+	outputDir := filepath.Join(t.TempDir(), "oauth-precedence-pp-cli")
+	require.NoError(t, New(apiSpec, outputDir).Generate())
+
+	cfgSrc, err := os.ReadFile(filepath.Join(outputDir, "internal", "config", "config.go"))
+	require.NoError(t, err)
+	content := string(cfgSrc)
+
+	envCheck := "if c." + resolveEnvVarField("OAUTH_AUTH_TEST_TOKEN") + ` != ""`
+	tokenCheck := `if c.AccessToken != ""`
+
+	require.Contains(t, content, envCheck)
+	require.Contains(t, content, tokenCheck)
+
+	body := authHeaderBody(t, content)
+	envIdx := strings.Index(body, envCheck)
+	tokenIdx := strings.Index(body, tokenCheck)
+	assert.Less(t, envIdx, tokenIdx,
+		"env-var bearer fallback should win over file token for OAuth2 authorization_code")
+}
+
+func TestAuthLoginEnvVarsUseShellSafePrefix(t *testing.T) {
+	t.Parallel()
+
+	apiSpec := minimalSpec("hyphen-api")
+	apiSpec.Auth = spec.AuthConfig{
+		Type:             "oauth2",
+		Header:           "Authorization",
+		AuthorizationURL: "https://example.com/auth",
+		TokenURL:         "https://example.com/token",
+	}
+
+	outputDir := filepath.Join(t.TempDir(), "hyphen-api-pp-cli")
+	require.NoError(t, New(apiSpec, outputDir).Generate())
+
+	authSrc, err := os.ReadFile(filepath.Join(outputDir, "internal", "cli", "auth.go"))
+	require.NoError(t, err)
+	content := string(authSrc)
+
+	require.Contains(t, content, `os.Getenv("HYPHEN_API_CLIENT_ID")`)
+	require.Contains(t, content, `os.Getenv("HYPHEN_API_CLIENT_SECRET")`)
+	require.NotContains(t, content, `HYPHEN-API_CLIENT_ID`)
+}
+
 // TestAuthHeader_EnvVarWinsOverFileToken pins env-first precedence for
 // the non-client_credentials cases — plain bearer_token (PAT-style),
 // cookie, and composed all follow the env > config convention so a
diff --git a/internal/generator/templates/auth.go.tmpl b/internal/generator/templates/auth.go.tmpl
index bf4ec25b..aee7d8ed 100644
--- a/internal/generator/templates/auth.go.tmpl
+++ b/internal/generator/templates/auth.go.tmpl
@@ -182,8 +182,8 @@ func newAuthLoginCmd(flags *rootFlags) *cobra.Command {
 		},
 	}
 
-	cmd.Flags().StringVar(&clientID, "client-id", os.Getenv("{{upper .Name}}_CLIENT_ID"), "OAuth2 client ID")
-	cmd.Flags().StringVar(&clientSecret, "client-secret", os.Getenv("{{upper .Name}}_CLIENT_SECRET"), "OAuth2 client secret")
+	cmd.Flags().StringVar(&clientID, "client-id", os.Getenv("{{envName .Name}}_CLIENT_ID"), "OAuth2 client ID")
+	cmd.Flags().StringVar(&clientSecret, "client-secret", os.Getenv("{{envName .Name}}_CLIENT_SECRET"), "OAuth2 client secret")
 	cmd.Flags().IntVar(&port, "port", 8085, "Local callback server port")
 
 	return cmd
diff --git a/internal/generator/templates/config.go.tmpl b/internal/generator/templates/config.go.tmpl
index dd55f37d..d1a0d50e 100644
--- a/internal/generator/templates/config.go.tmpl
+++ b/internal/generator/templates/config.go.tmpl
@@ -154,7 +154,7 @@ func (c *Config) AuthHeader() string {
 	{{- else}}
 	return ""
 	{{- end}}
-{{- else if eq .Auth.Type "bearer_token"}}
+{{- else if or (eq .Auth.Type "bearer_token") (eq .Auth.Type "oauth2")}}
 {{- if eq .Auth.EffectiveOAuth2Grant "client_credentials"}}
 	// Under OAuth2 client_credentials the env var is the Client ID, not a
 	// usable bearer; the minted AccessToken must win.

← c5e7a801 chore(main): release 3.9.1 (#607)  ·  back to Cli Printing Press  ·  docs(cli): codify lookup-priority discipline in layout contr e08c268b →