Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ public class SomeController : Controller
}
```

Notice that since there's a `Controller.Context` property, they can pass that through, but it generally looks the same. Using implicit conversions, the `Microsoft.AspNetCore.Http.HttpContext` can be converted into the adapter that could then be passed around through the levels utilizing the code in the same way.
Notice that since there's a `Controller.Context` property, they can pass that through, but it generally looks the same. Using implicit conversions, the `Microsoft.AspNetCore.Http.HttpContext` can be converted into the adapter that could then be passed around through the levels utilizing the code in the same way.
1 change: 0 additions & 1 deletion docs/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,3 @@ app.Run();
```

This opts into all the behavior (described below), as well as sets up a remote session state (see [here](session-state/session.md) for details).

2 changes: 1 addition & 1 deletion docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Session is a commonly used feature of ASP.NET that shares a name with a feature

## Enable shared authentication support

It is possible to share authentication between the original ASP.NET app and the new ASP.NET Core app by using the System.Web adapters remote authentication feature. This feature allows the ASP.NET Core app to defer authentication to the ASP.NET app. Please see the [remote authentication docs]((remote-authentication/remote-authentication.md)) for more details.
It is possible to share authentication between the original ASP.NET app and the new ASP.NET Core app by using the System.Web adapters remote authentication feature. This feature allows the ASP.NET Core app to defer authentication to the ASP.NET app. Please see the [remote app connection](remote-app-setup.md) and [remote authentication]((remote-authentication/remote-authentication.md)) docs for more details.

## General Usage Guidance

Expand Down
51 changes: 51 additions & 0 deletions docs/remote-app-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Remote app setup

In some incremental upgrade scenarios, it's useful for the new ASP.NET Core app to be able to communicate with the original ASP.NET app.

Specifically, this capability is used, currently, for [remote app authentication](remote-authentication/remote-authentication.md) and [remote session](session-state/remote-session.md) features.

## Configuration

To enable the ASP.NET Core app to communicate with the ASP.NET app, it's necessary to make a couple small changes to each app.

### ASP.NET app configuration

To setup the ASP.NET app to be able to receive requests from the ASP.NET Core app, call the `AddRemoteApp` extension method on the `ISystemWebAdapterBuilder` as shown here.

```CSharp
SystemWebAdapterConfiguration.AddSystemWebAdapters(this)
.AddRemoteApp(options =>
{
// ApiKey is a string representing a GUID
options.ApiKey = "00000000-0000-0000-0000-000000000000";
});
```

In the options configuration method passed to the `AddRemoteApp` call, you must specify an API key which is used to secure the endpoint so that only trusted callers can make requests to it (this same API key will be provided to the ASP.NET Core app when it is configured). The API key is a string and must be parsable as a GUID (128-bit hex number). Hyphens in the key are optional.

### ASP.NET Core app

To setup the ASP.NET Core app to be able to send requests to the ASP.NET app, you need to make a similar change, calling `AddRemoteApp` after registering System.Web adapter services with `AddSystemWebAdapters`.

```CSharp
builder.Services.AddSystemWebAdapters()
.AddRemoteApp(options =>
{
options.RemoteAppUrl = new(builder.Configuration["http://URL-for-the-ASPNet-app"]);

// ApiKey is a string representing a GUID
options.ApiKey = "00000000-0000-0000-0000-000000000000";
});
```

The `AddRemoteApp` call is used to configure the remote app's URL and the shared secret API key.

With both the ASP.NET and ASP.NET Core app updated, extension methods can now be used to setup [remote app authentication](remote-authentication/remote-authentication.md) or [remote session](session-state/remote-session.md), as needed.

## Securing the remote app connection

Because remote app features involve serving requests on new endpoints from the ASP.NET app, it's important that communication to and from the ASP.NET app be secure.

First, make sure that the API key string used to authenticate the ASP.NET Core app with the ASP.NET app is unique and kept secret. It is a best practice to not store the API key in source control. Instead, load it at runtime from a secure source such as Azure Key Vault or other secure runtime configuration. In order to encourage secure API keys, remote app connections require that the keys be non-empty GUIDs (128-bit hex numbers).

Second, because it's important for the ASP.NET Core app to be able to trust that it is requesting information from the correct ASP.NET app, the ASP.NET app should use HTTPS in any production scenarios so that the ASP.NET Core app can know responses are being served by a trusted source.
17 changes: 9 additions & 8 deletions docs/remote-authentication/remote-authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,39 @@ The System.Web adapter's remote authentication feature allows an ASP.NET Core ap

There are just a few small code changes needed to enable remote authentication in a solution that's already set up according to the [Getting Started](../getting_started.md).

First, follow the [remote app setup](../remote-app-setup.md) instructions to connect the ASP.NET Core and ASP.NET apps. Then, there are just a couple extra extension methods to call to enable remote app authentication.

### ASP.NET app configuration

First, the ASP.NET app needs to be configured to add the authentication endpoint. This is done by calling the `AddRemoteApp` extension method on the `ISystemWebAdapterBuilder` to configure receiving remote calls, and by calling `AddRemoteAuthentication` to set up the HTTP module that will watch for requests to the authentication endpoint. Note that remote authentication scenarios typically want to add proxy support, as well, so that any auth-related redirects will correctly route to the ASP.NET Core app rather than the ASP.NET one.
First, the ASP.NET app needs to be configured to add the authentication endpoint. This is done by calling the `AddRemoteAuthentication` extension method to set up the HTTP module that will watch for requests to the authentication endpoint. Note that remote authentication scenarios typically want to add proxy support, as well, so that any auth-related redirects will correctly route to the ASP.NET Core app rather than the ASP.NET one.

```CSharp
SystemWebAdapterConfiguration.AddSystemWebAdapters(this)
.AddProxySupport(options => options.UseForwardedHeaders = true)
.AddRemoteApp(options =>
{
options.ApiKey = "MySecretKey";
// ApiKey is a string representing a GUID
options.ApiKey = "00000000-0000-0000-0000-000000000000";
})
.AddRemoteAppAuthentication();
```

In the options configuration method passed to the `AddRemoteApp` call, you must specify an API key which is used to secure the endpoint so that only trusted callers can make requests to it (this same API key will be provided to the ASP.NET Core app when it is configured).

### ASP.NET Core app configuration

Next, the ASP.NET Core app needs to be configured to enable the authentication handler that will authenticate users by making an HTTP request to the ASP.NET app. Again, this is done by calling `AddRemoteApp` and `AddRemoteAppAuthentication` when registering System.Web adapters services:
Next, the ASP.NET Core app needs to be configured to enable the authentication handler that will authenticate users by making an HTTP request to the ASP.NET app. Again, this is done by calling `AddRemoteAppAuthentication` when registering System.Web adapters services:

```CSharp
builder.Services.AddSystemWebAdapters()
.AddRemoteApp(options =>
{
options.RemoteAppUrl = new(builder.Configuration["http://URL-for-the-ASPNet-app"]);
options.ApiKey = "MySecretKey";

// ApiKey is a string representing a GUID
options.ApiKey = "00000000-0000-0000-0000-000000000000";
})
.AddRemoteAppAuthentication(true);
```

The `AddRemoteApp` call is used to configure the remote app's URL and the shared secret API key.

The boolean that is passed to the `AddRemoteAuthentication` call specifies whether remote app authentication should be the default authentication scheme. Passing `true` will cause the user to be authenticated via remote app authentication for all requests, whereas passing `false` means that the user will only be authenticated with remote app authentication if the remote app scheme is specifically requested (with `[Authorize(AuthenticationSchemes = RemoteAppAuthenticationDefaults.AuthenticationScheme)]` on a controller or action method, for example). Passing false for this parameter has the advantage of only making HTTP requests to the original ASP.NET app for authentication for endpoints that require remote app authentication but has the disadvantage of requiring annotating all such endpoints to indicate that they will use remote app auth.

In addition to the require boolean, an optional callback may be passed to `AddRemoteAppAuthentication` to modify some other aspects of the remote authentication process's behavior:
Expand Down
15 changes: 7 additions & 8 deletions docs/session-state/remote-session.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,9 @@ builder.Services.AddSystemWebAdapters()

## Configuration

In order to configure it, both the framework and core app must set an API key as well as register known app settings types. These properties are:
First, follow the [remote app setup](../remote-app-setup.md) instructions to connect the ASP.NET Core and ASP.NET apps. Then, there are just a couple extra extension methods to call to enable remote app session state.

- `ApiKeyHeader` - header name that will contain an API key to secure the endpoint added on .NET Framework
- `ApiKey` - the shared API key that will be validated in the .NET Framework handler

Configuration for ASP.NET Core would look similar to the following:
Configuration for ASP.NET Core involves calling `AddRemoteAppSession` and `AddJsonSessionSerializer` to register known session item types. The code should look similar to the following:

```csharp
builder.Services.AddSystemWebAdapters()
Expand All @@ -31,7 +28,8 @@ builder.Services.AddSystemWebAdapters()
options.RemoteAppUrl = new(builder.Configuration["ReverseProxy:Clusters:fallbackCluster:Destinations:fallbackApp:Address"]);

// Provide a strong API key that will be used to authenticate the request on the remote app for querying the session
options.ApiKey = "strong-api-key";
// ApiKey is a string representing a GUID
options.ApiKey = "00000000-0000-0000-0000-000000000000";
})
.AddRemoteAppSession()
.AddJsonSessionSerializer(options =>
Expand Down Expand Up @@ -60,13 +58,13 @@ app.MapDefaultControllerRoute()
.RequireSystemWebAdapterSession();
```


The framework equivalent would look like the following change in `Global.asax.cs`:

```csharp
SystemWebAdapterConfiguration.AddSystemWebAdapters(this)
// Provide a strong API key that will be used to authenticate the request on the remote app for querying the session
.AddRemoteApp(options => options.ApiKey = "strong-api-key")
// ApiKey is a string representing a GUID
.AddRemoteApp(options => options.ApiKey = "00000000-0000-0000-0000-000000000000")
.AddRemoteAppSession()
.AddJsonSessionSerializer(options =>
{
Expand All @@ -75,6 +73,7 @@ SystemWebAdapterConfiguration.AddSystemWebAdapters(this)
options.RegisterKey<SessionDemoModel>("SampleSessionItem");
});
```

# Protocol

## Readonly
Expand Down
2 changes: 0 additions & 2 deletions samples/ClassLibrary/RemoteServiceUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ namespace ClassLibrary;

public class RemoteServiceUtils
{
public static string ApiKey = "test-key";

public static void RegisterSessionKeys(IDictionary<string, Type> knownTypes)
{
knownTypes.Add("test-value", typeof(int));
Expand Down
3 changes: 2 additions & 1 deletion samples/MvcApp/Global.asax.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Configuration;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
Expand All @@ -20,7 +21,7 @@ protected void Application_Start()
.AddProxySupport(options => options.UseForwardedHeaders = true)
.AddJsonSessionSerializer(options => ClassLibrary.RemoteServiceUtils.RegisterSessionKeys(options.KnownKeys))
.AddRemoteAppServer(remote => remote
.Configure(options => options.ApiKey = ClassLibrary.RemoteServiceUtils.ApiKey)
.Configure(options => options.ApiKey = ConfigurationManager.AppSettings["RemoteAppApiKey"])
.AddAuthentication()
.AddSession());
}
Expand Down
3 changes: 3 additions & 0 deletions samples/MvcApp/Web.config
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\aspnet-MvcApp-20220502113315.mdf;Initial Catalog=aspnet-MvcApp-20220502113315;Integrated Security=True" providerName="System.Data.SqlClient"/>
</connectionStrings>
<appSettings>
<!-- Do not re-use this ApiKey; every solution should use a unique ApiKey -->
<add key="RemoteAppApiKey" value="54b69938-90dd-4f79-adcd-27fbd6f0e4b7"/>

<add key="webpages:Version" value="3.0.0.0"/>
<add key="webpages:Enabled" value="false"/>
<add key="ClientValidationEnabled" value="true"/>
Expand Down
2 changes: 1 addition & 1 deletion samples/MvcCoreApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
.Configure(options =>
{
options.RemoteAppUrl = new(builder.Configuration["ReverseProxy:Clusters:fallbackCluster:Destinations:fallbackApp:Address"]);
options.ApiKey = ClassLibrary.RemoteServiceUtils.ApiKey;
options.ApiKey = builder.Configuration["RemoteAppApiKey"];
})
.AddAuthentication(true)
.AddSession())
Expand Down
4 changes: 3 additions & 1 deletion samples/MvcCoreApp/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
// Do not re-use this API key. Every solution should have a unique API key.
"RemoteAppApiKey": "54b69938-90dd-4f79-adcd-27fbd6f0e4b7",
"Logging": {
"LogLevel": {
"Default": "Information",
Expand Down Expand Up @@ -26,4 +28,4 @@
}
}
}
}
}
3 changes: 2 additions & 1 deletion samples/RemoteAuth/Bearer/RemoteBearer/Global.asax.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Configuration;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
Expand All @@ -17,7 +18,7 @@ protected void Application_Start()
{
// A real application would not hard code this, but load it
// securely from environment or configuration
options.ApiKey = "TopSecretString";
options.ApiKey = ConfigurationManager.AppSettings["RemoteAppApiKey"];
})
.AddAuthentication());
}
Expand Down
3 changes: 3 additions & 0 deletions samples/RemoteAuth/Bearer/RemoteBearer/Web.config
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
-->
<configuration>
<appSettings>
<!-- Do not re-use this ApiKey; every solution should use a unique ApiKey -->
<add key="RemoteAppApi" value="40c807bd-6c00-4e5a-9650-ea20c2e6c02d"/>

<add key="webpages:Version" value="3.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="ClientValidationEnabled" value="true" />
Expand Down
5 changes: 1 addition & 4 deletions samples/RemoteAuth/Bearer/RemoteBearerCore/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@
.Configure(options =>
{
options.RemoteAppUrl = new(builder.Configuration["ReverseProxy:Clusters:fallbackCluster:Destinations:fallbackApp:Address"]);

// A real application would not hard code this, but load it
// securely from environment or configuration
options.ApiKey = "TopSecretString";
options.ApiKey = builder.Configuration["RemoteAppApiKey"];
})

// This registers the remote app authentication handler. The boolean argument indicates whether remote app auth
Expand Down
4 changes: 3 additions & 1 deletion samples/RemoteAuth/Bearer/RemoteBearerCore/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
// Do not re-use this ApiKey; every solution should use a unique ApiKey
"RemoteAppApiKey": "40c807bd-6c00-4e5a-9650-ea20c2e6c02d",
"Logging": {
"LogLevel": {
"Default": "Information",
Expand Down Expand Up @@ -26,4 +28,4 @@
}
}
}
}
}
3 changes: 2 additions & 1 deletion samples/RemoteAuth/Forms/FormsAuth/Global.asax.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Configuration;
using System.Web;
using System.Web.Optimization;
using System.Web.Routing;
Expand All @@ -19,7 +20,7 @@ void Application_Start(object sender, EventArgs e)
.AddRemoteAppServer(remote => remote
.Configure(options =>
{
options.ApiKey = "FormsAuthSampleKey";
options.ApiKey = ConfigurationManager.AppSettings["RemoteAppApiKey"];
})
.AddAuthentication());
}
Expand Down
4 changes: 4 additions & 0 deletions samples/RemoteAuth/Forms/FormsAuth/Web.config
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<!-- Do not re-use this ApiKey; every solution should use a unique ApiKey -->
<add key="RemoteAppApiKey" value="8e470586-24e5-4f2a-8245-69bbdbf9f767"/>
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.7.2" />
<httpRuntime targetFramework="4.7.2" />
Expand Down
2 changes: 1 addition & 1 deletion samples/RemoteAuth/Forms/FormsAuthCore/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
.Configure(options =>
{
options.RemoteAppUrl = new(builder.Configuration["ReverseProxy:Clusters:fallbackCluster:Destinations:fallbackApp:Address"]);
options.ApiKey = "FormsAuthSampleKey";
options.ApiKey = builder.Configuration["RemoteAppApiKey"];
})
.AddAuthentication(true));

Expand Down
4 changes: 3 additions & 1 deletion samples/RemoteAuth/Forms/FormsAuthCore/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
// Do not re-use this ApiKey; every solution should use a unique ApiKey
"RemoteAppApiKey": "8e470586-24e5-4f2a-8245-69bbdbf9f767",
"Logging": {
"LogLevel": {
"Default": "Information",
Expand Down Expand Up @@ -26,4 +28,4 @@
}
}
}
}
}
3 changes: 2 additions & 1 deletion samples/RemoteAuth/OIDC/OIDCAuth/Global.asax.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Configuration;
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
Expand All @@ -17,7 +18,7 @@ protected void Application_Start()
SystemWebAdapterConfiguration.AddSystemWebAdapters(this)
.AddProxySupport(options => options.UseForwardedHeaders = true)
.AddRemoteAppServer(remote => remote
.Configure(options => options.ApiKey = "test-key")
.Configure(options => options.ApiKey = ConfigurationManager.AppSettings["RemoteAppApiKey"])
.AddAuthentication());

}
Expand Down
3 changes: 3 additions & 0 deletions samples/RemoteAuth/OIDC/OIDCAuth/Web.config
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
-->
<configuration>
<appSettings>
<!-- Do not re-use this ApiKey; every solution should use a unique ApiKey -->
<add key="RemoteAppApiKey" value="121257f2-c121-4f51-b30c-d1f617933290"/>

<add key="webpages:Version" value="3.0.0.0"/>
<add key="webpages:Enabled" value="false"/>
<add key="ClientValidationEnabled" value="true"/>
Expand Down
2 changes: 1 addition & 1 deletion samples/RemoteAuth/OIDC/OIDCAuthCore/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
.Configure(options =>
{
options.RemoteAppUrl = new(builder.Configuration["ReverseProxy:Clusters:fallbackCluster:Destinations:fallbackApp:Address"]);
options.ApiKey = "test-key";
options.ApiKey = builder.Configuration["RemoteAppApiKey"];
})
.AddAuthentication(true));

Expand Down
4 changes: 3 additions & 1 deletion samples/RemoteAuth/OIDC/OIDCAuthCore/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
// Do not re-use this ApiKey; every solution should use a unique ApiKey
"RemoteAppApiKey": "121257f2-c121-4f51-b30c-d1f617933290",
"Logging": {
"LogLevel": {
"Default": "Information",
Expand Down Expand Up @@ -26,4 +28,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class RemoteAppClientOptions
/// <summary>
/// Gets or sets an API key used to secure the endpoint
/// </summary>
[Required]
[Required, ApiKey]
public string ApiKey { get; set; } = null!;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.ComponentModel.DataAnnotations;

namespace Microsoft.AspNetCore.SystemWebAdapters;
Expand All @@ -20,6 +19,6 @@ public class RemoteAppServerOptions
/// <summary>
/// Gets or sets an API key used to secure the endpoint
/// </summary>
[Required]
[Required, ApiKey]
public string ApiKey { get; set; } = null!;
}
Loading