1

I am aware of the answer given here which is about using Forms Authentication & SAML. In My case I am using Asp.net core Identity on .Net 5. Also I am using two authentication schemes (Cookies & JWT).

My auth pipeline goes as;

//include identity
services.AddIdentity<ApplicationUser, ApplicationRole>(SetupIdentityOptions)
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

//configure cookie and Jwt scheme
services.ConfigureApplicationCookie(...)
services.AddAuthentication(...) //configures default Identity
    .AddJwtBearer(options => {...})

    services.AddAuthorization(options =>
    {
        options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
    });

What I want to know is where should I add SAML2 in this pipeline.

In general the application should;

  1. Be able to login with Cookie authentication (Identity takes care of this)
  2. Jwt tokens should work as well for Apis (this also works with Jwt scheme)
  3. SSO clients should get authenticated from their IdP and when redirected to AssertionConsumer() I will create additional claims create new ClaimsIdentity, create a JWT token(pass it to client) and get the user to dashboard.

I am stuck at 3rd point which is to properly add the SAML scheme without overriding cookie/jwt schemes.

Peter B
  • 22,460
  • 5
  • 32
  • 69
user2058413
  • 691
  • 3
  • 10
  • 28

3 Answers3

2

The error No sign-in authentication handler is registered for the scheme 'saml2' probably occurs bedause you need to add services.AddSaml2() and app.UseSaml2()

Anders Revsgaard
  • 3,636
  • 1
  • 9
  • 25
1

You can use the setup from the provided example project. Newly added middleware should not interfere with what you already have.

When a SAML2 IdP redirects back to your application, you are given a result that identifies the authenticated user, e.g. Email Address or SSN (in case it is a government Id Provider).

You can combine that information with a Role (e.g. SpecialCustomer, Citizen, or an existing Role that you already have) into a cookie or JWT Token as you probably already do for other users. This can always be distinguished from other cookies and tokens by the Role. Regular [Authorize(....)] attributes will also work just fine.

Peter B
  • 22,460
  • 5
  • 32
  • 69
  • 1
    Thank you for the response. I managed to go a bit further. However after IdP send the assertion, It posts data to my AssertionConsumerService() method where I can add additional claims etc. as required. But when the method is done with it's work an exception is thrown ["InvalidOperationException: No sign-in authentication handler is registered for the scheme 'saml2'. "] So, I am not sure how I should add a new scheme maintaining JwT & Cookie from Identity. – user2058413 May 24 '21 at 08:01
0

I was stuck at the same point.. The solution I found:

If you check the source code of IFOXTEC.IDENTITY.SAML2, the method AddSaml2 overrides your AddAuthentication method and adds the AddCookie section.

public static IServiceCollection AddSaml2(this IServiceCollection services, string loginPath = "/Auth/Login", bool slidingExpiration = false, string accessDeniedPath = null, ITicketStore sessionStore = null, SameSiteMode cookieSameSite = SameSiteMode.Lax, string cookieDomain = null, CookieSecurePolicy cookieSecurePolicy = CookieSecurePolicy.SameAsRequest)
    {
        services.AddAuthentication(Saml2Constants.AuthenticationScheme)
            .AddCookie(Saml2Constants.AuthenticationScheme, o =>
            {
                o.LoginPath = new PathString(loginPath);
                o.SlidingExpiration = slidingExpiration;
                if(!string.IsNullOrEmpty(accessDeniedPath))
                {
                    o.AccessDeniedPath = new PathString(accessDeniedPath);
                }
                if (sessionStore != null)
                {
                    o.SessionStore = sessionStore;
                }
                o.Cookie.SameSite = cookieSameSite;
                o.Cookie.SecurePolicy = cookieSecurePolicy;
                if (!string.IsNullOrEmpty(cookieDomain))
                {
                    o.Cookie.Domain = cookieDomain;
                }
            });

        return services;
    }   

So, to add SAML to your pipeline, you can remove the services.AddSaml2(), add the AddCookie section and, inside your policy, you can add the verification of any cookie with name saml2 to forward to your SAML schema.

services
            .AddAuthentication(sharedOptions =>
            {
                sharedOptions.DefaultScheme = "custom-schema";
                sharedOptions.DefaultChallengeScheme = "custom-schema";
            })
            .AddPolicyScheme("custom-schema", null, options =>
            {
                options.ForwardDefaultSelector = context =>
                {
                    if (context.Request.Headers["Authorization"].Any(x => x.StartsWith("Bearer ")))
                        return JwtBearerDefaults.AuthenticationScheme;
                    else if (context.Request.Headers["Cookie"].Any(x => x.Contains(".saml2=")))
                        return Saml2Constants.AuthenticationScheme;
                    return "Identity.Application";
                };
            })
            .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, null, options =>
            {
                //...
            })
            .AddCookie(Saml2Constants.AuthenticationScheme, o =>
            {
                o.LoginPath = new PathString("/Auth/Login");
                o.SlidingExpiration = false;
                o.Cookie.SameSite = SameSiteMode.Lax;
                o.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
            });