protected override void ProcessRecord()
        {
            source = new CancellationTokenSource();
            var messageWriter = new CmdletMessageWriter(this);
            CancellationToken cancellationToken = source.Token;

            var endPoint = string.Empty;

            using (var authManager = new AuthenticationManager())
            {
                endPoint = authManager.GetAzureADLoginEndPoint(AzureEnvironment);
            }

            Task.Factory.StartNew(() =>
            {
                var deviceCodeApplication = PublicClientApplicationBuilder.Create(PnPConnection.PnPManagementShellClientId).WithAuthority($"{endPoint}/organizations/").WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient").Build();
                deviceCodeApplication.AcquireTokenWithDeviceCode(new[] { "https://graph.microsoft.com/.default" }, codeResult =>
                {
                    if (Utilities.OperatingSystem.IsWindows())
                    {
                        ClipboardService.SetText(codeResult.UserCode);
                        messageWriter.WriteMessage($"Provide consent for the PnP Management Shell application to access SharePoint.\n\nWe opened a browser and navigated to {codeResult.VerificationUrl}\n\nEnter code: {codeResult.UserCode} (we copied this code to your clipboard)");
                        BrowserHelper.GetWebBrowserPopup(codeResult.VerificationUrl, "Provide consent for the PnP Management Shell application");
                    }
                    else
                    {
                        messageWriter.WriteMessage($"Please provide consent for the PnP Management Shell application by navigating to\n\n{codeResult.VerificationUrl}\n\nEnter code: {codeResult.UserCode}.");
                    }
                    return(Task.FromResult(0));
                }).ExecuteAsync(cancellationToken).GetAwaiter().GetResult();
                messageWriter.Finished = true;
            }, cancellationToken);
            messageWriter.Start();
        }
Exemplo n.º 2
0
        /// <summary>
        /// Connect using the parameter set DEVICELOGIN
        /// </summary>
        /// <returns>PnPConnection based on the parameters provided in the parameter set</returns>
        private PnPConnection ConnectDeviceLogin(CancellationToken cancellationToken)
        {
            var           messageWriter = new CmdletMessageWriter(this);
            PnPConnection connection    = null;
            var           uri           = new Uri(Url);

            if ($"https://{uri.Host}".Equals(Url.ToLower()))
            {
                Url += "/";
            }
            var task = Task.Factory.StartNew(() =>
            {
                Uri oldUri = null;

                if (PnPConnection.CurrentConnection != null)
                {
                    if (PnPConnection.CurrentConnection.Url != null)
                    {
                        oldUri = new Uri(PnPConnection.CurrentConnection.Url);
                    }
                }
                if (oldUri != null && oldUri.Host == new Uri(Url).Host&& PnPConnection.CurrentConnection?.ConnectionMethod == ConnectionMethod.DeviceLogin)
                {
                    ReuseAuthenticationManager();
                }

                var returnedConnection = PnPConnectionHelper.InstantiateDeviceLoginConnection(Url, LaunchBrowser, messageWriter, AzureEnvironment, cancellationToken);
                connection             = returnedConnection;
                messageWriter.Finished = true;
            }, cancellationToken);

            messageWriter.Start();
            return(connection);
        }
Exemplo n.º 3
0
        private void StartConsentFlow(string loginEndPoint, AzureApp azureApp, string redirectUri, string token, HttpClient httpClient, PSObject record, CmdletMessageWriter messageWriter, List <PermissionScope> scopes)
        {
            Host.UI.WriteLine(ConsoleColor.Yellow, Host.UI.RawUI.BackgroundColor, $"Starting consent flow.");

            var resource = scopes.FirstOrDefault(s => s.resourceAppId == PermissionScopes.ResourceAppId_Graph) != null ? "https://graph.microsoft.com/.default" : "https://microsoft.sharepoint-df.com/.default";

            var consentUrl = $"{loginEndPoint}/{Tenant}/v2.0/adminconsent?client_id={azureApp.AppId}&scope={resource}&redirect_uri={redirectUri}";


            if (OperatingSystem.IsWindows() && !NoPopup)
            {
                var waitTime = 60;
                CmdletMessageWriter.WriteFormattedWarning(this, $"Waiting {waitTime} seconds to launch the consent flow in a popup window.\n\nThis wait is required to make sure that Azure AD is able to initialize all required artifacts. You can always navigate to the consent page manually:\n\n{consentUrl}");

                for (var i = 0; i < waitTime; i++)
                {
                    if (Convert.ToDouble(i) % Convert.ToDouble(10) > 0)
                    {
                        Host.UI.Write(ConsoleColor.Yellow, Host.UI.RawUI.BackgroundColor, "-");
                    }
                    else
                    {
                        Host.UI.Write(ConsoleColor.Yellow, Host.UI.RawUI.BackgroundColor, $"[{i}]");
                    }
                    System.Threading.Thread.Sleep(1000);

                    // Check if CTRL+C has been pressed and if so, abort the wait
                    if (Stopping)
                    {
                        break;
                    }
                }

                if (!Stopping)
                {
                    Host.UI.WriteLine(ConsoleColor.Yellow, Host.UI.RawUI.BackgroundColor, $"[{waitTime}]");

                    Host.UI.WriteLine();

                    if (ParameterSpecified(nameof(Interactive)))
                    {
                        using (var authManager = AuthenticationManager.CreateWithInteractiveLogin(azureApp.AppId, (url, port) =>
                        {
                            BrowserHelper.OpenBrowserForInteractiveLogin(url, port, true, cancellationTokenSource);
                        }, Tenant, "You successfully provided consent", "You failed to provide consent.", AzureEnvironment))
                        {
                            authManager.GetAccessToken(resource, Microsoft.Identity.Client.Prompt.Consent);
                        }
                    }
                    else
                    {
                        BrowserHelper.GetWebBrowserPopup(consentUrl, "Please provide consent", new[] { ("https://pnp.github.io/powershell/consent.html", BrowserHelper.UrlMatchType.StartsWith) }, cancellationTokenSource: cancellationTokenSource, cancelOnClose: false);
Exemplo n.º 4
0
 /// <summary>
 /// Connect using the paramater set TOKEN
 /// </summary>
 /// <returns>PnPConnection based on the parameters provided in the parameter set</returns>
 private PnPConnection ConnectACSAppOnly()
 {
     CmdletMessageWriter.WriteFormattedMessage(this, new CmdletMessageWriter.Message {
         Text = "Connecting with Client Secret uses legacy authentication and provides limited functionality. We can for instance not execute requests towards the Microsoft Graph, which limits cmdlets related to Microsoft Teams, Microsoft Planner, Microsoft Flow and Microsoft 365 Groups. You can hide this warning by using Connect-PnPOnline [your parameters] -WarningAction Ignore", Formatted = true, Type = CmdletMessageWriter.MessageType.Warning
     });
     if (PnPConnection.Current?.ClientId == ClientId &&
         PnPConnection.Current?.ClientSecret == ClientSecret &&
         PnPConnection.Current?.Tenant == Realm)
     {
         ReuseAuthenticationManager();
     }
     return(PnPConnection.CreateWithACSAppOnly(new Uri(Url), Realm, ClientId, ClientSecret, TenantAdminUrl, AzureEnvironment));
 }
Exemplo n.º 5
0
        private string GetAuthToken(CmdletMessageWriter messageWriter)
        {
            var token = string.Empty;

            if (DeviceLogin.IsPresent)
            {
                Task.Factory.StartNew(() =>
                {
                    token = AzureAuthHelper.AuthenticateDeviceLogin(cancellationTokenSource, messageWriter, NoPopup, AzureEnvironment);
                    if (token == null)
                    {
                        messageWriter.WriteWarning("Operation cancelled or no token retrieved.");
                    }
                    messageWriter.Stop();
                });
                messageWriter.Start();
            }
            else if (Interactive.IsPresent)
            {
                Task.Factory.StartNew(() =>
                {
                    token = AzureAuthHelper.AuthenticateInteractive(cancellationTokenSource, messageWriter, NoPopup, AzureEnvironment);
                    if (token == null)
                    {
                        messageWriter.WriteWarning("Operation cancelled or no token retrieved.");
                    }
                    messageWriter.Stop();
                });
                messageWriter.Start();
            }
            else
            {
                if (PnPConnection.CurrentConnection?.PSCredential != null)
                {
                    Username = PnPConnection.CurrentConnection.PSCredential.UserName;
                    Password = PnPConnection.CurrentConnection.PSCredential.Password;
                }
                if (string.IsNullOrEmpty(Username))
                {
                    throw new PSArgumentException("Username is required or use -DeviceLogin or -Interactive");
                }
                if (Password == null || Password.Length == 0)
                {
                    throw new PSArgumentException("Password is required or use -DeviceLogin or -Interactive");
                }
                token = AzureAuthHelper.AuthenticateAsync(Tenant, Username, Password, AzureEnvironment).GetAwaiter().GetResult();
            }

            return(token);
        }
Exemplo n.º 6
0
        /// <summary>
        /// Connect using the parameter set DEVICELOGIN
        /// </summary>
        /// <returns>PnPConnection based on the parameters provided in the parameter set</returns>
        private PnPConnection ConnectDeviceLogin()
        {
            var           messageWriter = new CmdletMessageWriter(this);
            PnPConnection connection    = null;
            var           uri           = new Uri(Url);

            if ($"https://{uri.Host}".Equals(Url.ToLower()))
            {
                Url += "/";
            }
            var task = Task.Factory.StartNew(() =>
            {
                try
                {
                    Uri oldUri = null;

                    if (PnPConnection.Current != null)
                    {
                        if (PnPConnection.Current.Url != null)
                        {
                            oldUri = new Uri(PnPConnection.Current.Url);
                        }
                    }
                    if (oldUri != null && oldUri.Host == new Uri(Url).Host&& PnPConnection.Current?.ConnectionMethod == ConnectionMethod.DeviceLogin)
                    {
                        ReuseAuthenticationManager();
                    }

                    var clientId = PnPConnection.PnPManagementShellClientId;
                    if (ParameterSpecified(nameof(ClientId)))
                    {
                        clientId = ClientId;
                    }


                    var returnedConnection = PnPConnection.CreateWithDeviceLogin(clientId, Url, Tenant, LaunchBrowser, messageWriter, AzureEnvironment, cancellationTokenSource);
                    connection             = returnedConnection;
                    messageWriter.Finished = true;
                }
                catch (Exception ex)
                {
                    messageWriter.WriteWarning(ex.Message, false);
                    messageWriter.Finished = true;
                }
            }, cancellationTokenSource.Token);

            messageWriter.Start();
            return(connection);
        }
Exemplo n.º 7
0
        private void StartConsentFlow(string loginEndPoint, AzureApp azureApp, string redirectUri, string token, HttpClient httpClient, PSObject record)
        {
            var consentUrl = $"{loginEndPoint}/{Tenant}/v2.0/adminconsent?client_id={azureApp.AppId}&scope=https://microsoft.sharepoint-df.com/.default&redirect_uri={redirectUri}";


            if (OperatingSystem.IsWindows() && !NoPopup)
            {
                var waitTime = 60;
                CmdletMessageWriter.WriteFormattedWarning(this, $"Waiting {waitTime} seconds to launch consent flow in a popup window.\n\nThis wait is required to make sure that Azure AD is able to initialize all required artifacts. You can always navigate to the consent page manually:\n\n{consentUrl}");

                for (var i = 0; i < waitTime; i++)
                {
                    if (Convert.ToDouble(i) % Convert.ToDouble(10) > 0)
                    {
                        Host.UI.Write(ConsoleColor.Yellow, Host.UI.RawUI.BackgroundColor, "-");
                    }
                    else
                    {
                        Host.UI.Write(ConsoleColor.Yellow, Host.UI.RawUI.BackgroundColor, $"[{i}]");
                    }
                    System.Threading.Thread.Sleep(1000);

                    // Check if CTRL+C has been pressed and if so, abort the wait
                    if (Stopping)
                    {
                        break;
                    }
                }

                if (!Stopping)
                {
                    Host.UI.WriteLine(ConsoleColor.Yellow, Host.UI.RawUI.BackgroundColor, $"[{waitTime}]");

                    Host.UI.WriteLine();

                    BrowserHelper.GetWebBrowserPopup(consentUrl, "Please provide consent", new[] { (redirectUri, BrowserHelper.UrlMatchType.StartsWith) });
Exemplo n.º 8
0
        internal static PnPConnection CreateWithDeviceLogin(string clientId, string url, bool launchBrowser, CmdletMessageWriter messageWriter, AzureEnvironment azureEnvironment, CancellationTokenSource cancellationTokenSource)
        {
            var connectionUri = new Uri(url);
            var scopes        = new[] { $"{connectionUri.Scheme}://{connectionUri.Authority}//.default" }; // the second double slash is not a typo.

            PnP.Framework.AuthenticationManager authManager = null;
            if (PnPConnection.CachedAuthenticationManager != null)
            {
                authManager = PnPConnection.CachedAuthenticationManager;
                PnPConnection.CachedAuthenticationManager = null;
            }
            else
            {
                authManager = PnP.Framework.AuthenticationManager.CreateWithDeviceLogin(clientId, (deviceCodeResult) =>
                {
                    if (launchBrowser)
                    {
                        if (Utilities.OperatingSystem.IsWindows())
                        {
                            ClipboardService.SetText(deviceCodeResult.UserCode);
                            messageWriter.WriteWarning($"\n\nCode {deviceCodeResult.UserCode} has been copied to your clipboard\n\n");
                            BrowserHelper.GetWebBrowserPopup(deviceCodeResult.VerificationUrl, "Please log in", cancellationTokenSource: cancellationTokenSource, cancelOnClose: false);
                        }
                        else
                        {
                            messageWriter.WriteWarning($"\n\n{deviceCodeResult.Message}\n\n");
                        }
                    }
                    else
                    {
                        messageWriter.WriteWarning($"\n\n{deviceCodeResult.Message}\n\n");
                    }
                    return(Task.FromResult(0));
                }, azureEnvironment);
            }
            using (authManager)
            {
                var clientContext = authManager.GetContext(url.ToString(), cancellationTokenSource.Token);
                var context       = PnPClientContext.ConvertFrom(clientContext);

                var connectionType = ConnectionType.O365;

                var spoConnection = new PnPConnection(context, connectionType, null, clientId, null, url.ToString(), null, PnPPSVersionTag, InitializationType.DeviceLogin)
                {
                    ConnectionMethod = ConnectionMethod.DeviceLogin,
                    AzureEnvironment = azureEnvironment
                };
                return(spoConnection);
            }
        }
Exemplo n.º 9
0
        protected override void ProcessRecord()
        {
            if (ParameterSpecified(nameof(Store)) && !OperatingSystem.IsWindows())
            {
                throw new PSArgumentException("The Store parameter is only supported on Microsoft Windows");
            }

            if (!string.IsNullOrWhiteSpace(OutPath))
            {
                if (!Path.IsPathRooted(OutPath))
                {
                    OutPath = Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, OutPath);
                }
            }
            else
            {
                OutPath = SessionState.Path.CurrentFileSystemLocation.Path;
            }

            var redirectUri = "https://pnp.github.io/powershell/consent.html";

            var messageWriter = new CmdletMessageWriter(this);

            cancellationTokenSource = new CancellationTokenSource();
            CancellationToken cancellationToken = cancellationTokenSource.Token;

            WriteVerbose(ParameterSetName);

            var loginEndPoint = string.Empty;
            var record        = new PSObject();

            using (var authenticationManager = new AuthenticationManager())
            {
                loginEndPoint = authenticationManager.GetAzureADLoginEndPoint(AzureEnvironment);
            }

            string token = GetAuthToken(messageWriter, loginEndPoint);

            if (!string.IsNullOrEmpty(token))
            {
                var cert = GetCertificate(record);


                using (var httpClient = new HttpClient())
                {
                    if (!AppExists(ApplicationName, httpClient, token))
                    {
                        var azureApp = CreateApp(loginEndPoint, httpClient, token, cert, redirectUri);

                        record.Properties.Add(new PSVariableProperty(new PSVariable("AzureAppId/ClientId", azureApp.AppId)));
                        record.Properties.Add(new PSVariableProperty(new PSVariable("Certificate Thumbprint", cert.GetCertHashString())));

                        StartConsentFlow(loginEndPoint, azureApp, redirectUri, token, httpClient, record);
                    }
                    else
                    {
                        throw new PSInvalidOperationException($"The application with name {ApplicationName} already exists.");
                    }
                }
            }
        }
Exemplo n.º 10
0
        protected override void ProcessRecord()
        {
            if (ParameterSpecified(nameof(Store)) && !OperatingSystem.IsWindows())
            {
                throw new PSArgumentException("The Store parameter is only supported on Microsoft Windows");
            }

            if (!string.IsNullOrWhiteSpace(OutPath))
            {
                if (!Path.IsPathRooted(OutPath))
                {
                    OutPath = Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, OutPath);
                }
            }
            else
            {
                OutPath = SessionState.Path.CurrentFileSystemLocation.Path;
            }

            var redirectUri = "http://localhost";

            if (ParameterSpecified(nameof(DeviceLogin)))
            {
                redirectUri = "https://pnp.github.io/powershell/consent.html";
            }

            var messageWriter = new CmdletMessageWriter(this);

            cancellationTokenSource = new CancellationTokenSource();
            CancellationToken cancellationToken = cancellationTokenSource.Token;

            var loginEndPoint = string.Empty;

            using (var authenticationManager = new AuthenticationManager())
            {
                loginEndPoint = authenticationManager.GetAzureADLoginEndPoint(AzureEnvironment);
            }

            var permissionScopes = new PermissionScopes();
            var scopes           = new List <PermissionScope>();

            if (this.Scopes != null)
            {
                foreach (var scopeIdentifier in this.Scopes)
                {
                    scopes.Add(permissionScopes.GetScope(scopeIdentifier));
                }
            }
            else
            {
                scopes.Add(permissionScopes.GetScope("SPO.Sites.FullControl.All"));
                scopes.Add(permissionScopes.GetScope("MSGraph.Group.ReadWrite.All"));
                scopes.Add(permissionScopes.GetScope("SPO.User.Read.All"));
                scopes.Add(permissionScopes.GetScope("MSGraph.User.Read.All"));
            }

            var record = new PSObject();

            string token = GetAuthToken(messageWriter);

            if (!string.IsNullOrEmpty(token))
            {
                var cert = GetCertificate(record);

                using (var httpClient = new HttpClient())
                {
                    if (!AppExists(ApplicationName, httpClient, token))
                    {
                        var azureApp = CreateApp(loginEndPoint, httpClient, token, cert, redirectUri, scopes);

                        record.Properties.Add(new PSVariableProperty(new PSVariable("AzureAppId/ClientId", azureApp.AppId)));
                        record.Properties.Add(new PSVariableProperty(new PSVariable("Certificate Thumbprint", cert.GetCertHashString())));
                        byte[] certPfxData  = cert.Export(X509ContentType.Pfx, CertificatePassword);
                        var    base64String = Convert.ToBase64String(certPfxData);
                        record.Properties.Add(new PSVariableProperty(new PSVariable("Base64Encoded", base64String)));
                        StartConsentFlow(loginEndPoint, azureApp, redirectUri, token, httpClient, record, messageWriter, scopes);
                    }
                    else
                    {
                        throw new PSInvalidOperationException($"The application with name {ApplicationName} already exists.");
                    }
                }
            }
        }
Exemplo n.º 11
0
        protected override void ProcessRecord()
        {
            source = new CancellationTokenSource();
            var messageWriter = new CmdletMessageWriter(this);
            CancellationToken cancellationToken = source.Token;

            var endPoint = string.Empty;

            using (var authManager = new AuthenticationManager())
            {
                endPoint = authManager.GetAzureADLoginEndPoint(AzureEnvironment);
            }

            Task.Factory.StartNew(() =>
            {
                if (ParameterSetName == ParameterSet_REGISTER)
                {
                    using (var authManager = AuthenticationManager.CreateWithInteractiveLogin(PnPConnection.PnPManagementShellClientId, (url, port) =>
                    {
                        BrowserHelper.OpenBrowserForInteractiveLogin(url, port, !LaunchBrowser, source);
                    },
                                                                                              successMessageHtml: $"You successfully consented the PnP Management Shell Application for use by PnP PowerShell. Feel free to close this window.",
                                                                                              failureMessageHtml: $"You did not consent for the PnP Management Shell Application for use by PnP PowerShell. Feel free to close this browser window.",
                                                                                              azureEnvironment: AzureEnvironment))
                    {
                        try
                        {
                            authManager.GetAccessTokenAsync(new[] { $"https://{GetGraphEndPoint()}/.default" }, cancellationToken, Microsoft.Identity.Client.Prompt.Consent).GetAwaiter().GetResult();
                        }
                        catch (Microsoft.Identity.Client.MsalException)
                        {
                        }
                    }
                }
                else
                {
                    if (!string.IsNullOrEmpty(TenantName))
                    {
                        messageWriter.WriteMessage($"Share the following URL with a person that has appropriate access rights on the Azure AD to grant consent for Application Registrations:\n\nhttps://login.microsoftonline.com/{TenantName}/adminconsent?client_id={PnPConnection.PnPManagementShellClientId}");
                    }
                    else
                    {
                        using (var authManager = AuthenticationManager.CreateWithInteractiveLogin(PnPConnection.AzureManagementShellClientId, (url, port) =>
                        {
                            BrowserHelper.OpenBrowserForInteractiveLogin(url, port, !LaunchBrowser, source);
                        },
                                                                                                  successMessageHtml: $"You successfully logged in. Feel free to close this window.",
                                                                                                  failureMessageHtml: $"You failed to login succesfully. Feel free to close this browser window.",
                                                                                                  azureEnvironment: AzureEnvironment))
                        {
                            var tenantId    = "{M365-Tenant-Id}";
                            var accessToken = string.Empty;
                            try
                            {
                                accessToken = authManager.GetAccessTokenAsync(new[] { $"https://{GetGraphEndPoint()}/.default" }, cancellationToken).GetAwaiter().GetResult();
                            }
                            catch (Microsoft.Identity.Client.MsalException)
                            {
                            }

                            if (!string.IsNullOrEmpty(accessToken))
                            {
                                using (var httpClient = new HttpClient())
                                {
                                    using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, $"https://{GetGraphEndPoint()}/v1.0/organization"))
                                    {
                                        requestMessage.Headers.Add("Authorization", $"Bearer {accessToken}");
                                        requestMessage.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
                                        var response = httpClient.SendAsync(requestMessage).GetAwaiter().GetResult();
                                        if (response.IsSuccessStatusCode)
                                        {
                                            var responseContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
                                            var responseJson    = JsonSerializer.Deserialize <JsonElement>(responseContent);
                                            if (responseJson.TryGetProperty("value", out JsonElement valueElement))
                                            {
                                                foreach (var organization in valueElement.EnumerateArray())
                                                {
                                                    if (organization.TryGetProperty("id", out JsonElement idElement))
                                                    {
                                                        tenantId = idElement.GetString();

                                                        break;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                            messageWriter.WriteMessage($"Share the following URL with a person that has appropriate access rights on the Azure AD to grant consent for Application Registrations:\n\nhttps://login.microsoftonline.com/{tenantId}/adminconsent?client_id={PnPConnection.PnPManagementShellClientId}");
                            if (tenantId == "{M365-Tenant-Id}")
                            {
                                messageWriter.WriteMessage($"To get M365-Tenant-Id value, use the Get-PnPTenantId cmdlet:\nhttps://pnp.github.io/powershell/cmdlets/Get-PnPTenantId.html");
                            }
                        }
                    }
                }
                messageWriter.Finished = true;
            }, cancellationToken);
            messageWriter.Start();
        }
Exemplo n.º 12
0
        internal static PnPConnection CreateWithDeviceLogin(string clientId, string url, string tenantId, bool launchBrowser, CmdletMessageWriter messageWriter, AzureEnvironment azureEnvironment, CancellationTokenSource cancellationTokenSource)
        {
            var connectionUri = new Uri(url);
            var scopes        = new[] { $"{connectionUri.Scheme}://{connectionUri.Authority}//.default" }; // the second double slash is not a typo.

            PnP.Framework.AuthenticationManager authManager = null;
            if (PnPConnection.CachedAuthenticationManager != null)
            {
                authManager = PnPConnection.CachedAuthenticationManager;
                PnPConnection.CachedAuthenticationManager = null;
            }
            else
            {
                authManager = PnP.Framework.AuthenticationManager.CreateWithDeviceLogin(clientId, tenantId, (deviceCodeResult) =>
                {
                    if (launchBrowser)
                    {
                        if (Utilities.OperatingSystem.IsWindows())
                        {
                            ClipboardService.SetText(deviceCodeResult.UserCode);
                            messageWriter.WriteWarning($"\n\nCode {deviceCodeResult.UserCode} has been copied to your clipboard\n\n");
                            BrowserHelper.GetWebBrowserPopup(deviceCodeResult.VerificationUrl, "Please log in", cancellationTokenSource: cancellationTokenSource, cancelOnClose: false);
                        }
                        else
                        {
                            messageWriter.WriteWarning($"\n\n{deviceCodeResult.Message}\n\n");
                        }
                    }
                    else
                    {
                        messageWriter.WriteWarning($"\n\n{deviceCodeResult.Message}\n\n");
                    }
                    return(Task.FromResult(0));
                }, azureEnvironment);
            }
            using (authManager)
            {
                try
                {
                    var clientContext = authManager.GetContext(url.ToString(), cancellationTokenSource.Token);

                    var context = PnPClientContext.ConvertFrom(clientContext);
                    context.ExecutingWebRequest += (sender, e) =>
                    {
                        e.WebRequestExecutor.WebRequest.UserAgent = $"NONISV|SharePointPnP|PnPPS/{((AssemblyFileVersionAttribute)Assembly.GetExecutingAssembly().GetCustomAttribute(typeof(AssemblyFileVersionAttribute))).Version} ({System.Environment.OSVersion.VersionString})";
                    };
                    var connectionType = ConnectionType.O365;

                    var spoConnection = new PnPConnection(context, connectionType, null, clientId, null, url.ToString(), null, PnPPSVersionTag, InitializationType.DeviceLogin)
                    {
                        ConnectionMethod = ConnectionMethod.DeviceLogin,
                        AzureEnvironment = azureEnvironment
                    };
                    return(spoConnection);
                }
                catch (Microsoft.Identity.Client.MsalServiceException msalServiceException)
                {
                    if (msalServiceException.Message.StartsWith("AADSTS50059:"))
                    {
                        cancellationTokenSource.Cancel();
                        throw new Exception("Please specify -Tenant with either the tenant id or hostname.");
                    }
                    else
                    {
                        throw;
                    }
                }
            }
        }