Sustainsys / Saml2

Saml2 Authentication services for ASP.NET
Other
961 stars 602 forks source link

Using the library correctly #1463

Closed luctas closed 2 months ago

luctas commented 3 months ago

Hi, I'm learning the SAML standard and I'm trying to implement it in my software. Here is a part of my code public class UserAuthentication : IUserAuthentication { private readonly LogSplitter _logger; private HttpRequest _request; private string _serviceProviderEntityId; private string _returnUrl; private string _identityProviderEntityId; private string _identityProviderMetadataLocation; private string _organizationCode;

 public UserAuthentication(LogSplitter logger)
 {
     _logger = logger;
 }

 public void Configuration(IAppBuilder app, AuthParams parameters)
 {
     _serviceProviderEntityId = parameters["ServiceProviderEntityId"];
     _returnUrl = parameters["ReturnUrl"];
     _identityProviderEntityId = parameters["IdentityProviderEntityId"];
     _identityProviderMetadataLocation = parameters["IdentityProviderMetadataLocation"];
     _organizationCode = parameters["OrganizationCode"];
     app.UseSaml2Authentication(CreateSamlOptions());
 }

 private Saml2AuthenticationOptions CreateSamlOptions()
 {
     var samlOptions = new Saml2AuthenticationOptions(false) // "false" to specify that the module should not work as active
     {
         SPOptions = new Sustainsys.Saml2.Configuration.SPOptions
         {
             EntityId = new EntityId(_serviceProviderEntityId),
             ReturnUrl = new Uri(_returnUrl), // Should match the ACS endpoint
                                              // The certificate used for signing SAML requests.
         },
         AuthenticationType = "Saml2",
         SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie,
         AuthenticationMode = AuthenticationMode.Active
     };

     //samlOptions.SPOptions.ServiceCertificates.Add(new X509Certificate2("C:\\temp\\cert.pfx", "prova"));

     var idp = new IdentityProvider(new EntityId(_identityProviderEntityId), samlOptions.SPOptions)
     {
         // Set up the Identity Provider options as needed
         MetadataLocation = _identityProviderMetadataLocation, // Metadata URL
         LoadMetadata = true,
         // Configure more IdP options as needed, e.g., bindings, signing certificates
     };
     idp.SigningKeys.AddConfiguredKey(new X509Certificate2("C:\\temp\\cert.pfx", "prova"));
     // Add the identity provider to the samlOptions
     samlOptions.IdentityProviders.Add(idp);

     return samlOptions;
 }

 private AuthUserRequestInfo GetUserInfo(HttpContext context)
 {
     try
     {
         // Retrieve SAML attributes from the context
         var samlAttributes = context.Items["Saml2Attributes"] as Dictionary<string, string>;
         AuthUserRequestInfo userInfo;
         bool identificationCodeExists = samlAttributes.TryGetValue("IdentificationCode", out string identificationCode);
         userInfo = new AuthUserRequestInfo()
         {
             ProviderAuthentication = System.Reflection.Assembly.GetExecutingAssembly().FullName,
             IdentificationCode = identificationCode,
             OrganizationCode = _organizationCode
         };

         return userInfo;
     }
     catch(Exception ex)
     {
         _logger.Error("", $"Error during SAML2 authentication: {ex.Message}");
         return new AuthUserRequestInfo();
     }
 }

 public async Task<bool> SetClaims(HttpContext context, IAuthenticationManager authenticationManager, Func<string, string, string, bool, Task<UserDto>> getUser, UserApplication applicationType)
 {
     AuthUserRequestInfo userInfo = GetUserInfo(context);

     var useUserIdObjectOnly = false;
     // Fetch user credentials from the database
     UserDto user = await getUser(String.Empty, userInfo.IdentificationCode, userInfo.OrganizationCode, useUserIdObjectOnly);
     if (user == null) return false;
     // Verify credentials
     var profile = ProfileFilter.GetAuthority(applicationType, ((UserProfile)user.ProfileId).GetName());
     if (user.IsForbidden || profile == UserProfile.None) return false;

     var identity = new ClaimsIdentity(
         new[]
         {
             new Claim("UserId", user.UserId.ToString()),
             new Claim("FullName", user.FullName),
             new Claim("OrganizationCode", user.OrganizationCode),
             new Claim("Institute", user.Institute),
             new Claim("UserName", user.UserName),
             new Claim(ClaimTypes.Role, ((UserProfile)user.ProfileId).GetName()),
             new Claim("RoleCode", user.ProfileId.ToString())
         },
         DefaultAuthenticationTypes.ApplicationCookie);

     authenticationManager.SignIn(new AuthenticationProperties
     {
         AllowRefresh = true,
         IsPersistent = false
     }, identity);

     return true;
 }

 public void SignIn(HttpContext context, IAuthenticationManager authenticationManager)
 {
     // Trigger the SAML2 authentication process
     var properties = new AuthenticationProperties
     {
         RedirectUri = _returnUrl
     };

     // Challenge the SAML2 authentication
     try
     {
         // Challenge the SAML2 authentication
         authenticationManager.Challenge(properties, "Saml2");

         //authenticationManager.AuthenticateAsync("Saml2");
         _logger.Info("", () => "SAML2 sign-in challenge triggered.");
     }
     catch (Exception ex)
     {
         _logger.Error("", $"Error during SAML2 sign-in: {ex.Message}");
         throw;
     }

 }

 public void SignOut(IAuthenticationManager authenticationManager)
 {
     authenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
 }

}

In the SignIn method is it correct to call authenticationManager.Challenge(properties, "Saml2"); ? It doesn't redirect me to the IdP login page and I don't understand how to get logs/informations on what's happening.

Thanks