Menu

Authentication in android

Tom
2017-03-23
2017-03-23
  • Tom

    Tom - 2017-03-23

    For a native android app project for school I need the user to login using his SamAccountName and password. First I need to check wether the user exists and then query wether he is inside a group so that he has the rights to use the app. Do you guys have any sample code for this?

    I'm verry new to LDAP in general so any help is welcome :)

     
  • Neil Wilson

    Neil Wilson - 2017-03-23

    Well, I don’t want to do your homework for you, but I’ll definitely give you some tips. Also, Active Directory doesn’t always behave like a standard LDAP directory server, and I’m not super familiar with all of its ins and outs, but I think that I can at least get you on the right path.

    Using the UnboundID LDAP SDK for Java on Android isn’t really that much different from using it in regular Java. You can’t use certain SASL mechanisms because Android doesn’t support them (or, at least, it didn’t the last time I checked), but I doubt that’ll be an issue for you.

    In order to verify a user’s password, and also to ensure that the user is allowed to authenticate (that their account isn’t locked, their password isn’t expired, etc.), you need to perform a bind operation. And in this case, it’s almost certainly going to be a simple bind. The LDAP SDK supports this through the com.unboundid.ldap.sdk.SimpleBindRequest class (https://docs.ldap.com/ldap-sdk/docs/javadoc/index.html?com/unboundid/ldap/sdk/SimpleBindRequest.html).

    The official LDAP specification says that a simple bind should identify a user with their DN (distinguished name, which is kind of like the absolute path to the user’s entry in the directory), and authenticate them with a password. If you don’t know the user’s DN, then you’ll generally do a search to find the user and get their DN. I do think that Active Directory uses the sAMAccountName attribute to hold the username, so you’d perform the search based on that attribute. For example, if you want to log in as the user with a username of “tom”, then you’d use a filter like “(sAMAccountName=tom)” However, you definitely do not want to construct the filter from its string representation because that leaves you open to injection attacks (https://nawilson.com/2017/03/22/understanding-and-defending-against-ldap-injection-attacks/). Instead, you should use the com.unboundid.ldap.sdk.Filter class to construct a filter, like:

    Filter searchForUserFilter = Filter.createEqualityFilter("sAMAccountName", userID);
    

    The ability to perform a search also gives you greater flexibility, since you could identify the user in other ways. For example, if you wanted to allow the user to authenticate with either their username or email address (stored in the “mail” attribute), you could use:

    Filter searchForUserFilter = Filter.createORFilter(
         Filter.createEqualityFilter("sAMAccountName", userID),
         Filter.createEqualityFilter("mail", userID));
    

    Whenever you’re searching, you always want to make sure that you get back exactly one entry. If you don’t get back any entries, then that means the user provided the wrong username (or email address, or whatever other attribute you want to allow them to search for), and if you get back more than one entry then you can’t know which one it is, and in that case you might need to make the search more specific by ANDing in other filter components.

    Unfortunately, sometimes the directory server is configured so that you can’t perform a search anonymously. I think Active Directory might be that way by default. In that case, you typically have a couple of options:

    • Get some application-specific credentials that you can use to perform the search to find the user. This is typically only an option for server-side applications (like web applications). It’s not really an option for a client application, like you’d have on an Android device.
    • Use a different kind of authentication that allows you to identify the user with something other than a DN. In standard LDAP, I’d recommend the PLAIN SASL mechanism, but I’m pretty sure that Active Directory doesn’t support that.

    For Active Directory specifically, I think there is a third option. Even though the official LDAP specification states that a simple bind request must include a DN, Active Directory ignores that requirement and allows you to put certain other values in place of the DN, and I think that the sAMAccountName value is one of them. So for Active Directory, you might be able to authenticate with a simple bind that has the username in place of the DN. If we assume that your password is “secret123”, then you could try code like the following:

    BindResult bindResult;
    try
    {
      bindResult = connection.bind(new SimpleBindRequest("tom", "secret123");
    }
    catch (LDAPException le)
    {
      bindResult = new BindResult(le);
    }
    
    if (bindResult.getResultCode() == ResultCode.SUCCESS)
    {
      // The user authenticated successfully.
    }
    else
    {
      // The authentication attempt failed for some reason.
      // Look at the result code and error message for additional
      // information about the reason.
    }
    

    As for the second step in your process, the act of determining whether a user is a member of a group is another case where Active Directory chose not to use the same behavior as other directory servers but do their own thing. I think that in Active Directory, a user’s group membership is listed in the memberOf attribute in the user’s entry. In that case, there are basically two ways you can approach this:

    The first way is to retrieve the user entry (and now that you’re authenticated, you should be able to perform the search to get the user entry) and look at the values of the memberOf attribute to see if it includes the DN of the group that you’re interested in.

    But this can be potentially inefficient, especially if the user is a member of a lot of groups, or if you don’t need anything else from the user entry. So there’s a second option, which is to include the group membership criteria in the search request used to retrieve the user entry. In that case, you’d use a compound filter, like:

    Filter searchForUserFilter = Filter.createANDFilter(
         Filter.createORFilter(
              Filter.createEqualityFilter("sAMAccountName", userID),
              Filter.createEqualityFilter("mail", userID)),
         Filter.createEqualityFilter("memberOf", groupDN));
    

    In the event that you only know the name of the group but not its DN, then you could use the first approach and just look at all of the values of the memberOf attribute to see if any of them have an RDN with a cn value that is the name of the desired group. Alternately, you can perform a search to find the group entry and get its DN that way. I think the Active Directory search to accomplish this would use a filter like:

    Filter searchForGroupFilter = Filter.createANDFilter(
         Filter.createEqualityFilter("objectCategory", "group"),
         Filter.createEqualityFilter("cn", groupName));
    

    Hopefully, this is helpful. This is about the extent of my knowledge of the way that Active Directory does things versus regular LDAP directory servers, so I may not be very helpful in any AD-specific issues, but if you’ve got any other questions that pertain to the UnboundID LDAP SDK, then I’ll be happy to help.

    Neil

     

    Last edit: Neil Wilson 2017-03-23
  • Tom

    Tom - 2017-03-24

    Thanks for your reply it already helped alot, I got my connection setup and now that I understand the Bindresult.resultcode I have my authentication the only part I still need to check is wethere he is in the CN group for my app :)

     

Log in to post a comment.