Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion cli/azd/cmd/auth_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ func (la *loginAction) login(ctx context.Context) error {
}

if oneauth.Supported && !la.flags.browser {
err = la.authManager.LoginWithOneAuth(ctx, la.flags.tenantID, la.flags.scopes)
err = la.authManager.LoginWithOneAuth(ctx, la.flags.tenantID, la.flags.scopes, claims)
} else {
_, err = la.authManager.LoginInteractive(ctx, la.flags.scopes, claims,
&auth.LoginInteractiveOptions{
Expand Down
34 changes: 26 additions & 8 deletions cli/azd/pkg/auth/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -748,19 +748,37 @@ func (m *Manager) LoginWithBrokerAccount() error {
}

// LoginWithOneAuth starts OneAuth's interactive login flow.
func (m *Manager) LoginWithOneAuth(ctx context.Context, tenantID string, scopes []string) error {
func (m *Manager) LoginWithOneAuth(ctx context.Context, tenantID string, scopes []string, claims string) error {
if len(scopes) == 0 {
scopes = m.LoginScopes()
}

var claimsFile string
if claims == "" {
c, path, err := loadClaims()
if err != nil {
return err
}

claims = c
claimsFile = path
}

authority := m.cloud.Configuration.ActiveDirectoryAuthorityHost + tenantID
accountID, err := oneauth.LogIn(authority, azdClientID, strings.Join(scopes, " "))
if err == nil {
err = m.saveUserProperties(&userProperties{
FromOneAuth: true,
HomeAccountID: &accountID,
})
accountID, err := oneauth.LogIn(authority, azdClientID, strings.Join(scopes, " "), claims)
if err != nil {
return err
}
return err

if err = m.saveUserProperties(&userProperties{FromOneAuth: true, HomeAccountID: &accountID}); err != nil {
return err
}

if claimsFile != "" {
_ = os.Remove(claimsFile)
}

return nil
}

func (m *Manager) LoginWithDeviceCode(
Expand Down
10 changes: 8 additions & 2 deletions cli/azd/pkg/oneauth/bridge/bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,15 @@ WrappedAuthResult *wrapAuthResult(const AuthResult *ar)
return wrapped;
}

WrappedAuthResult *Authenticate(const char *authority, const char *scope, const char *accountID, bool allowPrompt)
WrappedAuthResult *Authenticate(const char *authority, const char *scope, const char *claims, const char *accountID, bool allowPrompt)
{
auto authParams = AuthParameters::CreateForBearer(authority, scope);
auto authParams = AuthParameters::CreateForBearer(
authority,
scope,
"", // accessTokenToRenew
claims,
{} // additionalParameters
);
auto telemetryParams = TelemetryParameters(UUID::Generate());

std::promise<AuthResult> promise;
Expand Down
3 changes: 2 additions & 1 deletion cli/azd/pkg/oneauth/bridge/bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ extern "C"
// The parameters are:
// - authority: authority for token requests e.g. "https://login.microsoftonline.com/tenant"
// - scope: scope of the desired access token
// - claims: optional claims to include in the token request
// - accountID: optional account ID of a user to authenticate, as returned by a previous call to this function. Required for silent
// authentication. If empty or no account associated with azd matches the given value, this function will fall back to
// interactive authentication, provided allowPrompt is true.
// - allowPrompt: whether to display an interactive login window when necessary
__declspec(dllexport) WrappedAuthResult *Authenticate(const char *authority, const char *scope, const char *accountID, bool allowPrompt);
__declspec(dllexport) WrappedAuthResult *Authenticate(const char *authority, const char *scope, const char *claims, const char *accountID, bool allowPrompt);

// SignInSilently authenticates an account inferred from the OS e.g. the active Windows user, without displaying UI.
// It returns an error when that's impossible.
Expand Down
2 changes: 1 addition & 1 deletion cli/azd/pkg/oneauth/oneauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const Supported = false

var errNotSupported = errors.New("this build doesn't support OneAuth authentication")

func LogIn(authority, clientID, scope string) (string, error) {
func LogIn(authority, clientID, scope string, claims string) (string, error) {
return "", errNotSupported
}

Expand Down
2 changes: 1 addition & 1 deletion cli/azd/pkg/oneauth/oneauth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
)

func TestLogin(t *testing.T) {
_, err := LogIn("authority", "clientID", "scope")
_, err := LogIn("authority", "clientID", "scope", "claims")
require.ErrorIs(t, err, errNotSupported)
}

Expand Down
12 changes: 7 additions & 5 deletions cli/azd/pkg/oneauth/oneauth_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,15 @@ func NewCredential(authority, clientID string, opts CredentialOptions) (azcore.T
// GetToken acquires a token from OneAuth. If doing so requires user interaction and NoPrompt is true, it returns
// an error. Otherwise, OneAuth will display a login window and this call must occur on the main thread.
func (c *credential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
ar, err := authn(c.authority, c.clientID, c.homeAccountID, strings.Join(opts.Scopes, " "), c.opts.NoPrompt)
ar, err := authn(c.authority, c.clientID, c.homeAccountID, strings.Join(opts.Scopes, " "), opts.Claims, c.opts.NoPrompt)
if err == nil {
c.homeAccountID = ar.homeAccountID
}
return ar.token, err
}

func LogIn(authority, clientID, scope string) (string, error) {
ar, err := authn(authority, clientID, "", scope, false)
func LogIn(authority, clientID, scope string, claims string) (string, error) {
ar, err := authn(authority, clientID, "", scope, claims, false)
return ar.homeAccountID, err
}

Expand Down Expand Up @@ -182,7 +182,7 @@ func start(clientID string) error {
return nil
}

func authn(authority, clientID, homeAccountID, scope string, noPrompt bool) (authResult, error) {
func authn(authority, clientID, homeAccountID, scope string, claims string, noPrompt bool) (authResult, error) {
res := authResult{}
if err := start(clientID); err != nil {
return res, err
Expand All @@ -195,11 +195,13 @@ func authn(authority, clientID, homeAccountID, scope string, noPrompt bool) (aut
scope = strings.ReplaceAll(scope, "/.default", "")
scp := unsafe.Pointer(C.CString(scope))
defer C.free(scp)
claimsP := unsafe.Pointer(C.CString(claims))
defer C.free(claimsP)
allowPrompt := 1
if noPrompt {
allowPrompt = 0
}
p, _, _ := authenticate.Call(uintptr(a), uintptr(scp), uintptr(accountID), uintptr(allowPrompt))
p, _, _ := authenticate.Call(uintptr(a), uintptr(scp), uintptr(accountID), uintptr(claimsP), uintptr(allowPrompt))
if p == 0 {
// this shouldn't happen but if it did, this vague error would be better than a panic
return res, fmt.Errorf("authentication failed")
Expand Down