From abc80323c2a35ae030c1017fe6b4b6b3c4e3482f Mon Sep 17 00:00:00 2001 From: Mike Wasson <3992422+MikeWasson@users.noreply.github.com> Date: Mon, 5 Aug 2019 16:02:19 -0700 Subject: [PATCH 1/3] Retire two topics --- .openpublishing.redirection.json | 10 + docs/multitenant-identity/authenticate.md | 2 +- docs/multitenant-identity/client-assertion.md | 16 +- docs/multitenant-identity/index.md | 3 +- docs/multitenant-identity/key-vault.md | 306 ---------------- docs/multitenant-identity/run-the-app.md | 328 ------------------ docs/multitenant-identity/tailspin.md | 3 +- docs/multitenant-identity/web-api.md | 62 +++- docs/toc.experimental.yml | 4 - docs/toc.yml | 4 - 10 files changed, 68 insertions(+), 670 deletions(-) delete mode 100644 docs/multitenant-identity/key-vault.md delete mode 100644 docs/multitenant-identity/run-the-app.md diff --git a/.openpublishing.redirection.json b/.openpublishing.redirection.json index b45593ce639..924a958cfd7 100644 --- a/.openpublishing.redirection.json +++ b/.openpublishing.redirection.json @@ -370,6 +370,16 @@ { "source_path": "docs/cloud-adoption/operations/monitor/cloud-app-howto.md", "redirect_url": "/azure/architecture/cloud-adoption/operations/monitor/cloud-models-monitor-overview" + }, + { + "source_path": "docs/multitenant-identity/run-the-app.md", + "redirect_url": "/azure/architecture/multitenant-identity/tailspin", + "redirect_document_id": true + }, + { + "source_path": "docs/multitenant-identity/key-vault.md", + "redirect_url": "/azure/architecture/multitenant-identity/web-api", + "redirect_document_id": true } ] } diff --git a/docs/multitenant-identity/authenticate.md b/docs/multitenant-identity/authenticate.md index f1c57246995..a9d9c49d236 100644 --- a/docs/multitenant-identity/authenticate.md +++ b/docs/multitenant-identity/authenticate.md @@ -32,7 +32,7 @@ To enable OpenID Connect, the SaaS provider registers the application inside the To register the application, follow the steps in [Quickstart: Register an application with the Microsoft identity platform](/azure/active-directory/develop/quickstart-register-app). -See [Run the Surveys application](./run-the-app.md) for the specific steps for the Surveys application. Note the following: +To enable this functionality in the sample Surveys application, see the [GitHub readme](https://github.com/mspnp/multitenant-saas-guidance/blob/master/get-started.md). Note the following: - For a multitenant application, you must configure the multitenanted option explicitly. This enables other organizations to access the application. diff --git a/docs/multitenant-identity/client-assertion.md b/docs/multitenant-identity/client-assertion.md index 72d3f723619..cd887fd61d5 100644 --- a/docs/multitenant-identity/client-assertion.md +++ b/docs/multitenant-identity/client-assertion.md @@ -15,7 +15,9 @@ pnp.series.next: key-vault [![GitHub](../_images/github.png) Sample code][sample application] -## Background +This article describes how to add client assertion to the [Tailspin Surveys][Surveys] sample application. + +## Understanding client assertion in OpenID Connect When using authorization code flow or hybrid flow in OpenID Connect, the client exchanges an authorization code for an access token. During this step, the client has to authenticate itself to the server. @@ -63,6 +65,8 @@ Notice that the `client_secret` parameter is no longer used. Instead, the `clien At run time, the web application reads the certificate from the certificate store. The certificate must be installed on the same machine as the web app. +## Implementing client assertion + The Surveys application includes a helper class that creates a [ClientAssertionCertificate](/dotnet/api/microsoft.identitymodel.clients.activedirectory.clientassertioncertificate) that you can pass to the [AuthenticationContext.AcquireTokenSilentAsync](/dotnet/api/microsoft.identitymodel.clients.activedirectory.authenticationcontext.acquiretokensilentasync) method to acquire a token from Azure AD. ```csharp @@ -93,19 +97,13 @@ public class CertificateCredentialService : ICredentialService } ``` -For information about setting up client assertion in the Surveys application, see [Use Azure Key Vault to protect application secrets -][key vault]. - -[**Next**][key vault] +[**Next**](./adfs.md) [configure-web-app]: /azure/app-service-web/web-sites-configure/ [azure-management-portal]: https://portal.azure.com [client assertion]: https://tools.ietf.org/html/rfc7521 -[key vault]: key-vault.md -[Setup-KeyVault]: https://github.com/mspnp/multitenant-saas-guidance/blob/master/scripts/Setup-KeyVault.ps1 +[sample application]: https://github.com/mspnp/multitenant-saas-guidance [Surveys]: tailspin.md [using-certs-in-websites]: https://azure.microsoft.com/blog/using-certificates-in-azure-websites-applications/ - -[sample application]: https://github.com/mspnp/multitenant-saas-guidance diff --git a/docs/multitenant-identity/index.md b/docs/multitenant-identity/index.md index 4ddc56d6a29..41d3a5f2a7b 100644 --- a/docs/multitenant-identity/index.md +++ b/docs/multitenant-identity/index.md @@ -22,7 +22,7 @@ When you're building a multitenant application, one of the first challenges is m Azure Active Directory (Azure AD) has some great features that support all of these scenarios. -To accompany this series of articles, we created a complete [end-to-end implementation][sample-application] of a multitenant application. The articles reflect what we learned in the process of building the application. To get started with the application, see [Run the Surveys application][running-the-app]. +To accompany this series of articles, we created a complete [end-to-end implementation][sample-application] of a multitenant application. The articles reflect what we learned in the process of building the application. To get started with the application, see the [GitHub readme](https://github.com/mspnp/multitenant-saas-guidance/blob/master/get-started.md). ## Introduction @@ -94,4 +94,3 @@ This guidance does not consider other aspects of multitenancy such as data parti [sample-application]: https://github.com/mspnp/multitenant-saas-guidance -[running-the-app]: ./run-the-app.md diff --git a/docs/multitenant-identity/key-vault.md b/docs/multitenant-identity/key-vault.md deleted file mode 100644 index 5b6b9d6ad8b..00000000000 --- a/docs/multitenant-identity/key-vault.md +++ /dev/null @@ -1,306 +0,0 @@ ---- -title: Use Key Vault to protect application secrets -description: How to use the Key Vault service to store application secrets. -author: MikeWasson -ms.date: 07/21/2017 -ms.topic: guide -ms.service: architecture-center -ms.subservice: reference-architecture -pnp.series.title: Manage Identity in Multitenant Applications -pnp.series.prev: client-assertion ---- - -# Use Azure Key Vault to protect application secrets - -[![GitHub](../_images/github.png) Sample code][sample application] - -It's common to have application settings that are sensitive and must be protected, such as: - -* Database connection strings -* Passwords -* Cryptographic keys - -As a security best practice, you should never store these secrets in source control. It's too easy for them to leak — even if your source code repository is private. And it's not just about keeping secrets from the general public. On larger projects, you might want to restrict which developers and operators can access the production secrets. (Settings for test or development environments are different.) - -A more secure option is to store these secrets in [Azure Key Vault][KeyVault]. Key Vault is a cloud-hosted service for managing cryptographic keys and other secrets. This article shows how to use Key Vault to store configuration settings for your app. - -In the [Tailspin Surveys][Surveys] application, the following settings are secret: - -* The database connection string. -* The Redis connection string. -* The client secret for the web application. - -The Surveys application loads configuration settings from the following places: - -* The appsettings.json file -* The [user secrets store][user-secrets] (development environment only; for testing) -* The hosting environment (app settings in Azure web apps) -* Key Vault (when enabled) - -Each of these overrides the previous one, so any settings stored in Key Vault take precedence. - -> [!NOTE] -> By default, the Key Vault configuration provider is disabled. It's not needed for running the application locally. You would enable it in a production deployment. - -At startup, the application reads settings from every registered configuration provider, and uses them to populate a strongly typed options object. For more information, see [Using Options and configuration objects][options]. - -## Setting up Key Vault in the Surveys app - -Prerequisites: - -* Install the [Azure Resource Manager Cmdlets][azure-rm-cmdlets]. -* Configure the Surveys application as described in [Run the Surveys application][readme]. - -High-level steps: - -1. Set up an admin user in the tenant. -2. Set up a client certificate. -3. Create a key vault. -4. Add configuration settings to your key vault. -5. Uncomment the code that enables key vault. -6. Update the application's user secrets. - -## Set up an admin user - -> [!NOTE] -> To create a key vault, you must use an account which can manage your Azure subscription. Also, any application that you authorize to read from the key vault must be registered in the same tenant as that account. - -In this step, you will make sure that you can create a key vault while signed in as a user from the tenant where the Surveys app is registered. - -Create an administrator user within the Azure AD tenant where the Surveys application is registered. - -1. Log into the [Azure portal][azure-portal]. -2. Select the Azure AD tenant where your application is registered. -3. Click **More service** > **SECURITY + IDENTITY** > **Azure Active Directory** > **User and groups** > **All users**. -4. At the top of the portal, click **New user**. -5. Fill in the fields and assign the user to the **Global administrator** directory role. -6. Click **Create**. - -![Global admin user](./images/running-the-app/global-admin-user.png) - -Now assign this user as the subscription owner. - -1. On the Hub menu, select **Subscriptions**. - - ![Screenshot of the Azure portal hub](./images/running-the-app/subscriptions.png) - -2. Select the subscription that you want the administrator to access. -3. In the subscription blade, select **Access control (IAM)**. -4. Click **Add**. -5. Under **Role**, select **Owner**. -6. Type the email address of the user you want to add as owner. -7. Select the user and click **Save**. - -## Set up a client certificate - -1. Run the PowerShell script [/Scripts/Setup-KeyVault.ps1][Setup-KeyVault] as follows: - - ```powershell - .\Setup-KeyVault.ps1 -Subject <> - ``` - - For the `Subject` parameter, enter any name, such as "surveysapp". The script generates a self-signed certificate and stores it in the "Current User/Personal" certificate store. The output from the script is a JSON fragment. Copy this value. - -2. In the [Azure portal][azure-portal], switch to the directory where the Surveys application is registered, by selecting your account in the top right corner of the portal. - -3. Select **Azure Active Directory** > **App Registrations** > Surveys - -4. Click **Manifest** and then **Edit**. - -5. Paste the output from the script into the `keyCredentials` property. It should look similar to the following: - - ```json - "keyCredentials": [ - { - "type": "AsymmetricX509Cert", - "usage": "Verify", - "keyId": "29d4f7db-0539-455e-b708-....", - "customKeyIdentifier": "ZEPpP/+KJe2fVDBNaPNOTDoJMac=", - "value": "MIIDAjCCAeqgAwIBAgIQFxeRiU59eL..... - } - ], - ``` - -6. Click **Save**. - -7. Repeat steps 3-6 to add the same JSON fragment to the application manifest of the web API (Surveys.WebAPI). - -8. From the PowerShell window, run the following command to get the thumbprint of the certificate. - - ```powershell - certutil -store -user my [subject] - ``` - - For `[subject]`, use the value that you specified for Subject in the PowerShell script. The thumbprint is listed under "Cert Hash(sha1)". Copy this value. You will use the thumbprint later. - -## Create a key vault - -1. Run the PowerShell script [/Scripts/Setup-KeyVault.ps1][Setup-KeyVault] as follows: - - ```powershell - .\Setup-KeyVault.ps1 -KeyVaultName <> -ResourceGroupName <> -Location <> - ``` - - When prompted for credentials, sign in as the Azure AD user that you created earlier. The script creates a new resource group, and a new key vault within that resource group. - -2. Run Setup-KeyVault.ps1 again as follows: - - ```powershell - .\Setup-KeyVault.ps1 -KeyVaultName <> -ApplicationIds @("<>", "<>") - ``` - - Set the following parameter values: - - * key vault name = The name that you gave the key vault in the previous step. - * Surveys app ID = The application ID for the Surveys web application. - * Surveys.WebApi app ID = The application ID for the Surveys.WebAPI application. - - Example: - - ```powershell - .\Setup-KeyVault.ps1 -KeyVaultName tailspinkv -ApplicationIds @("f84df9d1-91cc-4603-b662-302db51f1031", "8871a4c2-2a23-4650-8b46-0625ff3928a6") - ``` - - This script authorizes the web app and web API to retrieve secrets from your key vault. See [Get started with Azure Key Vault](/azure/key-vault/key-vault-get-started/) for more information. - -## Add configuration settings to your key vault - -1. Run Setup-KeyVault.ps1 as follows: - - ```powershell - .\Setup-KeyVault.ps1 -KeyVaultName < -KeyName Redis--Configuration -KeyValue "<>.redis.cache.windows.net,password=<>,ssl=true" - ``` - - where - - * key vault name = The name that you gave the key vault in the previous step. - * Redis DNS name = The DNS name of your Redis cache instance. - * Redis access key = The access key for your Redis cache instance. - -2. At this point, it's a good idea to test whether you successfully stored the secrets to key vault. Run the following PowerShell command: - - ```powershell - Get-AzureKeyVaultSecret <> Redis--Configuration | Select-Object * - ``` - -3. Run Setup-KeyVault.ps1 again to add the database connection string: - - ```powershell - .\Setup-KeyVault.ps1 -KeyVaultName < -KeyName Data--SurveysConnectionString -KeyValue <> -ConfigName "Data:SurveysConnectionString" - ``` - - where `<>` is the value of the database connection string. - - For testing with the local database, copy the connection string from the Tailspin.Surveys.Web/appsettings.json file. If you do that, make sure to change the double backslash ('\\\\') into a single backslash. The double backslash is an escape character in the JSON file. - - Example: - - ```powershell - .\Setup-KeyVault.ps1 -KeyVaultName mykeyvault -KeyName Data--SurveysConnectionString -KeyValue "Server=(localdb)\MSSQLLocalDB;Database=Tailspin.SurveysDB;Trusted_Connection=True;MultipleActiveResultSets=true" - ``` - -## Uncomment the code that enables Key Vault - -1. Open the Tailspin.Surveys solution. -2. In Tailspin.Surveys.Web/Startup.cs, locate the following code block and uncomment it. - - ```csharp - //var config = builder.Build(); - //builder.AddAzureKeyVault( - // $"https://{config["KeyVault:Name"]}.vault.azure.net/", - // config["AzureAd:ClientId"], - // config["AzureAd:ClientSecret"]); - ``` - -3. In Tailspin.Surveys.Web/Startup.cs, locate the code that registers the `ICredentialService`. Uncomment the line that uses `CertificateCredentialService`, and comment out the line that uses `ClientCredentialService`: - - ```csharp - // Uncomment this: - services.AddSingleton(); - // Comment out this: - //services.AddSingleton(); - ``` - - This change enables the web app to use [Client assertion][client-assertion] to get OAuth access tokens. With client assertion, you don't need an OAuth client secret. Alternatively, you could store the client secret in key vault. However, key vault and client assertion both use a client certificate, so if you enable key vault, it's a good practice to enable client assertion as well. - -## Update the user secrets - -In Solution Explorer, right-click the Tailspin.Surveys.Web project and select **Manage User Secrets**. In the secrets.json file, delete the existing JSON and paste in the following: - -```json -{ - "AzureAd": { - "ClientId": "[Surveys web app client ID]", - "ClientSecret": "[Surveys web app client secret]", - "PostLogoutRedirectUri": "https://localhost:44300/", - "WebApiResourceId": "[App ID URI of your Surveys.WebAPI application]", - "Asymmetric": { - "CertificateThumbprint": "[certificate thumbprint. Example: 105b2ff3bc842c53582661716db1b7cdc6b43ec9]", - "StoreName": "My", - "StoreLocation": "CurrentUser", - "ValidationRequired": "false" - } - }, - "KeyVault": { - "Name": "[key vault name]" - } -} -``` - -Replace the entries in [square brackets] with the correct values. - -* `AzureAd:ClientId`: The client ID of the Surveys app. -* `AzureAd:ClientSecret`: The key that you generated when you registered the Surveys application in Azure AD. -* `AzureAd:WebApiResourceId`: The App ID URI that you specified when you created the Surveys.WebAPI application in Azure AD. -* `Asymmetric:CertificateThumbprint`: The certificate thumbprint that you got previously, when you created the client certificate. -* `KeyVault:Name`: The name of your key vault. - -> [!NOTE] -> `Asymmetric:ValidationRequired` is false because the certificate that you created previously was not signed by a root certificate authority (CA). In production, use a certificate that is signed by a root CA and set `ValidationRequired` to true. - -Save the updated secrets.json file. - -Next, in Solution Explorer, right-click the Tailspin.Surveys.WebApi project and select **Manage User Secrets**. Delete the existing JSON and paste in the following: - -```json -{ - "AzureAd": { - "ClientId": "[Surveys.WebAPI client ID]", - "WebApiResourceId": "https://tailspin5.onmicrosoft.com/surveys.webapi", - "Asymmetric": { - "CertificateThumbprint": "[certificate thumbprint]", - "StoreName": "My", - "StoreLocation": "CurrentUser", - "ValidationRequired": "false" - } - }, - "KeyVault": { - "Name": "[key vault name]" - } -} -``` - -Replace the entries in [square brackets] and save the secrets.json file. - -> [!NOTE] -> For the web API, make sure to use the client ID for the Surveys.WebAPI application, not the Surveys application. - -[**Next**][adfs] - - - -[adfs]: ./adfs.md -[authorize-app]: /azure/key-vault/key-vault-get-started//#authorize -[azure-portal]: https://portal.azure.com -[azure-rm-cmdlets]: https://msdn.microsoft.com/library/mt125356.aspx -[client-assertion]: client-assertion.md -[configuration]: /aspnet/core/fundamentals/configuration -[KeyVault]: https://azure.microsoft.com/services/key-vault/ -[key-tags]: https://msdn.microsoft.com/library/azure/dn903623.aspx#BKMK_Keytags -[Microsoft.Azure.KeyVault]: https://www.nuget.org/packages/Microsoft.Azure.KeyVault/ -[options]: /aspnet/core/fundamentals/configuration/options -[readme]: ./run-the-app.md -[Setup-KeyVault]: https://github.com/mspnp/multitenant-saas-guidance/blob/master/scripts/Setup-KeyVault.ps1 -[Surveys]: tailspin.md -[user-secrets]: /aspnet/core/security/app-secrets -[sample application]: https://github.com/mspnp/multitenant-saas-guidance diff --git a/docs/multitenant-identity/run-the-app.md b/docs/multitenant-identity/run-the-app.md deleted file mode 100644 index b6dec8a45cc..00000000000 --- a/docs/multitenant-identity/run-the-app.md +++ /dev/null @@ -1,328 +0,0 @@ ---- -title: Run the Surveys application -description: How to run the Surveys sample application locally. -author: MikeWasson -ms.date: 07/21/2017 -ms.topic: guide -ms.service: architecture-center -ms.subservice: reference-architecture ---- - -# Run the Surveys application - -This article describes how to run the [Tailspin Surveys](./tailspin.md) application locally, from Visual Studio. In these steps, you won't deploy the application to Azure. However, you will need to create some Azure resources — an Azure Active Directory (Azure AD) directory and a Redis cache. - -Here is a summary of the steps: - -1. Create an Azure AD directory (tenant) for the fictitious Tailspin company. -2. Register the Surveys application and the backend web API with Azure AD. -3. Create an Azure Cache for Redis instance. -4. Configure application settings and create a local database. -5. Run the application and sign up a new tenant. -6. Add application roles to users. - -## Prerequisites - -- [Visual Studio 2017][VS2017] with the [ASP.NET and web development workload](https://visualstudio.microsoft.com/vs/support/selecting-workloads-visual-studio-2017) installed -- [Microsoft Azure](https://azure.microsoft.com) account - -## Create the Tailspin tenant - -Tailspin is the fictitious company that hosts the Surveys application. Tailspin uses Azure AD to enable other tenants to register with the app. Those customers can then use their Azure AD credentials to sign into the app. - -In this step, you'll create an Azure AD directory for Tailspin. - -1. Sign into the [Azure portal][portal] and head to [App registrations][appregistrations]. - -2. Click **+ New Registration**. - -3. Set **Name** to `Tailspin` for the organization name, and enter a domain name. The domain name will have the form `xxxx.onmicrosoft.com` and must be globally unique. - -4. Set **Supported account types** to **Accounts in any organizational directory and personal Microsoft accounts**. - -5. Leave **Redirect URI** empty. - -6. Choose **Register**. - -To complete the end-to-end scenario, you'll need a second Azure AD directory to represent a customer that signs up for the application. You can use your default Azure AD directory (not Tailspin), or create a new directory for this purpose. In the examples, we use Contoso as the fictitious customer. - -## Register the Surveys web API - -1. In the [Azure portal][portal], switch to the new Tailspin directory by selecting your account in the top right corner of the portal. - -2. In the left-hand navigation pane, choose **Azure Active Directory**. - -3. Click **App registrations** > **New application registration**. - -4. In the **Create** blade, enter the following information: - - - **Name**: `Surveys.WebAPI` - - - **Application type**: `Web app / API` - - - **Sign-on URL**: `https://localhost:44301/` - - ![Screenshot for registering the Web API](./images/running-the-app/register-web-api.png) - -5. Click **Create**. - -6. In the **App registrations** blade, select the new **Surveys.WebAPI** application. - -7. Click **Settings** > **Properties**. - -8. In the **App ID URI** edit box, enter `https:///surveys.webapi`, where `` is the domain name of the directory. For example: `https://tailspin.onmicrosoft.com/surveys.webapi` - - ![Settings](./images/running-the-app/settings.png) - -9. Set **Multi-tenanted** to **YES**. - -10. Click **Save**. - -## Register the Surveys web app - -1. Navigate back to the **App registrations** blade, and click **New application registration**. - -2. In the **Create** blade, enter the following information: - - - **Name**: `Surveys` - - **Application type**: `Web app / API` - - **Sign-on URL**: `https://localhost:44300/` - - Notice that the sign-on URL has a different port number from the `Surveys.WebAPI` app in the previous step. - -3. Click **Create**. - -4. In the **App registrations** blade, select the new **Surveys** application. - -5. Copy the application ID. You will need this later. - - ![Screenshot of copying the application ID](./images/running-the-app/application-id.png) - -6. Click **Properties**. - -7. In the **App ID URI** edit box, enter `https:///surveys`, where `` is the domain name of the directory. - - ![Settings](./images/running-the-app/settings.png) - -8. Set **Multi-tenanted** to **YES**. - -9. Click **Save**. - -10. In the **Settings** blade, click **Reply URLs**. - -11. Add the following reply URL: `https://localhost:44300/signin-oidc`. - -12. Click **Save**. - -13. Under **API ACCESS**, click **Keys**. - -14. Enter a description, such as `client secret`. - -15. In the **Select Duration** dropdown, select **1 year**. - -16. Click **Save**. The key will be generated when you save. - -17. Before you navigate away from this blade, copy the value of the key. - - > [!NOTE] - > The key won't be visible again after you navigate away from the blade. - -18. Under **API ACCESS**, click **Required permissions**. - -19. Click **Add** > **Select an API**. - -20. In the search box, search for `Surveys.WebAPI`. - - ![Permissions](./images/running-the-app/permissions.png) - -21. Select `Surveys.WebAPI` and click **Select**. - -22. Under **Delegated Permissions**, check **Access Surveys.WebAPI**. - - ![Setting delegated permissions](./images/running-the-app/delegated-permissions.png) - -23. Click **Select** > **Done**. - -## Update the application manifests - -1. Navigate back to the **Settings** blade for the `Surveys.WebAPI` app. - -2. Click **Manifest** > **Edit**. - - ![Screenshot of editing the application manifest](./images/running-the-app/manifest.png) - -3. Add the following JSON to the `appRoles` element. Generate new GUIDs for the `id` properties. - - ```json - { - "allowedMemberTypes": ["User"], - "description": "Creators can create surveys", - "displayName": "SurveyCreator", - "id": "", - "isEnabled": true, - "value": "SurveyCreator" - }, - { - "allowedMemberTypes": ["User"], - "description": "Administrators can manage the surveys in their tenant", - "displayName": "SurveyAdmin", - "id": "", - "isEnabled": true, - "value": "SurveyAdmin" - } - ``` - -4. In the `knownClientApplications` property, add the application ID for the Surveys web application, which you got when you registered the Surveys application earlier. For example: - - ```json - "knownClientApplications": ["be2cea23-aa0e-4e98-8b21-2963d494912e"], - ``` - - This setting adds the Surveys app to the list of clients authorized to call the web API. - -5. Click **Save**. - -Now repeat the same steps for the Surveys app, except do not add an entry for `knownClientApplications`. Use the same role definitions, but generate new GUIDs for the IDs. - -## Create a new Azure Cache for Redis instance - -The Surveys application uses Azure Cache for Redis to cache OAuth 2 access tokens. To create the cache: - -1. Go to the [Azure portal](https://portal.azure.com) and click **+ Create a Resource** > **Databases** > **Azure Cache for Redis**. - -2. Fill in the required information, including DNS name, resource group, location, and pricing tier. You can create a new resource group or use an existing resource group. - -3. Click **Create**. - -4. After the Redis cache is created, navigate to the resource in the portal. - -5. Click **Access keys** and copy the primary key. - -For more information about creating a Redis cache, see [How to Use Azure Cache for Redis](/azure/redis-cache/cache-dotnet-how-to-use-azure-redis-cache). - -## Set application secrets - -1. Open the Tailspin.Surveys solution in Visual Studio. - -2. In Solution Explorer, right-click the Tailspin.Surveys.Web project and select **Manage User Secrets**. - -3. In the secrets.json file, paste in the following: - - ```json - { - "AzureAd": { - "ClientId": "", - "ClientSecret": "", - "PostLogoutRedirectUri": "https://localhost:44300/", - "WebApiResourceId": "" - }, - "Redis": { - "Configuration": ".redis.cache.windows.net,password=,ssl=true" - } - } - ``` - - Replace the items shown in angle brackets, as follows: - - - `AzureAd:ClientId`: The application ID of the Surveys app. - - `AzureAd:ClientSecret`: The key that you generated when you registered the Surveys application in Azure AD. - - `AzureAd:WebApiResourceId`: The App ID URI that you specified when you created the Surveys.WebAPI application in Azure AD. It should have the form `https://.onmicrosoft.com/surveys.webapi` - - `Redis:Configuration`: Build this string from the DNS name of the Redis cache and the primary access key. For example, "tailspin.redis.cache.windows.net,password=2h5tBxxx,ssl=true". - -4. Save the updated secrets.json file. - -5. Repeat these steps for the Tailspin.Surveys.WebAPI project, but paste the following into secrets.json. Replace the items in angle brackets, as before. - - ```json - { - "AzureAd": { - "WebApiResourceId": "" - }, - "Redis": { - "Configuration": ".redis.cache.windows.net,password=,ssl=true" - } - } - ``` - -## Initialize the database - -In this step, you will use Entity Framework 7 to create a local SQL database, using LocalDB. - -1. Open a command window - -2. Navigate to the Tailspin.Surveys.Data project. - -3. Run the following command: - - ```bat - dotnet ef database update --startup-project ..\Tailspin.Surveys.Web - ``` - -## Run the application - -To run the application, start both the Tailspin.Surveys.Web and Tailspin.Surveys.WebAPI projects. - -You can set Visual Studio to run both projects automatically on F5, as follows: - -1. In Solution Explorer, right-click the solution and click **Set Startup Projects**. -2. Select **Multiple startup projects**. -3. Set **Action** = **Start** for the Tailspin.Surveys.Web and Tailspin.Surveys.WebAPI projects. - -## Sign up a new tenant - -When the application starts, you are not signed in, so you see the welcome page: - -![Welcome page](./images/running-the-app/screenshot1.png) - -To sign up an organization: - -1. Click **Enroll your company in Tailspin**. -2. Sign in to the Azure AD directory that represents the organization using the Surveys app. You must sign in as an admin user. -3. Accept the consent prompt. - -The application registers the tenant, and then signs you out. The app signs you out because you need to set up the application roles in Azure AD, before using the application. - -![After sign up](./images/running-the-app/screenshot2.png) - -## Assign application roles - -When a tenant signs up, an AD admin for the tenant must assign application roles to users. - -1. In the [Azure portal][portal], switch to the Azure AD directory that you used to sign up for the Surveys app. - -2. In the left-hand navigation pane, choose **Azure Active Directory**. - -3. Click **Enterprise applications** > **All applications**. The portal will list `Survey` and `Survey.WebAPI`. If not, make sure that you completed the sign up process. - -4. Click on the Surveys application. - -5. Click **Users and Groups**. - -6. Click **Add user**. - -7. If you have Azure AD Premium, click **Users and groups**. Otherwise, click **Users**. (Assigning a role to a group requires Azure AD Premium.) - -8. Select one or more users and click **Select**. - - ![Select user or group](./images/running-the-app/select-user-or-group.png) - -9. Select the role and click **Select**. - - ![Select user or group](./images/running-the-app/select-role.png) - -10. Click **Assign**. - -Repeat the same steps to assign roles for the Survey.WebAPI application. - -> [!IMPORTANT] -> A user should always have the same roles in both Survey and Survey.WebAPI. Otherwise, the user will have inconsistent permissions, which may lead to 403 (Forbidden) errors from the Web API. - -Now go back to the app and sign in again. Click **My Surveys**. If the user is assigned to the SurveyAdmin or SurveyCreator role, you will see a **Create Survey** button, indicating that the user has permissions to create a new survey. - -![My surveys](./images/running-the-app/screenshot3.png) - - - -[portal]: https://portal.azure.com -[VS2017]: https://www.visualstudio.com/vs/ -[appregistrations]: https://ms.portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade diff --git a/docs/multitenant-identity/tailspin.md b/docs/multitenant-identity/tailspin.md index f66c2fb4406..3e13cb1061c 100644 --- a/docs/multitenant-identity/tailspin.md +++ b/docs/multitenant-identity/tailspin.md @@ -19,7 +19,7 @@ Tailspin is a fictitious company that is developing a SaaS application named Sur * Users can create, edit, and publish surveys. > [!NOTE] -> To get started with the application, see [Run the Surveys application]. +> To get started with the application, see the [GitHub readme](https://github.com/mspnp/multitenant-saas-guidance/blob/master/get-started.md). ## Users can create, edit, and view surveys @@ -63,6 +63,5 @@ The web application uses Azure Active Directory (Azure AD) to authenticate users [authentication]: authenticate.md -[Run the Surveys application]: ./run-the-app.md [ASP.NET Core]: /aspnet/core [sample application]: https://github.com/mspnp/multitenant-saas-guidance diff --git a/docs/multitenant-identity/web-api.md b/docs/multitenant-identity/web-api.md index 98aba77e8ef..c914bd40401 100644 --- a/docs/multitenant-identity/web-api.md +++ b/docs/multitenant-identity/web-api.md @@ -15,7 +15,7 @@ pnp.series.next: token-cache [![GitHub](../_images/github.png) Sample code][sample application] -The [Tailspin Surveys] application uses a backend web API to manage CRUD operations on surveys. For example, when a user clicks "My Surveys", the web application sends an HTTP request to the web API: +The [Tailspin Surveys][surveys] application uses a backend web API to manage CRUD operations on surveys. For example, when a user clicks "My Surveys", the web application sends an HTTP request to the web API: ```http GET /users/{userId}/surveys @@ -76,9 +76,9 @@ In order for Azure AD to issue a bearer token for the web API, you need to confi 1. Register the web API in Azure AD. -2. Add the client ID of the web app to the web API application manifest, in the `knownClientApplications` property. See [Update the application manifests]. +2. Add the client ID of the web app to the web API application manifest, in the `knownClientApplications` property. See the [GitHub readme](https://github.com/mspnp/multitenant-saas-guidance/blob/master/get-started.md#update-the-application-manifests) for more information. -3. Give the web application permission to call the web API. In the Azure Management Portal, you can set two types of permissions: "Application Permissions" for application identity (client credential flow), or "Delegated Permissions" for delegated user identity. +3. Give the web application permission to call the web API. In the Azure portal, you can set two types of permissions: "Application Permissions" for application identity (client credential flow), or "Delegated Permissions" for delegated user identity. ![Delegated permissions](./images/delegated-permissions.png) @@ -113,7 +113,7 @@ Here are the various parameters that are needed: * `clientSecret`. The web application's client secret. * `redirectUri`. The redirect URI that you set for OpenID Connect. This is where the IDP calls back with the token. * `resourceID`. The App ID URI of the web API, which you created when you registered the web API in Azure AD -* `tokenCache`. An object that caches the access tokens. See [Token caching]. +* `tokenCache`. An object that caches the access tokens. See [Token caching][token-cache]. If `AcquireTokenByAuthorizationCodeAsync` succeeds, ADAL caches the token. Later, you can get the token from the cache by calling AcquireTokenSilentAsync: @@ -266,18 +266,52 @@ public void ConfigureServices(IServiceCollection services) } ``` -[**Next**][token cache] +## Protecting application secrets - -[ADAL]: https://msdn.microsoft.com/library/azure/jj573266.aspx -[JwtBearer]: https://www.nuget.org/packages/Microsoft.AspNet.Authentication.JwtBearer +It's common to have application settings that are sensitive and must be protected, such as: + +* Database connection strings +* Passwords +* Cryptographic keys + +As a security best practice, you should never store these secrets in source control. It's too easy for them to leak — even if your source code repository is private. And it's not just about keeping secrets from the general public. On larger projects, you might want to restrict which developers and operators can access the production secrets. (Settings for test or development environments are different.) + +A more secure option is to store these secrets in [Azure Key Vault][KeyVault]. Key Vault is a cloud-hosted service for managing cryptographic keys and other secrets. This article shows how to use Key Vault to store configuration settings for your app. + +In the [Tailspin Surveys][surveys] application, the following settings are secret: + +* The database connection string. +* The Redis connection string. +* The client secret for the web application. + +The Surveys application loads configuration settings from the following places: + +* The appsettings.json file +* The [user secrets store][user-secrets] (development environment only; for testing) +* The hosting environment (app settings in Azure web apps) +* Key Vault (when enabled) + +Each of these overrides the previous one, so any settings stored in Key Vault take precedence. + +> [!NOTE] +> By default, the Key Vault configuration provider is disabled. It's not needed for running the application locally. You would enable it in a production deployment. -[Tailspin Surveys]: tailspin.md +At startup, the application reads settings from every registered configuration provider, and uses them to populate a strongly typed options object. For more information, see [Using Options and configuration objects][options]. + +[**Next**][token-cache] + + +[Authorization]: authorize.md +[ADAL]: /previous-versions/azure/jj573266 +[claims-transformation]: claims.md#claims-transformations [IdentityServer4]: https://github.com/IdentityServer/IdentityServer4 -[Update the application manifests]: ./run-the-app.md#update-the-application-manifests -[Token caching]: token-cache.md +[JwtBearer]: https://www.nuget.org/packages/Microsoft.AspNet.Authentication.JwtBearer +[KeyVault]: https://azure.microsoft.com/services/key-vault/ +[options]: /aspnet/core/fundamentals/configuration/options [tenant sign-up]: signup.md -[claims-transformation]: claims.md#claims-transformations -[Authorization]: authorize.md +[Token caching]: token-cache.md [sample application]: https://github.com/mspnp/multitenant-saas-guidance -[token cache]: token-cache.md +[surveys]: tailspin.md +[token-cache]: token-cache.md +[user-secrets]: /aspnet/core/security/app-secrets + diff --git a/docs/toc.experimental.yml b/docs/toc.experimental.yml index bb80b084687..faf1864f60b 100644 --- a/docs/toc.experimental.yml +++ b/docs/toc.experimental.yml @@ -506,12 +506,8 @@ items: href: multitenant-identity/token-cache.md - name: Client assertion href: multitenant-identity/client-assertion.md - - name: Protect application secrets - href: multitenant-identity/key-vault.md - name: Federate with a customer's AD FS href: multitenant-identity/adfs.md - - name: Run the Surveys application - href: multitenant-identity/run-the-app.md - name: Reference architectures items: - name: Choose an Active Directory integration architecture diff --git a/docs/toc.yml b/docs/toc.yml index 5c40a15b02c..1fda68d0018 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -606,12 +606,8 @@ items: href: multitenant-identity/token-cache.md - name: Client assertion href: multitenant-identity/client-assertion.md - - name: Protect application secrets - href: multitenant-identity/key-vault.md - name: Federate with a customer's AD FS href: multitenant-identity/adfs.md - - name: Run the Surveys application - href: multitenant-identity/run-the-app.md - name: Modernize enterprise applications with Azure Service Fabric href: service-fabric/modernize-app-azure-service-fabric.md - name: Migrate from Cloud Services to Service Fabric From 0f8f6357c698000674c61fbdea86346741dd4ab4 Mon Sep 17 00:00:00 2001 From: Beth Harvey Date: Mon, 5 Aug 2019 16:28:38 -0700 Subject: [PATCH 2/3] fix spelling --- docs/multitenant-identity/authenticate.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/multitenant-identity/authenticate.md b/docs/multitenant-identity/authenticate.md index a9d9c49d236..361b521f2f1 100644 --- a/docs/multitenant-identity/authenticate.md +++ b/docs/multitenant-identity/authenticate.md @@ -83,7 +83,7 @@ app.UseCookieAuthentication(new CookieAuthenticationOptions { ## Initiate the authentication flow -To start the authentication flow in ASP.NET MVC, return a **ChallengeResult** from the contoller: +To start the authentication flow in ASP.NET MVC, return a **ChallengeResult** from the controller: ```csharp [AllowAnonymous] @@ -166,7 +166,7 @@ By default, the OIDC middleware knows how to fetch this metadata. Set the **Auth By default, the OIDC middleware uses hybrid flow with form post response mode. - *Hybrid flow* means the client can get an ID token and an authorization code in the same round-trip to the authorization server. -- *Form post reponse mode* means the authorization server uses an HTTP POST request to send the ID token and authorization code to the app. The values are form-urlencoded (content type = "application/x-www-form-urlencoded"). +- *Form post response mode* means the authorization server uses an HTTP POST request to send the ID token and authorization code to the app. The values are form-urlencoded (content type = "application/x-www-form-urlencoded"). When the OIDC middleware redirects to the authorization endpoint, the redirect URL includes all of the query string parameters needed by OIDC. For hybrid flow: From ce82dd7474dab2df6b0cb50c3a24a71dd4e1bf21 Mon Sep 17 00:00:00 2001 From: Beth Harvey Date: Mon, 5 Aug 2019 16:29:04 -0700 Subject: [PATCH 3/3] fix spelling --- docs/multitenant-identity/web-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/multitenant-identity/web-api.md b/docs/multitenant-identity/web-api.md index c914bd40401..121bfa78671 100644 --- a/docs/multitenant-identity/web-api.md +++ b/docs/multitenant-identity/web-api.md @@ -233,7 +233,7 @@ The JwtBearer middleware handles the authorization responses. For example, to re This returns a 401 status code if the user is not authenticated. -To restrict a controller action by authorizaton policy, specify the policy name in the **[Authorize]** attribute: +To restrict a controller action by authorization policy, specify the policy name in the **[Authorize]** attribute: ```csharp [Authorize(Policy = PolicyNames.RequireSurveyCreator)]