├── .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 "" -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 "" -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 "
ApplicationAppIdUrl in the Azure portal
pythonwebapp$currentAppIdpython-webapp
" -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 | 23 | 24 | 25 | 26 | --------------------------------------------------------------------------------