OpenIdDict in ASP.NET 4.5 or alternative frameworks?

1.3k Views Asked by At

In an old project we used ASP.NET 4.5 and I would like to use the Framework OpenIdDict in it. It's made vor ASP.NET Core 1 and 2. Can I still use it? What do I have to pay attention to? If I can't use that, which alternatives do you know?

Link to OpenIdDict: https://github.com/openiddict/openiddict-core

1

There are 1 best solutions below

0
On

Edit: OpenIddict 3.0 is now natively compatible with OWIN/Katana and can be used in any >= ASP.NET 4.6.1 application without using any adapter. Read these two blog posts for more information:


Technically, it's possible to use middleware designed for ASP.NET Core 1.x/2.x in an ASP.NET 4.x application by using the Microsoft.AspNetCore.Owin adapter package and some tweaking.

Here's a prototype of an OWIN self-hosted console application (it would be very similar with an ASP.NET 4.x application using the OWIN SystemWeb host):

Program.cs:

using System;
using Microsoft.Owin.Hosting;

namespace OpenIddictOwinDemo
{
    public static class Program
    {
        public static void Main(string[] args)
        {
            const string address = "http://localhost:12556/";

            using (WebApp.Start<Startup>(address))
            {
                Console.WriteLine($"Server is running on {address}, press CTRL+C to stop.");
                Console.ReadLine();
            }
        }
    }
}

Startup.cs:

using System;
using System.Diagnostics;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Owin;

namespace OpenIddictOwinDemo
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.UseBuilder(ConfigureServices(), Configure);
        }

        IServiceProvider ConfigureServices()
        {
            var services = new ServiceCollection();

            // In a real ASP.NET Core application, these services are
            // registered by the hosting stack. Since this application
            // is actually an OWIN app, they must be registered manually.
            var listener = new DiagnosticListener("Microsoft.AspNetCore");
            services.AddSingleton<DiagnosticListener>(listener);
            services.AddSingleton<DiagnosticSource>(listener);
            services.AddSingleton<IHostingEnvironment, HostingEnvironment>();

            services.AddDbContext<DbContext>(options =>
            {
                options.UseInMemoryDatabase("db");
            });

            services.AddOpenIddict()
                .AddCore(options =>
                {
                    options.UseEntityFrameworkCore()
                        .UseDbContext<DbContext>();
                })

                .AddServer(options =>
                {
                    options.AcceptAnonymousClients();
                    options.AllowPasswordFlow();
                    options.EnableTokenEndpoint("/connect/token");
                    options.DisableHttpsRequirement();

                    options.UseCustomTokenEndpoint();
                });

            return services.BuildServiceProvider(validateScopes: true);
        }

        void Configure(IApplicationBuilder app)
        {
            // This inline middleware is required to be able to use scoped services.
            // In a real ASP.NET Core application, this is done for you by a special
            // middleware automatically injected by the default hosting components.
            app.Use(next => async context =>
            {
                var provider = context.RequestServices;
                using (var scope = provider.CreateScope())
                {
                    try
                    {
                        context.RequestServices = scope.ServiceProvider;

                        await next(context);
                    }

                    finally
                    {
                        context.RequestServices = provider;
                    }
                }
            });

            app.UseDeveloperExceptionPage();

            app.UseAuthentication();
        }
    }
}

KatanaExtensions.cs:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.FileProviders;
using Owin;

namespace OpenIddictOwinDemo
{
    using AddMiddleware = Action<Func<
        Func<IDictionary<string, object>, Task>,
        Func<IDictionary<string, object>, Task>
    >>;
    using AppFunc = Func<IDictionary<string, object>, Task>;

    public static class KatanaExtensions
    {
        public static IAppBuilder UseBuilder(this IAppBuilder app,
            IServiceProvider provider, Action<IApplicationBuilder> configuration)
        {
            if (app == null)
            {
                throw new ArgumentNullException(nameof(app));
            }

            if (provider == null)
            {
                throw new ArgumentNullException(nameof(provider));
            }

            if (configuration == null)
            {
                throw new ArgumentNullException(nameof(configuration));
            }

            AddMiddleware add = middleware =>
            {
                app.Use(new Func<AppFunc, AppFunc>(next => middleware(next)));
            };

            add.UseBuilder(configuration, provider);

            return app;
        }
    }

    public class HostingEnvironment : IHostingEnvironment
    {
        public string EnvironmentName { get; set; }
        public string ApplicationName { get; set; }
        public string WebRootPath { get; set; }
        public IFileProvider WebRootFileProvider { get; set; }
        public string ContentRootPath { get; set; }
        public IFileProvider ContentRootFileProvider { get; set; }
    }
}

OpenIddictExtensions:

using System;
using System.Security.Claims;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Primitives;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Server;

namespace OpenIddictOwinDemo
{
    public static class CustomOpenIddictServerExtensions
    {
        public static OpenIddictServerBuilder UseCustomTokenEndpoint(
            this OpenIddictServerBuilder builder)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            return builder.AddEventHandler<OpenIddictServerEvents.HandleTokenRequest>(
                notification =>
                {
                    var request = notification.Context.Request;
                    if (!request.IsPasswordGrantType())
                    {
                        return Task.CompletedTask;
                    }

                    // Validate the user credentials.

                    // Note: to mitigate brute force attacks, you SHOULD strongly consider
                    // applying a key derivation function like PBKDF2 to slow down
                    // the password validation process. You SHOULD also consider
                    // using a time-constant comparer to prevent timing attacks.
                    if (request.Username != "[email protected]" ||
                        request.Password != "P@ssw0rd")
                    {
                        notification.Context.Reject(
                            error: OpenIdConnectConstants.Errors.InvalidGrant,
                            description: "The specified credentials are invalid.");

                        return Task.CompletedTask;
                    }

                    // Create a new ClaimsIdentity holding the user identity.
                    var identity = new ClaimsIdentity(
                        notification.Context.Scheme.Name,
                        OpenIdConnectConstants.Claims.Name,
                        OpenIdConnectConstants.Claims.Role);

                    // Add a "sub" claim containing the user identifier, and attach
                    // the "access_token" destination to allow OpenIddict to store it
                    // in the access token, so it can be retrieved from your controllers.
                    identity.AddClaim(OpenIdConnectConstants.Claims.Subject,
                        "71346D62-9BA5-4B6D-9ECA-755574D628D8",
                        OpenIdConnectConstants.Destinations.AccessToken);
                    identity.AddClaim(OpenIdConnectConstants.Claims.Name, "Alice",
                        OpenIdConnectConstants.Destinations.AccessToken);

                    // ... add other claims, if necessary.
                    var principal = new ClaimsPrincipal(identity);
                    notification.Context.Validate(principal);

                    return Task.CompletedTask;
                });
        }
    }
}

In practice, I wouldn't recommend it as it's a bit hackish (the Microsoft.AspNetCore.Owin adapter hasn't been updated for years). Instead, you may want to adopt a micro-service architecture and move your OpenIddict-based authorization server to a separate ASP.NET Core service.