Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions cli/azd/cmd/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,13 @@ func authActions(root *actions.ActionDescriptor) *actions.ActionDescriptor {
ActionResolver: newLogoutAction,
})

group.Add("status", &actions.ActionDescriptorOptions{
Command: newAuthStatusCmd(),
FlagsResolver: newAuthStatusFlags,
ActionResolver: newAuthStatusAction,
OutputFormats: []output.Format{output.JsonFormat, output.NoneFormat},
DefaultFormat: output.NoneFormat,
})

return group
}
163 changes: 163 additions & 0 deletions cli/azd/cmd/auth_status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package cmd

import (
"context"
"errors"
"fmt"
"io"
"log"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/azure/azure-dev/cli/azd/cmd/actions"
"github.com/azure/azure-dev/cli/azd/internal"
"github.com/azure/azure-dev/cli/azd/pkg/auth"
"github.com/azure/azure-dev/cli/azd/pkg/contracts"
"github.com/azure/azure-dev/cli/azd/pkg/input"
"github.com/azure/azure-dev/cli/azd/pkg/output"
"github.com/azure/azure-dev/cli/azd/pkg/output/ux"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

type authStatusFlags struct {
tenantID string
scopes []string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to expose these flags on azd auth status? Could the user even have a different status in one tenant vs another? As far as I'm aware we only keep the auth tokens for the most recent auth login.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're correct - removed the tenant-id and scope flags in commit bb9541f. The auth system stores a single current user credential, and these flags don't make sense for a status command. The command now checks whatever the user is currently logged in with using the default login scopes.

global *internal.GlobalCommandOptions
}

func newAuthStatusFlags(cmd *cobra.Command, global *internal.GlobalCommandOptions) *authStatusFlags {
flags := &authStatusFlags{}
flags.Bind(cmd.Flags(), global)
return flags
}

func (f *authStatusFlags) Bind(local *pflag.FlagSet, global *internal.GlobalCommandOptions) {
local.StringVar(
&f.tenantID,
"tenant-id",
"",
"The tenant id or domain name to authenticate with.")
local.StringArrayVar(
&f.scopes,
"scope",
nil,
"The scope to use when verifying authentication status")
_ = local.MarkHidden("scope")
f.global = global
}

func newAuthStatusCmd() *cobra.Command {
return &cobra.Command{
Use: "status",
Short: "Check the authentication status.",
Long: "Check the authentication status. Returns information about the logged-in user and when credentials expire.",
}
}

type authStatusAction struct {
formatter output.Formatter
writer io.Writer
console input.Console
authManager *auth.Manager
flags *authStatusFlags
}

func newAuthStatusAction(
formatter output.Formatter,
writer io.Writer,
authManager *auth.Manager,
flags *authStatusFlags,
console input.Console,
) actions.Action {
return &authStatusAction{
formatter: formatter,
writer: writer,
console: console,
authManager: authManager,
flags: flags,
}
}

func (a *authStatusAction) Run(ctx context.Context) (*actions.ActionResult, error) {
if len(a.flags.scopes) == 0 {
a.flags.scopes = a.authManager.LoginScopes()
}

// In check status mode, we always print the final status to stdout.
// We print any non-setup related errors to stderr.
// We always return a zero exit code.
Comment on lines +75 to +77
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "In check status mode, we always print the final status to stdout" appears to be copied from auth_login.go where it makes sense in the context of the --check-status flag. However, for the dedicated auth_status command, this comment is slightly misleading since the command is always in "status mode" - there's no mode switching.

Consider updating the comment to better reflect that this is the primary behavior of the status command, for example: "The status command always prints the final status to stdout and returns a zero exit code. Any non-setup related errors are printed to stderr."

Copilot uses AI. Check for mistakes.
token, err := a.verifyLoggedIn(ctx)
var loginExpiryError *auth.ReLoginRequiredError
if err != nil &&
!errors.Is(err, auth.ErrNoCurrentUser) &&
!errors.As(err, &loginExpiryError) {
fmt.Fprintln(a.console.Handles().Stderr, err.Error())
}

res := contracts.LoginResult{}
if err != nil {
res.Status = contracts.LoginStatusUnauthenticated
} else {
res.Status = contracts.LoginStatusSuccess
res.ExpiresOn = &token.ExpiresOn
}

if a.formatter.Kind() != output.NoneFormat {
return nil, a.formatter.Format(res, a.writer, nil)
} else {
var msg string
switch res.Status {
case contracts.LoginStatusSuccess:
msg = "Logged in to Azure"
case contracts.LoginStatusUnauthenticated:
msg = "Not logged in, run `azd auth login` to login to Azure"
default:
panic("Unhandled login status")
}

// get user account information
details, err := a.authManager.LogInDetails(ctx)

// error getting user account or not logged in
if err != nil {
log.Printf("error: getting signed in account: %v", err)
fmt.Fprintln(a.console.Handles().Stdout, msg)
return nil, nil
}

// only print the message if the user is logged in
a.console.MessageUxItem(ctx, &ux.LoggedIn{
LoggedInAs: details.Account,
LoginType: ux.LoginType(details.LoginType),
})
return nil, nil
Comment on lines 97 to 131
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should consider outputting the expiration date/time of the current token.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added credential expiration to text output in commit bb9541f. The expiration was already in the JSON output, now it's also shown in the text format:

Logged in to Azure as [email protected]

Credential expires on: 2024-01-15 10:30:45 UTC

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also share this logic with azd auth login --check-status for consistency?

image

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, we should!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added credential expiration to azd auth login --check-status in commit bec39dc for consistency. Both commands now display the expiration time in the same format:

Logged in to Azure as [email protected]

Credential expires on: 2025-12-15 21:41:56 UTC

}
}

// Verifies that the user has credentials stored,
// and that the credentials stored is accepted by the identity server (can be exchanged for access token).
func (a *authStatusAction) verifyLoggedIn(ctx context.Context) (*azcore.AccessToken, error) {
credOptions := auth.CredentialForCurrentUserOptions{
TenantID: a.flags.tenantID,
}

cred, err := a.authManager.CredentialForCurrentUser(ctx, &credOptions)
if err != nil {
return nil, err
}

// Ensure credential is valid, and can be exchanged for an access token
token, err := cred.GetToken(ctx, policy.TokenRequestOptions{
Scopes: a.flags.scopes,
})

if err != nil {
return nil, err
}

return &token, nil
}
19 changes: 19 additions & 0 deletions cli/azd/cmd/testdata/TestFigSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,21 @@ const completionSpec: Fig.Spec = {
name: ['logout'],
description: 'Log out of Azure.',
},
{
name: ['status'],
description: 'Check the authentication status.',
options: [
{
name: ['--tenant-id'],
description: 'The tenant id or domain name to authenticate with.',
args: [
{
name: 'tenant-id',
},
],
},
],
},
],
},
{
Expand Down Expand Up @@ -1540,6 +1555,10 @@ const completionSpec: Fig.Spec = {
name: ['logout'],
description: 'Log out of Azure.',
},
{
name: ['status'],
description: 'Check the authentication status.',
},
],
},
{
Expand Down
19 changes: 19 additions & 0 deletions cli/azd/cmd/testdata/TestUsage-azd-auth-status.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

Check the authentication status.

Usage
azd auth status [flags]

Flags
--tenant-id string : The tenant id or domain name to authenticate with.

Global Flags
-C, --cwd string : Sets the current working directory.
--debug : Enables debugging and diagnostics logging.
--docs : Opens the documentation for azd auth status in your web browser.
-h, --help : Gets help for status.
--no-prompt : Accepts the default value instead of prompting, or it fails if there is no default.

Find a bug? Want to let us know how we're doing? Fill out this brief survey: https://aka.ms/azure-dev/hats.


1 change: 1 addition & 0 deletions cli/azd/cmd/testdata/TestUsage-azd-auth.snap
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Usage
Available Commands
login : Log in to Azure.
logout : Log out of Azure.
status : Check the authentication status.

Global Flags
-C, --cwd string : Sets the current working directory.
Expand Down
Loading