├── .gitignore
├── AppCreationScripts
├── AppCreationScripts.md
├── Apps.json
├── Cleanup.ps1
├── Configure.ps1
├── readme.txt
└── sample.json
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── ReadmeFiles
├── architecture_v1.png
├── architecture_v2.png
└── topology.png
├── SECURITY.md
├── app.py
├── app_config.py
├── blog-webappaad-backend
├── .gitignore
├── .vscode
│ └── extensions.json
├── HttpBackend
│ ├── __init__.py
│ └── function.json
├── host.json
└── requirements.txt
├── flask_session
├── 00abd19a9adbeed5b64e418f71b5ee39
├── 02913272f2a3ca8faf88a8eb9e93035f
├── 02f8a251fe824076d84a67e3f029f504
├── 05ae107f34b98c210eab2f21abe35ea2
├── 0912865d2b743635ddc70bcd1c4db2e7
├── 0a0dc05395fd5dab0e803f96c37a23f8
├── 0c2feb0d0b92fe8de16e8d4b7b92f377
├── 0c318037e31974913e2619105ebf313d
├── 0d3994050c7510f4df9c20a0f7180dc6
├── 0ee76d8fe1efae244aebe8784e7ba45d
├── 0f618c9e147651a1f68b3578b93b1173
├── 0fb14e093f516e444f87fbabca615b84
├── 1b3a6e4ce7cb7feba1c7c4dcbde6acad
├── 1bd9a749507b2ddedac8d48a80cc4830
├── 1d488ed6fd5729d8cc3b0c43cf9006a1
├── 1eb0e7cf4779b851282ca25e59c5a806
├── 2029240f6d1128be89ddc32729463129
├── 2123f5246722d6d81447acc733ad3d55
├── 25610c405c32efbbeafb45bb914876c5
├── 272e5895a0e53a234adb4bc7badbfbaa
├── 2b69075f3e9b6c168395f0090ba915b4
├── 30c47b3cce5060a16b272976dbedbe89
├── 376860a2cc824a2a5d73cc07f6c1a4e3
├── 39aa60231baccc348b302d488f892bd3
├── 3b714d8b1e7d44b583b2b3a9f8e4a9e3
├── 3bd7c4146854c4b6181eac50a2f664f4
├── 46b92326e6b656a71fa634461430d2f8
├── 4813ad950545a9abeafb70df8137b285
├── 4a73432d9c719e71ce71f09bd30e340e
├── 4afd04a848eb713ecf61d73a775830b2
├── 4c3d9b3d803203cab8075152b0c444c2
├── 4c9f1d911dbbeb7c6cd123975c76b50b
├── 4f1db28418a10310da6498693ce803bd
├── 5058f0457d73745f90407a79639e4838
├── 52a24e452d77c77fc94b90a1e44d59c1
├── 52b482e5b79fd3a04e6792d1e62b74fe
├── 554449ccb7bfadd159453ab1431c7471
├── 55b9b44aa2e76d32fa54ef19c1af9888
├── 58660006990adc7373d1f09467538b75
├── 588a28650b287fa777fdbee4abbab57c
├── 5afe0e3f2b00bb8b38c0c4942b8f298b
├── 5ea8e76b658adc2b2d0147d932120560
├── 5fe2165827d816160f8de147470218d9
├── 631a3cda1e17e3d361827f226c972091
├── 676707ef32204fbaf0a1cf55843147d6
├── 6eb72e281b19fd61455f3350db154c34
├── 6ffc02375fe8751f87fa4f0287f2d67d
├── 74c736642f2ca0fd494baa07cffbd695
├── 7945d5d869a4486246bd2420266fd85f
├── 7a62115d0d27111fc98f4614fec4ee2a
├── 7eb265131747b74f31c43dec6252d9b1
├── 7ecf902714220f3f08590ee8281bc03b
├── 8786589aab16485e9b2f584e1bf1169e
├── 89ee9d3c01b65f3a990b7b6564a92736
├── 8ebc2ca270fa736466231723239cffdc
├── 8ed82fdeb4163b5f9540290624b748fd
├── 908709ef0668e45c6b4e51ff33c1eeef
├── 91da6fdb956b0210de1ebbbacb504239
├── 932fe46bb1cc1d83e9949554bbdd6595
├── 93dd3c25d0a98f295e6003ec2542e72e
├── 93ed61cc2cf34227ede3ea8345b71dd7
├── 963bfe07041bfbf5e4033368991aa0fd
├── 9cf3bf55d121a7a039e822ed827bebd7
├── 9d1734df324b6b5679d43566d6375ab8
├── 9dbe2792966d99b48bf148663dac28fc
├── 9dca751f77a9ccbfaec3c7bf3d79d44c
├── 9f2787b491f430f5c47c1e83e6363feb
├── a04a83053787814f3c951add73874821
├── a0fbe1f404a5abb878c728fe971bbc5d
├── a6131427f0c4e7311521f526ecb744b3
├── a9c1891644aa19494c691c3790963fca
├── a9c916c28edd36061815b3a974eb1272
├── ac300107cdd359d52cb289ad58c173da
├── ad4fd208a644108c98e121557b59b7f0
├── b175872a1e761509ec29378ad2c25ca3
├── b600d2b1bac1001f883b63d5b36289b8
├── b79ed7cabf6692e444fd5fcf6207bfa5
├── b7eadf22ae1a93a0ca47f524ad5e4fab
├── ba4dfdef16931fa749278dae279e5fc2
├── bab93b77a29b8a8fc078ff6837c2a955
├── bbecbe4ae52f91a52cc1570c9489448c
├── bc6ed66c7b5efe7b7d07914c14cf0f1d
├── c07e5d95630c2eda3a1e58a679f06a82
├── c19269aba40c71054afc141823e871c4
├── c563c8373d9b43b77712c1a5b19dced3
├── c7377606b26183d4eee47dd47fa9fcee
├── cae63cb2d81c788d26596886dc3fe4b1
├── cd522f65a6a693ab88ab990044dcea4b
├── ce7ea206cba7e3e5a424d48e545149f2
├── cf9d7c060672c3724b5472749b25e3e2
├── d19f81ac4e3e09015887636bb3f35415
├── d27e26f492ae9a08a5ff6794f899637c
├── d27e5e55aad695b33bcd07750df3aba4
├── d29b9bb8b1a3d77ee66565178d506ce3
├── d318764f012c5d727bb9f8de1bdc108b
├── d532435332fd4eb6642f03d1d51469ac
├── dba39c8ec266614d9fdea10ffa704779
├── dd292d85b046af37c8933fab43eec0b7
├── dd41e15f9853bdc145185c64a6489291
├── dd487150c7e8d40c672900de049639ec
├── df0998812a5f7a56ab046b9d3d229d8b
├── df9babbe73a36daed47dbe18587b21cf
├── e14ee9bc0b66f0bedec0ab97a38a087a
├── e229e5b309a720f4801fe9acd095842c
├── e2ec44653b64af4b9e70c4f3e5511a07
├── e319afc9fe9f25e55c2c212d423644b0
├── e895d5704ae73f512f0d4b534d9dfe79
├── edc30d9b53a482d5e9b707f1cefa2db4
├── ef4cf4bb2cf913d5445baae3ea5c4dc6
├── efe98641d0e3c2f827a53a6e9fa3678c
├── f5bba97d9e6c36ea5299a29e1c3514fb
├── f8e24cb19bd80791c04e06bf329abe91
└── f9b89b35a44f78f8565f5efeac0bd052
├── requirements.txt
└── templates
├── auth_error.html
├── display.html
├── index.html
└── login.html
/.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 | secrets/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *.cover
48 | .hypothesis/
49 | .pytest_cache/
50 |
51 | # Translations
52 | *.mo
53 | *.pot
54 |
55 | # Django stuff:
56 | *.log
57 | local_settings.py
58 | db.sqlite3
59 |
60 | # Flask stuff:
61 | instance/
62 | .webassets-cache
63 |
64 | # Scrapy stuff:
65 | .scrapy
66 |
67 | # Sphinx documentation
68 | docs/_build/
69 |
70 | # PyBuilder
71 | target/
72 |
73 | # Jupyter Notebook
74 | .ipynb_checkpoints
75 |
76 | # pyenv
77 | .python-version
78 |
79 | # celery beat schedule file
80 | celerybeat-schedule
81 |
82 | # SageMath parsed files
83 | *.sage.py
84 |
85 | # Environments
86 | .env
87 | .venv
88 | env/
89 | venv/
90 | ENV/
91 | env.bak/
92 | venv.bak/
93 |
94 | # Spyder project settings
95 | .spyderproject
96 | .spyproject
97 |
98 | # Rope project settings
99 | .ropeproject
100 |
101 | # mkdocs documentation
102 | /site
103 |
104 | # mypy
105 | .mypy_cache/
106 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/AppCreationScripts/Apps.json:
--------------------------------------------------------------------------------
1 | {
2 | "Sample": {
3 | "Title": "Integrating Azure AD into a Python web application",
4 | "Level": 400,
5 | "Client": "Python, MSAL.Python"
6 | },
7 | "AppRegistrations": [
8 | {
9 | "x-ms-id": "PythonWebApp",
10 | "x-ms-name": "ms-identity-python-webapp",
11 | "x-ms-version": "2.0",
12 | "replyUrlsWithType": [
13 | {
14 | "url": "http://localhost:5000/getAToken",
15 | "type": "Web"
16 | }
17 | ],
18 | "x-ms-passwordCredentials": "Auto",
19 | "oauth2AllowImplicitFlow": false,
20 | "oauth2AllowIdTokenImplicitFlow": false,
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 | "id": "b340eb25-3456-403f-be2f-af7a0d370277",
33 | "type": "Scope",
34 | "x-ms-name": "User.ReadBasic.All"
35 | }
36 | ]
37 | }
38 | ],
39 | "codeConfigurations": [
40 | {
41 | "settingFile": "/app_config.py",
42 | "replaceTokens": {
43 | "appId": "Enter_the_Application_Id_here",
44 | "tenantId": "common",
45 | "clientSecret": "Enter_the_Client_Secret_Here",
46 | "authorityEndpointHost": "https://login.microsoftonline.com/",
47 | "msgraphEndpointHost": "https://graph.microsoft.com/"
48 | }
49 | }
50 | ]
51 | }
52 | ]
53 | }
54 |
--------------------------------------------------------------------------------
/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 'pythonwebapp' (python-webapp) if needed"
53 | Get-AzureADApplication -Filter "DisplayName eq 'python-webapp'" | ForEach-Object {Remove-AzureADApplication -ObjectId $_.ObjectId }
54 | $apps = Get-AzureADApplication -Filter "DisplayName eq 'python-webapp'"
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-webapp.."
64 | }
65 | # also remove service principals of this app
66 | Get-AzureADServicePrincipal -filter "DisplayName eq 'python-webapp'" | ForEach-Object {Remove-AzureADServicePrincipal -ObjectId $_.Id -Confirm:$false}
67 |
68 | }
69 |
70 | Cleanup -Credential $Credential -tenantId $TenantId
--------------------------------------------------------------------------------
/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 ReplaceInLine([string] $line, [string] $key, [string] $value)
102 | {
103 | $index = $line.IndexOf($key)
104 | if ($index -ige 0)
105 | {
106 | $index2 = $index+$key.Length
107 | $line = $line.Substring(0, $index) + $value + $line.Substring($index2)
108 | }
109 | return $line
110 | }
111 |
112 | Function ReplaceInTextFile([string] $configFilePath, [System.Collections.HashTable] $dictionary)
113 | {
114 | $lines = Get-Content $configFilePath
115 | $index = 0
116 | while($index -lt $lines.Length)
117 | {
118 | $line = $lines[$index]
119 | foreach($key in $dictionary.Keys)
120 | {
121 | if ($line.Contains($key))
122 | {
123 | $lines[$index] = ReplaceInLine $line $key $dictionary[$key]
124 | }
125 | }
126 | $index++
127 | }
128 |
129 | Set-Content -Path $configFilePath -Value $lines -Force
130 | }
131 |
132 | Set-Content -Value "
" -Path createdApps.html
133 | Add-Content -Value "Application AppId Url in the Azure portal " -Path createdApps.html
134 |
135 | $ErrorActionPreference = "Stop"
136 |
137 | Function ConfigureApplications
138 | {
139 | <#.Description
140 | This function creates the Azure AD applications for the sample in the provided Azure AD tenant and updates the
141 | configuration files in the client and service project of the visual studio solution (App.Config and Web.Config)
142 | so that they are consistent with the Applications parameters
143 | #>
144 | $commonendpoint = "common"
145 |
146 | # $tenantId is the Active Directory Tenant. This is a GUID which represents the "Directory ID" of the AzureAD tenant
147 | # into which you want to create the apps. Look it up in the Azure portal in the "Properties" of the Azure AD.
148 |
149 | # Login to Azure PowerShell (interactive if credentials are not already provided:
150 | # you'll need to sign-in with creds enabling your to create apps in the tenant)
151 | if (!$Credential -and $TenantId)
152 | {
153 | $creds = Connect-AzureAD -TenantId $tenantId
154 | }
155 | else
156 | {
157 | if (!$TenantId)
158 | {
159 | $creds = Connect-AzureAD -Credential $Credential
160 | }
161 | else
162 | {
163 | $creds = Connect-AzureAD -TenantId $tenantId -Credential $Credential
164 | }
165 | }
166 |
167 | if (!$tenantId)
168 | {
169 | $tenantId = $creds.Tenant.Id
170 | }
171 |
172 | $tenant = Get-AzureADTenantDetail
173 | $tenantName = ($tenant.VerifiedDomains | Where { $_._Default -eq $True }).Name
174 |
175 | # Get the user running the script to add the user as the app owner
176 | $user = Get-AzureADUser -ObjectId $creds.Account.Id
177 |
178 | # Create the pythonwebapp AAD application
179 | Write-Host "Creating the AAD application (python-webapp)"
180 | # Get a 2 years application key for the pythonwebapp Application
181 | $pw = ComputePassword
182 | $fromDate = [DateTime]::Now;
183 | $key = CreateAppKey -fromDate $fromDate -durationInYears 2 -pw $pw
184 | $pythonwebappAppKey = $pw
185 | # create the application
186 | $pythonwebappAadApplication = New-AzureADApplication -DisplayName "python-webapp" `
187 | -ReplyUrls "http://localhost:5000/getAToken" `
188 | -IdentifierUris "https://$tenantName/python-webapp" `
189 | -AvailableToOtherTenants $True `
190 | -PasswordCredentials $key `
191 | -Oauth2AllowImplicitFlow $true `
192 | -PublicClient $False
193 |
194 | # create the service principal of the newly created application
195 | $currentAppId = $pythonwebappAadApplication.AppId
196 | $pythonwebappServicePrincipal = New-AzureADServicePrincipal -AppId $currentAppId -Tags {WindowsAzureActiveDirectoryIntegratedApp}
197 |
198 | # add the user running the script as an app owner if needed
199 | $owner = Get-AzureADApplicationOwner -ObjectId $pythonwebappAadApplication.ObjectId
200 | if ($owner -eq $null)
201 | {
202 | Add-AzureADApplicationOwner -ObjectId $pythonwebappAadApplication.ObjectId -RefObjectId $user.ObjectId
203 | Write-Host "'$($user.UserPrincipalName)' added as an application owner to app '$($pythonwebappServicePrincipal.DisplayName)'"
204 | }
205 |
206 |
207 | Write-Host "Done creating the pythonwebapp application (python-webapp)"
208 |
209 | # URL of the AAD application in the Azure portal
210 | # Future? $pythonwebappPortalUrl = "https://portal.azure.com/#@"+$tenantName+"/blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/Overview/appId/"+$pythonwebappAadApplication.AppId+"/objectId/"+$pythonwebappAadApplication.ObjectId+"/isMSAApp/"
211 | $pythonwebappPortalUrl = "https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/CallAnAPI/appId/"+$pythonwebappAadApplication.AppId+"/objectId/"+$pythonwebappAadApplication.ObjectId+"/isMSAApp/"
212 | Add-Content -Value "pythonwebapp $currentAppId python-webapp " -Path createdApps.html
213 |
214 | $requiredResourcesAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.RequiredResourceAccess]
215 |
216 | # Add Required Resources Access (from 'pythonwebapp' to 'Microsoft Graph')
217 | Write-Host "Getting access from 'pythonwebapp' to 'Microsoft Graph'"
218 | $requiredPermissions = GetRequiredPermissions -applicationDisplayName "Microsoft Graph" `
219 | -requiredDelegatedPermissions "User.ReadBasic.All" `
220 |
221 | $requiredResourcesAccess.Add($requiredPermissions)
222 |
223 |
224 | Set-AzureADApplication -ObjectId $pythonwebappAadApplication.ObjectId -RequiredResourceAccess $requiredResourcesAccess
225 | Write-Host "Granted permissions."
226 |
227 | # Update config file for 'pythonwebapp'
228 | $configFile = $pwd.Path + "\..\app_config.py"
229 | Write-Host "Updating the sample code ($configFile)"
230 | $dictionary = @{ "Enter_the_Tenant_Name_Here" = $tenantName;"Enter_the_Client_Secret_Here" = $pythonwebappAppKey;"Enter_the_Application_Id_here" = $pythonwebappAadApplication.AppId };
231 | ReplaceInTextFile -configFilePath $configFile -dictionary $dictionary
232 |
233 | Add-Content -Value "
" -Path createdApps.html
234 | }
235 |
236 | # Pre-requisites
237 | if ((Get-Module -ListAvailable -Name "AzureAD") -eq $null) {
238 | Install-Module "AzureAD" -Scope CurrentUser
239 | }
240 |
241 | Import-Module AzureAD
242 |
243 | # Run interactively (will ask you for the tenant ID)
244 | ConfigureApplications -Credential $Credential -tenantId $TenantId
--------------------------------------------------------------------------------
/AppCreationScripts/readme.txt:
--------------------------------------------------------------------------------
1 | See https://github.com/Azure-Samples/ms-identity-python-webapp/tree/master/AppCreationScripts
--------------------------------------------------------------------------------
/AppCreationScripts/sample.json:
--------------------------------------------------------------------------------
1 | {
2 | "Sample": {
3 | "RepositoryUrl": "https://github.com/Azure-Samples/ms-identity-python-webapp",
4 | "Title": "Integrating Microsoft Identity Platform with a Python web application",
5 | "Level": 300,
6 | "Client": "Python Web Application",
7 | "Service": "Microsoft Graph",
8 | "Endpoint": "Microsoft identity platform (formerly Azure AD v2.0)"
9 | },
10 |
11 | /*
12 | This section describes the Azure AD Applications to configure, and their dependencies
13 | */
14 | "AADApps": [
15 | {
16 | "Id": "pythonwebapp",
17 | "Name": "python-webapp",
18 | "Kind": "WebApp", /* SinglePageApplication, WebApp, Mobile, UWP, Desktop, Daemon, WebApi, Browserless */
19 | "Audience": "AzureADandPersonalMicrosoftAccount", /* AzureADMyOrg, AzureADMultipleOrgs, AzureADandPersonalMicrosoftAccount, PersonalMicrosoftAccount */
20 | "PasswordCredentials": "Auto",
21 | "RequiredResourcesAccess": [
22 | {
23 | "Resource": "Microsoft Graph",
24 | "DelegatedPermissions": [
25 | "User.ReadBasic.All"
26 | ]
27 | }
28 | ],
29 | "ReplyUrls": "http://localhost:5000/getAToken"
30 | }
31 | ],
32 |
33 | /*
34 | This section describes how to update the code in configuration files from the apps coordinates, once the apps
35 | are created in Azure AD.
36 | Each section describes a configuration file, for one of the apps, it's type (XML, JSon, plain text), its location
37 | with respect to the root of the sample, and the mappping (which string in the config file is mapped to which value
38 | */
39 | "CodeConfiguration": [
40 | {
41 | "App": "pythonwebapp",
42 | "SettingKind": "Replace",
43 | "SettingFile": "\\..\\app_config.py",
44 | "Mappings": [
45 | {
46 | "key": "Enter_the_Tenant_Name_Here",
47 | "value": "$tenantName"
48 | },
49 | {
50 | "key": "Enter_the_Client_Secret_Here",
51 | "value": ".AppKey"
52 | },
53 | {
54 | "key": "Enter_the_Application_Id_here",
55 | "value": ".AppId"
56 | }
57 | ]
58 | }
59 | ]
60 | }
61 |
--------------------------------------------------------------------------------
/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 | ## Python web application using Azure AD to authenticate against backend
2 |
3 | Python web app using delegated permissions from signed-in user or application permissions to authenticate to backend. Backend can be Azure Function or SQLDB. Detailed documentation can be found in my blogs:
4 |
5 | - In case you want to authenticate against a SQLDB as backend, see https://towardsdatascience.com/how-to-secure-python-flask-web-apis-with-azure-ad-14b46b8abf22
6 | - In case you want to authenticate against an Azure Function as backend, see https://towardsdatascience.com/how-to-use-the-microsoft-identity-platform-in-your-azure-web-app-fcb3839a44e5
7 |
8 | This sample is derived from https://github.com/Azure-Samples/ms-identity-python-webapp, in which user data is retrieved from Microsoft Graph.
9 |
--------------------------------------------------------------------------------
/ReadmeFiles/architecture_v1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/ReadmeFiles/architecture_v1.png
--------------------------------------------------------------------------------
/ReadmeFiles/architecture_v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/ReadmeFiles/architecture_v2.png
--------------------------------------------------------------------------------
/ReadmeFiles/topology.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/ReadmeFiles/topology.png
--------------------------------------------------------------------------------
/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 [many more](https://opensource.microsoft.com/).
6 |
7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [definition](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.** Instead, please report them to the Microsoft Security Response Center at [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://technet.microsoft.com/en-us/security/dn606155).
12 |
13 | 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).
14 |
15 | 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:
16 |
17 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
18 | * Full paths of source file(s) related to the manifestation of the issue
19 | * The location of the affected source code (tag/branch/commit or direct URL)
20 | * Any special configuration required to reproduce the issue
21 | * Step-by-step instructions to reproduce the issue
22 | * Proof-of-concept or exploit code (if possible)
23 | * Impact of the issue, including how an attacker might exploit the issue
24 |
25 | This information will help us triage your report more quickly.
26 |
27 | ## Preferred Languages
28 |
29 | We prefer all communications to be in English.
30 |
31 | ## Policy
32 |
33 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
34 |
35 |
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | import uuid
2 | import requests
3 | from flask import Flask, render_template, session, request, redirect, url_for
4 | from flask_session import Session # https://pythonhosted.org/Flask-Session
5 | import msal
6 | import app_config
7 | import pyodbc
8 | import struct
9 | import adal
10 | from msrestazure.azure_active_directory import AADTokenCredentials
11 |
12 | app = Flask(__name__)
13 | app.config.from_object(app_config)
14 | Session(app)
15 |
16 | # This section is needed for url_for("foo", _external=True) to automatically
17 | # generate http scheme when this sample is running on localhost,
18 | # and to generate https scheme when it is deployed behind reversed proxy.
19 | # See also https://flask.palletsprojects.com/en/1.0.x/deploying/wsgi-standalone/#proxy-setups
20 | from werkzeug.middleware.proxy_fix import ProxyFix
21 | app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)
22 |
23 | @app.route("/")
24 | def index():
25 | if not session.get("user"):
26 | return redirect(url_for("login"))
27 | return render_template('index.html', user=session["user"], version=msal.__version__)
28 |
29 | @app.route("/login")
30 | def login():
31 | session["state"] = str(uuid.uuid4())
32 |
33 | # Technically we could use empty list [] as scopes to do just sign in,
34 | # here we choose to also collect end user consent upfront
35 | auth_url = _build_auth_url(scopes=app_config.DELEGATED_PERMISSONS, state=session["state"])
36 | return render_template("login.html", auth_url=auth_url, version=msal.__version__)
37 |
38 | @app.route(app_config.REDIRECT_PATH) # Its absolute URL must match your app's redirect_uri set in AAD
39 | def authorized():
40 | if request.args.get('state') != session.get("state"):
41 | return redirect(url_for("index")) # No-OP. Goes back to Index page
42 | if "error" in request.args: # Authentication/Authorization failure
43 | return render_template("auth_error.html", result=request.args)
44 | if request.args.get('code'):
45 | cache = _load_cache()
46 | result = _build_msal_app(cache=cache).acquire_token_by_authorization_code(
47 | request.args['code'],
48 | scopes=app_config.DELEGATED_PERMISSONS, # Misspelled scope would cause an HTTP 400 error here
49 | redirect_uri=url_for("authorized", _external=True))
50 | if "error" in result:
51 | return render_template("auth_error.html", result=result)
52 | session["user"] = result.get("id_token_claims")
53 | _save_cache(cache)
54 | return redirect(url_for("index"))
55 |
56 | @app.route("/logout")
57 | def logout():
58 | session.clear() # Wipe out user and its token cache from session
59 | return redirect( # Also logout from your tenant's web session
60 | app_config.AUTHORITY + "/oauth2/v2.0/logout" +
61 | "?post_logout_redirect_uri=" + url_for("index", _external=True))
62 |
63 | @app.route("/getcustomerdata")
64 | def get_customer_data():
65 | return _get_data("premium_user_access", "SalesLT.Customer")
66 |
67 | @app.route("/getproductdata")
68 | def get_product_data():
69 | return _get_data("basic_user_access", "SalesLT.Product")
70 |
71 | @app.route("/getproductdatadaemon")
72 | def get_product_data_daemon():
73 |
74 | app2 = msal.ConfidentialClientApplication(
75 | app_config.CLIENT_ID, authority=app_config.AUTHORITY,
76 | client_credential=app_config.CLIENT_SECRET
77 | )
78 | result = app2.acquire_token_for_client(scopes=app_config.APPLICATION_PERMISSIONS)
79 | token = result['access_token']
80 |
81 | if app_config.BACKEND_SETTINGS.get("Type") == "Database":
82 | row = _retrieve_data_from_database(token, "SalesLT.Product")
83 | else:
84 | row = _retrieve_data_from_function(token,None)
85 |
86 | return render_template('display.html', result={'message': str(row)})
87 |
88 | @app.route("/graphcall")
89 | def graphcall():
90 | token = _get_token_from_cache(app_config.DELEGATED_PERMISSONS)
91 | if not token:
92 | return redirect(url_for("login"))
93 |
94 | graph_data = requests.get( # Use token to call downstream service
95 | app_config.GRAPH_ENDPOINT,
96 | headers={'Authorization': 'Bearer ' + token['access_token']},
97 | ).json()
98 | return render_template('display.html', result=graph_data)
99 |
100 | def _load_cache():
101 | cache = msal.SerializableTokenCache()
102 | if session.get("token_cache"):
103 | cache.deserialize(session["token_cache"])
104 | return cache
105 |
106 | def _save_cache(cache):
107 | if cache.has_state_changed:
108 | session["token_cache"] = cache.serialize()
109 |
110 | def _build_msal_app(cache=None, authority=None):
111 | return msal.ConfidentialClientApplication(
112 | app_config.CLIENT_ID, authority=authority or app_config.AUTHORITY,
113 | client_credential=app_config.CLIENT_SECRET, token_cache=cache)
114 |
115 | def _build_auth_url(authority=None, scopes=None, state=None):
116 | return _build_msal_app(authority=authority).get_authorization_request_url(
117 | scopes or [],
118 | state=state or str(uuid.uuid4()),
119 | redirect_uri=url_for("authorized", _external=True))
120 |
121 | def _get_token_from_cache(scope):
122 | cache = _load_cache() # This web app maintains one cache per session
123 | cca = _build_msal_app(cache=cache)
124 | accounts = cca.get_accounts()
125 | if accounts: # So all account(s) belong to the current signed-in user
126 | result = cca.acquire_token_silent(scope, account=accounts[0])
127 | _save_cache(cache)
128 | return result
129 |
130 | app.jinja_env.globals.update(_build_auth_url=_build_auth_url) # Used in template
131 |
132 | #
133 | # New code compared to original ms-identity-python-webapp to access the database
134 | #
135 | def _get_data(role_required, table):
136 |
137 | token = _get_token_from_cache(app_config.DELEGATED_PERMISSONS)
138 | if not token:
139 | return redirect(url_for("login"))
140 |
141 | if app_config.AAD_ROLE_CHECK and role_required != "basic_user_access":
142 | # check if claims in bearer token of user allows to retrieve data
143 | if _check_user_has_role_in_token(role_required) == False:
144 | error_message = "role " + role_required + " not present in id token of user"
145 | return render_template('display.html', result={'message': "'" + error_message + "'"})
146 |
147 | if app_config.DELEGATED_PERMISSONS[0] == "User.Read":
148 | # MI of app registration is needed to authenticate to backend
149 | token = _create_token_from_app_registration()
150 | else:
151 | # use bearer token of user for AAD passthrough
152 | token = token['access_token']
153 |
154 | # Retrieve data from database and return it
155 | if app_config.BACKEND_SETTINGS.get("Type") == "Database":
156 | row = _retrieve_data_from_database(token, table)
157 | else:
158 | row = _retrieve_data_from_function(token, table)
159 | return render_template('display.html', result={'message': "'" + str(row) + "'"})
160 |
161 | def _check_user_has_role_in_token(role):
162 | # Check if required user role is present as claim in the ID token of the user
163 | if not "roles" in session["user"]:
164 | return False
165 |
166 | user_roles = session["user"]["roles"]
167 | for user_role in user_roles:
168 | print (user_role)
169 | if user_role == role:
170 | return True
171 | return False
172 |
173 | def _create_token_from_app_registration():
174 | # Authenticate using service principal w/ key.
175 | app3 = msal.ConfidentialClientApplication(
176 | app_config.CLIENT_ID, authority=app_config.AUTHORITY,
177 | client_credential=app_config.CLIENT_SECRET
178 | )
179 |
180 | result = app3.acquire_token_for_client(scopes=app_config.APPLICATION_PERMISSIONS)
181 | return result['access_token']
182 |
183 | def _retrieve_data_from_function(token, table):
184 |
185 | url_function= app_config.BACKEND_SETTINGS.get("Connection").get("URL") + "&name=" + str(table)
186 | response = requests.get(url_function, headers={'Authorization': "Bearer " + token})
187 |
188 | status = str(response.status_code)
189 | print("token: " + token + "status: " + status)
190 |
191 | return status + str(response.content).replace("\u0027", "")
192 |
193 | def _retrieve_data_from_database(token, table):
194 |
195 | accessToken = bytes(token, 'utf-8')
196 | exptoken = b""
197 | for i in accessToken:
198 | exptoken += bytes({i})
199 | exptoken += bytes(1)
200 | tokenstruct = struct.pack("=i", len(exptoken)) + exptoken
201 |
202 | server = app_config.BACKEND_SETTINGS.get("Connection").get("SQL_SERVER")
203 | database = app_config.BACKEND_SETTINGS.get("Connection").get("DATABASE")
204 | connstr = 'DRIVER={ODBC Driver 17 for SQL Server};SERVER='+server+';DATABASE='+database
205 | #tokenstruct = struct.pack("=i", len(exptoken)) + exptoken
206 | conn = pyodbc.connect(connstr, attrs_before = { 1256:tokenstruct })
207 |
208 | cursor = conn.cursor()
209 | cursor.execute("SELECT top 10 * FROM " + str(table))
210 | row = cursor.fetchall()
211 | return row
212 |
213 | if __name__ == "__main__":
214 | app.run()
--------------------------------------------------------------------------------
/app_config.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | CLIENT_SECRET = "<>"
4 | # In your production app, we recommend you to use other ways to store your secret,
5 | # such as KeyVault, or environment variable as described in Flask's documentation here
6 | # https://flask.palletsprojects.com/en/1.1.x/config/#configuring-from-environment-variables
7 | # CLIENT_SECRET = os.getenv("CLIENT_SECRET")
8 | # if not CLIENT_SECRET:
9 | # raise ValueError("Need to define CLIENT_SECRET environment variable")
10 |
11 | #AUTHORITY = "https://login.microsoftonline.com/common" # For multi-tenant app
12 | AUTHORITY = "https://login.microsoftonline.com/<>"
13 |
14 |
15 | CLIENT_ID = "<>"
16 |
17 | REDIRECT_PATH = "/getAToken" # It will be used to form an absolute URL
18 | # And that absolute URL must match your app's redirect_uri set in AAD
19 |
20 | # You can find the proper permission names from this document
21 | # https://docs.microsoft.com/en-us/graph/permissions-reference
22 |
23 | SESSION_TYPE = "filesystem" # So token cache will be stored in server-side session
24 |
25 | GRAPH_ENDPOINT = 'https://graph.microsoft.com/v1.0/me' # This resource requires no admin consent
26 |
27 | #
28 | # New settings compared to original ms-identity-python-webapp to access the database
29 | #
30 | # 1. Usage of deamon or user
31 | #
32 | DAEMON_ENABLED = False
33 | #
34 |
35 | # 2. Type of BACKEND
36 | #
37 | # Option 2a. Database
38 | BACKEND_SETTINGS = {"Type": "Database", "Connection":{"SQL_SERVER": "<>.database.windows.net", "DATABASE": "<>"}}
39 | # Option 2b. Azure Function
40 | #BACKEND_SETTINGS = {"Type": "AzureFunction", "Connection":{"URL": "https://<>.azurewebsites.net/api/HttpBackend?code=<>"}}
41 |
42 | #
43 | # 3. Permissions:
44 | #
45 | # Choose how to authenticate to resources. Only one delegated user scope can be defined
46 | # https://docs.microsoft.com/en-us/azure/active-directory/develop/developer-glossary#permissions
47 | #
48 | # Option 3a. Delegated user is used to authenticate to Graph API, MI is then used to authenticate to backend
49 | # Backend can either be database (2a) or Azure Function (2b)
50 | DELEGATED_PERMISSONS = ["User.Read"]
51 | if BACKEND_SETTINGS.get("Type") == "AzureFunction":
52 | APPLICATION_PERMISSIONS = [BACKEND_SETTINGS.get("Connection").get("URL").split('.net/')[0] + ".net/.default"]
53 | else:
54 | APPLICATION_PERMISSIONS = ["https://database.windows.net//.default"]
55 |
56 | # Option 3b. Delegated user is used to authenticate to backend, graph API disabled
57 | # Backend can either be database (2a) or Azure Function (2b)
58 | #if BACKEND_SETTINGS.get("Type") == "AzureFunction":
59 | # DELEGATED_PERMISSONS = [BACKEND_SETTINGS.get("Connection").get("URL").split('.net/')[0] + ".net/user_impersonation"]
60 | #else:
61 | # DELEGATED_PERMISSONS = ["https://sql.azuresynapse-dogfood.net/user_impersonation"]
62 | #APPLICATION_PERMISSIONS = ["disabled"]
63 |
64 | #
65 | # 4. AAD_ROLE_CHECK
66 | #
67 | # In case AAD Role check is true, the user claimes in the id token are used to verify if user is allowed to retrieve data
68 | # Notice that user shall also be added to the database as externa user and be granted the correct roles to retrieve data from tables
69 | AAD_ROLE_CHECK = False
--------------------------------------------------------------------------------
/blog-webappaad-backend/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 | obj
3 | csx
4 | .vs
5 | edge
6 | Publish
7 |
8 | *.user
9 | *.suo
10 | *.cscfg
11 | *.Cache
12 | project.lock.json
13 |
14 | /packages
15 | /TestResults
16 |
17 | /tools/NuGet.exe
18 | /App_Data
19 | /secrets
20 | /data
21 | .secrets
22 | appsettings.json
23 | local.settings.json
24 |
25 | node_modules
26 | dist
27 |
28 | # Local python packages
29 | .python_packages/
30 |
31 | # Python Environments
32 | .env
33 | .venv
34 | env/
35 | venv/
36 | ENV/
37 | env.bak/
38 | venv.bak/
39 |
40 | # Byte-compiled / optimized / DLL files
41 | __pycache__/
42 | *.py[cod]
43 | *$py.class
--------------------------------------------------------------------------------
/blog-webappaad-backend/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "ms-azuretools.vscode-azurefunctions"
4 | ]
5 | }
--------------------------------------------------------------------------------
/blog-webappaad-backend/HttpBackend/__init__.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | import azure.functions as func
4 | from jose import jwt
5 |
6 | import pyodbc
7 | import struct
8 |
9 | def main(req: func.HttpRequest) -> func.HttpResponse:
10 | logging.info('Python HTTP trigger function processed a request.')
11 | name = req.params.get('name')
12 |
13 | auth = str(req.headers.get("Authorization", None))
14 | parts = auth.split()
15 | payload = parts[1]
16 | unverified_header = jwt.get_unverified_claims(payload)
17 |
18 | output = "appid: " + str(unverified_header.get("appid")) + ", "
19 | output += "roles in backend: " + str(unverified_header.get("roles")) + ", "
20 | output += "name: " + str(unverified_header.get("name")) + ", "
21 | output += "scp: " + str(unverified_header.get("scp")) + ", "
22 | output += "all: " + str(unverified_header)
23 |
24 | output.replace("\\u0027","")
25 | output.replace("'","")
26 |
27 | return func.HttpResponse(f"Table {name}, Token properties: {output}. ")
28 |
29 | #accessToken = bytes(payload, 'utf-8')
30 | #exptoken = b""
31 | #for i in accessToken:
32 | # exptoken += bytes({i})
33 | # exptoken += bytes(1)
34 | #tokenstruct = struct.pack("=i", len(exptoken)) + exptoken
35 |
36 | #server = "test-sqldbauth-sql.database.windows.net"
37 | #database = "test-sqldbauth-db"
38 | #connstr = 'DRIVER={ODBC Driver 17 for SQL Server};SERVER='+server+';DATABASE='+database
39 | #tokenstruct = struct.pack("=i", len(exptoken)) + exptoken
40 | #conn = pyodbc.connect(connstr, attrs_before = { 1256:tokenstruct })
41 |
42 | #cursor = conn.cursor()
43 | #cursor.execute("SELECT top 10 * FROM SalesLT.Customer")
44 | #row = cursor.fetchall()
45 | #return row
--------------------------------------------------------------------------------
/blog-webappaad-backend/HttpBackend/function.json:
--------------------------------------------------------------------------------
1 | {
2 | "scriptFile": "__init__.py",
3 | "bindings": [
4 | {
5 | "authLevel": "function",
6 | "type": "httpTrigger",
7 | "direction": "in",
8 | "name": "req",
9 | "methods": [
10 | "get",
11 | "post"
12 | ]
13 | },
14 | {
15 | "type": "http",
16 | "direction": "out",
17 | "name": "$return"
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/blog-webappaad-backend/host.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0",
3 | "logging": {
4 | "applicationInsights": {
5 | "samplingSettings": {
6 | "isEnabled": true,
7 | "excludedTypes": "Request"
8 | }
9 | }
10 | },
11 | "extensionBundle": {
12 | "id": "Microsoft.Azure.Functions.ExtensionBundle",
13 | "version": "[1.*, 2.0.0)"
14 | }
15 | }
--------------------------------------------------------------------------------
/blog-webappaad-backend/requirements.txt:
--------------------------------------------------------------------------------
1 | azure-functions
2 | python-jose>=3.0.0
3 | pyodbc
--------------------------------------------------------------------------------
/flask_session/00abd19a9adbeed5b64e418f71b5ee39:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/00abd19a9adbeed5b64e418f71b5ee39
--------------------------------------------------------------------------------
/flask_session/02913272f2a3ca8faf88a8eb9e93035f:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/02913272f2a3ca8faf88a8eb9e93035f
--------------------------------------------------------------------------------
/flask_session/02f8a251fe824076d84a67e3f029f504:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/02f8a251fe824076d84a67e3f029f504
--------------------------------------------------------------------------------
/flask_session/05ae107f34b98c210eab2f21abe35ea2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/05ae107f34b98c210eab2f21abe35ea2
--------------------------------------------------------------------------------
/flask_session/0912865d2b743635ddc70bcd1c4db2e7:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/0912865d2b743635ddc70bcd1c4db2e7
--------------------------------------------------------------------------------
/flask_session/0a0dc05395fd5dab0e803f96c37a23f8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/0a0dc05395fd5dab0e803f96c37a23f8
--------------------------------------------------------------------------------
/flask_session/0c2feb0d0b92fe8de16e8d4b7b92f377:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/0c2feb0d0b92fe8de16e8d4b7b92f377
--------------------------------------------------------------------------------
/flask_session/0c318037e31974913e2619105ebf313d:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/0c318037e31974913e2619105ebf313d
--------------------------------------------------------------------------------
/flask_session/0d3994050c7510f4df9c20a0f7180dc6:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/0d3994050c7510f4df9c20a0f7180dc6
--------------------------------------------------------------------------------
/flask_session/0ee76d8fe1efae244aebe8784e7ba45d:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/0ee76d8fe1efae244aebe8784e7ba45d
--------------------------------------------------------------------------------
/flask_session/0f618c9e147651a1f68b3578b93b1173:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/0f618c9e147651a1f68b3578b93b1173
--------------------------------------------------------------------------------
/flask_session/0fb14e093f516e444f87fbabca615b84:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/0fb14e093f516e444f87fbabca615b84
--------------------------------------------------------------------------------
/flask_session/1b3a6e4ce7cb7feba1c7c4dcbde6acad:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/1b3a6e4ce7cb7feba1c7c4dcbde6acad
--------------------------------------------------------------------------------
/flask_session/1bd9a749507b2ddedac8d48a80cc4830:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/1bd9a749507b2ddedac8d48a80cc4830
--------------------------------------------------------------------------------
/flask_session/1d488ed6fd5729d8cc3b0c43cf9006a1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/1d488ed6fd5729d8cc3b0c43cf9006a1
--------------------------------------------------------------------------------
/flask_session/1eb0e7cf4779b851282ca25e59c5a806:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/1eb0e7cf4779b851282ca25e59c5a806
--------------------------------------------------------------------------------
/flask_session/2029240f6d1128be89ddc32729463129:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/2029240f6d1128be89ddc32729463129
--------------------------------------------------------------------------------
/flask_session/2123f5246722d6d81447acc733ad3d55:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/2123f5246722d6d81447acc733ad3d55
--------------------------------------------------------------------------------
/flask_session/25610c405c32efbbeafb45bb914876c5:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/25610c405c32efbbeafb45bb914876c5
--------------------------------------------------------------------------------
/flask_session/272e5895a0e53a234adb4bc7badbfbaa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/272e5895a0e53a234adb4bc7badbfbaa
--------------------------------------------------------------------------------
/flask_session/2b69075f3e9b6c168395f0090ba915b4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/2b69075f3e9b6c168395f0090ba915b4
--------------------------------------------------------------------------------
/flask_session/30c47b3cce5060a16b272976dbedbe89:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/30c47b3cce5060a16b272976dbedbe89
--------------------------------------------------------------------------------
/flask_session/376860a2cc824a2a5d73cc07f6c1a4e3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/376860a2cc824a2a5d73cc07f6c1a4e3
--------------------------------------------------------------------------------
/flask_session/39aa60231baccc348b302d488f892bd3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/39aa60231baccc348b302d488f892bd3
--------------------------------------------------------------------------------
/flask_session/3b714d8b1e7d44b583b2b3a9f8e4a9e3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/3b714d8b1e7d44b583b2b3a9f8e4a9e3
--------------------------------------------------------------------------------
/flask_session/3bd7c4146854c4b6181eac50a2f664f4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/3bd7c4146854c4b6181eac50a2f664f4
--------------------------------------------------------------------------------
/flask_session/46b92326e6b656a71fa634461430d2f8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/46b92326e6b656a71fa634461430d2f8
--------------------------------------------------------------------------------
/flask_session/4813ad950545a9abeafb70df8137b285:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/4813ad950545a9abeafb70df8137b285
--------------------------------------------------------------------------------
/flask_session/4a73432d9c719e71ce71f09bd30e340e:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/4a73432d9c719e71ce71f09bd30e340e
--------------------------------------------------------------------------------
/flask_session/4afd04a848eb713ecf61d73a775830b2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/4afd04a848eb713ecf61d73a775830b2
--------------------------------------------------------------------------------
/flask_session/4c3d9b3d803203cab8075152b0c444c2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/4c3d9b3d803203cab8075152b0c444c2
--------------------------------------------------------------------------------
/flask_session/4c9f1d911dbbeb7c6cd123975c76b50b:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/4c9f1d911dbbeb7c6cd123975c76b50b
--------------------------------------------------------------------------------
/flask_session/4f1db28418a10310da6498693ce803bd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/4f1db28418a10310da6498693ce803bd
--------------------------------------------------------------------------------
/flask_session/5058f0457d73745f90407a79639e4838:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/5058f0457d73745f90407a79639e4838
--------------------------------------------------------------------------------
/flask_session/52a24e452d77c77fc94b90a1e44d59c1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/52a24e452d77c77fc94b90a1e44d59c1
--------------------------------------------------------------------------------
/flask_session/52b482e5b79fd3a04e6792d1e62b74fe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/52b482e5b79fd3a04e6792d1e62b74fe
--------------------------------------------------------------------------------
/flask_session/554449ccb7bfadd159453ab1431c7471:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/554449ccb7bfadd159453ab1431c7471
--------------------------------------------------------------------------------
/flask_session/55b9b44aa2e76d32fa54ef19c1af9888:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/55b9b44aa2e76d32fa54ef19c1af9888
--------------------------------------------------------------------------------
/flask_session/58660006990adc7373d1f09467538b75:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/58660006990adc7373d1f09467538b75
--------------------------------------------------------------------------------
/flask_session/588a28650b287fa777fdbee4abbab57c:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/588a28650b287fa777fdbee4abbab57c
--------------------------------------------------------------------------------
/flask_session/5afe0e3f2b00bb8b38c0c4942b8f298b:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/5afe0e3f2b00bb8b38c0c4942b8f298b
--------------------------------------------------------------------------------
/flask_session/5ea8e76b658adc2b2d0147d932120560:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/5ea8e76b658adc2b2d0147d932120560
--------------------------------------------------------------------------------
/flask_session/5fe2165827d816160f8de147470218d9:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/5fe2165827d816160f8de147470218d9
--------------------------------------------------------------------------------
/flask_session/631a3cda1e17e3d361827f226c972091:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/631a3cda1e17e3d361827f226c972091
--------------------------------------------------------------------------------
/flask_session/676707ef32204fbaf0a1cf55843147d6:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/676707ef32204fbaf0a1cf55843147d6
--------------------------------------------------------------------------------
/flask_session/6eb72e281b19fd61455f3350db154c34:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/6eb72e281b19fd61455f3350db154c34
--------------------------------------------------------------------------------
/flask_session/6ffc02375fe8751f87fa4f0287f2d67d:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/6ffc02375fe8751f87fa4f0287f2d67d
--------------------------------------------------------------------------------
/flask_session/74c736642f2ca0fd494baa07cffbd695:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/74c736642f2ca0fd494baa07cffbd695
--------------------------------------------------------------------------------
/flask_session/7945d5d869a4486246bd2420266fd85f:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/7945d5d869a4486246bd2420266fd85f
--------------------------------------------------------------------------------
/flask_session/7a62115d0d27111fc98f4614fec4ee2a:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/7a62115d0d27111fc98f4614fec4ee2a
--------------------------------------------------------------------------------
/flask_session/7eb265131747b74f31c43dec6252d9b1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/7eb265131747b74f31c43dec6252d9b1
--------------------------------------------------------------------------------
/flask_session/7ecf902714220f3f08590ee8281bc03b:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/7ecf902714220f3f08590ee8281bc03b
--------------------------------------------------------------------------------
/flask_session/8786589aab16485e9b2f584e1bf1169e:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/8786589aab16485e9b2f584e1bf1169e
--------------------------------------------------------------------------------
/flask_session/89ee9d3c01b65f3a990b7b6564a92736:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/89ee9d3c01b65f3a990b7b6564a92736
--------------------------------------------------------------------------------
/flask_session/8ebc2ca270fa736466231723239cffdc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/8ebc2ca270fa736466231723239cffdc
--------------------------------------------------------------------------------
/flask_session/8ed82fdeb4163b5f9540290624b748fd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/8ed82fdeb4163b5f9540290624b748fd
--------------------------------------------------------------------------------
/flask_session/908709ef0668e45c6b4e51ff33c1eeef:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/908709ef0668e45c6b4e51ff33c1eeef
--------------------------------------------------------------------------------
/flask_session/91da6fdb956b0210de1ebbbacb504239:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/91da6fdb956b0210de1ebbbacb504239
--------------------------------------------------------------------------------
/flask_session/932fe46bb1cc1d83e9949554bbdd6595:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/932fe46bb1cc1d83e9949554bbdd6595
--------------------------------------------------------------------------------
/flask_session/93dd3c25d0a98f295e6003ec2542e72e:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/93dd3c25d0a98f295e6003ec2542e72e
--------------------------------------------------------------------------------
/flask_session/93ed61cc2cf34227ede3ea8345b71dd7:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/93ed61cc2cf34227ede3ea8345b71dd7
--------------------------------------------------------------------------------
/flask_session/963bfe07041bfbf5e4033368991aa0fd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/963bfe07041bfbf5e4033368991aa0fd
--------------------------------------------------------------------------------
/flask_session/9cf3bf55d121a7a039e822ed827bebd7:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/9cf3bf55d121a7a039e822ed827bebd7
--------------------------------------------------------------------------------
/flask_session/9d1734df324b6b5679d43566d6375ab8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/9d1734df324b6b5679d43566d6375ab8
--------------------------------------------------------------------------------
/flask_session/9dbe2792966d99b48bf148663dac28fc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/9dbe2792966d99b48bf148663dac28fc
--------------------------------------------------------------------------------
/flask_session/9dca751f77a9ccbfaec3c7bf3d79d44c:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/9dca751f77a9ccbfaec3c7bf3d79d44c
--------------------------------------------------------------------------------
/flask_session/9f2787b491f430f5c47c1e83e6363feb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/9f2787b491f430f5c47c1e83e6363feb
--------------------------------------------------------------------------------
/flask_session/a04a83053787814f3c951add73874821:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/a04a83053787814f3c951add73874821
--------------------------------------------------------------------------------
/flask_session/a0fbe1f404a5abb878c728fe971bbc5d:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/a0fbe1f404a5abb878c728fe971bbc5d
--------------------------------------------------------------------------------
/flask_session/a6131427f0c4e7311521f526ecb744b3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/a6131427f0c4e7311521f526ecb744b3
--------------------------------------------------------------------------------
/flask_session/a9c1891644aa19494c691c3790963fca:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/a9c1891644aa19494c691c3790963fca
--------------------------------------------------------------------------------
/flask_session/a9c916c28edd36061815b3a974eb1272:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/a9c916c28edd36061815b3a974eb1272
--------------------------------------------------------------------------------
/flask_session/ac300107cdd359d52cb289ad58c173da:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/ac300107cdd359d52cb289ad58c173da
--------------------------------------------------------------------------------
/flask_session/ad4fd208a644108c98e121557b59b7f0:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/ad4fd208a644108c98e121557b59b7f0
--------------------------------------------------------------------------------
/flask_session/b175872a1e761509ec29378ad2c25ca3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/b175872a1e761509ec29378ad2c25ca3
--------------------------------------------------------------------------------
/flask_session/b600d2b1bac1001f883b63d5b36289b8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/b600d2b1bac1001f883b63d5b36289b8
--------------------------------------------------------------------------------
/flask_session/b79ed7cabf6692e444fd5fcf6207bfa5:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/b79ed7cabf6692e444fd5fcf6207bfa5
--------------------------------------------------------------------------------
/flask_session/b7eadf22ae1a93a0ca47f524ad5e4fab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/b7eadf22ae1a93a0ca47f524ad5e4fab
--------------------------------------------------------------------------------
/flask_session/ba4dfdef16931fa749278dae279e5fc2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/ba4dfdef16931fa749278dae279e5fc2
--------------------------------------------------------------------------------
/flask_session/bab93b77a29b8a8fc078ff6837c2a955:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/bab93b77a29b8a8fc078ff6837c2a955
--------------------------------------------------------------------------------
/flask_session/bbecbe4ae52f91a52cc1570c9489448c:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/bbecbe4ae52f91a52cc1570c9489448c
--------------------------------------------------------------------------------
/flask_session/bc6ed66c7b5efe7b7d07914c14cf0f1d:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/bc6ed66c7b5efe7b7d07914c14cf0f1d
--------------------------------------------------------------------------------
/flask_session/c07e5d95630c2eda3a1e58a679f06a82:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/c07e5d95630c2eda3a1e58a679f06a82
--------------------------------------------------------------------------------
/flask_session/c19269aba40c71054afc141823e871c4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/c19269aba40c71054afc141823e871c4
--------------------------------------------------------------------------------
/flask_session/c563c8373d9b43b77712c1a5b19dced3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/c563c8373d9b43b77712c1a5b19dced3
--------------------------------------------------------------------------------
/flask_session/c7377606b26183d4eee47dd47fa9fcee:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/c7377606b26183d4eee47dd47fa9fcee
--------------------------------------------------------------------------------
/flask_session/cae63cb2d81c788d26596886dc3fe4b1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/cae63cb2d81c788d26596886dc3fe4b1
--------------------------------------------------------------------------------
/flask_session/cd522f65a6a693ab88ab990044dcea4b:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/cd522f65a6a693ab88ab990044dcea4b
--------------------------------------------------------------------------------
/flask_session/ce7ea206cba7e3e5a424d48e545149f2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/ce7ea206cba7e3e5a424d48e545149f2
--------------------------------------------------------------------------------
/flask_session/cf9d7c060672c3724b5472749b25e3e2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/cf9d7c060672c3724b5472749b25e3e2
--------------------------------------------------------------------------------
/flask_session/d19f81ac4e3e09015887636bb3f35415:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/d19f81ac4e3e09015887636bb3f35415
--------------------------------------------------------------------------------
/flask_session/d27e26f492ae9a08a5ff6794f899637c:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/d27e26f492ae9a08a5ff6794f899637c
--------------------------------------------------------------------------------
/flask_session/d27e5e55aad695b33bcd07750df3aba4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/d27e5e55aad695b33bcd07750df3aba4
--------------------------------------------------------------------------------
/flask_session/d29b9bb8b1a3d77ee66565178d506ce3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/d29b9bb8b1a3d77ee66565178d506ce3
--------------------------------------------------------------------------------
/flask_session/d318764f012c5d727bb9f8de1bdc108b:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/d318764f012c5d727bb9f8de1bdc108b
--------------------------------------------------------------------------------
/flask_session/d532435332fd4eb6642f03d1d51469ac:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/d532435332fd4eb6642f03d1d51469ac
--------------------------------------------------------------------------------
/flask_session/dba39c8ec266614d9fdea10ffa704779:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/dba39c8ec266614d9fdea10ffa704779
--------------------------------------------------------------------------------
/flask_session/dd292d85b046af37c8933fab43eec0b7:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/dd292d85b046af37c8933fab43eec0b7
--------------------------------------------------------------------------------
/flask_session/dd41e15f9853bdc145185c64a6489291:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/dd41e15f9853bdc145185c64a6489291
--------------------------------------------------------------------------------
/flask_session/dd487150c7e8d40c672900de049639ec:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/dd487150c7e8d40c672900de049639ec
--------------------------------------------------------------------------------
/flask_session/df0998812a5f7a56ab046b9d3d229d8b:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/df0998812a5f7a56ab046b9d3d229d8b
--------------------------------------------------------------------------------
/flask_session/df9babbe73a36daed47dbe18587b21cf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/df9babbe73a36daed47dbe18587b21cf
--------------------------------------------------------------------------------
/flask_session/e14ee9bc0b66f0bedec0ab97a38a087a:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/e14ee9bc0b66f0bedec0ab97a38a087a
--------------------------------------------------------------------------------
/flask_session/e229e5b309a720f4801fe9acd095842c:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/e229e5b309a720f4801fe9acd095842c
--------------------------------------------------------------------------------
/flask_session/e2ec44653b64af4b9e70c4f3e5511a07:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/e2ec44653b64af4b9e70c4f3e5511a07
--------------------------------------------------------------------------------
/flask_session/e319afc9fe9f25e55c2c212d423644b0:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/e319afc9fe9f25e55c2c212d423644b0
--------------------------------------------------------------------------------
/flask_session/e895d5704ae73f512f0d4b534d9dfe79:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/e895d5704ae73f512f0d4b534d9dfe79
--------------------------------------------------------------------------------
/flask_session/edc30d9b53a482d5e9b707f1cefa2db4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/edc30d9b53a482d5e9b707f1cefa2db4
--------------------------------------------------------------------------------
/flask_session/ef4cf4bb2cf913d5445baae3ea5c4dc6:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/ef4cf4bb2cf913d5445baae3ea5c4dc6
--------------------------------------------------------------------------------
/flask_session/efe98641d0e3c2f827a53a6e9fa3678c:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/efe98641d0e3c2f827a53a6e9fa3678c
--------------------------------------------------------------------------------
/flask_session/f5bba97d9e6c36ea5299a29e1c3514fb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/f5bba97d9e6c36ea5299a29e1c3514fb
--------------------------------------------------------------------------------
/flask_session/f8e24cb19bd80791c04e06bf329abe91:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/f8e24cb19bd80791c04e06bf329abe91
--------------------------------------------------------------------------------
/flask_session/f9b89b35a44f78f8565f5efeac0bd052:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebremer/ms-identity-python-webapp-backend/5ace4ecaf6f7d4f7f7cea37c0d6625f21b7a5072/flask_session/f9b89b35a44f78f8565f5efeac0bd052
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | # Taken from https://github.com/Azure-Samples/ms-identity-python-webapp/blob/master/requirements.txt
2 |
3 | # Create virtual env: https://learn.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-python?tabs=azure-cli%2Cpowershell&pivots=python-mode-configuration#create-venv
4 | #
5 | # Powershell
6 | #
7 | # py -m venv .venv
8 | # .venv\scripts\activate
9 | #
10 | # Bash
11 | #
12 | # python -m venv .venv
13 | # source .venv/bin/activate
14 |
15 | Flask>=2
16 | werkzeug>=2
17 | flask-session>=0.3.2,<0.5
18 | requests>=2,<3
19 | msal>=1.7,<2
20 | #
21 | cryptography
22 | pyodbc
23 | adal
24 | msrestazure
25 |
--------------------------------------------------------------------------------
/templates/auth_error.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {% if config.get("B2C_RESET_PASSWORD_AUTHORITY") and "AADB2C90118" in result.get("error_description") %}
7 |
8 |
9 | {% endif %}
10 |
11 |
12 | Login Failure
13 |
14 | {{ result.get("error") }}
15 | {{ result.get("error_description") }}
16 |
17 |
18 | Homepage
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/templates/display.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Back
8 | {% if config.get("BACKEND_SETTINGS").get("Type") == "Database" %}
9 | Retrieve data from database
10 | {% endif %}
11 | {% if config.get("BACKEND_SETTINGS").get("Type") == "AzureFunction" %}
12 | Retrieve data from Azure Function
13 | {% endif %}
14 | {{ result |tojson(indent=4) }}
15 |
16 |
17 |
--------------------------------------------------------------------------------
/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Microsoft Identity Python Web App.
8 | Use URL localhost:5000 instead of 127.0.0.1:5000 since app registration reply URL contains localhost
9 | Welcome {{ user.get("name") }}!
10 |
11 | {% if config.get("BACKEND_SETTINGS").get("Type") == "Database" %}
12 | Call Microsoft Graph API
13 | {% endif %}
14 | {% if config.get("BACKEND_SETTINGS").get("Type") == "Database" %}
15 | {% if config.get("AAD_ROLE_CHECK") %}
16 | (Premium users only) Get Customer data from Database
17 | {% endif %}
18 | {% if config.get("AAD_ROLE_CHECK") == 0 %}
19 | Get Customer data from Database
20 | {% endif %}
21 | Get Product data from Database
22 | {% endif %}
23 | {% if config.get("BACKEND_SETTINGS").get("Type") == "AzureFunction" %}
24 | {% if config.get("AAD_ROLE_CHECK") %}
25 | (Premium users only) Get Customer data from AzureFunction
26 | {% endif %}
27 | {% if config.get("AAD_ROLE_CHECK") == 0 %}
28 | Get Customer data from AzureFunction
29 | {% endif %}
30 | Get Product from AzureFunction
31 | {% endif %}
32 |
33 | Logout
34 |
35 | Powered by MSAL Python {{ version }}
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/templates/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Microsoft Identity Python Web App.
8 | Use localhost:5000 instead of 127.0.0.1:5000 since app registration reply URL contains localhost
9 |
10 | {% if config.get("DAEMON_ENABLED") == False %}
11 | Sign In.
12 | {% endif %}
13 | {% if config.get("DAEMON_ENABLED") %}
14 | {% if config.get("BACKEND_SETTINGS").get("Type") == "Database" %}
15 | Get Product data from Database as Daemon
16 | {% endif %}
17 | {% if config.get("BACKEND_SETTINGS").get("Type") == "AzureFunction" %}
18 | Get Product data from Azure Function as Daemon
19 | {% endif %}
20 | {% endif %}
21 |
22 | Powered by MSAL Python {{ version }}
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------