Conversation
Add --check flag to 'azd auth token' for lightweight auth validation. Agents can call 'azd auth token --check' to validate authentication state with exit code 0 (valid) or non-zero (invalid) without producing standard output. This prevents costly retry loops where agents speculatively call auth token and parse errors. Enhance 'azd auth status --output json' to include expiresOn field, giving agents machine-readable token expiry information for proactive re-authentication. Improve LoginGuardMiddleware to wrap ErrNoCurrentUser with actionable ErrorWithSuggestion guidance, while preserving original error types for cancellations and transient failures. Changes: - cmd/auth_token.go: Add --check flag with early-exit validation - cmd/auth_token_test.go: Add 3 test cases (check success/failure/not-logged-in) - cmd/auth_status.go: Populate ExpiresOn from token validation - pkg/contracts/auth.go: Add ExpiresOn field to StatusResult - cmd/middleware/login_guard.go: Wrap ErrNoCurrentUser with suggestion Fixes #7234 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds agent-friendly authentication pre-flight validation and exposes token expiry in auth status output to help avoid late-stage failures in long-running commands.
Changes:
- Added
azd auth token --checkto validate that a token can be acquired (no stdout; error => non-zero exit). - Extended
azd auth status --output jsoncontract/output with anexpiresOnfield. - Wrapped
auth.ErrNoCurrentUserinLoginGuardMiddlewarewithErrorWithSuggestionfor actionable login guidance.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| cli/azd/pkg/contracts/auth.go | Adds expiresOn to the auth status JSON contract. |
| cli/azd/cmd/middleware/login_guard.go | Wraps ErrNoCurrentUser with ErrorWithSuggestion during login-guard token validation. |
| cli/azd/cmd/auth_token.go | Implements --check flag behavior for lightweight auth validation. |
| cli/azd/cmd/auth_status.go | Populates expiresOn in JSON output using the verified access token expiry. |
| cli/azd/cmd/auth_token_test.go | Adds unit tests covering --check success/failure/not-logged-in behaviors. |
| // --check mode: validate that a token can be acquired, produce no output. | ||
| // Exit code 0 = auth valid, exit code 1 = auth invalid. | ||
| if a.flags.check { | ||
| _, err := cred.GetToken(ctx, policy.TokenRequestOptions{ | ||
| Scopes: a.flags.scopes, | ||
| }) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("authentication check failed: %w", err) | ||
| } | ||
| // Auth is valid — return success with no output | ||
| return nil, nil | ||
| } |
There was a problem hiding this comment.
--check mode bypasses the --claims handling entirely: it doesn't validate that --claims is valid base64 and it doesn't include decoded claims in the token request. This can make azd auth token --check --claims ... report success even though the equivalent non-check invocation would fail locally (invalid base64) or behave differently (missing claims). Consider either rejecting --claims when --check is set, or decoding/validating claims and passing them through to the GetToken call in check mode as well.
| if tenantId == "" { | ||
| tenantIdFromAzdEnv, err := getTenantIdFromAzdEnv(ctx, a.envResolver, a.subResolver) | ||
| if err != nil { | ||
| if a.flags.check { | ||
| return nil, err | ||
| } | ||
| return nil, err | ||
| } | ||
| tenantId = tenantIdFromAzdEnv | ||
| } | ||
| // 3) From system env | ||
| if tenantId == "" { | ||
| tenantIdFromSysEnv, err := getTenantIdFromEnv(ctx, a.subResolver) | ||
| if err != nil { | ||
| if a.flags.check { | ||
| return nil, err | ||
| } | ||
| return nil, err | ||
| } | ||
| tenantId = tenantIdFromSysEnv | ||
| } | ||
|
|
||
| // If tenantId is still empty, the fallback is to use current logged in user's home-tenant id. | ||
| cred, err := a.credentialProvider(ctx, &auth.CredentialForCurrentUserOptions{ | ||
| NoPrompt: true, | ||
| TenantID: tenantId, | ||
| }) | ||
| if err != nil { | ||
| if a.flags.check { | ||
| return nil, err | ||
| } | ||
| return nil, err |
There was a problem hiding this comment.
The if a.flags.check { return nil, err } branches in the tenant-id resolution and credential-provider error handling are redundant because both branches return the same value. This adds noise and makes it look like --check is intended to change error behavior here when it doesn’t. Consider removing the duplicated if a.flags.check blocks (or, if different behavior is intended for --check, implement it explicitly).
| token, err := a.verifyLoggedIn(ctx, scopes) | ||
| if err != nil { | ||
| res.Status = contracts.AuthStatusUnauthenticated | ||
| log.Printf("error: verifying logged in status: %v", err) | ||
| } else if token != nil { | ||
| expiresOn := contracts.RFC3339Time(token.ExpiresOn) | ||
| res.ExpiresOn = &expiresOn | ||
| } |
There was a problem hiding this comment.
expiresOn is now populated in the JSON contract, but there’s no test asserting its presence/format when authenticated. Since this field is intended for agent consumption, consider adding a test (unit test for verifyLoggedIn/Run, or extending the existing external-auth functional test) that verifies expiresOn is emitted and matches the token expiry when auth status --output json reports authenticated.
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
- Remove redundant 'if a.flags.check' branches in auth_token.go that duplicated the same return (Copilot review comment #2) - Add StatusResult JSON serialization tests verifying expiresOn is present when authenticated and omitted when unauthenticated (Copilot review comment #3) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Fixes #7234
Adds pre-flight auth validation capabilities to improve the experience for AI agents (Claude Code, GitHub Copilot CLI, OpenCode) and interactive users.
Changes
1.
azd auth token --check(agent-friendly auth validation)New
--checkflag for lightweight auth validation. Agents can call this before running expensive commands to determine if auth is valid:This prevents the costly retry loop pattern where agents speculatively call
azd auth token --output json, fail, and retry — which accounts for 61% failure rate on Copilot CLI and 41% on Claude Code per telemetry.2. Enhanced
azd auth status --output jsonAdded
expiresOnfield to the JSON output so agents can proactively re-authenticate before tokens expire:{ "status": "authenticated", "type": "user", "email": "user@example.com", "expiresOn": "2026-03-22T14:30:00Z" }3. Improved LoginGuardMiddleware error messages
When
ErrNoCurrentUseris encountered during token validation in the login guard, it's now wrapped withErrorWithSuggestionproviding actionable guidance. Cancellations and transient errors propagate unchanged.Test Coverage
TestAuthTokenCheckSuccess— validates exit 0 with no output when auth is validTestAuthTokenCheckFailure— validates error return when token acquisition failsTestAuthTokenCheckNotLoggedIn— validates error return when not logged inExpected Impact
auth tokenretry stormsexpiresOnisomitempty),--checkis opt-in