├── .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 "Application | AppId | Url in the Azure portal |
" -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 "client | $currentAppId | python-daemon-console |
" -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 "
" -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 | 
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 "Application | AppId | Url in the Azure portal |
" -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 "client | $currentAppId | python-daemon-console |
" -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 "
" -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 | 
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 
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**. 
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 |
--------------------------------------------------------------------------------