├── .gitignore ├── 1-Call-MsGraph-WithSecret ├── AppCreationScripts │ ├── AppCreationScripts.md │ ├── Cleanup.ps1 │ ├── Configure.ps1 │ ├── apps.json │ └── sample.json ├── README.md ├── ReadmeFiles │ └── topology.svg ├── confidential_client_secret_sample.py ├── parameters.json └── requirements.txt ├── 2-Call-MsGraph-WithCertificate ├── AppCreationScripts │ ├── AppCreationScripts.md │ ├── Cleanup.ps1 │ ├── Configure.ps1 │ └── sample.json ├── README.md ├── ReadmeFiles │ └── topology.svg ├── confidential_client_certificate_sample.py ├── parameters.json └── requirements.txt ├── AppCreationScripts └── apps.json ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md └── SECURITY.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /1-Call-MsGraph-WithSecret/AppCreationScripts/AppCreationScripts.md: -------------------------------------------------------------------------------- 1 | # Registering the sample apps with Microsoft Identity Platform and updating the configuration files using PowerShell scripts 2 | 3 | ## Overview 4 | 5 | ### Quick summary 6 | 7 | 1. On Windows run PowerShell and navigate to the root of the cloned directory 8 | 1. In PowerShell run: 9 | ```PowerShell 10 | Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force 11 | ``` 12 | 1. Run the script to create your Azure AD application and configure the code of the sample application accordingly. (Other ways of running the scripts are described below) 13 | ```PowerShell 14 | .\AppCreationScripts\Configure.ps1 15 | ``` 16 | 1. Open the Visual Studio solution and click start 17 | 18 | ### More details 19 | 20 | The following paragraphs: 21 | 22 | - [Present the scripts](#presentation-of-the-scripts) and explain their [usage patterns](#usage-pattern-for-tests-and-devops-scenarios) for test and DevOps scenarios. 23 | - Explain the [pre-requisites](#pre-requisites) 24 | - Explain [four ways of running the scripts](#four-ways-to-run-the-script): 25 | - [Interactively](#option-1-interactive) to create the app in your home tenant 26 | - [Passing credentials](#option-2-non-interactive) to create the app in your home tenant 27 | - [Interactively in a specific tenant](#option-3-interactive-but-create-apps-in-a-specified-tenant) 28 | - [Passing credentials in a specific tenant](#option-4-non-interactive-and-create-apps-in-a-specified-tenant) 29 | 30 | ## Goal of the scripts 31 | 32 | ### Presentation of the scripts 33 | 34 | This sample comes with two PowerShell scripts, which automate the creation of the Azure Active Directory applications, and the configuration of the code for this sample. Once you run them, you will only need to build the solution and you are good to test. 35 | 36 | These scripts are: 37 | 38 | - `Configure.ps1` which: 39 | - creates Azure AD applications and their related objects (permissions, dependencies, secrets), 40 | - changes the configuration files in the C# and JavaScript projects. 41 | - creates a summary file named `createdApps.html` in the folder from which you ran the script, and containing, for each Azure AD application it created: 42 | - the identifier of the application 43 | - the AppId of the application 44 | - the url of its registration in the [Azure portal](https://portal.azure.com). 45 | 46 | - `Cleanup.ps1` which cleans-up the Azure AD objects created by `Configure.ps1`. Note that this script does not revert the changes done in the configuration files, though. You will need to undo the change from source control (from Visual Studio, or from the command line using, for instance, git reset). 47 | 48 | ### Usage pattern for tests and DevOps scenarios 49 | 50 | The `Configure.ps1` will stop if it tries to create an Azure AD application which already exists in the tenant. For this, if you are using the script to try/test the sample, or in DevOps scenarios, you might want to run `Cleanup.ps1` just before `Configure.ps1`. This is what is shown in the steps below. 51 | 52 | ## How to use the app creation scripts ? 53 | 54 | ### Pre-requisites 55 | 56 | 1. Open PowerShell (On Windows, press `Windows-R` and type `PowerShell` in the search window) 57 | 2. Navigate to the root directory of the project. 58 | 3. Until you change it, the default [Execution Policy](https:/go.microsoft.com/fwlink/?LinkID=135170) for scripts is usually `Restricted`. In order to run the PowerShell script you need to set the Execution Policy to `RemoteSigned`. You can set this just for the current PowerShell process by running the command: 59 | ```PowerShell 60 | Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process 61 | ``` 62 | ### (Optionally) install AzureAD PowerShell modules 63 | The scripts install the required PowerShell module (AzureAD) for the current user if needed. However, if you want to install if for all users on the machine, you can follow the following steps: 64 | 65 | 4. If you have never done it already, in the PowerShell window, install the AzureAD PowerShell modules. For this: 66 | 67 | 1. Open PowerShell as admin (On Windows, Search Powershell in the search bar, right click on it and select Run as administrator). 68 | 2. Type: 69 | ```PowerShell 70 | Install-Module AzureAD 71 | ``` 72 | 73 | or if you cannot be administrator on your machine, run: 74 | ```PowerShell 75 | Install-Module AzureAD -Scope CurrentUser 76 | ``` 77 | 78 | ### Run the script and start running 79 | 80 | 5. Go to the `AppCreationScripts` sub-folder. From the folder where you cloned the repo, 81 | ```PowerShell 82 | cd AppCreationScripts 83 | ``` 84 | 6. Run the scripts. See below for the [four options](#four-ways-to-run-the-script) to do that. 85 | 7. Open the Visual Studio solution, and in the solution's context menu, choose **Set Startup Projects**. 86 | 8. select **Start** for the projects 87 | 88 | You're done. this just works! 89 | 90 | ### Four ways to run the script 91 | 92 | We advise four ways of running the script: 93 | 94 | - Interactive: you will be prompted for credentials, and the scripts decide in which tenant to create the objects, 95 | - non-interactive: you will provide credentials, and the scripts decide in which tenant to create the objects, 96 | - Interactive in specific tenant: you will provide the tenant in which you want to create the objects and then you will be prompted for credentials, and the scripts will create the objects, 97 | - non-interactive in specific tenant: you will provide tenant in which you want to create the objects and credentials, and the scripts will create the objects. 98 | 99 | Here are the details on how to do this. 100 | 101 | #### Option 1 (interactive) 102 | 103 | - Just run ``. .\Configure.ps1``, and you will be prompted to sign-in (email address, password, and if needed MFA). 104 | - The script will be run as the signed-in user and will use the tenant in which the user is defined. 105 | 106 | Note that the script will choose the tenant in which to create the applications, based on the user. Also to run the `Cleanup.ps1` script, you will need to re-sign-in. 107 | 108 | #### Option 2 (non-interactive) 109 | 110 | When you know the indentity and credentials of the user in the name of whom you want to create the applications, you can use the non-interactive approach. It's more adapted to DevOps. Here is an example of script you'd want to run in a PowerShell Window 111 | 112 | ```PowerShell 113 | $secpasswd = ConvertTo-SecureString "[Password here]" -AsPlainText -Force 114 | $mycreds = New-Object System.Management.Automation.PSCredential ("[login@tenantName here]", $secpasswd) 115 | . .\Cleanup.ps1 -Credential $mycreds 116 | . .\Configure.ps1 -Credential $mycreds 117 | ``` 118 | 119 | Of course, in real life, you might already get the password as a `SecureString`. You might also want to get the password from KeyVault. 120 | 121 | #### Option 3 (Interactive, but create apps in a specified tenant) 122 | 123 | if you want to create the apps in a particular tenant, you can use the following option: 124 | - open the [Azure portal](https://portal.azure.com) 125 | - Select the Azure Active directory you are interested in (in the combo-box below your name on the top right of the browser window) 126 | - Find the "Active Directory" object in this tenant 127 | - Go to **Properties** and copy the content of the **Directory Id** property 128 | - Then use the full syntax to run the scripts: 129 | 130 | ```PowerShell 131 | $tenantId = "yourTenantIdGuid" 132 | . .\Cleanup.ps1 -TenantId $tenantId 133 | . .\Configure.ps1 -TenantId $tenantId 134 | ``` 135 | 136 | #### Option 4 (non-interactive, and create apps in a specified tenant) 137 | 138 | This option combines option 2 and option 3: it creates the application in a specific tenant. See option 3 for the way to get the tenant Id. Then run: 139 | 140 | ```PowerShell 141 | $secpasswd = ConvertTo-SecureString "[Password here]" -AsPlainText -Force 142 | $mycreds = New-Object System.Management.Automation.PSCredential ("[login@tenantName here]", $secpasswd) 143 | $tenantId = "yourTenantIdGuid" 144 | . .\Cleanup.ps1 -Credential $mycreds -TenantId $tenantId 145 | . .\Configure.ps1 -Credential $mycreds -TenantId $tenantId 146 | ``` 147 | -------------------------------------------------------------------------------- /1-Call-MsGraph-WithSecret/AppCreationScripts/Cleanup.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param( 3 | [PSCredential] $Credential, 4 | [Parameter(Mandatory=$False, HelpMessage='Tenant ID (This is a GUID which represents the "Directory ID" of the AzureAD tenant into which you want to create the apps')] 5 | [string] $tenantId 6 | ) 7 | 8 | if ($null -eq (Get-Module -ListAvailable -Name "AzureAD")) { 9 | Install-Module "AzureAD" -Scope CurrentUser 10 | } 11 | Import-Module AzureAD 12 | $ErrorActionPreference = "Stop" 13 | 14 | Function Cleanup 15 | { 16 | <# 17 | .Description 18 | This function removes the Azure AD applications for the sample. These applications were created by the Configure.ps1 script 19 | #> 20 | 21 | # $tenantId is the Active Directory Tenant. This is a GUID which represents the "Directory ID" of the AzureAD tenant 22 | # into which you want to create the apps. Look it up in the Azure portal in the "Properties" of the Azure AD. 23 | 24 | # Login to Azure PowerShell (interactive if credentials are not already provided: 25 | # you'll need to sign-in with creds enabling your to create apps in the tenant) 26 | if (!$Credential -and $TenantId) 27 | { 28 | $creds = Connect-AzureAD -TenantId $tenantId 29 | } 30 | else 31 | { 32 | if (!$TenantId) 33 | { 34 | $creds = Connect-AzureAD -Credential $Credential 35 | } 36 | else 37 | { 38 | $creds = Connect-AzureAD -TenantId $tenantId -Credential $Credential 39 | } 40 | } 41 | 42 | if (!$tenantId) 43 | { 44 | $tenantId = $creds.Tenant.Id 45 | } 46 | $tenant = Get-AzureADTenantDetail 47 | $tenantName = ($tenant.VerifiedDomains | Where-Object { $_._Default -eq $True }).Name 48 | 49 | # Removes the applications 50 | Write-Host "Cleaning-up applications from tenant '$tenantName'" 51 | 52 | Write-Host "Removing 'client' (python-daemon-console) if needed" 53 | Get-AzureADApplication -Filter "DisplayName eq 'python-daemon-console'" | ForEach-Object {Remove-AzureADApplication -ObjectId $_.ObjectId } 54 | $apps = Get-AzureADApplication -Filter "DisplayName eq 'python-daemon-console'" 55 | if ($apps) 56 | { 57 | Remove-AzureADApplication -ObjectId $apps.ObjectId 58 | } 59 | 60 | foreach ($app in $apps) 61 | { 62 | Remove-AzureADApplication -ObjectId $app.ObjectId 63 | Write-Host "Removed python-daemon-console.." 64 | } 65 | # also remove service principals of this app 66 | Get-AzureADServicePrincipal -filter "DisplayName eq 'python-daemon-console'" | ForEach-Object {Remove-AzureADServicePrincipal -ObjectId $_.Id -Confirm:$false} 67 | 68 | } 69 | 70 | Cleanup -Credential $Credential -tenantId $TenantId -------------------------------------------------------------------------------- /1-Call-MsGraph-WithSecret/AppCreationScripts/Configure.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param( 3 | [PSCredential] $Credential, 4 | [Parameter(Mandatory=$False, HelpMessage='Tenant ID (This is a GUID which represents the "Directory ID" of the AzureAD tenant into which you want to create the apps')] 5 | [string] $tenantId 6 | ) 7 | 8 | <# 9 | This script creates the Azure AD applications needed for this sample and updates the configuration files 10 | for the visual Studio projects from the data in the Azure AD applications. 11 | 12 | Before running this script you need to install the AzureAD cmdlets as an administrator. 13 | For this: 14 | 1) Run Powershell as an administrator 15 | 2) in the PowerShell window, type: Install-Module AzureAD 16 | 17 | There are four ways to run this script. For more information, read the AppCreationScripts.md file in the same folder as this script. 18 | #> 19 | 20 | # Create a password that can be used as an application key 21 | Function ComputePassword 22 | { 23 | $aesManaged = New-Object "System.Security.Cryptography.AesManaged" 24 | $aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC 25 | $aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::Zeros 26 | $aesManaged.BlockSize = 128 27 | $aesManaged.KeySize = 256 28 | $aesManaged.GenerateKey() 29 | return [System.Convert]::ToBase64String($aesManaged.Key) 30 | } 31 | 32 | # Create an application key 33 | # See https://www.sabin.io/blog/adding-an-azure-active-directory-application-and-key-using-powershell/ 34 | Function CreateAppKey([DateTime] $fromDate, [double] $durationInYears, [string]$pw) 35 | { 36 | $endDate = $fromDate.AddYears($durationInYears) 37 | $keyId = (New-Guid).ToString(); 38 | $key = New-Object Microsoft.Open.AzureAD.Model.PasswordCredential 39 | $key.StartDate = $fromDate 40 | $key.EndDate = $endDate 41 | $key.Value = $pw 42 | $key.KeyId = $keyId 43 | return $key 44 | } 45 | 46 | # Adds the requiredAccesses (expressed as a pipe separated string) to the requiredAccess structure 47 | # The exposed permissions are in the $exposedPermissions collection, and the type of permission (Scope | Role) is 48 | # described in $permissionType 49 | Function AddResourcePermission($requiredAccess, ` 50 | $exposedPermissions, [string]$requiredAccesses, [string]$permissionType) 51 | { 52 | foreach($permission in $requiredAccesses.Trim().Split("|")) 53 | { 54 | foreach($exposedPermission in $exposedPermissions) 55 | { 56 | if ($exposedPermission.Value -eq $permission) 57 | { 58 | $resourceAccess = New-Object Microsoft.Open.AzureAD.Model.ResourceAccess 59 | $resourceAccess.Type = $permissionType # Scope = Delegated permissions | Role = Application permissions 60 | $resourceAccess.Id = $exposedPermission.Id # Read directory data 61 | $requiredAccess.ResourceAccess.Add($resourceAccess) 62 | } 63 | } 64 | } 65 | } 66 | 67 | # 68 | # Example: GetRequiredPermissions "Microsoft Graph" "Graph.Read|User.Read" 69 | # See also: http://stackoverflow.com/questions/42164581/how-to-configure-a-new-azure-ad-application-through-powershell 70 | Function GetRequiredPermissions([string] $applicationDisplayName, [string] $requiredDelegatedPermissions, [string]$requiredApplicationPermissions, $servicePrincipal) 71 | { 72 | # If we are passed the service principal we use it directly, otherwise we find it from the display name (which might not be unique) 73 | if ($servicePrincipal) 74 | { 75 | $sp = $servicePrincipal 76 | } 77 | else 78 | { 79 | $sp = Get-AzureADServicePrincipal -Filter "DisplayName eq '$applicationDisplayName'" 80 | } 81 | $appid = $sp.AppId 82 | $requiredAccess = New-Object Microsoft.Open.AzureAD.Model.RequiredResourceAccess 83 | $requiredAccess.ResourceAppId = $appid 84 | $requiredAccess.ResourceAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.ResourceAccess] 85 | 86 | # $sp.Oauth2Permissions | Select Id,AdminConsentDisplayName,Value: To see the list of all the Delegated permissions for the application: 87 | if ($requiredDelegatedPermissions) 88 | { 89 | AddResourcePermission $requiredAccess -exposedPermissions $sp.Oauth2Permissions -requiredAccesses $requiredDelegatedPermissions -permissionType "Scope" 90 | } 91 | 92 | # $sp.AppRoles | Select Id,AdminConsentDisplayName,Value: To see the list of all the Application permissions for the application 93 | if ($requiredApplicationPermissions) 94 | { 95 | AddResourcePermission $requiredAccess -exposedPermissions $sp.AppRoles -requiredAccesses $requiredApplicationPermissions -permissionType "Role" 96 | } 97 | return $requiredAccess 98 | } 99 | 100 | 101 | Function UpdateLine([string] $line, [string] $value) 102 | { 103 | $index = $line.IndexOf('=') 104 | $delimiter = ';' 105 | if ($index -eq -1) 106 | { 107 | $index = $line.IndexOf(':') 108 | $delimiter = ',' 109 | } 110 | if ($index -ige 0) 111 | { 112 | $line = $line.Substring(0, $index+1) + " "+'"'+$value+'"'+$delimiter 113 | } 114 | return $line 115 | } 116 | 117 | Function UpdateTextFile([string] $configFilePath, [System.Collections.HashTable] $dictionary) 118 | { 119 | $lines = Get-Content $configFilePath 120 | $index = 0 121 | while($index -lt $lines.Length) 122 | { 123 | $line = $lines[$index] 124 | foreach($key in $dictionary.Keys) 125 | { 126 | if ($line.Contains($key)) 127 | { 128 | $lines[$index] = UpdateLine $line $dictionary[$key] 129 | } 130 | } 131 | $index++ 132 | } 133 | 134 | Set-Content -Path $configFilePath -Value $lines -Force 135 | } 136 | 137 | Function ReplaceInLine([string] $line, [string] $key, [string] $value) 138 | { 139 | $index = $line.IndexOf($key) 140 | if ($index -ige 0) 141 | { 142 | $index2 = $index+$key.Length 143 | $line = $line.Substring(0, $index) + $value + $line.Substring($index2) 144 | } 145 | return $line 146 | } 147 | 148 | Function ReplaceInTextFile([string] $configFilePath, [System.Collections.HashTable] $dictionary) 149 | { 150 | $lines = Get-Content $configFilePath 151 | $index = 0 152 | while($index -lt $lines.Length) 153 | { 154 | $line = $lines[$index] 155 | foreach($key in $dictionary.Keys) 156 | { 157 | if ($line.Contains($key)) 158 | { 159 | $lines[$index] = ReplaceInLine $line $key $dictionary[$key] 160 | } 161 | } 162 | $index++ 163 | } 164 | 165 | Set-Content -Path $configFilePath -Value $lines -Force 166 | } 167 | 168 | Set-Content -Value "" -Path createdApps.html 169 | Add-Content -Value "" -Path createdApps.html 170 | 171 | $ErrorActionPreference = "Stop" 172 | 173 | Function ConfigureApplications 174 | { 175 | <#.Description 176 | This function creates the Azure AD applications for the sample in the provided Azure AD tenant and updates the 177 | configuration files in the client and service project of the visual studio solution (App.Config and Web.Config) 178 | so that they are consistent with the Applications parameters 179 | #> 180 | $commonendpoint = "common" 181 | 182 | # $tenantId is the Active Directory Tenant. This is a GUID which represents the "Directory ID" of the AzureAD tenant 183 | # into which you want to create the apps. Look it up in the Azure portal in the "Properties" of the Azure AD. 184 | 185 | # Login to Azure PowerShell (interactive if credentials are not already provided: 186 | # you'll need to sign-in with creds enabling your to create apps in the tenant) 187 | if (!$Credential -and $TenantId) 188 | { 189 | $creds = Connect-AzureAD -TenantId $tenantId 190 | } 191 | else 192 | { 193 | if (!$TenantId) 194 | { 195 | $creds = Connect-AzureAD -Credential $Credential 196 | } 197 | else 198 | { 199 | $creds = Connect-AzureAD -TenantId $tenantId -Credential $Credential 200 | } 201 | } 202 | 203 | if (!$tenantId) 204 | { 205 | $tenantId = $creds.Tenant.Id 206 | } 207 | 208 | $tenant = Get-AzureADTenantDetail 209 | $tenantName = ($tenant.VerifiedDomains | Where { $_._Default -eq $True }).Name 210 | 211 | # Get the user running the script to add the user as the app owner 212 | $user = Get-AzureADUser -ObjectId $creds.Account.Id 213 | 214 | # Create the client AAD application 215 | Write-Host "Creating the AAD application (python-daemon-console)" 216 | # Get a 2 years application key for the client Application 217 | $pw = ComputePassword 218 | $fromDate = [DateTime]::Now; 219 | $key = CreateAppKey -fromDate $fromDate -durationInYears 2 -pw $pw 220 | $clientAppKey = $pw 221 | # create the application 222 | $clientAadApplication = New-AzureADApplication -DisplayName "python-daemon-console" ` 223 | -ReplyUrls "https://daemon" ` 224 | -IdentifierUris "https://$tenantName/python-daemon-console" ` 225 | -PasswordCredentials $key ` 226 | -PublicClient $False 227 | 228 | # create the service principal of the newly created application 229 | $currentAppId = $clientAadApplication.AppId 230 | $clientServicePrincipal = New-AzureADServicePrincipal -AppId $currentAppId -Tags {WindowsAzureActiveDirectoryIntegratedApp} 231 | 232 | # add the user running the script as an app owner if needed 233 | $owner = Get-AzureADApplicationOwner -ObjectId $clientAadApplication.ObjectId 234 | if ($owner -eq $null) 235 | { 236 | Add-AzureADApplicationOwner -ObjectId $clientAadApplication.ObjectId -RefObjectId $user.ObjectId 237 | Write-Host "'$($user.UserPrincipalName)' added as an application owner to app '$($clientServicePrincipal.DisplayName)'" 238 | } 239 | 240 | 241 | Write-Host "Done creating the client application (python-daemon-console)" 242 | 243 | # URL of the AAD application in the Azure portal 244 | # Future? $clientPortalUrl = "https://portal.azure.com/#@"+$tenantName+"/blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/Overview/appId/"+$clientAadApplication.AppId+"/objectId/"+$clientAadApplication.ObjectId+"/isMSAApp/" 245 | $clientPortalUrl = "https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/CallAnAPI/appId/"+$clientAadApplication.AppId+"/objectId/"+$clientAadApplication.ObjectId+"/isMSAApp/" 246 | Add-Content -Value "" -Path createdApps.html 247 | 248 | $requiredResourcesAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.RequiredResourceAccess] 249 | 250 | # Add Required Resources Access (from 'client' to 'Microsoft Graph') 251 | Write-Host "Getting access from 'client' to 'Microsoft Graph'" 252 | $requiredPermissions = GetRequiredPermissions -applicationDisplayName "Microsoft Graph" ` 253 | -requiredApplicationPermissions "User.Read.All" ` 254 | 255 | $requiredResourcesAccess.Add($requiredPermissions) 256 | 257 | 258 | Set-AzureADApplication -ObjectId $clientAadApplication.ObjectId -RequiredResourceAccess $requiredResourcesAccess 259 | Write-Host "Granted permissions." 260 | 261 | # Update config file for 'client' 262 | $configFile = $pwd.Path + "\..\parameters.json" 263 | Write-Host "Updating the sample code ($configFile)" 264 | $dictionary = @{ "client_id" = $clientAadApplication.AppId;"secret" = $clientAppKey }; 265 | UpdateTextFile -configFilePath $configFile -dictionary $dictionary 266 | 267 | # Update config file for 'client' 268 | $configFile = $pwd.Path + "\..\parameters.json" 269 | Write-Host "Updating the sample code ($configFile)" 270 | $dictionary = @{ "Enter_the_Tenant_Name_Here" = $tenantName }; 271 | ReplaceInTextFile -configFilePath $configFile -dictionary $dictionary 272 | Write-Host "" 273 | Write-Host -ForegroundColor Green "------------------------------------------------------------------------------------------------" 274 | Write-Host "IMPORTANT: Please follow the instructions below to complete a few manual step(s) in the Azure portal": 275 | Write-Host "- For 'client'" 276 | Write-Host " - Navigate to '$clientPortalUrl'" 277 | Write-Host " - Navigate to the API permissions page and click on 'Grant admin consent for {tenant}'" -ForegroundColor Red 278 | 279 | Write-Host -ForegroundColor Green "------------------------------------------------------------------------------------------------" 280 | 281 | Add-Content -Value "
ApplicationAppIdUrl in the Azure portal
client$currentAppIdpython-daemon-console
" -Path createdApps.html 282 | } 283 | 284 | # Pre-requisites 285 | if ((Get-Module -ListAvailable -Name "AzureAD") -eq $null) { 286 | Install-Module "AzureAD" -Scope CurrentUser 287 | } 288 | 289 | Import-Module AzureAD 290 | 291 | # Run interactively (will ask you for the tenant ID) 292 | ConfigureApplications -Credential $Credential -tenantId $TenantId -------------------------------------------------------------------------------- /1-Call-MsGraph-WithSecret/AppCreationScripts/apps.json: -------------------------------------------------------------------------------- 1 | { 2 | /* 3 | This section describes the Azure AD Applications to configure, and their dependencies 4 | */ 5 | 6 | "Sample": { 7 | "Title": "Acquire a token and call Microsoft Graph API from a console app using app's identity", 8 | "Level": 300, 9 | "Client": "Python" 10 | }, 11 | "AppRegistrations": [ 12 | { 13 | "x-ms-id": "ms-identity-python-daemon", 14 | "x-ms-name": "python-daemon", 15 | "x-ms-version": "2.0", 16 | "passwordCredentials": [ 17 | { 18 | "value": "{auto}" 19 | } 20 | ], 21 | "requiredResourceAccess": [ 22 | { 23 | "x-ms-resourceAppName": "Microsoft Graph", 24 | "resourceAppId": "00000003-0000-0000-c000-000000000000", 25 | "resourceAccess": [ 26 | { 27 | "id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d", 28 | "type": "Scope", 29 | "x-ms-name": "User.Read" 30 | } 31 | ] 32 | }, 33 | { 34 | "x-ms-resourceAppName": "Microsoft Graph", 35 | "resourceAppId": "00000003-0000-0000-c000-000000000000", 36 | "resourceAccess": [ 37 | { 38 | "id": "df021288-bdef-4463-88db-98f22de89214", 39 | "type": "Role", 40 | "x-ms-name": "User.Read.All" 41 | } 42 | ] 43 | } 44 | ] 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /1-Call-MsGraph-WithSecret/AppCreationScripts/sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sample": { 3 | "Title": "A Python simple daemon console application calling the graph with its own identity (client secret variation)", 4 | "Level": 200, 5 | "Client": "Python", 6 | "Service": "Microsoft Graph", 7 | "RepositoryUrl": "ms-identity-python-daemon", 8 | "Endpoint": "AAD v2.0" 9 | }, 10 | 11 | /* 12 | This section describes the Azure AD Applications to configure, and their dependencies 13 | */ 14 | "AADApps": [ 15 | { 16 | "Id": "client", 17 | "Name": "python-daemon-console", 18 | "Kind": "Daemon", 19 | "Audience": "AzureADMyOrg", 20 | "PasswordCredentials": "Auto", 21 | "UsesROPCOrIWA": false, 22 | "ReplyUrls": "https://daemon", 23 | "RequiredResourcesAccess": [ 24 | { 25 | "Resource": "Microsoft Graph", 26 | "ApplicationPermissions": [ "User.Read.All" ] 27 | } 28 | ], 29 | "ManualSteps": [ 30 | { 31 | "Comment" : "Navigate to the API permissions page and click on 'Grant admin consent for {tenant}'" 32 | } 33 | ] 34 | } 35 | ], 36 | 37 | /* 38 | This section describes how to update the code in configuration files from the apps coordinates, once the apps 39 | are created in Azure AD. 40 | Each section describes a configuration file, for one of the apps, it's type (XML, JSon, plain text), its location 41 | with respect to the root of the sample, and the mappping (which string in the config file is mapped to which value 42 | */ 43 | "CodeConfiguration": [ 44 | { 45 | "App": "client", 46 | "SettingKind": "JSon", 47 | "SettingFile": "\\..\\parameters.json", 48 | "Mappings": [ 49 | { 50 | "key": "client_id", 51 | "value": ".AppId" 52 | }, 53 | { 54 | "key": "secret", 55 | "value": ".AppKey" 56 | } 57 | ] 58 | }, 59 | { 60 | "App": "client", 61 | "SettingKind": "Replace", 62 | "SettingFile": "\\..\\parameters.json", 63 | "Mappings": [ 64 | { 65 | "key": "Enter_the_Tenant_Name_Here", 66 | "value": "$tenantName" 67 | } 68 | ] 69 | } 70 | ] 71 | } 72 | -------------------------------------------------------------------------------- /1-Call-MsGraph-WithSecret/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | topic: sample 3 | languages: 4 | - python 5 | - azurepowershell 6 | products: 7 | - azure-active-directory 8 | - dotnet-core 9 | - office-ms-graph 10 | description: "Python daemon console app using MSAL Python to get an access token and call Microsoft Graph (client secret variation)." 11 | --- 12 | 13 | # A simple Python daemon console application calling Microsoft Graph with its own identity (client secret variation) 14 | 15 | ## About this sample 16 | 17 | ### Overview 18 | 19 | This sample application shows how to use the [Microsoft identity platform endpoint](http://aka.ms/aadv2) to access the data of Microsoft business customers in a long-running, non-interactive process. It uses the [OAuth 2 client credentials grant](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow) to acquire an access token, which can be used to call the [Microsoft Graph](https://graph.microsoft.io) and access organizational data 20 | 21 | The app is Python Console application. It gets the list of users in an Azure AD tenant by using `Microsoft Authentication Library (MSAL) for Python` to acquire a token. 22 | 23 | ## Scenario 24 | 25 | The console application: 26 | 27 | - gets a token from microsoft identity platform in its own name (without a user) 28 | - and then calls the Microsoft Graph /users endpoint to get the list of user, which it then displays (as Json blob) 29 | 30 | ![Topology](./ReadmeFiles/topology.svg) 31 | 32 | For more information on the concepts used in this sample, be sure to read the [Daemon app scenario](https://docs.microsoft.com/azure/active-directory/develop/scenario-daemon-overview) and, if you're insterested in protocol details, the [Microsoft identity platform endpoint client credentials protocol documentation](https://azure.microsoft.com/documentation/articles/active-directory-v2-protocols-oauth-client-creds). 33 | 34 | > ### Daemon applications can use two forms of secrets to authenticate themselves with Azure AD: 35 | > 36 | > - **application secrets** (this Readme.md). 37 | > - **certificates**, in the [../2-Call-MsGraph-WithCertificate](../2-Call-MsGraph-WithCertificate) folder 38 | 39 | ## How to run this sample 40 | 41 | To run this sample, you'll need: 42 | 43 | > - [Python 2.7+](https://www.python.org/downloads/release/python-2713/) or [Python 3+](https://www.python.org/downloads/release/python-364/) 44 | > - An Azure Active Directory (Azure AD) tenant. For more information on how to get an Azure AD tenant, see [how to get an Azure AD tenant.](https://docs.microsoft.com/azure/active-directory/develop/quickstart-create-new-tenant) 45 | 46 | ### Step 1: Clone or download this repository 47 | 48 | From your shell or command line: 49 | 50 | ```Shell 51 | git clone https://github.com/Azure-Samples/ms-identity-python-daemon.git 52 | ``` 53 | 54 | Go to the `"1-Call-MsGraph-WithSecret"` folder 55 | 56 | ```Shell 57 | cd "1-Call-MsGraph-WithSecret" 58 | ``` 59 | 60 | or download and exact the repository .zip file. 61 | 62 | > Given that the name of the sample is pretty long, you might want to clone it in a folder close to the root of your hard drive, to avoid file size limitations on Windows. 63 | 64 | ### Step 2: Register the sample with your Azure Active Directory tenant 65 | 66 | There is one project in this sample. To register it, you can: 67 | 68 | - either follow the steps [Step 2: Register the sample with your Azure Active Directory tenant](#step-2-register-the-sample-with-your-azure-active-directory-tenant) and [Step 3: Configure the sample to use your Azure AD tenant](#choose-the-azure-ad-tenant-where-you-want-to-create-your-applications) 69 | - or use PowerShell scripts that: 70 | - **automatically** creates the Azure AD applications and related objects (passwords, permissions, dependencies) for you 71 | - modify the Visual Studio projects' configuration files. 72 | 73 | If you want to use this automation: 74 | 1. On Windows run PowerShell and navigate to the root of the cloned directory 75 | 1. In PowerShell run: 76 | ```PowerShell 77 | Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force 78 | ``` 79 | 1. Run the script to create your Azure AD application and configure the code of the sample application accordingly. 80 | ```PowerShell 81 | .\AppCreationScripts\Configure.ps1 82 | ``` 83 | > Other ways of running the scripts are described in [App Creation Scripts](./AppCreationScripts/AppCreationScripts.md) 84 | 85 | 1. Run the sample 86 | 87 | You'll need to install the dependencies using pip as follows: 88 | 89 | ```Shell 90 | pip install -r requirements.txt 91 | ``` 92 | 93 | Run `confidential_client_secret_sample.py` with the parameters for the app: 94 | 95 | ```Shell 96 | python confidential_client_secret_sample.py parameters.json 97 | ``` 98 | 99 | If ou don't want to use this automation, follow the steps below 100 | 101 | #### Choose the Azure AD tenant where you want to create your applications 102 | 103 | As a first step you'll need to: 104 | 105 | 1. Sign in to the [Azure portal](https://portal.azure.com) using either a work or school account or a personal Microsoft account. 106 | 1. If your account is present in more than one Azure AD tenant, select `Directory + Subscription` at the top right corner in the menu on top of the page, and switch your portal session to the desired Azure AD tenant. 107 | 1. In the left-hand navigation pane, select the **Azure Active Directory** service, and then select **App registrations**. 108 | 109 | #### Register the client app (daemon-console) 110 | 111 | 1. Navigate to the Microsoft identity platform for developers [App registrations](https://go.microsoft.com/fwlink/?linkid=2083908) page. 112 | 1. Select **New registration**. 113 | - In the **Name** section, enter a meaningful application name that will be displayed to users of the app, for example `daemon-console`. 114 | - In the **Supported account types** section, select **Accounts in this organizational directory only ({tenant name})**. 115 | - Select **Register** to create the application. 116 | 1. On the app **Overview** page, find the **Application (client) ID** value and record it for later. You'll need it to configure the Visual Studio configuration file for this project. 117 | 1. From the **Certificates & secrets** page, in the **Client secrets** section, choose **New client secret**: 118 | 119 | - Type a key description (of instance `app secret`), 120 | - Select a key duration of either **In 1 year**, **In 2 years**, or **Never Expires**. 121 | - When you press the **Add** button, the key value will be displayed, copy, and save the value in a safe location. 122 | - You'll need this key later to configure the project in Visual Studio. This key value will not be displayed again, nor retrievable by any other means, 123 | so record it as soon as it is visible from the Azure portal. 124 | 1. In the list of pages for the app, select **API permissions** 125 | - Click the **Add a permission** button and then, 126 | - Ensure that the **Microsoft APIs** tab is selected 127 | - In the *Commonly used Microsoft APIs* section, click on **Microsoft Graph** 128 | - In the **Application permissions** section, ensure that the right permissions are checked: **User.Read.All** 129 | - Select the **Add permissions** button 130 | 131 | 1. At this stage permissions are assigned correctly but the client app does not allow interaction. 132 | Therefore no consent can be presented via a UI and accepted to use the service app. 133 | Click the **Grant/revoke admin consent for {tenant}** button, and then select **Yes** when you are asked if you want to grant consent for the 134 | requested permissions for all account in the tenant. 135 | You need to be an Azure AD tenant admin to do this. 136 | 137 | ### Step 3: Configure the sample to use your Azure AD tenant 138 | 139 | In the steps below, "ClientID" is the same as "Application ID" or "AppId". 140 | 141 | Open the parameters.json file 142 | 143 | #### Configure the client project 144 | 145 | > Note: if you used the setup scripts, the changes below will have been applied for you 146 | 147 | 1. Open the `parameters.json` file 148 | 1. Find the string key `organizations` in the `authority` variable and replace the existing value with your Azure AD tenant name. 149 | 1. Find the string key `your_client_id` and replace the existing value with the application ID (clientId) of the `daemon-console` application copied from the Azure portal. 150 | 1. Find the string key `The secret generated by AAD during your confidential app registration` and replace the existing value with the key you saved during the creation of the `daemon-console` app, in the Azure portal. 151 | 152 | ### Step 4: Run the sample 153 | 154 | You'll need to install the dependencies using pip as follows: 155 | 156 | ```Shell 157 | pip install -r requirements.txt 158 | ``` 159 | 160 | Start the application, it will display some Json string containing the users in the tenant. 161 | 162 | ```Shell 163 | python confidential_client_secret_sample.py parameters.json 164 | ``` 165 | 166 | ## About the code 167 | 168 | The relevant code for this sample is in the `confidential_client_secret_sample.py` file. The steps are: 169 | 170 | 1. Create the MSAL confidential client application. 171 | 172 | Important note: even if we are building a console application, it is a daemon, and therefore a confidential client application, as it does not 173 | access Web APIs on behalf of a user, but on its own application behalf. 174 | 175 | ```Python 176 | app = msal.ConfidentialClientApplication( 177 | config["client_id"], authority=config["authority"], 178 | client_credential=config["secret"], 179 | ) 180 | ``` 181 | 182 | 2. Define the scopes. 183 | 184 | Specific to client credentials, you don't specify, in the code, the individual scopes you want to access. You have statically declared 185 | them during the application registration step. Therefore the only possible scope is "resource/.default" (here "https://graph.microsoft.com/.default") 186 | which means "the static permissions defined in the application". 187 | 188 | In the parameters.json file you have: 189 | 190 | ```JSon 191 | "scope": [ "https://graph.microsoft.com/.default" ], 192 | ``` 193 | 194 | 3. Acquire the token 195 | 196 | ```Python 197 | # The pattern to acquire a token looks like this. 198 | result = None 199 | 200 | # Firstly, looks up a token from cache 201 | # Since we are looking for token for the current app, NOT for an end user, 202 | # notice we give account parameter as None. 203 | result = app.acquire_token_silent(config["scope"], account=None) 204 | 205 | if not result: 206 | logging.info("No suitable token exists in cache. Let's get a new one from AAD.") 207 | result = app.acquire_token_for_client(scopes=config["scope"]) 208 | ``` 209 | 210 | 4. Call the API 211 | 212 | In that case calling "https://graph.microsoft.com/v1.0/users" with the access token as a bearer token. 213 | 214 | ```Python 215 | if "access_token" in result: 216 | # Calling graph using the access token 217 | graph_data = requests.get( # Use token to call downstream service 218 | config["endpoint"], 219 | headers={'Authorization': 'Bearer ' + result['access_token']}, ).json() 220 | print("Users from graph: " + str(graph_data)) 221 | else: 222 | print(result.get("error")) 223 | print(result.get("error_description")) 224 | print(result.get("correlation_id")) # You may need this when reporting a bug 225 | ``` 226 | 227 | ## Troubleshooting 228 | 229 | ### Did you forget to provide admin consent? This is needed for daemon apps 230 | 231 | If you get an error when calling the API `Insufficient privileges to complete the operation.`, this is because the tenant administrator has not granted permissions 232 | to the application. See step 6 of [Register the client app (daemon-console)](#register-the-client-app-daemon-console) above. 233 | 234 | You will typically see, on the output window, something like the following: 235 | 236 | ```Json 237 | Failed to call the Web Api: Forbidden 238 | Content: { 239 | "error": { 240 | "code": "Authorization_RequestDenied", 241 | "message": "Insufficient privileges to complete the operation.", 242 | "innerError": { 243 | "request-id": "", 244 | "date": "" 245 | } 246 | } 247 | } 248 | ``` 249 | 250 | ## Variation: daemon application using client credentials with certificates 251 | 252 | See [../2-Call-MsGraph-WithCertificate](../2-Call-MsGraph-WithCertificate) 253 | 254 | ## Community Help and Support 255 | 256 | Use [Stack Overflow](http://stackoverflow.com/questions/tagged/msal) to get support from the community. 257 | Ask your questions on Stack Overflow first and browse existing issues to see if someone has asked your question before. 258 | Make sure that your questions or comments are tagged with [`msal` `python`]. 259 | 260 | If you find a bug in the sample, please raise the issue on [GitHub Issues](../../issues). 261 | 262 | If you find a bug in Msal Python, please raise the issue on [MSAL Python GitHub Issues](https://github.com/AzureAD/microsoft-authentication-library-for-python/issues). 263 | 264 | To provide a recommendation, visit the following [User Voice page](https://feedback.azure.com/forums/169401-azure-active-directory). 265 | 266 | ## Contributing 267 | 268 | If you'd like to contribute to this sample, see [CONTRIBUTING.MD](/CONTRIBUTING.md). 269 | 270 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 271 | 272 | ## More information 273 | 274 | For more information, see MSAL.NET's conceptual documentation: 275 | 276 | - [Quickstart: Register an application with the Microsoft identity platform](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app) 277 | - [Quickstart: Configure a client application to access web APIs](https://docs.microsoft.com/azure/active-directory/develop/quickstart-configure-app-access-web-apis) 278 | - [Daemon app scenario](https://docs.microsoft.com/azure/active-directory/develop/scenario-daemon-overview) 279 | 280 | For more information about the underlying protocol: 281 | 282 | - [Microsoft identity platform and the OAuth 2.0 client credentials flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow) 283 | -------------------------------------------------------------------------------- /1-Call-MsGraph-WithSecret/confidential_client_secret_sample.py: -------------------------------------------------------------------------------- 1 | """ 2 | The configuration file would look like this (sans those // comments): 3 | 4 | { 5 | "note": "You configure either the authority setting when you are using Entra ID or External ID, or the oidc_authority setting when you are using External ID with its custom domain. Change the other one to null", 6 | "authority": "https://login.microsoftonline.com/Enter_the_Tenant_Name_Here", 7 | "oidc_authority": "https://login.contoso.com/Enter_the_Tenant_Name_Here/v2.0", 8 | "client_id": "your_client_id", 9 | "scope": ["https://graph.microsoft.com/.default"], 10 | // For more information about scopes for an app, refer: 11 | // https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#second-case-access-token-request-with-a-certificate" 12 | 13 | "secret": "The secret generated by AAD during your confidential app registration", 14 | // For information about generating client secret, refer: 15 | // https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki/Client-Credentials#registering-client-secrets-using-the-application-registration-portal 16 | 17 | "endpoint": "https://graph.microsoft.com/v1.0/users" 18 | 19 | } 20 | 21 | You can then run this sample with a JSON configuration file: 22 | 23 | python sample.py parameters.json 24 | """ 25 | 26 | import sys # For simplicity, we'll read config file from 1st CLI param sys.argv[1] 27 | import json 28 | import logging 29 | 30 | import requests 31 | import msal 32 | 33 | 34 | # Optional logging 35 | # logging.basicConfig(level=logging.DEBUG) 36 | 37 | config = json.load(open(sys.argv[1])) 38 | 39 | # Create a preferably long-lived app instance which maintains a token cache. 40 | app = msal.ConfidentialClientApplication( 41 | config["client_id"], 42 | authority=config.get("authority"), # For Entra ID or External ID, 43 | oidc_authority=config.get("oidc_authority"), # For External ID with custom domain 44 | client_credential=config["secret"], 45 | # token_cache=... # Default cache is in memory only. 46 | # You can learn how to use SerializableTokenCache from 47 | # https://msal-python.rtfd.io/en/latest/#msal.SerializableTokenCache 48 | ) 49 | 50 | # The pattern to acquire a token looks like this. 51 | result = None 52 | 53 | # Firstly, looks up a token from cache 54 | # Since we are looking for token for the current app, NOT for an end user, 55 | # notice we give account parameter as None. 56 | result = app.acquire_token_silent(config["scope"], account=None) 57 | 58 | if not result: 59 | logging.info("No suitable token exists in cache. Let's get a new one from AAD.") 60 | result = app.acquire_token_for_client(scopes=config["scope"]) 61 | 62 | if "access_token" in result: 63 | # Calling graph using the access token 64 | graph_data = requests.get( # Use token to call downstream service 65 | config["endpoint"], 66 | headers={'Authorization': 'Bearer ' + result['access_token']}, ).json() 67 | print("Graph API call result: ") 68 | print(json.dumps(graph_data, indent=2)) 69 | else: 70 | print(result.get("error")) 71 | print(result.get("error_description")) 72 | print(result.get("correlation_id")) # You may need this when reporting a bug 73 | 74 | -------------------------------------------------------------------------------- /1-Call-MsGraph-WithSecret/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "note": "You configure either the authority setting when you are using Entra ID or External ID, or the oidc_authority setting when you are using External ID with its custom domain. Change the other one to null", 3 | "authority": "https://login.microsoftonline.com/Enter_the_Tenant_Name_Here", 4 | "oidc_authority": "https://login.contoso.com/Enter_the_Tenant_Name_Here/v2.0", 5 | "client_id": "your_client_id", 6 | "scope": [ "https://graph.microsoft.com/.default" ], 7 | "secret": "The secret generated by AAD during your confidential app registration", 8 | "endpoint": "https://graph.microsoft.com/v1.0/users" 9 | } 10 | -------------------------------------------------------------------------------- /1-Call-MsGraph-WithSecret/requirements.txt: -------------------------------------------------------------------------------- 1 | requests>=2,<3 2 | msal>=1.28,<2 3 | -------------------------------------------------------------------------------- /2-Call-MsGraph-WithCertificate/AppCreationScripts/AppCreationScripts.md: -------------------------------------------------------------------------------- 1 | # Registering the sample apps with Microsoft Identity Platform and updating the configuration files using PowerShell scripts 2 | 3 | ## Overview 4 | 5 | ### Quick summary 6 | 7 | 1. On Windows run PowerShell and navigate to the root of the cloned directory 8 | 1. In PowerShell run: 9 | ```PowerShell 10 | Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force 11 | ``` 12 | 1. Run the script to create your Azure AD application and configure the code of the sample application accordingly. (Other ways of running the scripts are described below) 13 | ```PowerShell 14 | .\AppCreationScripts\Configure.ps1 15 | ``` 16 | 1. Open the Visual Studio solution and click start 17 | 18 | ### More details 19 | 20 | The following paragraphs: 21 | 22 | - [Present the scripts](#presentation-of-the-scripts) and explain their [usage patterns](#usage-pattern-for-tests-and-devops-scenarios) for test and DevOps scenarios. 23 | - Explain the [pre-requisites](#pre-requisites) 24 | - Explain [four ways of running the scripts](#four-ways-to-run-the-script): 25 | - [Interactively](#option-1-interactive) to create the app in your home tenant 26 | - [Passing credentials](#option-2-non-interactive) to create the app in your home tenant 27 | - [Interactively in a specific tenant](#option-3-interactive-but-create-apps-in-a-specified-tenant) 28 | - [Passing credentials in a specific tenant](#option-4-non-interactive-and-create-apps-in-a-specified-tenant) 29 | 30 | ## Goal of the scripts 31 | 32 | ### Presentation of the scripts 33 | 34 | This sample comes with two PowerShell scripts, which automate the creation of the Azure Active Directory applications, and the configuration of the code for this sample. Once you run them, you will only need to build the solution and you are good to test. 35 | 36 | These scripts are: 37 | 38 | - `Configure.ps1` which: 39 | - creates Azure AD applications and their related objects (permissions, dependencies, secrets), 40 | - changes the configuration files in the C# and JavaScript projects. 41 | - creates a summary file named `createdApps.html` in the folder from which you ran the script, and containing, for each Azure AD application it created: 42 | - the identifier of the application 43 | - the AppId of the application 44 | - the url of its registration in the [Azure portal](https://portal.azure.com). 45 | 46 | - `Cleanup.ps1` which cleans-up the Azure AD objects created by `Configure.ps1`. Note that this script does not revert the changes done in the configuration files, though. You will need to undo the change from source control (from Visual Studio, or from the command line using, for instance, git reset). 47 | 48 | ### Usage pattern for tests and DevOps scenarios 49 | 50 | The `Configure.ps1` will stop if it tries to create an Azure AD application which already exists in the tenant. For this, if you are using the script to try/test the sample, or in DevOps scenarios, you might want to run `Cleanup.ps1` just before `Configure.ps1`. This is what is shown in the steps below. 51 | 52 | ## How to use the app creation scripts ? 53 | 54 | ### Pre-requisites 55 | 56 | 1. Open PowerShell (On Windows, press `Windows-R` and type `PowerShell` in the search window) 57 | 2. Navigate to the root directory of the project. 58 | 3. Until you change it, the default [Execution Policy](https:/go.microsoft.com/fwlink/?LinkID=135170) for scripts is usually `Restricted`. In order to run the PowerShell script you need to set the Execution Policy to `RemoteSigned`. You can set this just for the current PowerShell process by running the command: 59 | ```PowerShell 60 | Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process 61 | ``` 62 | ### (Optionally) install AzureAD PowerShell modules 63 | The scripts install the required PowerShell module (AzureAD) for the current user if needed. However, if you want to install if for all users on the machine, you can follow the following steps: 64 | 65 | 4. If you have never done it already, in the PowerShell window, install the AzureAD PowerShell modules. For this: 66 | 67 | 1. Open PowerShell as admin (On Windows, Search Powershell in the search bar, right click on it and select Run as administrator). 68 | 2. Type: 69 | ```PowerShell 70 | Install-Module AzureAD 71 | ``` 72 | 73 | or if you cannot be administrator on your machine, run: 74 | ```PowerShell 75 | Install-Module AzureAD -Scope CurrentUser 76 | ``` 77 | 78 | ### Run the script and start running 79 | 80 | 5. Go to the `AppCreationScripts` sub-folder. From the folder where you cloned the repo, 81 | ```PowerShell 82 | cd AppCreationScripts 83 | ``` 84 | 6. Run the scripts. See below for the [four options](#four-ways-to-run-the-script) to do that. 85 | 7. Open the Visual Studio solution, and in the solution's context menu, choose **Set Startup Projects**. 86 | 8. select **Start** for the projects 87 | 88 | You're done. this just works! 89 | 90 | ### Four ways to run the script 91 | 92 | We advise four ways of running the script: 93 | 94 | - Interactive: you will be prompted for credentials, and the scripts decide in which tenant to create the objects, 95 | - non-interactive: you will provide credentials, and the scripts decide in which tenant to create the objects, 96 | - Interactive in specific tenant: you will provide the tenant in which you want to create the objects and then you will be prompted for credentials, and the scripts will create the objects, 97 | - non-interactive in specific tenant: you will provide tenant in which you want to create the objects and credentials, and the scripts will create the objects. 98 | 99 | Here are the details on how to do this. 100 | 101 | #### Option 1 (interactive) 102 | 103 | - Just run ``. .\Configure.ps1``, and you will be prompted to sign-in (email address, password, and if needed MFA). 104 | - The script will be run as the signed-in user and will use the tenant in which the user is defined. 105 | 106 | Note that the script will choose the tenant in which to create the applications, based on the user. Also to run the `Cleanup.ps1` script, you will need to re-sign-in. 107 | 108 | #### Option 2 (non-interactive) 109 | 110 | When you know the indentity and credentials of the user in the name of whom you want to create the applications, you can use the non-interactive approach. It's more adapted to DevOps. Here is an example of script you'd want to run in a PowerShell Window 111 | 112 | ```PowerShell 113 | $secpasswd = ConvertTo-SecureString "[Password here]" -AsPlainText -Force 114 | $mycreds = New-Object System.Management.Automation.PSCredential ("[login@tenantName here]", $secpasswd) 115 | . .\Cleanup.ps1 -Credential $mycreds 116 | . .\Configure.ps1 -Credential $mycreds 117 | ``` 118 | 119 | Of course, in real life, you might already get the password as a `SecureString`. You might also want to get the password from KeyVault. 120 | 121 | #### Option 3 (Interactive, but create apps in a specified tenant) 122 | 123 | if you want to create the apps in a particular tenant, you can use the following option: 124 | - open the [Azure portal](https://portal.azure.com) 125 | - Select the Azure Active directory you are interested in (in the combo-box below your name on the top right of the browser window) 126 | - Find the "Active Directory" object in this tenant 127 | - Go to **Properties** and copy the content of the **Directory Id** property 128 | - Then use the full syntax to run the scripts: 129 | 130 | ```PowerShell 131 | $tenantId = "yourTenantIdGuid" 132 | . .\Cleanup.ps1 -TenantId $tenantId 133 | . .\Configure.ps1 -TenantId $tenantId 134 | ``` 135 | 136 | #### Option 4 (non-interactive, and create apps in a specified tenant) 137 | 138 | This option combines option 2 and option 3: it creates the application in a specific tenant. See option 3 for the way to get the tenant Id. Then run: 139 | 140 | ```PowerShell 141 | $secpasswd = ConvertTo-SecureString "[Password here]" -AsPlainText -Force 142 | $mycreds = New-Object System.Management.Automation.PSCredential ("[login@tenantName here]", $secpasswd) 143 | $tenantId = "yourTenantIdGuid" 144 | . .\Cleanup.ps1 -Credential $mycreds -TenantId $tenantId 145 | . .\Configure.ps1 -Credential $mycreds -TenantId $tenantId 146 | ``` 147 | -------------------------------------------------------------------------------- /2-Call-MsGraph-WithCertificate/AppCreationScripts/Cleanup.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param( 3 | [PSCredential] $Credential, 4 | [Parameter(Mandatory=$False, HelpMessage='Tenant ID (This is a GUID which represents the "Directory ID" of the AzureAD tenant into which you want to create the apps')] 5 | [string] $tenantId 6 | ) 7 | 8 | if ($null -eq (Get-Module -ListAvailable -Name "AzureAD")) { 9 | Install-Module "AzureAD" -Scope CurrentUser 10 | } 11 | Import-Module AzureAD 12 | $ErrorActionPreference = "Stop" 13 | 14 | Function Cleanup 15 | { 16 | <# 17 | .Description 18 | This function removes the Azure AD applications for the sample. These applications were created by the Configure.ps1 script 19 | #> 20 | 21 | # $tenantId is the Active Directory Tenant. This is a GUID which represents the "Directory ID" of the AzureAD tenant 22 | # into which you want to create the apps. Look it up in the Azure portal in the "Properties" of the Azure AD. 23 | 24 | # Login to Azure PowerShell (interactive if credentials are not already provided: 25 | # you'll need to sign-in with creds enabling your to create apps in the tenant) 26 | if (!$Credential -and $TenantId) 27 | { 28 | $creds = Connect-AzureAD -TenantId $tenantId 29 | } 30 | else 31 | { 32 | if (!$TenantId) 33 | { 34 | $creds = Connect-AzureAD -Credential $Credential 35 | } 36 | else 37 | { 38 | $creds = Connect-AzureAD -TenantId $tenantId -Credential $Credential 39 | } 40 | } 41 | 42 | if (!$tenantId) 43 | { 44 | $tenantId = $creds.Tenant.Id 45 | } 46 | $tenant = Get-AzureADTenantDetail 47 | $tenantName = ($tenant.VerifiedDomains | Where-Object { $_._Default -eq $True }).Name 48 | 49 | # Removes the applications 50 | Write-Host "Cleaning-up applications from tenant '$tenantName'" 51 | 52 | Write-Host "Removing 'client' (python-daemon-console) if needed" 53 | Get-AzureADApplication -Filter "DisplayName eq 'python-daemon-console'" | ForEach-Object {Remove-AzureADApplication -ObjectId $_.ObjectId } 54 | $apps = Get-AzureADApplication -Filter "DisplayName eq 'python-daemon-console'" 55 | if ($apps) 56 | { 57 | Remove-AzureADApplication -ObjectId $apps.ObjectId 58 | } 59 | 60 | foreach ($app in $apps) 61 | { 62 | Remove-AzureADApplication -ObjectId $app.ObjectId 63 | Write-Host "Removed python-daemon-console.." 64 | } 65 | # also remove service principals of this app 66 | Get-AzureADServicePrincipal -filter "DisplayName eq 'python-daemon-console'" | ForEach-Object {Remove-AzureADServicePrincipal -ObjectId $_.Id -Confirm:$false} 67 | 68 | # remove self-signed certificate 69 | Get-ChildItem -Path Cert:\CurrentUser\My | where { $_.subject -eq "CN=DaemonConsoleCert" } | Remove-Item 70 | } 71 | 72 | Cleanup -Credential $Credential -tenantId $TenantId -------------------------------------------------------------------------------- /2-Call-MsGraph-WithCertificate/AppCreationScripts/Configure.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param( 3 | [PSCredential] $Credential, 4 | [Parameter(Mandatory=$False, HelpMessage='Tenant ID (This is a GUID which represents the "Directory ID" of the AzureAD tenant into which you want to create the apps')] 5 | [string] $tenantId 6 | ) 7 | 8 | <# 9 | This script creates the Azure AD applications needed for this sample and updates the configuration files 10 | for the visual Studio projects from the data in the Azure AD applications. 11 | 12 | Before running this script you need to install the AzureAD cmdlets as an administrator. 13 | For this: 14 | 1) Run Powershell as an administrator 15 | 2) in the PowerShell window, type: Install-Module AzureAD 16 | 17 | There are four ways to run this script. For more information, read the AppCreationScripts.md file in the same folder as this script. 18 | #> 19 | 20 | # Adds the requiredAccesses (expressed as a pipe separated string) to the requiredAccess structure 21 | # The exposed permissions are in the $exposedPermissions collection, and the type of permission (Scope | Role) is 22 | # described in $permissionType 23 | Function AddResourcePermission($requiredAccess, ` 24 | $exposedPermissions, [string]$requiredAccesses, [string]$permissionType) 25 | { 26 | foreach($permission in $requiredAccesses.Trim().Split("|")) 27 | { 28 | foreach($exposedPermission in $exposedPermissions) 29 | { 30 | if ($exposedPermission.Value -eq $permission) 31 | { 32 | $resourceAccess = New-Object Microsoft.Open.AzureAD.Model.ResourceAccess 33 | $resourceAccess.Type = $permissionType # Scope = Delegated permissions | Role = Application permissions 34 | $resourceAccess.Id = $exposedPermission.Id # Read directory data 35 | $requiredAccess.ResourceAccess.Add($resourceAccess) 36 | } 37 | } 38 | } 39 | } 40 | 41 | # 42 | # Example: GetRequiredPermissions "Microsoft Graph" "Graph.Read|User.Read" 43 | # See also: http://stackoverflow.com/questions/42164581/how-to-configure-a-new-azure-ad-application-through-powershell 44 | Function GetRequiredPermissions([string] $applicationDisplayName, [string] $requiredDelegatedPermissions, [string]$requiredApplicationPermissions, $servicePrincipal) 45 | { 46 | # If we are passed the service principal we use it directly, otherwise we find it from the display name (which might not be unique) 47 | if ($servicePrincipal) 48 | { 49 | $sp = $servicePrincipal 50 | } 51 | else 52 | { 53 | $sp = Get-AzureADServicePrincipal -Filter "DisplayName eq '$applicationDisplayName'" 54 | } 55 | $appid = $sp.AppId 56 | $requiredAccess = New-Object Microsoft.Open.AzureAD.Model.RequiredResourceAccess 57 | $requiredAccess.ResourceAppId = $appid 58 | $requiredAccess.ResourceAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.ResourceAccess] 59 | 60 | # $sp.Oauth2Permissions | Select Id,AdminConsentDisplayName,Value: To see the list of all the Delegated permissions for the application: 61 | if ($requiredDelegatedPermissions) 62 | { 63 | AddResourcePermission $requiredAccess -exposedPermissions $sp.Oauth2Permissions -requiredAccesses $requiredDelegatedPermissions -permissionType "Scope" 64 | } 65 | 66 | # $sp.AppRoles | Select Id,AdminConsentDisplayName,Value: To see the list of all the Application permissions for the application 67 | if ($requiredApplicationPermissions) 68 | { 69 | AddResourcePermission $requiredAccess -exposedPermissions $sp.AppRoles -requiredAccesses $requiredApplicationPermissions -permissionType "Role" 70 | } 71 | return $requiredAccess 72 | } 73 | 74 | 75 | Function UpdateLine([string] $line, [string] $value) 76 | { 77 | $index = $line.IndexOf('=') 78 | $delimiter = ';' 79 | if ($index -eq -1) 80 | { 81 | $index = $line.IndexOf(':') 82 | $delimiter = ',' 83 | } 84 | if ($index -ige 0) 85 | { 86 | $line = $line.Substring(0, $index+1) + " "+'"'+$value+'"'+$delimiter 87 | } 88 | return $line 89 | } 90 | 91 | Function UpdateTextFile([string] $configFilePath, [System.Collections.HashTable] $dictionary) 92 | { 93 | $lines = Get-Content $configFilePath 94 | $index = 0 95 | while($index -lt $lines.Length) 96 | { 97 | $line = $lines[$index] 98 | foreach($key in $dictionary.Keys) 99 | { 100 | if ($line.Contains($key)) 101 | { 102 | $lines[$index] = UpdateLine $line $dictionary[$key] 103 | } 104 | } 105 | $index++ 106 | } 107 | 108 | Set-Content -Path $configFilePath -Value $lines -Force 109 | } 110 | 111 | Function ReplaceInLine([string] $line, [string] $key, [string] $value) 112 | { 113 | $index = $line.IndexOf($key) 114 | if ($index -ige 0) 115 | { 116 | $index2 = $index+$key.Length 117 | $line = $line.Substring(0, $index) + $value + $line.Substring($index2) 118 | } 119 | return $line 120 | } 121 | 122 | Function ReplaceInTextFile([string] $configFilePath, [System.Collections.HashTable] $dictionary) 123 | { 124 | $lines = Get-Content $configFilePath 125 | $index = 0 126 | while($index -lt $lines.Length) 127 | { 128 | $line = $lines[$index] 129 | foreach($key in $dictionary.Keys) 130 | { 131 | if ($line.Contains($key)) 132 | { 133 | $lines[$index] = ReplaceInLine $line $key $dictionary[$key] 134 | } 135 | } 136 | $index++ 137 | } 138 | 139 | Set-Content -Path $configFilePath -Value $lines -Force 140 | } 141 | 142 | Set-Content -Value "" -Path createdApps.html 143 | Add-Content -Value "" -Path createdApps.html 144 | 145 | $ErrorActionPreference = "Stop" 146 | 147 | Function ConfigureApplications 148 | { 149 | <#.Description 150 | This function creates the Azure AD applications for the sample in the provided Azure AD tenant and updates the 151 | configuration files in the client and service project of the visual studio solution (App.Config and Web.Config) 152 | so that they are consistent with the Applications parameters 153 | #> 154 | $commonendpoint = "common" 155 | 156 | # $tenantId is the Active Directory Tenant. This is a GUID which represents the "Directory ID" of the AzureAD tenant 157 | # into which you want to create the apps. Look it up in the Azure portal in the "Properties" of the Azure AD. 158 | 159 | # Login to Azure PowerShell (interactive if credentials are not already provided: 160 | # you'll need to sign-in with creds enabling your to create apps in the tenant) 161 | if (!$Credential -and $TenantId) 162 | { 163 | $creds = Connect-AzureAD -TenantId $tenantId 164 | } 165 | else 166 | { 167 | if (!$TenantId) 168 | { 169 | $creds = Connect-AzureAD -Credential $Credential 170 | } 171 | else 172 | { 173 | $creds = Connect-AzureAD -TenantId $tenantId -Credential $Credential 174 | } 175 | } 176 | 177 | if (!$tenantId) 178 | { 179 | $tenantId = $creds.Tenant.Id 180 | } 181 | 182 | $tenant = Get-AzureADTenantDetail 183 | $tenantName = ($tenant.VerifiedDomains | Where { $_._Default -eq $True }).Name 184 | 185 | # Get the user running the script to add the user as the app owner 186 | $user = Get-AzureADUser -ObjectId $creds.Account.Id 187 | 188 | # Create the client AAD application 189 | Write-Host "Creating the AAD application (python-daemon-console)" 190 | # create the application 191 | $clientAadApplication = New-AzureADApplication -DisplayName "python-daemon-console" ` 192 | -ReplyUrls "https://daemon" ` 193 | -IdentifierUris "https://$tenantName/python-daemon-console" ` 194 | -PublicClient $False 195 | 196 | # Generate a certificate 197 | Write-Host "Creating the client application (python-daemon-console)" 198 | $certificate=New-SelfSignedCertificate -Subject CN=DaemonConsoleCert ` 199 | -CertStoreLocation "Cert:\CurrentUser\My" ` 200 | -KeyExportPolicy Exportable ` 201 | -KeySpec Signature 202 | $certKeyId = [Guid]::NewGuid() 203 | $certBase64Value = [System.Convert]::ToBase64String($certificate.GetRawCertData()) 204 | $certBase64Thumbprint = [System.Convert]::ToBase64String($certificate.GetCertHash()) 205 | 206 | # Add a Azure Key Credentials from the certificate for the daemon application 207 | $clientKeyCredentials = New-AzureADApplicationKeyCredential -ObjectId $clientAadApplication.ObjectId ` 208 | -CustomKeyIdentifier "CN=DaemonConsoleCert" ` 209 | -Type AsymmetricX509Cert ` 210 | -Usage Verify ` 211 | -Value $certBase64Value ` 212 | -StartDate $certificate.NotBefore ` 213 | -EndDate $certificate.NotAfter 214 | 215 | # create the service principal of the newly created application 216 | $currentAppId = $clientAadApplication.AppId 217 | $clientServicePrincipal = New-AzureADServicePrincipal -AppId $currentAppId -Tags {WindowsAzureActiveDirectoryIntegratedApp} 218 | 219 | # add the user running the script as an app owner if needed 220 | $owner = Get-AzureADApplicationOwner -ObjectId $clientAadApplication.ObjectId 221 | if ($owner -eq $null) 222 | { 223 | Add-AzureADApplicationOwner -ObjectId $clientAadApplication.ObjectId -RefObjectId $user.ObjectId 224 | Write-Host "'$($user.UserPrincipalName)' added as an application owner to app '$($clientServicePrincipal.DisplayName)'" 225 | } 226 | 227 | 228 | Write-Host "Done creating the client application (python-daemon-console)" 229 | 230 | # URL of the AAD application in the Azure portal 231 | # Future? $clientPortalUrl = "https://portal.azure.com/#@"+$tenantName+"/blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/Overview/appId/"+$clientAadApplication.AppId+"/objectId/"+$clientAadApplication.ObjectId+"/isMSAApp/" 232 | $clientPortalUrl = "https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/CallAnAPI/appId/"+$clientAadApplication.AppId+"/objectId/"+$clientAadApplication.ObjectId+"/isMSAApp/" 233 | Add-Content -Value "" -Path createdApps.html 234 | 235 | $requiredResourcesAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.RequiredResourceAccess] 236 | 237 | # Add Required Resources Access (from 'client' to 'Microsoft Graph') 238 | Write-Host "Getting access from 'client' to 'Microsoft Graph'" 239 | $requiredPermissions = GetRequiredPermissions -applicationDisplayName "Microsoft Graph" ` 240 | -requiredApplicationPermissions "User.Read.All" ` 241 | 242 | $requiredResourcesAccess.Add($requiredPermissions) 243 | 244 | 245 | Set-AzureADApplication -ObjectId $clientAadApplication.ObjectId -RequiredResourceAccess $requiredResourcesAccess 246 | Write-Host "Granted permissions." 247 | 248 | # Update config file for 'client' 249 | $configFile = $pwd.Path + "\..\parameters.json" 250 | Write-Host "Updating the sample code ($configFile)" 251 | $dictionary = @{ "client_id" = $clientAadApplication.AppId;"thumbprint" = $certBase64Thumbprint }; 252 | UpdateTextFile -configFilePath $configFile -dictionary $dictionary 253 | 254 | # Update config file for 'client' 255 | $configFile = $pwd.Path + "\..\parameters.json" 256 | Write-Host "Updating the sample code ($configFile)" 257 | $dictionary = @{ "Enter_the_Tenant_Name_Here" = $tenantName }; 258 | ReplaceInTextFile -configFilePath $configFile -dictionary $dictionary 259 | Write-Host "" 260 | Write-Host -ForegroundColor Green "------------------------------------------------------------------------------------------------" 261 | Write-Host "IMPORTANT: Please follow the instructions below to complete a few manual step(s) in the Azure portal": 262 | Write-Host "- For 'client'" 263 | Write-Host " - Navigate to '$clientPortalUrl'" 264 | Write-Host " - Navigate to the API permissions page and click on 'Grant admin consent for {tenant}'" -ForegroundColor Red 265 | 266 | Write-Host -ForegroundColor Green "------------------------------------------------------------------------------------------------" 267 | 268 | Add-Content -Value "
ApplicationAppIdUrl in the Azure portal
client$currentAppIdpython-daemon-console
" -Path createdApps.html 269 | } 270 | 271 | # Pre-requisites 272 | if ((Get-Module -ListAvailable -Name "AzureAD") -eq $null) { 273 | Install-Module "AzureAD" -Scope CurrentUser 274 | } 275 | 276 | Import-Module AzureAD 277 | 278 | # Run interactively (will ask you for the tenant ID) 279 | ConfigureApplications -Credential $Credential -tenantId $TenantId -------------------------------------------------------------------------------- /2-Call-MsGraph-WithCertificate/AppCreationScripts/sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sample": { 3 | "Title": "A Python simple daemon console application calling the graph with its own identity (client certificate variation)", 4 | "Level": 200, 5 | "Client": "Python", 6 | "Service": "Microsoft Graph", 7 | "RepositoryUrl": "ms-identity-python-daemon", 8 | "Endpoint": "AAD v2.0" 9 | }, 10 | 11 | /* 12 | This section describes the Azure AD Applications to configure, and their dependencies 13 | */ 14 | "AADApps": [ 15 | { 16 | "Id": "client", 17 | "Name": "python-daemon-console", 18 | "Kind": "Daemon", 19 | "Audience": "AzureADMyOrg", 20 | "Certificate": "CN=DaemonConsoleCert", 21 | "UsesROPCOrIWA": false, 22 | "ReplyUrls": "https://daemon", 23 | "RequiredResourcesAccess": [ 24 | { 25 | "Resource": "Microsoft Graph", 26 | "ApplicationPermissions": [ "User.Read.All" ] 27 | } 28 | ], 29 | "ManualSteps": [ 30 | { 31 | "Comment": "Navigate to the API permissions page and click on 'Grant admin consent for {tenant}'" 32 | } 33 | ] 34 | } 35 | ], 36 | 37 | /* 38 | This section describes how to update the code in configuration files from the apps coordinates, once the apps 39 | are created in Azure AD. 40 | Each section describes a configuration file, for one of the apps, it's type (XML, JSon, plain text), its location 41 | with respect to the root of the sample, and the mappping (which string in the config file is mapped to which value 42 | */ 43 | "CodeConfiguration": [ 44 | { 45 | "App": "client", 46 | "SettingKind": "JSon", 47 | "SettingFile": "\\..\\parameters.json", 48 | "Mappings": [ 49 | { 50 | "key": "client_id", 51 | "value": ".AppId" 52 | }, 53 | { 54 | "key": "thumbprint", 55 | "value": "$certBase64Thumbprint" 56 | } 57 | ] 58 | }, 59 | { 60 | "App": "client", 61 | "SettingKind": "Replace", 62 | "SettingFile": "\\..\\parameters.json", 63 | "Mappings": [ 64 | { 65 | "key": "Enter_the_Tenant_Name_Here", 66 | "value": "$tenantName" 67 | } 68 | ] 69 | } 70 | ] 71 | } 72 | -------------------------------------------------------------------------------- /2-Call-MsGraph-WithCertificate/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | topic: sample 3 | languages: 4 | - python 5 | - azurepowershell 6 | products: 7 | - azure-active-directory 8 | - dotnet 9 | - office-ms-graph 10 | description: "Python daemon console app using MSAL Python to get an access token and call Microsoft Graph (client secret variation)." 11 | --- 12 | 13 | # A simple Python daemon console application calling Microsoft Graph with its own identity (client certificate variation) 14 | 15 | ## About this sample 16 | 17 | ### Overview 18 | 19 | This sample application shows how to use the [Microsoft identity platform endpoint](http://aka.ms/aadv2) to access the data of Microsoft business customers in a long-running, non-interactive process. It uses the [OAuth 2 client credentials grant](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow) to acquire an access token, which can be used to call the [Microsoft Graph](https://graph.microsoft.io) and access organizational data. 20 | 21 | The app is a Python Console application. It gets the list of users in an Azure AD tenant by using `Microsoft Authentication Library (MSAL) for Python` to acquire a token. 22 | 23 | ## Scenario 24 | 25 | The console application: 26 | 27 | - gets a token from microsoft identity platform in its own name (without a user) 28 | - and then calls the Microsoft Graph /users endpoint to get the list of user, which it then displays (as Json blob) 29 | 30 | ![Topology](./ReadmeFiles/topology.svg) 31 | 32 | For more information on the concepts used in this sample, be sure to read the [Daemon app scenario](https://docs.microsoft.com/azure/active-directory/develop/scenario-daemon-overview) and, if you're interested in protocol details, the [Microsoft identity platform endpoint client credentials protocol documentation](https://azure.microsoft.com/documentation/articles/active-directory-v2-protocols-oauth-client-creds). 33 | 34 | > ### Daemon applications can use two forms of secrets to prove their identity to the Microsoft identity platform: 35 | > 36 | > - **application secrets**. This is the object of the other folder: [../1-Call-MsGraph-WithSecret](../1-Call-MsGraph-WithSecret) folder 37 | > - **certificates**, in this folder 38 | 39 | ## How to run this sample 40 | 41 | To run this sample, you'll need: 42 | 43 | > - [Python 2.7+](https://www.python.org/downloads/release/python-2713/) or [Python 3+](https://www.python.org/downloads/release/python-364/) 44 | > - An Azure Active Directory (Azure AD) tenant. For more information on how to get an Azure AD tenant, see [how to get an Azure AD tenant.](https://docs.microsoft.com/azure/active-directory/develop/quickstart-create-new-tenant) 45 | 46 | ### Step 1: Clone or download this repository 47 | 48 | From your shell or command line: 49 | 50 | ```Shell 51 | git clone https://github.com/Azure-Samples/ms-identity-python-daemon.git 52 | ``` 53 | 54 | Go to the `"2-Call-MsGraph-WithCertificate"` folder 55 | 56 | ```Shell 57 | cd 2-Call-MsGraph-WithCertificate 58 | ``` 59 | 60 | or download and extract the repository .zip file. 61 | 62 | > Given that the name of the sample is pretty long, you might want to clone it in a folder close to the root of your hard drive, to avoid file size limitations on Windows. 63 | 64 | ### Step 2: Register the sample with your Azure Active Directory tenant 65 | 66 | There is one project in this sample. To register it, you can: 67 | 68 | - either follow the steps [Step 2: Register the sample with your Azure Active Directory tenant](#step-2-register-the-sample-with-your-azure-active-directory-tenant) and [Step 3: Configure the sample to use your Azure AD tenant](#choose-the-azure-ad-tenant-where-you-want-to-create-your-applications) 69 | - or use PowerShell scripts that: 70 | - **automatically** creates the Azure AD applications and related objects (passwords, permissions, dependencies) for you 71 | - modify the Visual Studio projects' configuration files. 72 | 73 | If you want to use this automation: 74 | 1. On Windows run PowerShell and navigate to the root of the cloned directory 75 | 1. In PowerShell run: 76 | ```PowerShell 77 | Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force 78 | ``` 79 | 1. Run the script to create your Azure AD application and configure the code of the sample application accordingly. 80 | ```PowerShell 81 | .\AppCreationScripts\Configure.ps1 82 | ``` 83 | > Other ways of running the scripts are described in [App Creation Scripts](./AppCreationScripts/AppCreationScripts.md) 84 | 85 | 1. Run the sample 86 | You'll need to install the dependencies using pip as follows: 87 | 88 | ```Shell 89 | pip install -r requirements.txt 90 | ``` 91 | 92 | Run `confidential_client_certificate_sample.py` with the parameters for the app: 93 | 94 | ```Shell 95 | python confidential_client_certificate_sample.py parameters.json 96 | ``` 97 | 98 | If you don't want to use this automation, follow the steps below: 99 | 100 | #### Choose the Azure AD tenant where you want to create your applications 101 | 102 | As a first step you'll need to: 103 | 104 | 1. Sign in to the [Azure portal](https://portal.azure.com) using either a work or school account or a personal Microsoft account. 105 | 1. If your account is present in more than one Azure AD tenant, select `Directory + Subscription` at the top right corner in the menu on top of the page, and switch your portal session to the desired Azure AD tenant. 106 | 1. In the left-hand navigation pane, select the **Azure Active Directory** service, and then select **App registrations**. 107 | 108 | #### (Optional) Create a self-signed certificate 109 | 110 | To generate certificate and private key to be used when implementing the client credential flow are as follows: 111 | 112 | 1. Generate a key: 113 | 114 | ``` openssl genrsa -out server.pem 2048 ``` 115 | 116 | 2. Create a certificate request: 117 | 118 | ```openssl req -new -key server.pem -out server.csr``` 119 | 120 | 3. Generate a certificate: 121 | 122 | ```openssl x509 -req -days 365 -in server.csr -signkey server.pem -out server.crt``` 123 | 124 | 4. You will have to upload this certificate (`server.crt`) on Azure Portal (See next step). Once you save this certificate, the portal will give you the thumbprint of this certificate which is needed in the acquire token call. The key will be the `server.pem` key you generated in the first step. 125 | 126 | Alternatively you can use an existing certificate if you have one. 127 | 128 | #### Register the client app (daemon-console) 129 | 130 | 1. Navigate to the Microsoft identity platform for developers [App registrations](https://go.microsoft.com/fwlink/?linkid=2083908) page. 131 | 1. Select **New registration**. 132 | - In the **Name** section, enter a meaningful application name that will be displayed to users of the app, for example `daemon-console`. 133 | - In the **Supported account types** section, select **Accounts in this organizational directory only ({tenant name})**. 134 | - Select **Register** to create the application. 135 | 1. On the app **Overview** page, find the **Application (client) ID** value and record it for later. You'll need it to configure the Visual Studio configuration file for this project. 136 | 1. In the **Certificates & secrets** page, in the **Certificates** section: 137 | - Click on **Upload certificate** and, in click the browse button on the right to select the certificate you just exported (`server.pem` or your existing certificate) 138 | - Click **Add** 139 | 1. In the list of pages for the app, select **API permissions** 140 | - Click the **Add a permission** button and then, 141 | - Ensure that the **Microsoft APIs** tab is selected 142 | - In the *Commonly used Microsoft APIs* section, click on **Microsoft Graph** 143 | - In the **Application permissions** section, ensure that the right permissions are checked: **User.Read.All** 144 | - Select the **Add permissions** button 145 | 146 | 1. At this stage permissions are assigned correctly but the client app does not allow interaction. 147 | Therefore no consent can be presented via a UI and accepted to use the service app. 148 | Click the **Grant/revoke admin consent for {tenant}** button, and then select **Yes** when you are asked if you want to grant consent for the 149 | requested permissions for all account in the tenant. 150 | You need to be an Azure AD tenant admin to do this. 151 | 152 | ### Step 3: Configure the sample to use your Azure AD tenant 153 | 154 | In the steps below, "ClientID" is the same as "Application ID" or "AppId". 155 | 156 | Open the `parameters.json` file 157 | 158 | #### Configure the client project 159 | 160 | > Note: if you used the setup scripts, the changes below will have been applied for you 161 | 162 | 1. Open the `parameters.json` file 163 | 1. Find the string key `organizations` in the `authority` variable and replace the existing value with your Azure AD tenant name. 164 | 1. Find the string key `your_client_id` and replace the existing value with the application ID (clientId) of the `daemon-console` application copied from the Azure portal. 165 | 1. Find the string key `790E... The thumbprint generated by AAD when you upload your public cert` and replace the existing value with the thumbprint of the certificate you got when uploading the certificate in the `daemon-console` app, in the Azure portal. 166 | 167 | ### Step 4: Run the sample 168 | 169 | You'll need to install the dependencies using pip as follows: 170 | 171 | ```Shell 172 | pip install -r requirements.txt 173 | ``` 174 | 175 | Start the application, it will display some Json string containing the users in the tenant. 176 | 177 | ```Shell 178 | python confidential_client_certificate_sample.py parameters.json 179 | ``` 180 | 181 | ## About the code 182 | 183 | The relevant code for this sample is in the `confidential_client_secret_sample.py` file. The steps are: 184 | 185 | 1. Create the MSAL confidential client application. 186 | 187 | Important note: even if we are building a console application, it is a daemon, and therefore a confidential client application, as it does not 188 | access Web APIs on behalf of a user, but on its own application behalf. 189 | 190 | ```Python 191 | app = msal.ConfidentialClientApplication( 192 | config["client_id"], authority=config["authority"], 193 | client_credential={"thumbprint": config["thumbprint"], "private_key": open(config['private_key_file']).read()}, 194 | ) 195 | ``` 196 | 197 | 2. Define the scopes. 198 | 199 | Specific to client credentials, you don't specify, in the code, the individual scopes you want to access. You have statically declared 200 | them during the application registration step. Therefore the only possible scope is "resource/.default" (here "https://graph.microsoft.com/.default") 201 | which means "the static permissions defined in the application". 202 | 203 | In the parameters.json file you have: 204 | 205 | ```JSon 206 | "scope": [ "https://graph.microsoft.com/.default" ], 207 | ``` 208 | 209 | 3. Acquire the token 210 | 211 | ```Python 212 | # The pattern to acquire a token looks like this. 213 | result = None 214 | 215 | # Firstly, looks up a token from cache 216 | # Since we are looking for token for the current app, NOT for an end user, 217 | # notice we give account parameter as None. 218 | result = app.acquire_token_silent(config["scope"], account=None) 219 | 220 | if not result: 221 | logging.info("No suitable token exists in cache. Let's get a new one from AAD.") 222 | result = app.acquire_token_for_client(scopes=config["scope"]) 223 | ``` 224 | 225 | 4. Call the API 226 | 227 | In that case calling "https://graph.microsoft.com/v1.0/users" with the access token as a bearer token. 228 | 229 | ```Python 230 | if "access_token" in result: 231 | # Calling graph using the access token 232 | graph_data = requests.get( # Use token to call downstream service 233 | config["endpoint"], 234 | headers={'Authorization': 'Bearer ' + result['access_token']}, ).json() 235 | print("Users from graph: " + str(graph_data)) 236 | else: 237 | print(result.get("error")) 238 | print(result.get("error_description")) 239 | print(result.get("correlation_id")) # You may need this when reporting a bug 240 | ``` 241 | 242 | ## Troubleshooting 243 | 244 | ### Did you forget to provide admin consent? This is needed for daemon apps 245 | 246 | If you get an error when calling the API `Insufficient privileges to complete the operation.`, this is because the tenant administrator has not granted permissions 247 | to the application. See step 6 of [Register the client app (daemon-console)](#register-the-client-app-daemon-console) above. 248 | 249 | You will typically see, on the output window, something like the following: 250 | 251 | ```Json 252 | Failed to call the Web Api: Forbidden 253 | Content: { 254 | "error": { 255 | "code": "Authorization_RequestDenied", 256 | "message": "Insufficient privileges to complete the operation.", 257 | "innerError": { 258 | "request-id": "
", 259 | "date": "" 260 | } 261 | } 262 | } 263 | ``` 264 | 265 | ## Variation: daemon application using client credentials with client secret 266 | 267 | For the other variation, see [../1-Call-MsGraph-WithSecret](../1-Call-MsGraph-WithSecret) 268 | 269 | ## Community Help and Support 270 | 271 | Use [Stack Overflow](http://stackoverflow.com/questions/tagged/msal) to get support from the community. 272 | Ask your questions on Stack Overflow first and browse existing issues to see if someone has asked your question before. 273 | Make sure that your questions or comments are tagged with [`msal` `python`]. 274 | 275 | If you find a bug in the sample, please raise the issue on [GitHub Issues](../../issues). 276 | 277 | If you find a bug in Msal Python, please raise the issue on [MSAL Python GitHub Issues](https://github.com/AzureAD/microsoft-authentication-library-for-python/issues). 278 | 279 | To provide a recommendation, visit the following [User Voice page](https://feedback.azure.com/forums/169401-azure-active-directory). 280 | 281 | ## Contributing 282 | 283 | If you'd like to contribute to this sample, see [CONTRIBUTING.MD](/CONTRIBUTING.md). 284 | 285 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 286 | 287 | ## More information 288 | 289 | For more information, see MSAL.NET's conceptual documentation: 290 | 291 | - [Quickstart: Register an application with the Microsoft identity platform](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app) 292 | - [Quickstart: Configure a client application to access web APIs](https://docs.microsoft.com/azure/active-directory/develop/quickstart-configure-app-access-web-apis) 293 | - [Daemon app scenario](https://docs.microsoft.com/azure/active-directory/develop/scenario-daemon-overview) 294 | 295 | For more information about the underlying protocol: 296 | 297 | - [Microsoft identity platform and the OAuth 2.0 client credentials flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow) 298 | -------------------------------------------------------------------------------- /2-Call-MsGraph-WithCertificate/confidential_client_certificate_sample.py: -------------------------------------------------------------------------------- 1 | """ 2 | The configuration file would look like this (sans those // comments): 3 | 4 | { 5 | "note": "You configure either the authority setting when you are using Entra ID or External ID, or the oidc_authority setting when you are using External ID with its custom domain. Change the other one to null", 6 | "authority": "https://login.microsoftonline.com/Enter_the_Tenant_Name_Here", 7 | "oidc_authority": "https://login.contoso.com/Enter_the_Tenant_Name_Here/v2.0", 8 | "client_id": "your_client_id", 9 | "scope": ["https://graph.microsoft.com/.default"], 10 | // For more information about scopes for an app, refer: 11 | // https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#second-case-access-token-request-with-a-certificate" 12 | 13 | "thumbprint": "790E... The thumbprint generated by AAD when you upload your public cert", 14 | "private_key_file": "filename.pem", 15 | // For information about generating thumbprint and private key file, refer: 16 | // https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki/Client-Credentials#client-credentials-with-certificate 17 | 18 | "endpoint": "https://graph.microsoft.com/v1.0/users" 19 | } 20 | 21 | You can then run this sample with a JSON configuration file: 22 | 23 | python sample.py parameters.json 24 | """ 25 | 26 | import sys # For simplicity, we'll read config file from 1st CLI param sys.argv[1] 27 | import json 28 | import logging 29 | 30 | import requests 31 | import msal 32 | 33 | 34 | # Optional logging 35 | # logging.basicConfig(level=logging.DEBUG) 36 | 37 | config = json.load(open(sys.argv[1])) 38 | 39 | # Create a preferably long-lived app instance which maintains a token cache. 40 | app = msal.ConfidentialClientApplication( 41 | config["client_id"], authority=config["authority"], 42 | client_credential={"thumbprint": config["thumbprint"], "private_key": open(config['private_key_file']).read()}, 43 | # token_cache=... # Default cache is in memory only. 44 | # You can learn how to use SerializableTokenCache from 45 | # https://msal-python.rtfd.io/en/latest/#msal.SerializableTokenCache 46 | ) 47 | 48 | # The pattern to acquire a token looks like this. 49 | result = None 50 | 51 | # Firstly, looks up a token from cache 52 | # Since we are looking for token for the current app, NOT for an end user, 53 | # notice we give account parameter as None. 54 | result = app.acquire_token_silent(config["scope"], account=None) 55 | 56 | if not result: 57 | logging.info("No suitable token exists in cache. Let's get a new one from AAD.") 58 | result = app.acquire_token_for_client(scopes=config["scope"]) 59 | 60 | if "access_token" in result: 61 | # Calling graph using the access token 62 | graph_data = requests.get( # Use token to call downstream service 63 | config["endpoint"], 64 | headers={'Authorization': 'Bearer ' + result['access_token']}, ).json() 65 | print("Graph API call result: ") 66 | print(json.dumps(graph_data, indent=2)) 67 | else: 68 | print(result.get("error")) 69 | print(result.get("error_description")) 70 | print(result.get("correlation_id")) # You may need this when reporting a bug 71 | 72 | -------------------------------------------------------------------------------- /2-Call-MsGraph-WithCertificate/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "note": "You configure either the authority setting when you are using Entra ID or External ID, or the oidc_authority setting when you are using External ID with its custom domain. Change the other one to null", 3 | "authority": "https://login.microsoftonline.com/Enter_the_Tenant_Name_Here", 4 | "oidc_authority": "https://login.contoso.com/Enter_the_Tenant_Name_Here/v2.0", 5 | "client_id": "your_client_id", 6 | "scope": [ "https://graph.microsoft.com/.default" ], 7 | "thumbprint": "790E... The thumbprint generated by AAD when you upload your public cert", 8 | "private_key_file": "server.pem", 9 | "endpoint": "https://graph.microsoft.com/v1.0/users" 10 | } 11 | -------------------------------------------------------------------------------- /2-Call-MsGraph-WithCertificate/requirements.txt: -------------------------------------------------------------------------------- 1 | requests>=2,<3 2 | msal>=1.28,<2 3 | -------------------------------------------------------------------------------- /AppCreationScripts/apps.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sample": { 3 | "Title": "Acquire a token and call Microsoft Graph API from a console app using app's identity", 4 | "Level": 300, 5 | "Client": "Python" 6 | }, 7 | "AppRegistrations": [ 8 | { 9 | "x-ms-id": "ms-identity-python-daemon", 10 | "x-ms-name": "python-daemon", 11 | "x-ms-version": "2.0", 12 | "passwordCredentials": [ 13 | { 14 | "value": "{auto}" 15 | } 16 | ], 17 | "requiredResourceAccess": [ 18 | { 19 | "x-ms-resourceAppName": "Microsoft Graph", 20 | "resourceAppId": "00000003-0000-0000-c000-000000000000", 21 | "resourceAccess": [ 22 | { 23 | "id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d", 24 | "type": "Scope", 25 | "x-ms-name": "User.Read" 26 | } 27 | ] 28 | }, 29 | { 30 | "x-ms-resourceAppName": "Microsoft Graph", 31 | "resourceAppId": "00000003-0000-0000-c000-000000000000", 32 | "resourceAccess": [ 33 | { 34 | "id": "df021288-bdef-4463-88db-98f22de89214", 35 | "type": "Role", 36 | "x-ms-name": "User.Read.All" 37 | } 38 | ] 39 | } 40 | ], 41 | "codeConfigurations": [ 42 | { 43 | "settingFile": "/1-Call-MsGraph-WithSecret/parameters.json", 44 | "replaceTokens": { 45 | "appId": "your_client_id", 46 | "tenantId": "Enter_the_Tenant_Name_Here", 47 | "clientSecret": "The secret generated by AAD during your confidential app registration", 48 | "authorityEndpointHost": "https://login.microsoftonline.com/", 49 | "msgraphEndpointHost": "https://graph.microsoft.com/" 50 | } 51 | } 52 | ] 53 | } 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - python 5 | - powershell 6 | products: 7 | - azure-active-directory 8 | description: "This sample demonstrates a Python daemon console app calling the Microsoft Graph that is secured using the Microsoft identity platform." 9 | urlFragment: ms-identity-python-webapp 10 | --- 11 | 12 | # A Python console daemon app calling Microsoft Graph 13 | 14 | 21 | 22 | ## About this sample 23 | 24 | ### Scenario 25 | 26 | You want to write a daemon application that consumes organizational data using Microsoft Graph and using **Microsoft Identity Platform** to acquire tokens. 27 | 28 | For details see [Daemon apps scenario](https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-daemon-overview). 29 | 30 | ### Structure of the repository 31 | 32 | This repository contains a progressive tutorial made of two parts: 33 | 34 | Sub folder | Description 35 | ----------------------------- | ----------- 36 | [1-Call-MsGraph-WithSecret](https://github.com/Azure-Samples/ms-identity-python-daemon/tree/master/1-Call-MsGraph-WithSecret) | This sample application shows how to use the Microsoft identity platform endpoint to access the data of Microsoft business customers in a long-running, non-interactive process. The daemon application proves its identity using a **client secret**. It uses the OAuth 2 client credentials grant to acquire an access token, which can be used to call the Microsoft Graph and access organizational data

![Topology](./1-Call-MsGraph-WithSecret/ReadmeFiles/topology.svg) 37 | [2-Call-MsGraph-WithCertificate](https://github.com/Azure-Samples/ms-identity-python-daemon/tree/master/2-Call-MsGraph-WithCertificate) | This variation of the first sample shows how the application proves its identity using a **certificate**.

![Topology](./2-Call-MsGraph-WithCertificate/ReadmeFiles/topology.svg) 38 | 39 | ## Prerequisites 40 | 41 | To run this sample, you'll need: 42 | 43 | > - [Python 2.7+](https://www.python.org/downloads/release/python-2713/) or [Python 3+](https://www.python.org/downloads/release/python-364/) 44 | > - An Azure Active Directory (Azure AD) tenant. For more information on how to get an Azure AD tenant, see [how to get an Azure AD tenant.](https://docs.microsoft.com/azure/active-directory/develop/quickstart-create-new-tenant) 45 | 46 | ## Setup and run the sample 47 | 48 | Download and extract the repository .zip file or, from your shell or command line: 49 | 50 | ```Shell 51 | git clone https://github.com/Azure-Samples/ms-identity-python-daemon.git 52 | ``` 53 | 54 | Then, see the readme of each sub-folder to understand how to setup and run the samples, and learn key concepts. 55 | 56 | ## Contributing 57 | 58 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 59 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 60 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 61 | 62 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 63 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 64 | provided by the bot. You will only need to do this once across all repos using our CLA. 65 | 66 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 67 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 68 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 69 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | 42 | --------------------------------------------------------------------------------