fix(security): fail on invalid JWT instead of sending long-lived token as Bearer
- commit
- f05fed4d7e98c004cfb49f75742f58c2f7f74706
- author
- brice · · 2026-03-23 05:18 UTC
- parent
- 2c89614
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +5 -7
M internal/adapters/in/cli/remote/client.go +5 -7
| @@ -68,9 +68,9 @@ func NewClient(baseURL string, opts ...ClientOption) *Client { | |||
| 68 | 68 | } | |
| 69 | 69 | ||
| 70 | 70 | // WithToken sets the authentication token. | |
| 71 | - | // If the token is a valid JWT, the subject claim is extracted for use | |
| 72 | - | // in ephemeral token exchange. If parsing fails the subject is left | |
| 73 | - | // empty and the token will be sent directly (backward compat). | |
| 71 | + | // The token must be a valid JWT — the subject claim is extracted for | |
| 72 | + | // ephemeral token exchange. If parsing fails, requests will return | |
| 73 | + | // an error rather than sending the long-lived token directly. | |
| 74 | 74 | func WithToken(token string) ClientOption { | |
| 75 | 75 | return func(c *Client) { | |
| 76 | 76 | c.token = token | |
| @@ -179,15 +179,13 @@ func (c *Client) exchangeToken(ctx context.Context) error { | |||
| 179 | 179 | } | |
| 180 | 180 | ||
| 181 | 181 | // bearerToken returns the token to use in Authorization headers. | |
| 182 | - | // If the client has a subject (valid JWT), it exchanges for an ephemeral token. | |
| 183 | - | // Otherwise it falls back to sending the long-lived token directly. | |
| 182 | + | // It exchanges the long-lived token for an ephemeral one via /auth/token. | |
| 184 | 183 | func (c *Client) bearerToken(ctx context.Context) (string, error) { | |
| 185 | 184 | if c.token == "" { | |
| 186 | 185 | return "", nil | |
| 187 | 186 | } | |
| 188 | - | // No subject means we couldn't parse the JWT; send token as-is (backward compat). | |
| 189 | 187 | if c.subject == "" { | |
| 190 | - | return c.token, nil | |
| 188 | + | return "", fmt.Errorf("invalid token: could not extract subject from JWT; regenerate with: gordon auth token generate") | |
| 191 | 189 | } | |
| 192 | 190 | c.mu.Lock() | |
| 193 | 191 | defer c.mu.Unlock() | |
| @@ -68,9 +68,9 @@ func NewClient(baseURL string, opts ...ClientOption) *Client { | |||||
| 68 | } | 68 | } | ||
| 69 | 69 | ||||
| 70 | // WithToken sets the authentication token. | 70 | // WithToken sets the authentication token. | ||
| 71 | - | // If the token is a valid JWT, the subject claim is extracted for use | 71 | + | // The token must be a valid JWT — the subject claim is extracted for |
| 72 | - | // in ephemeral token exchange. If parsing fails the subject is left | 72 | + | // ephemeral token exchange. If parsing fails, requests will return |
| 73 | - | // empty and the token will be sent directly (backward compat). | 73 | + | // an error rather than sending the long-lived token directly. |
| 74 | func WithToken(token string) ClientOption { | 74 | func WithToken(token string) ClientOption { | ||
| 75 | return func(c *Client) { | 75 | return func(c *Client) { | ||
| 76 | c.token = token | 76 | c.token = token | ||
| @@ -179,15 +179,13 @@ func (c *Client) exchangeToken(ctx context.Context) error { | |||||
| 179 | } | 179 | } | ||
| 180 | 180 | ||||
| 181 | // bearerToken returns the token to use in Authorization headers. | 181 | // bearerToken returns the token to use in Authorization headers. | ||
| 182 | - | // If the client has a subject (valid JWT), it exchanges for an ephemeral token. | 182 | + | // It exchanges the long-lived token for an ephemeral one via /auth/token. |
| 183 | - | // Otherwise it falls back to sending the long-lived token directly. | |||
| 184 | func (c *Client) bearerToken(ctx context.Context) (string, error) { | 183 | func (c *Client) bearerToken(ctx context.Context) (string, error) { | ||
| 185 | if c.token == "" { | 184 | if c.token == "" { | ||
| 186 | return "", nil | 185 | return "", nil | ||
| 187 | } | 186 | } | ||
| 188 | - | // No subject means we couldn't parse the JWT; send token as-is (backward compat). | |||
| 189 | if c.subject == "" { | 187 | if c.subject == "" { | ||
| 190 | - | return c.token, nil | 188 | + | return "", fmt.Errorf("invalid token: could not extract subject from JWT; regenerate with: gordon auth token generate") |
| 191 | } | 189 | } | ||
| 192 | c.mu.Lock() | 190 | c.mu.Lock() | ||
| 193 | defer c.mu.Unlock() | 191 | defer c.mu.Unlock() | ||