Esempio n. 1
0
        internal static PnPConnection InstantiateDeviceLoginConnection(string url, bool launchBrowser, string tenantAdminUrl, PSHost host, bool disableTelemetry, AzureEnvironment azureEnvironment)
        {
            var          connectionUri = new Uri(url);
            var          scopes        = new[] { $"{connectionUri.Scheme}://{connectionUri.Authority}//.default" }; // the second double slash is not a typo.
            var          context       = new ClientContext(url);
            GenericToken tokenResult   = null;

            try
            {
                tokenResult = GraphToken.AcquireApplicationTokenDeviceLogin(PnPConnection.PnPManagementShellClientId, scopes, PnPConnection.DeviceLoginCallback(host, launchBrowser), azureEnvironment);
            }
            catch (MsalUiRequiredException ex)
            {
                if (ex.Classification == UiRequiredExceptionClassification.ConsentRequired)
                {
                    host.UI.WriteLine("You need to provide consent to the PnP Management Shell application for your tenant. The easiest way to do this is by issueing: 'Connect-PnPOnline -Url [yoursiteur] -PnPManagementShell -LaunchBrowser'. Make sure to authenticate as a Azure administrator allowing to provide consent to the application. Follow the steps provided.");
                    throw ex;
                }
            }
            var spoConnection = new PnPConnection(context, tokenResult, ConnectionType.O365, null, url.ToString(), tenantAdminUrl, PnPPSVersionTag, disableTelemetry, InitializationType.DeviceLogin)
            {
                //var spoConnection = new PnPConnection(context, ConnectionType.O365, url.ToString(), tenantAdminUrl, PnPPSVersionTag, host, disableTelemetry, InitializationType.DeviceLogin);
                Scopes           = scopes,
                AzureEnvironment = azureEnvironment
            };

            if (spoConnection != null)
            {
                spoConnection.ConnectionMethod = ConnectionMethod.DeviceLogin;
            }
            return(spoConnection);
        }
Esempio n. 2
0
        internal static PnPConnection InstantiateGraphDeviceLoginConnection(bool launchBrowser, PSHost host, bool disableTelemetry, AzureEnvironment azureEnvironment)
        {
            var tokenResult   = GraphToken.AcquireApplicationTokenDeviceLogin(PnPConnection.PnPManagementShellClientId, new[] { "Group.Read.All", "openid", "email", "profile", "Group.ReadWrite.All", "User.Read.All", "Directory.ReadWrite.All" }, PnPConnection.DeviceLoginCallback(host, launchBrowser), azureEnvironment);
            var spoConnection = new PnPConnection(tokenResult, ConnectionMethod.GraphDeviceLogin, ConnectionType.O365, PnPPSVersionTag, disableTelemetry, InitializationType.GraphDeviceLogin)
            {
                Scopes           = new[] { "Group.Read.All", "openid", "email", "profile", "Group.ReadWrite.All", "User.Read.All", "Directory.ReadWrite.All" },
                AzureEnvironment = azureEnvironment
            };

            return(spoConnection);
        }
Esempio n. 3
0
 internal static bool DisconnectProvidedService(PnPConnection connection)
 {
     //connection.ClearTokens();
     Environment.SetEnvironmentVariable("PNPPSHOST", string.Empty);
     Environment.SetEnvironmentVariable("PNPPSSITE", string.Empty);
     if (connection == null)
     {
         return(false);
     }
     GraphToken.ClearCaches();
     connection.Context = null;
     connection         = null;
     return(true);
 }
        /// <summary>
        /// Connect using the parameter set AADWITHSCOPE
        /// </summary>
        /// <param name="credentials">Credentials to authenticate with for delegated access or NULL for application permissions</param>
        /// <returns>PnPConnection based on the parameters provided in the parameter set</returns>
        private PnPConnection ConnectAadWithScope(PSCredential credentials, AzureEnvironment azureEnvironment)
        {
            // Filter out the scopes for the Microsoft Office 365 Management API
            var officeManagementApiScopes = Enum.GetNames(typeof(OfficeManagementApiPermission)).Select(s => s.Replace("_", ".")).Intersect(Scopes).ToArray();

            // Take the remaining scopes and try requesting them from the Microsoft Graph API
            var graphScopes = Scopes.Except(officeManagementApiScopes).ToArray();

            PnPConnection connection = null;

            // If we have Office 365 scopes, get a token for those first
            if (officeManagementApiScopes.Length > 0)
            {
                var officeManagementApiToken = credentials == null?OfficeManagementApiToken.AcquireApplicationTokenDeviceLogin(PnPConnection.PnPManagementShellClientId, officeManagementApiScopes, PnPConnection.DeviceLoginCallback(this.Host, true), azureEnvironment) : OfficeManagementApiToken.AcquireDelegatedTokenWithCredentials(PnPConnection.PnPManagementShellClientId, officeManagementApiScopes, credentials.UserName, credentials.Password);

                connection = PnPConnection.GetConnectionWithToken(officeManagementApiToken, TokenAudience.OfficeManagementApi, InitializationType.InteractiveLogin, credentials, disableTelemetry: NoTelemetry.ToBool());
            }

            // If we have Graph scopes, get a token for it
            if (graphScopes.Length > 0)
            {
                var graphToken = credentials == null?GraphToken.AcquireApplicationTokenDeviceLogin(PnPConnection.PnPManagementShellClientId, graphScopes, PnPConnection.DeviceLoginCallback(this.Host, true), azureEnvironment) : GraphToken.AcquireDelegatedTokenWithCredentials(PnPConnection.PnPManagementShellClientId, graphScopes, credentials.UserName, credentials.Password, AzureEnvironment);

                // If there's a connection already, add the AAD token to it, otherwise set up a new connection with it
                if (connection != null)
                {
                    //connection.AddToken(TokenAudience.MicrosoftGraph, graphToken);
                }
                else
                {
                    connection = PnPConnection.GetConnectionWithToken(graphToken, TokenAudience.MicrosoftGraph, InitializationType.InteractiveLogin, credentials, disableTelemetry: NoTelemetry.ToBool());
                }
            }
            connection.Scopes = Scopes;
            return(connection);
        }
Esempio n. 5
0
        /// <summary>
        /// Tries to get a token for the provided audience
        /// </summary>
        /// <param name="tokenAudience">Audience to try to get a token for</param>
        /// <param name="orRoles">The specific roles to request access to (i.e. Group.ReadWrite.All). Optional, will use default groups assigned to clientId if not specified.</param>
        /// <returns><see cref="GenericToken"/> for the audience or NULL if unable to retrieve a token for the audience on the current connection</returns>
        internal GenericToken TryGetToken(TokenAudience tokenAudience, AzureEnvironment azureEnvironment, string[] orRoles = null, string[] andRoles = null, TokenType tokenType = TokenType.All)
        {
            GenericToken token = null;

            switch (tokenAudience)
            {
            case TokenAudience.MicrosoftGraph:

                if (ConnectionMethod == ConnectionMethod.DeviceLogin || ConnectionMethod == ConnectionMethod.GraphDeviceLogin)
                {
                    var officeManagementApiScopes = Enum.GetNames(typeof(OfficeManagementApiPermission)).Select(s => s.Replace("_", ".")).Intersect(Scopes).ToArray();
                    // Take the remaining scopes and try requesting them from the Microsoft Graph API
                    var scopes = Scopes.Except(officeManagementApiScopes).ToArray();
                    token = GraphToken.AcquireApplicationTokenDeviceLogin(PnPConnection.PnPManagementShellClientId, scopes, DeviceLoginCallback(null, false), AzureEnvironment);
                }
                else
                {
                    if (!string.IsNullOrEmpty(Tenant))
                    {
                        if (Certificate != null)
                        {
                            token = GraphToken.AcquireApplicationToken(Tenant, ClientId, Certificate, AzureEnvironment);
                        }
                        else if (ClientSecret != null)
                        {
                            token = GraphToken.AcquireApplicationToken(Tenant, ClientId, ClientSecret, AzureEnvironment);
                        }
                        else if (Scopes != null)
                        {
                            var officeManagementApiScopes = Enum.GetNames(typeof(OfficeManagementApiPermission)).Select(s => s.Replace("_", ".")).Intersect(Scopes).ToArray();
                            // Take the remaining scopes and try requesting them from the Microsoft Graph API
                            var scopes = Scopes.Except(officeManagementApiScopes).ToArray();
                            if (scopes.Length > 0)
                            {
                                token = PSCredential == null?GraphToken.AcquireApplicationTokenInteractive(PnPManagementShellClientId, scopes, azureEnvironment) : GraphToken.AcquireDelegatedTokenWithCredentials(PnPManagementShellClientId, scopes, PSCredential.UserName, PSCredential.Password, azureEnvironment);
                            }
                            else
                            {
                                throw new PSSecurityException($"Access to {tokenAudience} failed because you did not connect with any permission scopes related to this service (for instance 'Group.Read.All').");
                            }
                        }
                    }
                }
                break;

            case TokenAudience.OfficeManagementApi:
                if (!string.IsNullOrEmpty(Tenant))
                {
                    if (Certificate != null)
                    {
                        token = OfficeManagementApiToken.AcquireApplicationToken(Tenant, ClientId, Certificate, AzureEnvironment);
                    }
                    else if (ClientSecret != null)
                    {
                        token = OfficeManagementApiToken.AcquireApplicationToken(Tenant, ClientId, ClientSecret, AzureEnvironment);
                    }
                    else if (Scopes != null)
                    {
                        var scopes = Enum.GetNames(typeof(OfficeManagementApiPermission)).Select(s => s.Replace("_", ".")).Intersect(Scopes).ToArray();
                        // Take the remaining scopes and try requesting them from the Microsoft Graph API
                        if (scopes.Length > 0)
                        {
                            token = PSCredential == null?OfficeManagementApiToken.AcquireApplicationTokenInteractive(PnPManagementShellClientId, scopes, azureEnvironment) : OfficeManagementApiToken.AcquireDelegatedTokenWithCredentials(PnPManagementShellClientId, scopes, PSCredential.UserName, PSCredential.Password, azureEnvironment);
                        }
                        else
                        {
                            throw new PSSecurityException($"Access to {tokenAudience} failed because you did not connect with any permission scopes related to this service (for instance 'ServiceHealth.Read').");
                        }
                    }
                }
                break;

            case TokenAudience.SharePointOnline:
                // This is not a token type we can request on demand
                return(null);
            }

            if (token != null)
            {
                var validationResults = ValidateTokenForPermissions(token, tokenAudience, orRoles, andRoles, tokenType);
                if (!validationResults.valid)
                {
                    throw new PSSecurityException($"Access to {tokenAudience} failed because the app registration {ClientId} in tenant {Tenant} is not granted {validationResults.message}");
                }
                return(token);
            }

            // Didn't have a token yet and unable to retrieve one
            return(null);
        }
Esempio n. 6
0
        // GET api/values
        public async Task <HttpResponseMessage> Get()
        {
            Dictionary <string, string> errorObj = new Dictionary <string, string>();

            // OWIN middleware validated the audience and issuer, but the scope must also be validated; must contain "access_as_user".
            string[] addinScopes = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/scope").Value.Split(' ');
            if (addinScopes.Contains("access_as_user"))
            {
                // Get the raw token that the add-in page received from the Office host.
                var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext
                                       as BootstrapContext;

                // Get the access token for MS Graph.
                string[] graphScopes = { "Files.Read.All" };

                GraphToken result = null;
                try
                {
                    // The AcquireTokenOnBehalfOfAsync method will initiate the "on behalf of" flow
                    // with the Azure AD V2 endpoint.
                    result = await GraphTokenHelper.AcquireTokenOnBehalfOfAsync(bootstrapContext.Token, graphScopes);
                }
                catch (GraphTokenException e)
                {
                    // Even we have the access token from Office, it's possible the Azure AD fails the request.
                    // Potential reasons could be: 1) Require MFA, 2) Missing consent
                    // Why missing consent could happen?
                    // To get an access token from Office client, user only needs to grant the "access_as_user" scope.
                    // It doesnt mean user has granted all the required scopes.
                    // If the required scopes have updated (for example, the developer change the scopes in http://apps.dev.microsoft.com/,
                    // user has to consent again for those new scopes.
                    // Therefore, the server should send the error to the client and let the client to handle it.
                    errorObj["claims"]    = e.Claims;
                    errorObj["message"]   = e.Message;
                    errorObj["errorCode"] = e.ErrorCode;
                    errorObj["suberror"]  = e.SubError;
                    return(SendErrorToClient(HttpStatusCode.Unauthorized, errorObj));
                }
                catch (Exception e)
                {
                    errorObj["errorCode"] = "unknown_error";
                    errorObj["message"]   = e.Message;
                    return(SendErrorToClient(HttpStatusCode.InternalServerError, errorObj));
                }

                // Get the names of files and folders in OneDrive for Business by using the Microsoft Graph API. Select only properties needed.
                var fullOneDriveItemsUrl = GraphApiHelper.GetOneDriveItemNamesUrl("?$select=name&$top=3");

                IEnumerable <OneDriveItem> filesResult;
                try
                {
                    filesResult = await ODataHelper.GetItems <OneDriveItem>(fullOneDriveItemsUrl, result.AccessToken);
                }

                // If the token is invalid, MS Graph sends a "401 Unauthorized" error with the code
                // "InvalidAuthenticationToken". ASP.NET then throws a RuntimeBinderException. This
                // is also what happens when the token is expired, although MSAL should prevent that
                // from ever happening. In either case, the client should start the process over by
                // re-calling getAccessTokenAsync.
                catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException e)
                {
                    errorObj["errorCode"] = "invalid_graph_token";
                    errorObj["message"]   = e.Message;
                    return(SendErrorToClient(HttpStatusCode.Unauthorized, errorObj));
                }

                // The returned JSON includes OData metadata and eTags that the add-in does not use.
                // Return to the client-side only the filenames.
                List <string> itemNames = new List <string>();
                foreach (OneDriveItem item in filesResult)
                {
                    itemNames.Add(item.Name);
                }

                var requestMessage = new HttpRequestMessage();
                requestMessage.SetConfiguration(new HttpConfiguration());
                var response = requestMessage.CreateResponse <List <string> >(HttpStatusCode.OK, itemNames);
                return(response);
            }
            else
            {
                // The token from the client does not have "access_as_user" permission.
                errorObj["errorCode"] = "invalid_access_token";
                errorObj["message"]   = "Missing access_as_user. Microsoft Office does not have permission to get Microsoft Graph data on behalf of the current user.";
                return(SendErrorToClient(HttpStatusCode.Unauthorized, errorObj));
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Tries to get a token for the provided audience
        /// </summary>
        /// <param name="tokenAudience">Audience to try to get a token for</param>
        /// <param name="orRoles">The specific roles to request access to (i.e. Group.ReadWrite.All). Optional, will use default groups assigned to clientId if not specified.</param>
        /// <returns><see cref="GenericToken"/> for the audience or NULL if unable to retrieve a token for the audience on the current connection</returns>
        internal GenericToken TryGetToken(TokenAudience tokenAudience, string[] orRoles = null, string[] andRoles = null, TokenType tokenType = TokenType.All)
        {
            GenericToken token = null;

            //Validate if we have a token already
            //if (AccessTokens.ContainsKey(tokenAudience))
            //{
            //    // We have a token already, ensure it is still valid
            //    token = AccessTokens[tokenAudience];

            //    if (token.ExpiresOn > DateTime.Now)
            //    {
            //        var validationResults = ValidateTokenForPermissions(token, tokenAudience, orRoles, andRoles, tokenType);
            //        if (validationResults.valid)
            //        {
            //            return token;
            //        }
            //        throw new PSSecurityException($"Access to {tokenAudience} failed because the app registration {ClientId} in tenant {Tenant} is not granted {validationResults.message}");
            //    }

            //    // Token was no longer valid, proceed with trying to create a new token
            //}

            // We do not have a token for the requested audience yet or it was no longer valid, try to create (a new) one
            switch (tokenAudience)
            {
            case TokenAudience.MicrosoftGraph:
                if (ConnectionMethod == ConnectionMethod.DeviceLogin || ConnectionMethod == ConnectionMethod.GraphDeviceLogin)
                {
                    token = GraphToken.AcquireApplicationTokenDeviceLogin(PnPConnection.DeviceLoginClientId, Scopes, DeviceLoginCallback(null, false));
                }
                else
                {
                    if (!string.IsNullOrEmpty(Tenant))
                    {
                        if (Certificate != null)
                        {
                            token = GraphToken.AcquireApplicationToken(Tenant, ClientId, Certificate);
                        }
                        else if (ClientSecret != null)
                        {
                            token = GraphToken.AcquireApplicationToken(Tenant, ClientId, ClientSecret);
                        }
                        else if (Scopes != null)
                        {
                            token = PSCredential == null?GraphToken.AcquireApplicationTokenInteractive(DeviceLoginClientId, Scopes) : GraphToken.AcquireDelegatedTokenWithCredentials(DeviceLoginClientId, Scopes, PSCredential.UserName, PSCredential.Password);
                        }
                    }
                }
                break;

            case TokenAudience.OfficeManagementApi:
                if (!string.IsNullOrEmpty(Tenant))
                {
                    if (Certificate != null)
                    {
                        token = OfficeManagementApiToken.AcquireApplicationToken(Tenant, ClientId, Certificate);
                    }
                    else if (ClientSecret != null)
                    {
                        token = OfficeManagementApiToken.AcquireApplicationToken(Tenant, ClientId, ClientSecret);
                    }
                }
                break;

            case TokenAudience.SharePointOnline:
                // This is not a token type we can request on demand
                return(null);
            }

            if (token != null)
            {
                var validationResults = ValidateTokenForPermissions(token, tokenAudience, orRoles, andRoles, tokenType);
                if (!validationResults.valid)
                {
                    throw new PSSecurityException($"Access to {tokenAudience} failed because the app registration {ClientId} in tenant {Tenant} is not granted {validationResults.message}");
                }
                // Managed to create a token for the requested audience, add it to our collection with tokens
                //AccessTokens[tokenAudience] = token;
                return(token);
            }

            // Didn't have a token yet and unable to retrieve one
            return(null);
        }
Esempio n. 8
0
        /// <summary>
        /// Tries to get a token for the provided audience
        /// </summary>
        /// <param name="tokenAudience">Audience to try to get a token for</param>
        /// <param name="roles">The specific roles to request access to (i.e. Group.ReadWrite.All). Optional, will use default groups assigned to clientId if not specified.</param>
        /// <returns><see cref="GenericToken"/> for the audience or NULL if unable to retrieve a token for the audience on the current connection</returns>
        internal GenericToken TryGetToken(TokenAudience tokenAudience, string[] roles = null)
        {
            GenericToken token = null;

            // Validate if we have a token already
            if (AccessTokens.ContainsKey(tokenAudience))
            {
                // We have a token already, ensure it is still valid
                token = AccessTokens[tokenAudience];

                if (token.ExpiresOn > DateTime.Now)
                {
                    // Token is still valid, ensure we dont have specific roles to check for or the requested roles to execute the command are present in the token
                    if (roles == null || roles.Length == 0 || roles.Any(r => token.Roles.Contains(r)))
                    {
                        return(token);
                    }

                    if (roles != null)
                    {
                        // Requested role was not part of the access token, throw an exception explaining which application registration is missing which role
                        throw new PSSecurityException($"Access to {tokenAudience} failed because the app registration {ClientId} in tenant {Tenant} is not granted {(roles.Length != 1 ? "any of " : string.Empty)}the permission{(roles.Length != 1 ? "s" : string.Empty)} {string.Join(", ", roles).TrimEnd(new[] { ',', ' ' })}");
                    }
                }

                // Token was no longer valid, proceed with trying to create a new token
            }

            // We do not have a token for the requested audience yet or it was no longer valid, try to create (a new) one
            switch (tokenAudience)
            {
            case TokenAudience.MicrosoftGraph:
                if (!string.IsNullOrEmpty(Tenant))
                {
                    if (Certificate != null)
                    {
                        token = GraphToken.AcquireToken(Tenant, ClientId, Certificate);
                    }
                    else if (ClientSecret != null)
                    {
                        token = GraphToken.AcquireToken(Tenant, ClientId, ClientSecret);
                    }
                }
                break;

            case TokenAudience.OfficeManagementApi:
                if (!string.IsNullOrEmpty(Tenant))
                {
                    if (Certificate != null)
                    {
                        token = OfficeManagementApiToken.AcquireToken(Tenant, ClientId, Certificate);
                    }
                    else if (ClientSecret != null)
                    {
                        token = OfficeManagementApiToken.AcquireToken(Tenant, ClientId, ClientSecret);
                    }
                }
                break;

            case TokenAudience.SharePointOnline:
                // This is not a token type we can request on demand
                return(null);
            }

            if (token != null)
            {
                // Managed to create a token for the requested audience, add it to our collection with tokens
                AccessTokens[tokenAudience] = token;
                return(token);
            }

            // Didn't have a token yet and unable to retrieve one
            return(null);
        }