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
7 changes: 4 additions & 3 deletions docs/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ var builder = WebApplication.CreateBuilder();
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddSystemWebAdapters()
.AddJsonSessionSerializer(options => ClassLibrary.SessionUtils.RegisterSessionKeys(options))
.AddRemoteAppSession(options =>
.AddRemoteApp(options =>
{
options.RemoteApp = new(builder.Configuration["ReverseProxy:Clusters:fallbackCluster:Destinations:fallbackApp:Address"]);
options.ApiKey = ClassLibrary.SessionUtils.ApiKey;
});
})
.AddRemoteAppSession()
.AddJsonSessionSerializer(options => ClassLibrary.SessionUtils.RegisterSessionKeys(options));

var app = builder.Build();

Expand Down
8 changes: 4 additions & 4 deletions docs/framework.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ protected void Application_Start()
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);

Application.AddSystemWebAdapters()
SystemWebAdapterConfiguration.AddSystemWebAdapters(this)
.AddProxySupport(options => options.UseForwardedHeaders = true)
.AddRemoteAppSession(
options => options.ApiKey = ClassLibrary.SessionUtils.ApiKey,
options => ClassLibrary.SessionUtils.RegisterSessionKeys(options));
.AddRemoteApp(options => options.ApiKey = ClassLibrary.SessionUtils.ApiKey)
.AddRemoteAppSession()
.AddJsonSessionSerializer(options => ClassLibrary.SessionUtils.RegisterSessionKeys(options.KnownKeys));
}
```

Expand Down
46 changes: 24 additions & 22 deletions docs/remote-authentication/remote-authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,44 @@ There are just a few small code changes needed to enable remote authentication i

### ASP.NET app configuration

First, the ASP.NET app needs to be configured to add the authentication endpoint. This is done by calling the `AddRemoteAuthentication` extension method on the `ISystemWebAdapterBuilder`:
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.

```CSharp
Application.AddSystemWebAdapters()
.AddRemoteAuthentication(true, options =>
SystemWebAdapterConfiguration.AddSystemWebAdapters(this)
.AddProxySupport(options => options.UseForwardedHeaders = true)
.AddRemoteApp(options =>
{
options.RemoteServiceOptions.ApiKey = "MySecretKey";
});
options.ApiKey = "MySecretKey";
})
.AddRemoteAppAuthentication();
```

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 the options configuration method passed to the `AddRemoteAuthentication` 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). In addition to setting the API key, these options can also be used to specify the path for the authenticate endpoint (defaults to `/systemweb-adapters/authenticate`).
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. This is done by calling `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 `AddRemoteApp` and `AddRemoteAppAuthentication` when registering System.Web adapters services:

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

In addition to configuring the remote app's URL and the shared secret API key, the callback passed to `AddRemoteAppAuthentication` can also optionally specify some aspects of the remote authentication process's behavior:
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:

* `RequestHeadersToForward`: This property contains headers that should be forwarded from a request when calling the authenticate API. By default, the only headers forwarded are `Authorization` and `Cookie`. Additional headers can be forwarded by adding them to this list. Alternatively, if the list is cleared (so that no headers are specified), then all headers will be forwarded.
* `ResponseHeadersToForward`: This property lists response headers that should be propagated back from the authenticate request to the original call that prompted authentication in scenarios where identity is challenged. By default, this includes `Location`, `Set-Cookie`, and `WWW-Authenticate` headers.
* `AuthenticationEndpointPath`: The endpoint on the ASP.NET app where authenticate requests should be made. This defaults to `/systemweb-adapters/authenticate` and must match the endpoint specified in the ASP.NET authentication endpoint configuration.

In addition to these options, `RemoteAuthenticationOptions` derives from `AuthenticationSchemeOptions`, so it can be used to optionally specify authentication schemes to forward to for different authentication actions.

Finally, if the ASP.NET Core app didn't previously include authentication middleware, that will need to be enabled (after routing middleware, but before authorization middleware):

```CSharp
Expand All @@ -51,14 +54,13 @@ app.UseAuthentication();

## Design

1. When requests are processed by the ASP.NET Core app, the `RemoteAuthenticationAuthHandler` will attempt to authenticate the user for the request.
1. The handler will first check whether the endpoint the request is being routed to has remote authentication metadata enabled (via a `[RemoteAuthentication]` or a call to `RequireRemoteAuthentication`). If remote authentication is not enabled, the handler will exit with a result of `NoResult`.
1. If remote authentication is enabled for the endpoint, the handler will make an HTTP request to the ASP.NET app's authenticate endpoint. It will copy configured headers and cookies from the current request onto this new one in order to forward auth-relevant data. As mentioned above, default behavior is to copy the `Authorize` header and all cookies. The API key header is also added for security purposes.
1. The ASP.NET app will serve requests sent to the authenticate endpoint. As long as the API keys match, the ASP.NET app will return either the current user's ClaimsPrincipal serialized into the response body or it will return an HTTP status code (like 401 or 302) and response headers indicating failure.
1. When the ASP.NET Core app's `RemoteAuthenticationAuthHandler` receives the response from the ASP.NET app.
1. When requests are processed by the ASP.NET Core app, if remote app authentication is the default scheme or specified by the request's endpoint, the `RemoteAuthenticationAuthHandler` will attempt to authenticate the user.
1. The handler will make an HTTP request to the ASP.NET app's authenticate endpoint. It will copy configured headers from the current request onto this new one in order to forward auth-relevant data. As mentioned above, default behavior is to copy the `Authorize` and `Cookie` headers. The API key header is also added for security purposes.
1. The ASP.NET app will serve requests sent to the authenticate endpoint. As long as the API keys match, the ASP.NET app will return either the current user's `ClaimsPrincipal` serialized into the response body or it will return an HTTP status code (like 401 or 302) and response headers indicating failure.
1. When the ASP.NET Core app's `RemoteAuthenticationAuthHandler` receives the response from the ASP.NET app:
1. If a ClaimsPrincipal was successfully returned, the auth handler will deserialize it and use it as the current user's identity.
1. If a ClaimsPrincipal was not successfully returned, the handler will store the result and if authentication is challenged (because the user is accessing a protected resource, for example), the request's response will be updated with the status code and selected response headers from the response from the authenticate endpoint. This enables challenge responses (like redirects to a login page) to be propagated to end users.
1. Because results from the ASP.NET app's authenticate endpoint may include data specific to that endpoint, users can register `IRemoteAuthenticationResultProcessor` implementations with the ASP.NET Core app which will run on any authentication results before they are used. As an example, the one built-in `IRemoteAuthenticationResultProcessor` is `RedirectUrlProcessor` which looks for Location response headers returned from the authenticate endpoint and re-writes them so that instead of redirecting the user back to the authenticate endpoint after logging in, the header will instead redirect the user back to the URL of their original request after logging in.
1. Because results from the ASP.NET app's authenticate endpoint may include data specific to that endpoint, users can register `IRemoteAuthenticationResultProcessor` implementations with the ASP.NET Core app which will run on any authentication results before they are used. As an example, the one built-in `IRemoteAuthenticationResultProcessor` is `RedirectUrlProcessor` which looks for Location response headers returned from the authenticate endpoint and ensures that they redirect back to the host of the ASP.NET Core app and not the ASP.NET app directly.

## Known limitations

Expand Down
35 changes: 18 additions & 17 deletions docs/session-state/remote-session.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,20 @@ Configuration for ASP.NET Core would look similar to the following:

```csharp
builder.Services.AddSystemWebAdapters()
.AddJsonSessionSerializer(options =>
{
// Serialization/deserialization requires each session key to be registered to a type
options.RegisterKey<int>("test-value");
options.RegisterKey<SessionDemoModel>("SampleSessionItem");
})
.AddRemoteAppSession(options =>
.AddRemoteApp(options =>
{
// Provide the URL for the remote app that has enabled session querying
options.RemoteApp = 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";
})
.AddRemoteAppSession()
.AddJsonSessionSerializer(options =>
{
// Serialization/deserialization requires each session key to be registered to a type
options.RegisterKey<int>("test-value");
options.RegisterKey<SessionDemoModel>("SampleSessionItem");
});
```

Expand All @@ -63,16 +64,16 @@ app.MapDefaultControllerRoute()
The framework equivalent would look like the following change in `Global.asax.cs`:

```csharp
Application.AddSystemWebAdapters()
.AddJsonRemoteAppSession(
// Provide a strong API key that will be used to authenticate the request on the remote app for querying the session
options => options.ApiKey = "strong-api-key",
options =>
{
// Serialization/deserialization requires each session key to be registered to a type
options.RegisterKey<int>("test-value");
options.RegisterKey<SessionDemoModel>("SampleSessionItem");
});
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")
.AddRemoteAppSession()
.AddJsonSessionSerializer(options =>
{
// Serialization/deserialization requires each session key to be registered to a type
options.RegisterKey<int>("test-value");
options.RegisterKey<SessionDemoModel>("SampleSessionItem");
});
```
# Protocol

Expand Down