229 lines
18 KiB
Markdown
229 lines
18 KiB
Markdown
---
|
||
title: Share authentication cookies among ASP.NET apps
|
||
author: rick-anderson
|
||
description: Learn how to share authentication cookies among ASP.NET 4.x and ASP.NET Core apps.
|
||
monikerRange: '>= aspnetcore-2.1'
|
||
ms.author: riande
|
||
ms.custom: mvc
|
||
ms.date: 6/6/2022
|
||
uid: security/cookie-sharing
|
||
---
|
||
# Share authentication cookies among ASP.NET apps
|
||
|
||
By [Rick Anderson](https://twitter.com/RickAndMSFT)
|
||
|
||
Websites often consist of individual web apps working together. To provide a single sign-on (SSO) experience, web apps within a site must share authentication cookies. To support this scenario, the data protection stack allows sharing Katana cookie authentication and ASP.NET Core cookie authentication tickets.
|
||
|
||
:::moniker range=">= aspnetcore-6.0"
|
||
|
||
In the examples that follow:
|
||
|
||
* The authentication cookie name is set to a common value of `.AspNet.SharedCookie`.
|
||
* The `AuthenticationType` is set to `Identity.Application` either explicitly or by default.
|
||
* A common app name, `SharedCookieApp`, is used to enable the data protection system to share data protection keys.
|
||
* `Identity.Application` is used as the authentication scheme. Whatever scheme is used, it must be used consistently *within and across* the shared cookie apps either as the default scheme or by explicitly setting it. The scheme is used when encrypting and decrypting cookies, so a consistent scheme must be used across apps.
|
||
* A common [data protection key](xref:security/data-protection/implementation/key-management) storage location is used.
|
||
* In ASP.NET Core apps, <xref:Microsoft.AspNetCore.DataProtection.DataProtectionBuilderExtensions.PersistKeysToFileSystem*> is used to set the key storage location.
|
||
* In .NET Framework apps, Cookie Authentication Middleware uses an implementation of <xref:Microsoft.AspNetCore.DataProtection.DataProtectionProvider>. `DataProtectionProvider` provides data protection services for the encryption and decryption of authentication cookie payload data. The `DataProtectionProvider` instance is isolated from the data protection system used by other parts of the app. [DataProtectionProvider.Create(System.IO.DirectoryInfo, Action\<IDataProtectionBuilder>)](xref:Microsoft.AspNetCore.DataProtection.DataProtectionProvider.Create*) accepts a <xref:System.IO.DirectoryInfo> to specify the location for data protection key storage.
|
||
* `DataProtectionProvider` requires the [Microsoft.AspNetCore.DataProtection.Extensions](https://www.nuget.org/packages/Microsoft.AspNetCore.DataProtection.Extensions/) NuGet package:
|
||
* In .NET Framework apps, add a package reference to [Microsoft.AspNetCore.DataProtection.Extensions](https://www.nuget.org/packages/Microsoft.AspNetCore.DataProtection.Extensions/).
|
||
* <xref:Microsoft.AspNetCore.DataProtection.DataProtectionBuilderExtensions.SetApplicationName*> sets the common app name.
|
||
|
||
## Share authentication cookies with ASP.NET Core Identity
|
||
|
||
When using ASP.NET Core Identity:
|
||
|
||
* Data protection keys and the app name must be shared among apps. A common key storage location is provided to the <xref:Microsoft.AspNetCore.DataProtection.DataProtectionBuilderExtensions.PersistKeysToFileSystem*> method in the following examples. Use <xref:Microsoft.AspNetCore.DataProtection.DataProtectionBuilderExtensions.SetApplicationName*> to configure a common shared app name (`SharedCookieApp` in the following examples). For more information, see <xref:security/data-protection/configuration/overview>.
|
||
* Use the <xref:Microsoft.Extensions.DependencyInjection.IdentityServiceCollectionExtensions.ConfigureApplicationCookie*> extension method to set up the data protection service for cookies.
|
||
* The default authentication type is `Identity.Application`.
|
||
|
||
In `Program.cs`:
|
||
|
||
[!code-csharp[](~/security/cookie-sharing/samples/WebCookieShare/Program.cs?name=snippet&highlight=7-13)]
|
||
|
||
**Note:** The preceding instructions don't work with `ITicketStore` (`CookieAuthenticationOptions.SessionStore`). For more information, see [this GitHub issue](https://github.com/dotnet/AspNetCore.Docs/issues/21163).
|
||
|
||
[!INCLUDE[](~/includes/cookies-not-compressed.md)]
|
||
|
||
## Share authentication cookies without ASP.NET Core Identity
|
||
|
||
When using cookies directly without ASP.NET Core Identity, configure data protection and authentication. In the following example, the authentication type is set to `Identity.Application`:
|
||
|
||
[!code-csharp[](~/security/cookie-sharing/samples/WebCookieShare/Program.cs?name=snippet_swo&highlight=7-15)]
|
||
|
||
[!INCLUDE[](~/includes/cookies-not-compressed.md)]
|
||
|
||
## Share cookies across different base paths
|
||
|
||
An authentication cookie uses the [HttpRequest.PathBase](xref:Microsoft.AspNetCore.Http.HttpRequest.PathBase) as its default [Cookie.Path](xref:Microsoft.AspNetCore.Http.CookieBuilder.Path). If the app's cookie must be shared across different base paths, `Path` must be overridden:
|
||
|
||
[!code-csharp[](~/security/cookie-sharing/samples/WebCookieShare/Program.cs?name=snippet_dbp&highlight=7-15)]
|
||
|
||
## Share cookies across subdomains
|
||
|
||
When hosting apps that share cookies across subdomains, specify a common domain in the [Cookie.Domain](xref:Microsoft.AspNetCore.Http.CookieBuilder.Domain) property. To share cookies across apps at `contoso.com`, such as `first_subdomain.contoso.com` and `second_subdomain.contoso.com`, specify the `Cookie.Domain` as `.contoso.com`:
|
||
|
||
```csharp
|
||
options.Cookie.Domain = ".contoso.com";
|
||
```
|
||
|
||
## Encrypt data protection keys at rest
|
||
|
||
For production deployments, configure the `DataProtectionProvider` to encrypt keys at rest with DPAPI or an X509Certificate. For more information, see <xref:security/data-protection/implementation/key-encryption-at-rest>. In the following example, a certificate thumbprint is provided to <xref:Microsoft.AspNetCore.DataProtection.DataProtectionBuilderExtensions.ProtectKeysWithCertificate*>:
|
||
|
||
[!code-csharp[](~/security/cookie-sharing/samples/WebCookieShare/Program.cs?name=snippet_Encrypt&highlight=7-8)]
|
||
|
||
## Use a common user database
|
||
|
||
When apps use the same Identity schema (same version of Identity), confirm that the Identity system for each app is pointed at the same user database. Otherwise, the identity system produces failures at runtime when it attempts to match the information in the authentication cookie against the information in its database.
|
||
|
||
When the Identity schema is different among apps, usually because apps are using different Identity versions, sharing a common database based on the latest version of Identity isn't possible without remapping and adding columns in other app's Identity schemas. It's often more efficient to upgrade the other apps to use the latest Identity version so that a common database can be shared by the apps.
|
||
|
||
[!INCLUDE[](~/includes/appname6.md)]
|
||
|
||
## Share authentication cookies between ASP.NET 4.x and ASP.NET Core apps
|
||
|
||
ASP.NET 4.x apps that use Microsoft.Owin Cookie Authentication Middleware can be configured to generate authentication cookies that are compatible with the ASP.NET Core Cookie Authentication Middleware. This can be useful if a web application consists of both ASP.NET 4.x apps and ASP.NET Core apps that must share a single sign-on experience. A specific example of such a scenario is [incrementally migrating](xref:migration/inc/overview) a web app from ASP.NET to ASP.NET Core. In such scenarios, it's common for some parts of an app to be served by the original ASP.NET app while others are served by the new ASP.NET Core app. Users should only have to sign in once, though. This can be accomplished by either of the following approaches:
|
||
|
||
* Using the System.Web adapters' [remote authentication](xref:migration/inc/remote-authentication) feature, which uses the ASP.NET app to sign users in.
|
||
* Configuring the ASP.NET app to use Microsoft.Owin Cookie Authentication Middleware so that authentication cookies are shared with the ASP.NET Core app.
|
||
|
||
To configure ASP.NET Microsoft.Owin Cookie Authentication Middleware to share cookies with an ASP.NET Core app, follow the preceding instructions to configure the ASP.NET Core app to use a specific cookie name, app name, and to persist data protection keys to a well-known location. See [Configure ASP.NET Core Data Protection](xref:security/data-protection/configuration/overview) for more information on persisting data protection keys.
|
||
|
||
In the ASP.NET app, install the [`Microsoft.Owin.Security.Interop`](https://www.nuget.org/packages/Microsoft.Owin.Security.Interop/) package.
|
||
|
||
Update the `UseCookieAuthentication` call in Startup.Auth.cs to configure an AspNetTicketDataFormat to match the ASP.NET Core app's settings:
|
||
|
||
[!code-csharp[](~/security/cookie-sharing/samples/WebCookieShare-NetFx/App_Start/Startup.Auth.cs?name=snippet_netfx_cookie_auth&highlight=11-23)]
|
||
|
||
Important items configured here include:
|
||
|
||
* The cookie name is set to the same name as in the ASP.NET Core app.
|
||
* A data protection provider is created using the same key ring path. Note that in these examples, data protection keys are stored on disk but other data protection providers can be used. For example, Redis or Azure Blob Storage can be used for data protection providers as long as the configuration matches between the apps. See [Configure ASP.NET Core Data Protection](xref:security/data-protection/configuration/overview) for more information on persisting data protection keys.
|
||
* The app name is set to be the same as the app name used in the ASP.NET Core app.
|
||
* The authentication type is set to the name of the authentication scheme in the ASP.NET Core app.
|
||
* `System.Web.Helpers.AntiForgeryConfig.UniqueClaimTypeIdentifier` is set to a claim from the ASP.NET Core identity that will be unique to a user.
|
||
|
||
Because the authentication type was changed to match the authentication scheme of the ASP.NET Core app, it's also necessary to update how the ASP.NET app generates new identities to use that same name. This is typically done in `Models/IdentityModels.cs`:
|
||
|
||
[!code-csharp[](~/security/cookie-sharing/samples/WebCookieShare-NetFx/Models/IdentityModels.cs?name=snippet_generate_identity&highlight=5-6)]
|
||
|
||
With these changes, the ASP.NET and ASP.NET Core apps are able to use the same authentication cookies so that users signing in or out of one app are reflected in the other app.
|
||
|
||
Note that because there are differences between ASP.NET Identity and ASP.NET Core Identity's database schemas, it is recommended that users only *sign-in* using one of the apps - either the ASP.NET or ASP.NET Core app. Once users are signed in, the steps documented in this section will allow the authentication cookie to be *used* by either app and both apps should be able to log users out.
|
||
|
||
## Additional resources
|
||
|
||
* <xref:host-and-deploy/web-farm>
|
||
* [A primer on OWIN cookie authentication middleware for the ASP.NET developer](https://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/) by [Brock Allen](https://brockallen.com/)
|
||
* [OWIN Authentication Middleware Architecture](https://brockallen.com/2013/08/07/owin-authentication-middleware-architecture/) by [Brock Allen](https://brockallen.com/)
|
||
* [Posts from the ‘OWIN / Katana’ Category](https://brockallen.com/category/owin-katana/) by [Brock Allen](https://brockallen.com/)
|
||
|
||
:::moniker-end
|
||
|
||
:::moniker range="< aspnetcore-6.0"
|
||
|
||
In the examples that follow:
|
||
|
||
* The authentication cookie name is set to a common value of `.AspNet.SharedCookie`.
|
||
* The `AuthenticationType` is set to `Identity.Application` either explicitly or by default.
|
||
* A common app name is used to enable the data protection system to share data protection keys (`SharedCookieApp`).
|
||
* `Identity.Application` is used as the authentication scheme. Whatever scheme is used, it must be used consistently *within and across* the shared cookie apps either as the default scheme or by explicitly setting it. The scheme is used when encrypting and decrypting cookies, so a consistent scheme must be used across apps.
|
||
* A common [data protection key](xref:security/data-protection/implementation/key-management) storage location is used.
|
||
* In ASP.NET Core apps, <xref:Microsoft.AspNetCore.DataProtection.DataProtectionBuilderExtensions.PersistKeysToFileSystem*> is used to set the key storage location.
|
||
* In .NET Framework apps, Cookie Authentication Middleware uses an implementation of <xref:Microsoft.AspNetCore.DataProtection.DataProtectionProvider>. `DataProtectionProvider` provides data protection services for the encryption and decryption of authentication cookie payload data. The `DataProtectionProvider` instance is isolated from the data protection system used by other parts of the app. [DataProtectionProvider.Create(System.IO.DirectoryInfo, Action\<IDataProtectionBuilder>)](xref:Microsoft.AspNetCore.DataProtection.DataProtectionProvider.Create*) accepts a <xref:System.IO.DirectoryInfo> to specify the location for data protection key storage.
|
||
* `DataProtectionProvider` requires the [Microsoft.AspNetCore.DataProtection.Extensions](https://www.nuget.org/packages/Microsoft.AspNetCore.DataProtection.Extensions/) NuGet package:
|
||
* In ASP.NET Core 2.x apps, reference the [Microsoft.AspNetCore.App metapackage](xref:fundamentals/metapackage-app).
|
||
* In .NET Framework apps, add a package reference to [Microsoft.AspNetCore.DataProtection.Extensions](https://www.nuget.org/packages/Microsoft.AspNetCore.DataProtection.Extensions/).
|
||
* <xref:Microsoft.AspNetCore.DataProtection.DataProtectionBuilderExtensions.SetApplicationName*> sets the common app name.
|
||
|
||
## Share authentication cookies with ASP.NET Core Identity
|
||
|
||
When using ASP.NET Core Identity:
|
||
|
||
* Data protection keys and the app name must be shared among apps. A common key storage location is provided to the <xref:Microsoft.AspNetCore.DataProtection.DataProtectionBuilderExtensions.PersistKeysToFileSystem*> method in the following examples. Use <xref:Microsoft.AspNetCore.DataProtection.DataProtectionBuilderExtensions.SetApplicationName*> to configure a common shared app name (`SharedCookieApp` in the following examples). For more information, see <xref:security/data-protection/configuration/overview>.
|
||
* Use the <xref:Microsoft.Extensions.DependencyInjection.IdentityServiceCollectionExtensions.ConfigureApplicationCookie*> extension method to set up the data protection service for cookies.
|
||
* The default authentication type is `Identity.Application`.
|
||
|
||
In `Startup.ConfigureServices`:
|
||
|
||
```csharp
|
||
services.AddDataProtection()
|
||
.PersistKeysToFileSystem("{PATH TO COMMON KEY RING FOLDER}")
|
||
.SetApplicationName("SharedCookieApp");
|
||
|
||
services.ConfigureApplicationCookie(options => {
|
||
options.Cookie.Name = ".AspNet.SharedCookie";
|
||
});
|
||
```
|
||
|
||
**Note:** The preceding instructions don't work with `ITicketStore` (`CookieAuthenticationOptions.SessionStore`). For more information, see [this GitHub issue](https://github.com/dotnet/AspNetCore.Docs/issues/21163).
|
||
|
||
[!INCLUDE[](~/includes/cookies-not-compressed.md)]
|
||
|
||
## Share authentication cookies without ASP.NET Core Identity
|
||
|
||
When using cookies directly without ASP.NET Core Identity, configure data protection and authentication in `Startup.ConfigureServices`. In the following example, the authentication type is set to `Identity.Application`:
|
||
|
||
```csharp
|
||
services.AddDataProtection()
|
||
.PersistKeysToFileSystem("{PATH TO COMMON KEY RING FOLDER}")
|
||
.SetApplicationName("SharedCookieApp");
|
||
|
||
services.AddAuthentication("Identity.Application")
|
||
.AddCookie("Identity.Application", options =>
|
||
{
|
||
options.Cookie.Name = ".AspNet.SharedCookie";
|
||
});
|
||
```
|
||
|
||
[!INCLUDE[](~/includes/cookies-not-compressed.md)]
|
||
|
||
## Share cookies across different base paths
|
||
|
||
An authentication cookie uses the [HttpRequest.PathBase](xref:Microsoft.AspNetCore.Http.HttpRequest.PathBase) as its default [Cookie.Path](xref:Microsoft.AspNetCore.Http.CookieBuilder.Path). If the app's cookie must be shared across different base paths, `Path` must be overridden:
|
||
|
||
```csharp
|
||
services.AddDataProtection()
|
||
.PersistKeysToFileSystem("{PATH TO COMMON KEY RING FOLDER}")
|
||
.SetApplicationName("SharedCookieApp");
|
||
|
||
services.ConfigureApplicationCookie(options => {
|
||
options.Cookie.Name = ".AspNet.SharedCookie";
|
||
options.Cookie.Path = "/";
|
||
});
|
||
```
|
||
|
||
## Share cookies across subdomains
|
||
|
||
When hosting apps that share cookies across subdomains, specify a common domain in the [Cookie.Domain](xref:Microsoft.AspNetCore.Http.CookieBuilder.Domain) property. To share cookies across apps at `contoso.com`, such as `first_subdomain.contoso.com` and `second_subdomain.contoso.com`, specify the `Cookie.Domain` as `.contoso.com`:
|
||
|
||
```csharp
|
||
options.Cookie.Domain = ".contoso.com";
|
||
```
|
||
|
||
## Encrypt data protection keys at rest
|
||
|
||
For production deployments, configure the `DataProtectionProvider` to encrypt keys at rest with DPAPI or an X509Certificate. For more information, see <xref:security/data-protection/implementation/key-encryption-at-rest>. In the following example, a certificate thumbprint is provided to <xref:Microsoft.AspNetCore.DataProtection.DataProtectionBuilderExtensions.ProtectKeysWithCertificate*>:
|
||
|
||
```csharp
|
||
services.AddDataProtection()
|
||
.ProtectKeysWithCertificate("{CERTIFICATE THUMBPRINT}");
|
||
```
|
||
|
||
## Share authentication cookies between ASP.NET 4.x and ASP.NET Core apps
|
||
|
||
ASP.NET 4.x apps that use Katana Cookie Authentication Middleware can be configured to generate authentication cookies that are compatible with the ASP.NET Core Cookie Authentication Middleware. For more information, see [Share authentication cookies between ASP.NET 4.x and ASP.NET Core apps (dotnet/AspNetCore.Docs #21987)](https://github.com/dotnet/AspNetCore.Docs/issues/21987).
|
||
|
||
## Use a common user database
|
||
|
||
When apps use the same Identity schema (same version of Identity), confirm that the Identity system for each app is pointed at the same user database. Otherwise, the identity system produces failures at runtime when it attempts to match the information in the authentication cookie against the information in its database.
|
||
|
||
When the Identity schema is different among apps, usually because apps are using different Identity versions, sharing a common database based on the latest version of Identity isn't possible without remapping and adding columns in other app's Identity schemas. It's often more efficient to upgrade the other apps to use the latest Identity version so that a common database can be shared by the apps.
|
||
|
||
## Additional resources
|
||
|
||
* <xref:host-and-deploy/web-farm>
|
||
* [A primer on OWIN cookie authentication middleware for the ASP.NET developer](https://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/)
|
||
:::moniker-end
|