Showing posts with label active-directory. Show all posts
Showing posts with label active-directory. Show all posts

Tuesday, July 10, 2018

ADFS Federation Authentication does not return token and does not save cookie

Leave a Comment

We use WsFederation Authentication with an ADFS server. Most applications that we wrote work with the code below (excluded the debugging code of course) but my application just doesn't want to work.

I get a redirect to the loginpage on the AD server just fine and can enter UserId and Password without any problems but on return there should be a cookie saved but it isn't. Result is that on the next roundtrip the redirect happens again (this time without the login form though).

The debug code only hits the RedirectToIdentityProvider. None of the other is called.

The code is in the Startup.cs for OWIN.

private static void ConfigureAuth(IAppBuilder app, ISettings settings) {     app.SetDefaultSignInAsAuthenticationType(WsFederationAuthenticationDefaults.AuthenticationType);      // Work-around to fix Katana issue 197: https://katanaproject.codeplex.com/workitem/197     // https://github.com/KentorIT/owin-cookie-saver     app.UseKentorOwinCookieSaver();     app.UseCookieAuthentication(new CookieAuthenticationOptions     {         AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType     });      app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions     {         Wtrealm = settings.WsFedRealm,         MetadataAddress = settings.WsFedMetadataUrl,         TokenValidationParameters = new TokenValidationParameters         {             NameClaimType = ClaimsExtensions.WurNameIdentifier,             SaveSigninToken = true,             // ValidIssuer = settings.ValidIssuer         },         Notifications = new WsFederationAuthenticationNotifications         {             MessageReceived = context =>             {                 Log.Info($"Message received {context.ProtocolMessage}");                 return Task.FromResult(0);             },             RedirectToIdentityProvider = context =>             {                 Log.Info($"Redirect to identity provider {context?.Request?.Uri?.AbsolutePath}");                 return Task.FromResult(0);             },             SecurityTokenValidated = context =>             {                 Log.Info("Security token validated");                 return Task.FromResult(0);             },             SecurityTokenReceived = context =>             {                 Log.Info($"SecurityTokenReceived {context?.Response?.ReasonPhrase}");                 return Task.FromResult(0);             },             AuthenticationFailed = context =>             {                 Log.Error($"Authentication failed Uri:{context.Request?.Uri} User:{context.Request?.User?.Identity?.Name}");                 context.HandleResponse();                 context.Response.Redirect("~/Error?message=" + context.Exception.Message);                 return Task.FromResult(0);             }         }     });      app.SetDefaultSignInAsAuthenticationType(WsFederationAuthenticationDefaults.AuthenticationType);     AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name; } 

1 Answers

Answers 1

I think, the problem is, that both Middlewares have the AuthenticationMode Active

I recommend an custom controller. If the user visits this controller you must trigger the Authentication on the OwinContext.Authentication manually for the WsFederationAuthenticationDefaults.AuthenticationType and return an 401. That should trigger the ApplyResponseChallengeAsync in the WsFederationAuthenticationHandler

In the SecurityTokenValidated Method on the WsFederationAuthenticationOptions.Notifications you can issue a new AuthTicket with an identity of type CookieAuthenticationDefaults.AuthenticationType.

Now the identity from the identity provider is converted to a an local identity with cookieauth.

Read More

Friday, December 22, 2017

How to remove Zoom Button From Azure B2C in Xamarin Forms

Leave a Comment

I have azure b2c running in my xamarin forms app, its work well but the ui customization is so limit. And I have one problem in the login page, signup page and forgot password it has Zoom button in the bottom of corner which is so annoying because sometime when I'm trying to press the signup its press the zoom button instead.

And how I can remove this button because I can't find any setting in the azure b2c i'm using wingtip as my Azure b2c layout from here is that because css i use from wingtip template or its because the azure b2c setting or something here is the example. Can I remove this?

enter image description here

1 Answers

Answers 1

I Found the answer just add this <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/> in your html Templae instead of make custom renderer in your xamarin forms and it works perfectly

Read More

Sunday, November 26, 2017

Why do accountExpires and userAccountControl filters in SpringLDAP / plain Java AD queries do not work as expected?

Leave a Comment

I'm using SpringLDAP API within spring based webapp to query ActiveDirectory that is hosted on Windows Server 2012. Following are my environment details :- Java 1.8.0_101, apache-tomcat-8.0.36, SpringMVC 4.3.1 & SpringLDAP 2.3.1

The following AD filter query works in windows based (may be C++/C#) query tool (e.g., Lepide AD Query tool) and also in the LDAP Browser plugin within the eclipse IDE but does not work within the Java (JNDI/SpringLDAP API based) code & in the Java based application JXplorer :-

(&(objectclass=user)(objectCategory=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(accountExpires>=131554368000000000)(userPrincipalName=cgm@*)) 

I'm trying to get an user account that is ACTIVE, not yet expired given a date and with userPrincipalName value starting with string cgm@.

Following is the ldap configuration within the spring-servlet.xml file :-

<util:map id="ldapBaseEnvProps">         <entry key="java.naming.ldap.attributes.binary" value="objectGUID"/> </util:map> <ldap:context-source id="pooledLdapContextSrc" url="ldap://dc.myadserver.com:3268" base="DC=myadserver,DC=com" username="CN=adusername,OU=Mkt-Managers,DC=myadserver,DC=com" password="*****" base-env-props-ref="ldapBaseEnvProps">     <ldap:pooling max-total="16" max-active="16" max-idle="8" min-idle="0" max-wait="90000" when-exhausted="BLOCK" test-on-borrow="true" test-while-idle="true"/> </ldap:context-source> 

Are such AD filters supported by Java/SpringLDAP API at all? If yes, what needs to be changed for the above AD query filter to work in the Java based code?

1 Answers

Answers 1

I would suggest using Spring LDAP's query builder object in Java to help you build that query. Your question seems to indicate that you copied that query from your C (windows) environment into your Java environment.

I would start by building the query with .where() function in Spring LDAP as used here and seeing if it results in the same error: https://docs.spring.io/spring-ldap/docs/current/apidocs/org/springframework/ldap/query/LdapQueryBuilder.html

Read More

Wednesday, August 2, 2017

Why does Spring LDAP's LdapTemplate not return title, department & company attributes?

Leave a Comment

I'm using spring-ldap-core-2.3.1.RELEASE.jar over JDK 1.8 & Tomcat 8.0 to access AD information through LdapTemplate. The attributes such as title,department & company are not being returned by the ldapTemplate.search(..,.,..) method.

I'm using the following lines of code to search :-

LdapQuery ldapQuery = LdapQueryBuilder.query()                                        .where("objectclass").is("user")                                        .and("objectcategory").is("person")                                        .and("cn").like(strWildcardText+"*"); ldapTemplate.search(ldapQuery, new ADUserAttributesMapper()); 

Following is the ADUserAttributesMapper class :-

public class ADUserAttributesMapper implements AttributesMapper<ADUserBean> {     @Override     public ADUserBean mapFromAttributes(Attributes attributes) throws NamingException {         if(attributes==null) {             return null;         }          adUserBean.setName((attributes.get("name")!=null) ? attributes.get("name").get().toString() : null);         adUserBean.setCommonName((attributes.get("cn")!=null) ? attributes.get("cn").get().toString() : null);         adUserBean.setDisplayName((attributes.get("displayname")!=null) ? attributes.get("displayname").get().toString() : null);         adUserBean.setGivenName((attributes.get("givenname")!=null) ? attributes.get("givenname").get().toString() : null); // for FIRST NAME         adUserBean.setMiddleName((attributes.get("initials")!=null) ? attributes.get("initials").get().toString() : null); // for MIDDLE NAME / INITIALS         adUserBean.setLastName((attributes.get("sn")!=null) ? attributes.get("sn").get().toString() : null); // for LAST NAME         adUserBean.setDepartment((attributes.get("department")!=null) ? attributes.get("department").get().toString() : null);         adUserBean.setUserPrincipalName((attributes.get("userprincipalname")!=null) ? attributes.get("userprincipalname").get().toString() : null); // Logon Name         adUserBean.setsAMAccountName((attributes.get("samaccountname")!=null) ? attributes.get("samaccountname").get().toString() : null); // Logon Name (pre-Windows 2000)         adUserBean.setDistinguishedName((attributes.get("distinguishedname")!=null) ? attributes.get("distinguishedname").get().toString() : null);         adUserBean.setMailID((attributes.get("mail")!=null) ? attributes.get("mail").get().toString() : null);         adUserBean.setTitle((attributes.get("title")!=null) ? attributes.get("title").get().toString() : null); // Job Title         adUserBean.setTelephoneNumber((attributes.get("telephonenumber")!=null) ? attributes.get("telephonenumber").get().toString() : null);         adUserBean.setObjectCategory((attributes.get("objectcategory")!=null) ? attributes.get("objectcategory").get().toString() : null);          return adUserBean;     } } 

The title,department & company attributes belong to the Organization tab of the AD user properties as shown in the below image :- enter image description here

Also, from the General tab the initials(initials) attribute is not being picked up/listed by Spring-LDAP's ldapTemplate. The LdapQueryBuilder.query() object has access to attributes(...) method that takes a string array of attribute names that are to be fetched. But even after mentioning them there explicitly, values for attributes such as initials, title, department & company are not returned.

The LDAP Browser plugin within the Eclipse IDE lists the title,department & company properties under the Organization tab without a problem.

Even the com4j API returns the title, department & company attributes.

Is there any configuration that is limiting the attribute(s) listing or is it a limitation with Spring-LDAP API itself? Are these attributes not part of BasicAttributes? How to fetch these attributes through Spring-LDAP?

UPDATE (01-Aug-2017): The plain Java JNDI approach/code does NOT return department,company,title attributes (even with these attributes being explicitly mentioned in attributes string array), but surprisingly it does return the initials attribute value.

UPDATE (02-Aug-2017): Similar to @Pierre's suggestion tried the following code using SearchControls object :-

String strFilter= "(&(objectclass=top)(cn=cgma*))"; String[] attrs = new String[] {"cn","givenName","sn","initials","title","department","company"}; long maxResults = 10; // for example  SearchControls searchControls = new SearchControls(); searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); searchControls.setReturningAttributes(attrs); searchControls.setCountLimit(maxResults); List<String> aLstOfADUsers = ldapTemplate.search("",strFilter,searchControls,new AttributesMapper<String>()                                                                       {                                                                         public String mapFromAttributes(Attributes attrs) throws NamingException {                                                                             try                                                                             {                                                                                 System.out.println(attrs.toString());                                                                                 return attrs.get("cn").get().toString();                                                                             }                                                                             catch(Exception ex) {                                                                                 ex.printStackTrace();                                                                                 return null;                                                                             }                                                                         }                                                                      });  return aLstOfADUsers; 

Even this does not return the initials, title, company & department attribute values.

2 Answers

Answers 1

The person attributes might be internal attributes which you wouldn't get back by default. You can specify explicitly which attributes you want returned BUT not in the search method you're using (the one where you pass in an LdapQuery object). If you take a look at the org.springframework.ldap.core.LdapTemplate class, it doesn't seem like you can pass in the SearchControls object to the method signature you're using. So, to be able to specify attributes to fetch, replace this:

LdapQuery ldapQuery = LdapQueryBuilder.query()                                        .where("objectclass").is("user")                                        .and("objectcategory").is("person")                                        .and("cn").like(strWildcardText+"*"); ldapTemplate.search(ldapQuery, new ADUserAttributesMapper()); 

With this:

        LikeFilter filter = new LikeFilter("cn", strWildcardText+"*");          // list of attributes to retrieve         String[] attrs = new String[] {"title","department","company"};         long maxResults = 10; // for example           SearchControls searchControls = new SearchControls();         searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);         searchControls.setReturningAttributes(attrs);         searchControls.setCountLimit(numResults);          ldapTemplate.search(DistinguishedName.EMPTY_PATH, filter.encode(), searchControls, new ADUserAttributesMapper()); 

The above should work. You could also try something like this (I haven't tried that yet):

ldapTemplate.search( "dc=yourorg,dc=com",          "(&(cn=" +strWildcardText + "*)(&(objectClass=person)(objectcategory=person)))",         SearchControls.SUBTREE_SCOPE,         new String[]{ "title","department","company" },         new ADUserAttributesMapper() ); 

Finally, to get ALL attributes back, ask to retrieve ALL attributes in the code above (my above example only asked for 3 attributes, this would return ALL of them):

        String[] attrs = new String[]{"*","+"}; 

Answers 2

This is based on your AttributesMapper. I don't know what ADUserAttributesMapper is, so you'd have to provide that implementation.

Here's the javadoc for this interface. http://docs.spring.io/spring-ldap/docs/current/apidocs/org/springframework/ldap/core/AttributesMapper.html

Read More

Tuesday, June 27, 2017

IdentityServer and ADFS

Leave a Comment

I'm trying to setup IdentityServer to use ADFS for authentication. The flow will be:

User -> Custom app -> IS -> ADFS

I've setup almost everything, but I'm stuck at the communication between IS and ADFS. The user seems to login successfully in ADFS, but I get an error:

ID4037: The key needed to verify the signature could not be resolved from the following security key identifier 'SecurityKeyIdentifier

when I get back to IS.

It's obvious that there's an issue with the token signing certificates in one side or the other. I've tried unsuccessfully to find some documentation explaining the relation between different certificates.

Right now I have a self signed certificate in IS that is signing tokens (set up using SigningCertificate property of IdentityServerOptions) and I have a AD certificate configured in ADFS to sign tokens.

Is there any guide or recommendation on how to properly do it? Should it be the same in both or should I configure something else to make it work?

EDIT With Fiddler I can see that everything inside ADFS runs fine and the error is when the results are posted to IdentityServer. The XML posted in wresult param is:

<t:RequestSecurityTokenResponse xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">   <t:Lifetime>     <wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2017-06-20T12:25:31.148Z</wsu:Created>     <wsu:Expires xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2017-06-20T13:25:31.148Z</wsu:Expires>   </t:Lifetime>   <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">     <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">       <wsa:Address>urn:identityServer</wsa:Address>     </wsa:EndpointReference>   </wsp:AppliesTo>   <t:RequestedSecurityToken>     <saml:Assertion MajorVersion="1" MinorVersion="1" AssertionID="_fd1a14cd-4d18-407b-97d4-9f9dfcacd29a" Issuer="http://ssosrv.mydomain.com/adfs/services/trust" IssueInstant="2017-06-20T12:25:31.148Z" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion">       <saml:Conditions NotBefore="2017-06-20T12:25:31.148Z" NotOnOrAfter="2017-06-20T13:25:31.148Z">         <saml:AudienceRestrictionCondition>           <saml:Audience>urn:identityServer</saml:Audience>         </saml:AudienceRestrictionCondition>       </saml:Conditions>       <saml:AttributeStatement>         <saml:Subject>           <saml:NameIdentifier>user@mydomain.com</saml:NameIdentifier>           <saml:SubjectConfirmation>             <saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</saml:ConfirmationMethod>           </saml:SubjectConfirmation>         </saml:Subject>         <saml:Attribute AttributeName="emailaddress" AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">           <saml:AttributeValue>name.surname@mydomain.tv</saml:AttributeValue>         </saml:Attribute>         <saml:Attribute AttributeName="name" AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">           <saml:AttributeValue>Name Surname</saml:AttributeValue>         </saml:Attribute>         <saml:Attribute AttributeName="upn" AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">           <saml:AttributeValue>user@mydomain.com</saml:AttributeValue>         </saml:Attribute>       </saml:AttributeStatement>       <saml:AuthenticationStatement AuthenticationMethod="urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" AuthenticationInstant="2017-06-20T12:25:31.039Z">         <saml:Subject>           <saml:NameIdentifier>user@mydomain.com</saml:NameIdentifier>           <saml:SubjectConfirmation>             <saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</saml:ConfirmationMethod>           </saml:SubjectConfirmation>         </saml:Subject>       </saml:AuthenticationStatement>       <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">         <ds:SignedInfo>           <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />           <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />           <ds:Reference URI="#_fd1a14cd-4d18-407b-97d4-9f9dfcacd29a">             <ds:Transforms>               <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />               <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />             </ds:Transforms>             <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />             <ds:DigestValue>6CeXXXXXXXXXXXXXXXXXXXX=</ds:DigestValue>           </ds:Reference>         </ds:SignedInfo>         <ds:SignatureValue>q9hJBFFFFFFFFFFFFFFFFFFFF==</ds:SignatureValue>         <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">           <X509Data>             <X509Certificate>MIIFnzXXXXXXXXXXXXXXXXXXXX</X509Certificate>           </X509Data>         </KeyInfo>       </ds:Signature>     </saml:Assertion>   </t:RequestedSecurityToken>   <t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType>   <t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>   <t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType> </t:RequestSecurityTokenResponse> 

Thank you, Albert

2 Answers

Answers 1

Solved it. It was not related with ADFS integration, but how I had setup federation authentication in Identity Server. I was using two federation authentication identity providers: this one with ADFS and another using WinAuth. Without a callback the response from ADFS was being handled by WinAuth, so I configured different callbacks for each of them and it's working.

Answers 2

From memory:

  • You need to convert the IS certificate to .cer format.
  • In mmc, right click on the certificate and “All Tasks” / “Export”.
  • Click through Export Wizard selecting: “No, do not export the private key”. “DER encoded binary X.509 (.CER)”.
  • Select file name to export to and “Save”.
  • Review options and “Finish”.
  • In the ADFS wizard, import the .cer file into the Certificates tab.
Read More

Thursday, June 15, 2017

Active directory azure, handle sign in “access_denied” error using custom error page?

Leave a Comment

I have got below error while sign in the user who is not assign to the webapp. I want to display custom error page instead of this.

An error of type 'access_denied' occurred during the login process: 'xyz121': User account is disabled. Trace ID: xyz121 Correlation ID: xyz Timestamp: 2015-05-18 05:51:16

0 Answers

Read More

Sunday, June 11, 2017

Authenticating ldap user with multiple suffix value/domain

Leave a Comment

I am trying to authenticate and then query AD tree using Spring Ldap Security and Spring Ldap.

Following is my configuration file -

<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/security"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"      xmlns:beans="http://www.springframework.org/schema/beans"     xmlns:ldap="http://www.springframework.org/schema/ldap"     xmlns:util="http://www.springframework.org/schema/util"     xsi:schemaLocation="         http://www.springframework.org/schema/security          http://www.springframework.org/schema/security/spring-security-3.2.xsd         http://www.springframework.org/schema/beans          http://www.springframework.org/schema/beans/spring-beans-3.1.xsd         http://www.springframework.org/schema/context          http://www.springframework.org/schema/context/spring-context.xsd          http://www.springframework.org/schema/ldap          http://www.springframework.org/schema/ldap/spring-ldap.xsd         http://www.springframework.org/schema/util          http://www.springframework.org/schema/util/spring-util.xsd">      <http use-expressions="true">         <form-login login-page="/myApp/ldap" default-target-url="/myApp/ldap/config"             authentication-failure-url="/myApp/ldap?error=true" />         <logout />     </http>      <beans:bean         class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">         <beans:property name="location">             <beans:value>classpath:/ldap.properties</beans:value>         </beans:property>         <beans:property name="SystemPropertiesMode">             <beans:value>2</beans:value>         </beans:property>     </beans:bean>      <beans:bean id="adAuthenticationProvider" scope="prototype"         class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">         <!-- the domain name (may be null or empty). If no domain name is configured, it is assumed that the username will always contain the domain name. -->         <beans:constructor-arg index="0" value="${sample.ldap.domain}" />         <!-- an LDAP url (or multiple URLs) -->         <beans:constructor-arg index="1" value="${sample.ldap.url}" />         <!-- Determines whether the supplied password will be used as the credentials in the successful authentication token. -->         <beans:property name="useAuthenticationRequestCredentials"             value="true" />         <!-- by setting this property to true, when the authentication fails the error codes will also be used to control the exception raised. -->         <beans:property name="convertSubErrorCodesToExceptions"             value="true" />     </beans:bean>      <authentication-manager erase-credentials="false">         <authentication-provider ref="adAuthenticationProvider" />     </authentication-manager>      <beans:bean         class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">         <beans:property name="location">             <beans:value>classpath:/ldap.properties</beans:value>         </beans:property>         <beans:property name="SystemPropertiesMode">             <beans:value>2</beans:value> <!-- OVERRIDE is 2 -->         </beans:property>     </beans:bean>      <ldap:context-source id="contextSource"                           url="${sample.ldap.url}"                          base="${sample.ldap.base}"                           referral="follow"                          authentication-source-ref="authenticationSource"                           base-env-props-ref="baseEnvironmentProperties"/>      <util:map id="baseEnvironmentProperties">         <beans:entry key="com.sun.jndi.ldap.connect.timeout" value="60000" />         <beans:entry key="java.naming.ldap.attributes.binary" value="objectGUID objectSid"/>     </util:map>      <beans:bean id="authenticationSource"         class="org.springframework.security.ldap.authentication.SpringSecurityAuthenticationSource" />      <ldap:ldap-template id="ldapTemplate"         context-source-ref="contextSource" />  </beans:beans> 

And property file is -

sample.ldap.url=ldap://xxx.xxx.xxx.xxx:3268 sample.ldap.base=dc=example,dc=com sample.ldap.clean=true sample.ldap.directory.type=AD sample.ldap.domain=example.com 

These setting works fine for following login -

username - example@example.com or example Password - blah

but fails when i try - username - example2@example.net or example Password - blah2

These both are valid logins, and have been validated by login using AD Explorer.

Seems like i need to update my configuration to support UPN suffix/domains as default works fine and other do not.

Is there a way i can append to this config file to support this logic, supporting authenticating/querying multiple domains?

2 Answers

Answers 1

To explain @NewBee's solution:

1 ActiveDirectoryLdapAuthenticationProvider:

  • Specialized LDAP authentication provider which uses Active Directory configuration conventions.
  • It will authenticate using the Active Directory userPrincipalName or sAMAccountName (or a custom searchFilter) in the form username@domain. If the username does not already end with the domain name, the userPrincipalName will be built by appending the configured domain name to the username supplied in the authentication request. If no domain name is configured, it is assumed that the username will always contain the domain name.
  • The user authorities are obtained from the data contained in the memberOf attribute.

2 LDAP authentication in Spring Security:

  • Obtaining the unique LDAP Distinguished Name, or DN, from the login name.

    • This will often mean performing a search in the directory, unless the exact mapping of usernames to DNs is known in advance. So a user might enter his/her name when logging in, but the actual name used to authenticate to LDAP will be the full DN, such as uid=(username),ou=users,dc=springsource,dc=com.
  • Authenticating the user, either by binding as that user or by performing a remote compare operation of the user's password against the password attribute in the directory entry for the DN.

  • Loading the list of authorities for the user.

For a reference to How to configure multiple UPN Suffixes. Plus in ActiveDirectoryLdapAuthenticationProvider you could write a function for reading multiple suffixes, as the library is adaptable.

Answers 2

Reason it's not allowing me to login with configured UPN suffix is because ActiveDirectoryLdapAuthenticationProvider seems to be making assumption that UPN suffix is always same as Domain Name.

Please refer this post - https://github.com/spring-projects/spring-security/issues/3204

I think there should be a better way to handle this though, or maybe better library for authentication.

Read More

Tuesday, April 11, 2017

How to query Active Directory B if application server is in Active Directory A

Leave a Comment

So heres my question. I have a Asp.net application with a form based authentication. I have users in my database but the users also has to be in the active directory.

The following code is for me to check if user is in the domain A

            DirectoryEntry de = new DirectoryEntry();             de.Path = "LDAP://domainA.com";             de.AuthenticationType = AuthenticationTypes.None;             DirectorySearcher search = new DirectorySearcher(de);             search.Filter = "(SAMAccountName=" + account + ")";             search.PropertiesToLoad.Add("displayName");              SearchResult result = search.FindOne(); 

This code work fine. The problem is client is requesting that domain B should also be able to connect to the application. So created the following code:

            DirectoryEntry de = new DirectoryEntry();             de.Path = "LDAP://domainB.com";             de.AuthenticationType = AuthenticationTypes.None;             DirectorySearcher search = new DirectorySearcher(de);             search.Filter = "(SAMAccountName=" + account + ")";             search.PropertiesToLoad.Add("displayName");              SearchResult result = search.FindOne(); 

Since my server is in domainA this does not work. Is there a way for me to query domainB knowing that the server is in domainA? I found an article saying trust needs to be setup for domainA and B but this domains shouldnt be linked. Its only for this application that they need this functionality.

P.S. I might forgot to explain an important detail. domainA and B are not on the same network. But domainA can ping domainB

2 Answers

Answers 1

You will need to provide credentials that have permission to query AD on domain B.

var de = new DirectoryEntry("LDAP://domainB.com", "Username", "Password"); var search = new DirectorySearcher(de); 

Answers 2

While trying samples against a foreign domain, I noticed that the foreign DC is giving the error message "The server is unavailable" when using the wrong authentication type. Please try:

de.User = @"DOMAINB\user"; de.Password = "YourPassword"; de.AuthenticationType = AuthenticationTypes.None; 

Of course this results in an unsecured BASIC simple bind, which removes any encryption ADSI might offer. If this works, you should try a more secure authentication type that the server accepts.

An alternative might be using the "System.DirectoryServices.Protocols"-namespace which offers a more lightweight approach for AD access. I can provide you with a sample I you want to go in this direction.

Read More

Tuesday, March 7, 2017

How to set/change Active Directory user password across domains using C# .NET?

Leave a Comment

I have been searching around for quite some time now how to set/change a password and revoke/restore a user but have yet to find a solution that actually works for me.

I am beginning to lean towards the fact that I am crossing domains as the problem, even though I can programmatically create/delete/update and even connect/disconnect users from groups.

Basically, I've tried the following ways:

DirectoryEntry account = new DirectoryEntry("LDAP://" + adHostname + "/" + dn, adUserName, adPassword);  account.Invoke("SetPassword", "Password1"); account.Properties["LockOutTime"].Value = 0; account.CommitChanges(); 

And also

account.Invoke("SetPassword", new object[] { "Password1" }); 

They both ultimately throw the error "One or more input parameters are invalid\r\n"

I then have tried to use the .NET 3.5 approach using principal context.

using (var context = new PrincipalContext(ContextType.Domain, adHostname, myContainer, ContextOptions.SimpleBind, adUserName, adPassword))     {         using (var user = UserPrincipal.FindByIdentity(context, account.Properties["sAMAccountName"].Value.ToString()))         {              user.SetPassword(password);         }     }     

This approach is also throwing the same error as above. If I switch some things around (I can't seem to remember all the combinations I've tried), it will sometimes throw a "Local error has occurred" COM Exception.

Any help is much appreciated.

1 Answers

Answers 1

See this article: https://www.codeproject.com/Articles/18102/Howto-Almost-Everything-In-Active-Directory-via-C#7

You'll notice in all the samples that we're binding directly to the directoryEntry and not specifying a server or credentials. If you do not want to use an impersonation class you can send credentials directly into the DirectoryEntry constructor. The impersonation class is helpful for those times when you want to use a static method and don't want to go through the trouble of creating a DirectoryContext object to hold these details. Likewise you may want to target a specific domain controller.

Target Specific Domain Controllers or Credentials

Everywhere in the code that you see: LDAP:// you can replace with LDAP://MyDomainControllerNameOrIpAddress as well as everywhere you see a DirectoryEntry class being constructed you can send in specific credentials as well. This is especially helpful if you need to work on an Active Directory for which your machine is not a member of it's forest or domain or you want to target a DC to make the changes to.

//Rename an object and specify the domain controller and credentials directly

public static void Rename(string server,     string userName, string password, string objectDn, string newName) {     DirectoryEntry child = new DirectoryEntry("LDAP://" + server + "/" +          objectDn, userName, password);     child.Rename("CN=" + newName); } 
Read More

Wednesday, May 4, 2016

Generate GPO Report From Untrusted Domain

Leave a Comment

I'm calling LogonUser with LOGON_TYPE_NEW_CREDENTIALS and LOGON32_PROVIDER_WINNT50 to get my thread to impersonate a user in the other domain. I'm able to connect to remote file shares and everything else just fine into the untrusted domain.

The problem I'm running into now is when I use GPMGMTLib to generate a GPO report I keep getting exception "HRESULT: 0x80072020" when it calls GenerateReport().

using GPMGMTLib; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text;  namespace CrossDomainWork {     class Program     {         static void Main(string[] args)         {             ImpersonationContext context = new ImpersonationContext("ourdmzdomain.com", "dmzuser", "dmzpassword");             context.Start();              GPM gpm = new GPM();             var constants = gpm.GetConstants();             var domain = gpm.GetDomain("ourdmzdomain.com", "", constants.UseAnyDC);             var gpo = domain.GetGPO("{31B2F340-016D-11D2-945F-00C04FB984F9}");             object missing = Type.Missing;             var result = gpo.GenerateReport(GPMReportType.repHTML, ref missing, out missing).Result;              context.Stop();         }     } } 

1 Answers

Answers 1

I have no experience here, so this is just a guess.

Looking at the documentation for GenerateReport, the last two parameters are pvarGPMProgress (for reporting progress), and pvarGPMCancel (some kind of cancellation token).

You are passing the same object for both. I wonder if that's what's making it choke. You can try creating a second object.

Maybe it's also possible that it doesn't like getting Type.Missing as the value. You can try just setting them to null.

Also, does the group policy have any special permissions on it?

What namespace is that ImpersonationContext in that you're using? I can't find it. We do have an untrusted domain at work that I can test with, if I can get your code to compile.

Edit: If you have SetLastError = true in your DllImport statements, then you can use Marshal.GetLastWin32Error() to get some additional details. For example:

try {     result = gpo.GenerateReport(GPMReportType.repHTML, ref missing, out missing).Result; } catch {     var win32 = new Win32Exception(Marshal.GetLastWin32Error());     Console.Write(win32.Message); } 

For me, it tells me

An attempt was made to reference a token that does not exist

Which doesn't solve the puzzle, but it's another piece to the puzzle.

Read More

Sunday, March 20, 2016

ADFS authentication and impersonation from a Java (Spring MVC under Jetty) application

Leave a Comment

I have a Java web app which provides a search service, and in some cases needs to check security for results. If it matters, it's implemented in Spring MVC and running under jetty.

I have a customer who would like the web app's authentication to:

  • Be done via Active Directory Federation Services (ADFS) instead of the existing build-in mechanism (to avoid a seperate login).
  • Be able to impersonate the remote user on the search server, such that security checks can be performed by a executing a seperate application on the search server (which doesn't itself know anything about ADFS, but is able to perform the relevant checks when run as the user in question).

It this possible, and if so, how?

(Apologies if the Windows world terminology is a bit off - it's not something I know much about, but hopefully at least the intention is clear)


A few notes on pieces of the puzzle I've already looked at:

  1. Impersonating a user from a Java Servlet, is a question I had a number of years ago covering roughly the same ground, but without the ADFS requirement - I'm not sure how ADFS impacts things, but Waffle (the solution for that question) doesn't seem to provide any support for it.
  2. I've seen Java application with SSO (SAML) and ADFS and How do I talk to ADFS from Java?, which seem to provide a way forward for the ADFS authentication, but I'm unsure if that is compatible with subsequent impersonation.
  3. I've seen http://blogs.objectsharp.com/post/2010/09/10/Converting-Claims-to-Windows-Tokens-and-User-Impersonation.aspx and https://msdn.microsoft.com/en-au/library/ee517278.aspx but I'm unsure:

    1. If I'll have access to the necessary claims to do this if I follow the SAML or OAuth path above
    2. Whether it's possible to implement that from within Java
  4. I think the second (impersonation) part is roughly the same as Impersonating ASP.NET claims identity to windows identity, except that I want to do it from within Java rather than .Net.

2 Answers

Answers 1

You don't mention the ADFS version?

You have three choices:

  • WS-Fed
  • SAML
  • OAuth2

In the Java world, SAML is normally used. Which implies a SAML stack.

The SO link above has an answer from me with links to a list of SAML stacks.

Since you are already using Spring, Spring Security seems a good fit.

Spring Security SAML Extension

ADFS currently does not support OpenID Connect which rules OAuth out.

Yes - Spring Security provides you with a list of the claims generated by ADFS.

ADFS does provide impersonation via Identity Delegation.

Unfortunately, this is typically done via WCF and WIF (both .NET constructs).

Answers 2

I have a similar application- mine is a Swing client rather than web application, but the process should be similar. It needs to submit queries under an assumed role using an AWS API after first authenticating with an on-premise ADFS server. In our environment, ADFS has been configured to give out SAML assertions and AWS has been configured to recognise these. So, this is what I do:

  1. When required, the application prompts the user for their usual network credentials and these are used to request a SAML assertion from ADFS. I use Apache HttpClient to make the call:

    private String getAdfsResponse(String username, String password) throws Exception {  log.debug("Trying to log onto ADFS server for {}", username);  // Lax redirect policy is needed so that all HTTP 302 redirects are followed after hitting the initial ADFS URL. try (CloseableHttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build()) {      HttpUriRequest login = RequestBuilder.post()             .setUri(new URI(ADFS_URL))             .addParameter("UserName", username)             .addParameter("Password", password)             .build();      CloseableHttpResponse response = httpClient.execute(login);      if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {          HttpEntity responseEntity = response.getEntity();         String adfsResponse = EntityUtils.toString(responseEntity, "UTF-8");         log.debug("ADFS server responded with {}", adfsResponse);         return adfsResponse;      } else {          throw new Exception("ADFS server responded with " + response.getStatusLine());      } } } 
  2. If the credentials are validated, ADFS returns a SAML response that looks like an HTML form but contains a input element with a SAMLResponse name/value pair.

  3. When the SAMLResponse value attribute is base64-decoded it will contain the SAML assertion. For AWS, I need to extract some role information and I use this, along with the full SAMLResponse, to call the AWS STS (security token service). If all is OK with AWS, I receive a set of temporary security credentials that I can use for the queries I really want to make. The whole round trip is described in http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_saml.html

All this depends on ADFS and the other party being SAML-configured, and for the other party to provide a suitable API that lets you assume a particular role on their side. Is this the sort of thing you're facing?

Read More

Sunday, March 13, 2016

Grant Windows Easy Transfer permissions without Domain Administrator access

Leave a Comment

Currently we require Domain Administrator access to transfer a domain account between computers using Windows Easy Transfer. Is it possible to grant a user access to the Transfer without granting them full Domain rights?

Thanks.

0 Answers

Read More