diff --git a/cmd/list-users.go b/cmd/list-users.go index 6425807..0813ad9 100644 --- a/cmd/list-users.go +++ b/cmd/list-users.go @@ -133,5 +133,24 @@ func isGraphAuthorizationDenied(err error) bool { } msg := err.Error() msgLower := strings.ToLower(msg) - return strings.Contains(msg, "Authentication_MSGraphPermissionMissing") && strings.Contains(msgLower, "auditlog.read.all") + + // Standard Graph permission missing error (Application permissions via proper app registration) + if strings.Contains(msg, "Authentication_MSGraphPermissionMissing") && strings.Contains(msgLower, "auditlog.read.all") { + return true + } + + // Office client delegated permission error: when using FOCI tokens (e.g. Office client_id), + // signInActivity returns HTTP 403 with code "UnknownError" instead of the standard + // Authentication_MSGraphPermissionMissing, so the original check never matched. + if strings.Contains(msg, "UnknownError") && strings.Contains(msgLower, "does not have authorization") { + return true + } + + // Role-based access error: returned when the principal's role does not support + // reading signInActivity (e.g. non-privileged roles lacking AuditLog.Read.All). + if strings.Contains(msg, "Authentication_RequestFromUnsupportedUserRole") { + return true + } + + return false } diff --git a/cmd/list-users_test.go b/cmd/list-users_test.go index 4d0dd19..4a7bdf8 100644 --- a/cmd/list-users_test.go +++ b/cmd/list-users_test.go @@ -70,8 +70,47 @@ func TestListUsers(t *testing.T) { } func TestIsGraphAuthorizationDenied(t *testing.T) { - err := fmt.Errorf("map[error:map[code:Authentication_MSGraphPermissionMissing innerError:map[client-request-id:fac52490-ea06-48f1-941e-5f5bba8e35fc date:2026-01-28T15:29:16 request-id:fac52490-ea06-48f1-941e-5f5bba8e35fc] message:The principal does not have required Microsoft Graph permission(s): AuditLog.Read.All to call this API. For more information about Microsoft Graph permissions, please visit https://learn.microsoft.com/graph/permissions-overview.]]") - if !isGraphAuthorizationDenied(err) { - t.Errorf("expected true") + tests := []struct { + name string + err error + expected bool + }{ + { + name: "standard Authentication_MSGraphPermissionMissing error", + err: fmt.Errorf("map[error:map[code:Authentication_MSGraphPermissionMissing innerError:map[client-request-id:fac52490-ea06-48f1-941e-5f5bba8e35fc date:2026-01-28T15:29:16 request-id:fac52490-ea06-48f1-941e-5f5bba8e35fc] message:The principal does not have required Microsoft Graph permission(s): AuditLog.Read.All to call this API. For more information about Microsoft Graph permissions, please visit https://learn.microsoft.com/graph/permissions-overview.]]"), + expected: true, + }, + { + // FOCI / Office client_id returns UnknownError instead of Authentication_MSGraphPermissionMissing + // when signInActivity is requested without AuditLog.Read.All application permission. + name: "Office client delegated permission UnknownError", + err: fmt.Errorf("map[error:map[code:UnknownError message:Microsoft Office does not have authorization to call this API.]]"), + expected: true, + }, + { + // Returned when the principal's role does not support reading signInActivity. + name: "unsupported user role error", + err: fmt.Errorf("map[error:map[code:Authentication_RequestFromUnsupportedUserRole message:The request is not supported for the current user role.]]"), + expected: true, + }, + { + name: "unrelated error", + err: fmt.Errorf("some other error"), + expected: false, + }, + { + name: "nil error", + err: nil, + expected: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := isGraphAuthorizationDenied(tc.err) + if got != tc.expected { + t.Errorf("isGraphAuthorizationDenied(%v) = %v, want %v", tc.err, got, tc.expected) + } + }) } }