Skip to content

Maranics.AspNetCore.Authentication

This SDK offers methods to implement Maranics authentication.

Nuget Feed | Git Repository


Preparation

Application must be registred in Maranics AppStore service to get app ClientID. This ClientID will be used diring auth process.

Configuration

Add this configuration section to your application settings. ClientAppUrls must contain all possible application urls that will be in use - they will be used for callbacks. CookieDomain setup domain for which session cookies will be issued.

  "OpenIdConnect": {
    "ClientAppUrls": [ "https://localhost:port" ],
    "CookieDomain": "localhost"
  }

Application settings must contain AppStore and Redis connection section as well.

  "AppStoreSettings": {
    "ClientId": "",
    "ClientSecret": "",
    "AppStoreUrl": "http://localhost:port/",
    "Issuer": "http://localhost:port/"
  }
  "ConnectionStrings": {
    "Redis": ""
  }

Implementation

Add following code to your startup class

using Maranics.AspNetCore.Authentication.OpenIdConnect;
using Maranics.AspNetCore.Authentication.Extensions;
using Maranics.AspNetCore.Authentication.Services;
using Maranics.AspNetCore.Authentication;
using StackExchange.Redis;

public void ConfigureServices(IServiceCollection services)
{
...
  AppStoreSettings appStoreSettings = Configuration.GetSection(nameof(AppStoreSettings)).Get<AppStoreSettings>();
  OpenIdConnectSettings openIdConnectSettings = Configuration.GetSection(OpenIdConnectSettings.OpenIdConnectSettingsSectionName).Get<OpenIdConnectSettings>();
  services.AddSingleton(openIdConnectSettings);

  string redisConnectionString = Configuration.GetConnectionString(RedisConnectionStringName);
  services.SetupRedisTicketStore(redisConnectionString);
  services.AddMaranicsAuthentication(typeof(TenantResolver), appStoreSettings, openIdConnectSettings);
  services.AddScoped<AuthenticatedSchemeResolver>();

  var redis = ConnectionMultiplexer.Connect(redisConnectionString);  
  services.AddDataProtection()
    .PersistKeysToStackExchangeRedis(redis, "DataProtection-Keys").SetApplicationName("SSO");

  services.Configure<ForwardedHeadersOptions>(options =>
  {
    options.ForwardedHeaders =
    ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedHost | ForwardedHeaders.XForwardedProto;
    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();
  });
...
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IConfigurationService configurationService
{
  app.UseForwardedHeaders();
  ...
  app.UseAuthentication();
  app.UseAuthorization();
  app.UseMiddleware<SessionContextProvisioningMiddleware>();
  ...
}

TenantResolver

services.AddMaranicsAuthentication(typeof(TenantResolver), appStoreSettings, openIdConnectSettings); require ITenantResolver. Tenant resolver class is used to get current execution tenant for using in authentication / authorization process. With the help of a tenant, the correct UserManagement and the correct IdentityProvider are called.

TenantResolver must be implemented with your own tenant resolving logic but it must inherit ITenantResolver interface from

Maranics.AspNetCore.Authentication.Services;

and must return

namespace Maranics.AspNetCore.Authentication.Services
{
    public class Tenant
    {
        public string Name { get; set; }

        public string UserManagementUrl { get; set; }
    }
}

SessionContextProvisioningMiddleware

This middleware is used to set session context based on authentication result. Implementation of this middleware must be completed on your own based on the way how session is stored / used in your application. Example will provide the way how to handle different authentication schemas that are used in Maranics apps via AuthenticatedSchemeResolver.

You can ignore SessionContextProvisioningMiddleware and use default HttpContext.ClaimPrincipal.User in case if you do not need to convert default session to custom.

public class SessionContextProvisioningMiddleware
{
  private readonly RequestDelegate _next;
  private readonly AppStoreSettings _appStoreSettings;
  private string _tenantUnsensitivePath = "/api/service";

  public SessionContextProvisioningMiddleware(RequestDelegate next, AppStoreSettings appStoreSettings)
  {
      _next = next;
      _appStoreSettings = appStoreSettings;
  }

  public async Task InvokeAsync(HttpContext context)
  {
      AuthenticatedSchemeResolver authenticatedSchemeResolver = context.RequestServices.GetRequiredService<AuthenticatedSchemeResolver>();
      string authenticatedScheme = authenticatedSchemeResolver.ResolveScheme();
      if (!string.IsNullOrEmpty(authenticatedScheme))
      {
          context.Resolve(out IDependenciesProvider dependenciesProvider);
          context.Resolve(out IConfigurationService configurationService);
          TenantConfiguration tenant = null;

          if (!context.Request.Path.Value.ToLower().Contains(_tenantUnsensitivePath))
          {
              tenant = configurationService.GetCurrentTenant<TenantConfiguration>();
          }

          //ExecutingContext is session data provider used in all maranics applications.
          ExecutingContext executingContext = dependenciesProvider.GetCurentExecutingContext();
          executingContext.UserManagementUri = tenant?.UserManagementUri;

          switch (authenticatedScheme)
          {
              case AuthenticationSchemeConstants.AppStoreScheme:
                  SetAppStoreAuthorizedSessionContext(context, tenant, executingContext);
                  break;
              case AuthenticationSchemeConstants.UserManagementScheme:
                  SetUserManagementAuthorizedSessionContext(context, executingContext);
                  break;
              case AuthenticationSchemeConstants.UserManagementUserApiTokenScheme:
                  SetUserManagementApiTokenAuthorizedSessionContext(context, executingContext);
                  break;
              default:
                  break;
          }
      }

      await _next(context);
  }
}

Authorization policies

You can provide your own authorization policies as well. To add policy modify your startup class by adding following code.

...
services.AddMaranicsAuthentication(typeof(TenantResolver), appStoreSettings, openIdConnectSettings);
services.AddSingleton<IAuthorizationHandler, UserHasRoleRequirementHandler>();
services.AddScoped<AuthenticatedSchemeResolver>();

AuthorizationPolicy defaultPolicy = new AuthorizationPolicyBuilder()
  .AddRequirements(new UserHasRoleRequirement())
  .RequireAuthenticatedUser()
  .Build();

services.AddAuthorization(options =>
{
    options.DefaultPolicy = defaultPolicy;
});

Implement following classes for your policy (use Microsoft docs to get more information about authorization policies) You able to use Maranics.Authentication SDK tools as well. Example provide the way how to use AuthenticatedSchemeResolver to build your policy based on it

using Maranics.AspNetCore.Authentication;
using Maranics.AspNetCore.Authentication.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

public class UserHasRoleRequirement : IAuthorizationRequirement
{
}

public class UserHasRoleRequirementHandler : AuthorizationHandler<UserHasRoleRequirement>
{
  private readonly IHttpContextAccessor _httpContextAccessor;

  public UserHasRoleRequirementHandler(IHttpContextAccessor httpContextAccessor)
  {
    _httpContextAccessor = httpContextAccessor;
  }

  protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserHasRoleRequirement requirement)
  {
    AuthenticatedSchemeResolver authenticatedSchemeResolver = _httpContextAccessor.HttpContext.RequestServices.GetRequiredService<AuthenticatedSchemeResolver>();
    string authenticatedScheme = authenticatedSchemeResolver.ResolveScheme();

    // add your logic here

    return Task.CompletedTask;
  }
}

Using

To use Authentication / Authorization add following attributes to your controllers.

using Maranics.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;

...

[Authorize(AuthenticationSchemes = AuthenticationSchemeConstants.AppStoreScheme)]
[Authorize(AuthenticationSchemes = AuthenticationSchemeConstants.AllUsermanagementSchemes, Policy = AuthorizationPolicyConstants.RequireSpecificRolePolicy)]

First attribute allow to use AppStore token to access controller method. Second attribute allow to use all UserManagement schemas - UserManagement session, UserManagement API token as well. Also second attribute require authorization policy validation

SignIn

Using of this SDK help to setup single sign in logic with Maranics UserManagement. When your controller endpoint with [Authorize...] attribute get 401 - you will be redirected to UserManagement signIn page.

Back to top