Giter Site home page Giter Site logo

jkmu / saml2.authentication.core Goto Github PK

View Code? Open in Web Editor NEW
90.0 11.0 41.0 860 KB

A SAML 2.0 middleware for ASP.NET Core

License: Mozilla Public License 2.0

C# 94.09% CSS 0.11% JavaScript 0.01% HTML 5.79%
saml asp-net-core saml2 saml2-sp-sso saml-service-provider authentication sso slo

saml2.authentication.core's Introduction

Saml2.Authentication.Core

A SAML 2.0 authentication middleware for ASP.NET Core

This project is a fork of the OIOSAML.Net implementation of SAML 2.0 framework from digitaliser.dk. It has been ported and modified to support ASP.NET Core with all dependencies to ASP.NET removed.

Available in nuget.org

Features

Supports the following SAML 2.0 features for Web Browser SSO and Single Logout profiles

  • HTTP Redirect Binding
    SP Redirect Request; IdP POST/Redirect Response
  • HTTP Artifact Binding
    SP Redirect Request; IdP Redirect Artifact Response
  • SP-Initiated Single Logout with Multiple SPs
  • HTTP POST Binding
  • IDP-Initiated Single Logout

Configuration

Startup

  // This method gets called by the runtime. Use this method to add services to the container.
  public void ConfigureServices(IServiceCollection services)
  {
      services.AddScoped<IUserClaimsPrincipalFactory<TUser>, DemoWebAppClaimsPrincipalFactory>();		
      services.Configure<Saml2Configuration>(Configuration.GetSection("Saml2"));

      services.AddSaml();

      // Single idp
      services.AddAuthentication()
          .AddCookie("saml2.cookies", options =>
          {
              options.Cookie.HttpOnly = true;
              options.Cookie.SameSite = SameSiteMode.None;
              options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
          })
          .AddSaml("saml2", "saml2", options =>
          {
              options.SignInScheme = "saml2.cookies";
              options.IdentityProviderName = "stubidp.sustainsys";
          });
          
      // Multiple idps
       services.AddAuthentication()
          .AddCookie("saml2.idp1.cookies", options =>
          {
              options.Cookie.HttpOnly = true;
              options.Cookie.SameSite = SameSiteMode.None;
              options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
          })
          .AddCookie("saml2.idp2.cookies", options =>
          {
              options.Cookie.HttpOnly = true;
              options.Cookie.SameSite = SameSiteMode.None;
              options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
          })
          .AddSaml("saml2.idp1", "saml2.idp1", options =>
          {
              options.SignInScheme = "saml2.idp1.cookies";
              options.IdentityProviderName = "idp1";
          })
          .AddSaml("saml2.idp2", "saml2.idp2", options =>
           {
               options.SignInScheme = "saml2.idp2.cookies";
               options.IdentityProviderName = "idp2";
           });

      services.AddMvc();
  }

appsettings.json

// Single idp
"Saml2": {
    "ServiceProviderConfiguration": {
      "EntityId": "Id of the sp",
      "Name": "name",
      "AssertionConsumerServiceUrl": "AssertionConsumerService",
      "SingleLogoutResponseServiceUrl": "SingleLogoutService",
      "OmitAssertionSignatureCheck": true, // check or not for valid idp's signature in AuthnResponse
      "Certificate": {
        "Thumbprint": "sp's certificate",
      }
    },
    "IdentityProviderConfiguration": [
      {
        "EntityId": "Id of the SAML 2.0 idp",
        "Name": "Name of the SAML 2.0 idp",
        "ForceAuth": "false",
        "IsPassive": "false",
        "SingleSignOnService": "idp's sso service endpoint",
        "SingleSignOutService": "idp's slo service endpoint",
        "ArtifactResolveService": "idp's artifact resolve service endpoint",
        "Certificate": {
          "Thumbprint": "idp's certificate",
        }
      }
    ]
  }
  
  // Multiple idps
  "Saml2": {
   "ServiceProviderConfiguration": {
      "EntityId": "Id of the sp",
      "Name": "name",
      "AssertionConsumerServiceUrl": "AssertionConsumerService",
      "SingleLogoutResponseServiceUrl": "SingleLogoutService",
      "OmitAssertionSignatureCheck": true, // check or not for valid idp's signature in AuthnResponse
      "Certificate": {
        "Thumbprint": "sp's certificate",
      }
    },
    "IdentityProviderConfiguration": [
      {
        "EntityId": "idp1",
        "Name": "name of idp1",
        "ForceAuth": "false",
        "IsPassive": "false",
        "SingleSignOnService": "idp1's sso service endpoint",
        "SingleSignOutService": "idp1's slo service endpoint",
        "ArtifactResolveService": "idp1's artifact resolve service endpoint",
        "Certificate": {
          "Thumbprint": "idp1's certificate",
        }
      },
      {
        "EntityId": "idp2",
        "Name": "name of idp2",
        "ForceAuth": "false",
        "IsPassive": "false",
        "SingleSignOnService": "idp2's sso service endpoint",
        "SingleSignOutService": "idp2's slo service endpoint",
        "ArtifactResolveService": "idp2's artifact resolve service endpoint",
        "Certificate": {
          "Thumbprint": "idp2's certificate",
        }
      }
    ]
  }

ClaimsPrincipalFactory

The SessionIndex and Subject claims are required for SLO. These needs to be stored and availed during logout. This example keeps all the claims from the idp in session cookie if using Identity

  public class DemoWebAppClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser>
  {
      private readonly IHttpContextAccessor _httpContextAccessor;

      public DemoWebAppClaimsPrincipalFactory(UserManager<ApplicationUser> userManager,
          IOptions<IdentityOptions> optionsAccessor, IHttpContextAccessor httpContextAccessor) : base(userManager,
          optionsAccessor)
      {
          _httpContextAccessor = httpContextAccessor;
      }

      protected override async Task<ClaimsIdentity> GenerateClaimsAsync(ApplicationUser user)
        {
            var signInManager =
                (SignInManager<ApplicationUser>)Context.RequestServices.GetService(
                    typeof(SignInManager<ApplicationUser>));

            var claims = new List<Claim>();
            var authenticationSchemes = await signInManager.GetExternalAuthenticationSchemesAsync();
            foreach (var scheme in authenticationSchemes)
            {
                var authenticateResult = await Context.AuthenticateAsync(scheme.Name);
                if (!authenticateResult.Succeeded)
                {
                    continue;
                }

                var sessionIndex = authenticateResult.Principal.Claims.First(c => c.Type == Saml2ClaimTypes.SessionIndex);
                var saml2Subject = authenticateResult.Principal.Claims.First(c => c.Type == Saml2ClaimTypes.Subject);
                claims.Add(sessionIndex);
                claims.Add(saml2Subject);
            }

            var claimsIdentity = await base.GenerateClaimsAsync(user);
            claimsIdentity.AddClaims(claims); //Add external claims to cookie. The SessionIndex and Subject are required for SLO
            return claimsIdentity;
        }
  }

saml2.authentication.core's People

Contributors

davidpenton-match avatar jkmu avatar vladrudych avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

saml2.authentication.core's Issues

Setting up a working poc

Hello,

I'd really like to give this project a try, but I've not set up Saml before. Is there any guidance on how to set up an Identity provider and fill out the settings such that I could run the project locally?

Invalid or missing value of the choice identifier 'ItemsElementName' of type 'dk.nita.saml20.Schema.Protocol.ItemsChoiceType7[]'.

am i missing something to get the following error ?

doc.LoadXml(Serialization.SerializeToXmlString(Request));

Invalid or missing value of the choice identifier 'ItemsElementName' of type 'dk.nita.saml20.Schema.Protocol.ItemsChoiceType7[]'.

System.InvalidOperationException: Invalid or missing value of the choice identifier 'ItemsElementName' of type 'dk.nita.saml20.Schema.Protocol.ItemsChoiceType7[]'.
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterAuthnRequest.Write75_RequestedAuthnContext(String n, String ns, RequestedAuthnContext o, Boolean isNullable, Boolean needType)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterAuthnRequest.Write83_AuthnRequest(String n, String ns, AuthnRequest o, Boolean isNullable, Boolean needType)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterAuthnRequest.Write84_AuthnRequest(Object o)

  • check the following debugging screenshot

image

Is it possible to use this with JWT tokens?

My app uses JWT authentication. How should be the flow if I use this with JWT? Like;

Once a SAML end point gets data (I am using IdP initiated, so I expect the SAML responce from IdP), I should create a token and forward it to my client (Angular app?)

How to make this work behind loadbalancer

I am using (a fork) of this library, but I don't know how to makes it work behind a load-balancer. Is it possible to make it work or do I need to somehow always return to the same server behind the load-balancer?

Assertion Response on Edge

I am receiving 'Empty protocol message id is not allowed.' message on Microsoft Edge browser and am unsure what settings are blocking the response from being consumed when it works great on firefox and chrome?

HTTP Redirect Binding uses HTTP 301 instead of HTTP 302

When performing an SP initiated sign on, the redirect that is given (in SamlService.cs) uses HTTP code 301, which is a permanent redirect. Most browsers cache these responses for a very long time, and so the next time the browser accesses the server, they reuse the old redirect with a possibly-expired AuthRequest. The SAML spec requires that either a 303 or 302 redirect should be used (see https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf page 19)

the HTTP status MUST be either 303 or 302.

On a related point, the spec also recommend the use of cache headers to prevent caching as quoted below.

When returning SAML protocol messages using HTTP 1.1, HTTP responders SHOULD:
• Include a Cache-Control header field set to "no-cache, no-store".
• Include a Pragma header field set to "no-cache".

documentation - for guidance

Hi,
I am a newbie for SAML. Wondering, do u have any documentation that can guide me along using the project as the basis.

Thanks

[Question] - describe interactions with IDP

Kudos for creating this project.

Apologies if this request seems unreasonable!
Is it possible you would be able to provide a rough description (perhaps a wiki page?) of the interactions that take place when you click the "saml2" login button on the website - in terms of http requests / responses / redirects? For example, does the server make a synchronous request to sustainsys and await a response with the assertion (I am guessing not..)? I'm just wondering how sustainsys is able to post the assertion back to the site that is running on localhost - or does this happen with a cookie / url parameter that it sets and then does response redirect from sustainsys back to localhost?

In addition to that, a description of anything that is stored in temporary session / state whilst the process is ongoing (i.e if anything is cached in memory or on disk - so we know if the process doesn't complete, what happens to this state - does it expire from the cache etc?)

P.S Many thanks for creating this, it looks awesome. I'm only experimenting with SAML at this stage (I have an OAUTH background and now a user story has appeared on my companies backlog involving SAML so i thought I'd get a head start on some research!)

Doesn't work with netcoreapp3.1

We tried using your package from a core 3.1 project, but it fails during start up. I've upgraded both your projects, but haven't been able to test it properly, apart from verifying that we can start the webapp and that the package doesn't fail during startup anymore.

Saml2Handler.ServiceProviderConfiguration is null

I've followed the instruction and the demo project but i always get a NullReferenceException because of the ServiceProviderConfiguration is null
Any suggestion?

System.NullReferenceException: Object reference not set to an instance of an object.
   at Saml2.Authentication.Core.Authentication.Saml2Handler.HandleSignIn()
   at Saml2.Authentication.Core.Authentication.Saml2Handler.HandleRequestAsync()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Question: act as saml2 identity provider?

Will I be able to use this library as a saml2 identity provider?
I would like to create a custom bridge between OpenID Connect and saml2. So the user wants to login to some service that supports saml2 for SSO. They will be redirected to my dotnet core application. That logs the user in (if not already logged-in) through OpenID Connect, and then the user is redirected back to the external service where it’s logged-in with the saml2 protocol

Saml2Exception: Assertion signature could not be verified

Hi!
First of all, thank you to made this amazing project to handle saml authentication!

Second, i have this exception:
image

The xml saml response sent by azure looks ok, but somehow fail in this point i can´t figute out why.

Can you help me please ?

Base64 decoding of RelayState

I am currently experiencing an error in my service provider, when signing in. Specifically when the middleware is processing a RelayState-parameter:

System.FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
at System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength)
at System.Convert.FromBase64String(String s)
at Saml2.Authentication.Core.Extensions.SamlStringExtensions.DeflateDecompress(String value)
at Saml2.Authentication.Core.Bindings.HttpRedirectBinding.GetResponse()
at Saml2.Authentication.Core.Services.SamlService.ReceiveHttpRedirectAuthnResponseAsync(String initialRequestId)
at Saml2.Authentication.Core.Authentication.Saml2Handler.HandleSignIn()
at Saml2.Authentication.Core.Authentication.Saml2Handler.HandleRequestAsync()

After comparing the SAML2 implementation to the standard, I found a possible discrepancy.
The code calls the DeflateDecompress() on RelayState, which is implemented to deflate and thereby base64-decode the value:

RelayState = form[SamlRelayStateQueryKey].ToString()?.DeflateDecompress()

However, looking in the SAML2 standard I see the following:

3.6.3.2 URL Encoding
...
If a “RelayState” value is to accompany the SAML artifact, it MUST be URL-encoded and placed in an
additional query string parameter named RelayState.

3.6.3.3 Form Encoding
...
If a “RelayState” value is to accompany the SAML artifact, it MUST be placed in an additional hidden form
control named RelayState, within the same form with the SAML message

Only the SAMLResponse is mentioned in the standard to be base64-encoded, not the separate RelayState-parameter in the query/formbody. (Note that there can also be a separate RelayState-parameter inside the SAMLResponse, which is of cause implicitly base64-encoded)

I would love some input on this, since I am not that familiar with SAML, and I might have missed something in the standard.

Saml Assertion signature verification can be fooled

The signature handling code does not pass the list of signed xml fragments to the signature reader in any way. It can be fooled by a document constructed as follows:

<!-- envelope omitted for brevity -->
<samlp:Assertion>
     <samlp:Assertion>
        <!-- original signature here -->
     <samlp:Assertion>
     <!-- whatever you want -->
</samlp:Assertion>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.