Adding Custom Claims to the SAML Response - (How to Write a Custom Claim Handler for WSO2 Identity Server)

Overview

The latest release of WSO2 Identity Server (version 5.0.0), is armed with an "application authentication framework" which provides lot of flexibility in authenticating users from various service providers who are using heterogeneous protocols. It has several extension points, which can be used to cater several customized requirements commonly found in enterprise systems. With this post, I am going to share the details on making use of one such extension point.

Functionality to be Extended

When SAML Single Sign On is used in enterprise systems it is through the SAML Response that the relying party get to know whether the user is authenticated or not. At this point relying party is not aware of other attributes of the authenticated user which it may need for business and authorization purposes. To provide these attribute details for the relying party, SAML specification has allowed to send attributes as well in the SAML Response. WSO2 Identity Server supports this out of the box via the GUI provided for administrators. You can refer [1] for the details on this functionality and configuration details.

The flexibility provided by this particular extension, comes handy when we have a requirement to add additional attributes to the SAML Response, apart from the attributes available in the underline user store. There may be external data sources we need to look, in order to provide all the attributes requested by the relying parties. 

In the sample I am to describe here, we will be looking into a scenario where the system needs to provide some local attributes of the user which are stored in user store, with some additional attributes I expect to be retrieved from an external data source.
Following SAML Response is what we need to send to the relying party from WSO2 IS.


 
In this response we are having one local attribute, which is role and two additional attributes http://pushpalanka.org/claims/keplerNumber and http://pushpalanka.org/claims/status which have been retrieved from some other method we can define in our extension.

How?

1. Implement the customized logic to get the external claims. There are just two facts we need to note at this effort.

  • The custom implementation should either implement the interface 'org.wso2.carbon.identity.application.authentication.framework.handler.claims.ClaimHandler' or extend the default implementation of the interface 'org.wso2.carbon.identity.application.authentication.framework.handler.claims.impl.DefaultClaimHandler'.  
  • The map returned at the method, 'public Map<String, String> handleClaimMappings' should contain all the attributes we want to add to the SAML Response.
Following is the sample code I was written, adhering to the above. The external claims may have been queried from a database, read from a file or using any other mechanism as required.




2.Drop the compiled OSGI bundle at IS_HOME/repository/components/dropins. (We developed this as a OSGI bundle as we need to get local claims as well using RealmService. You can find the complete bundle and source code here)

3. Point WSO2 Identity Server to use the new custom implementation we have.

In IS_HOME/repository/conf/security/application­authentication.xml configure the new handler name. (in 'ApplicationAuthentication.Extensions.ClaimHandler' element.)
   <ClaimHandler>com.wso2.sample.claim.handler.CustomClaimHandler</ClaimHandler>

Now if look at the generated SAML Response, we will see the external attributes added.
Cheers!

[1] - https://docs.wso2.com/display/IS500/Adding+a+Service+Provider

Comments

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. This comment has been removed by a blog administrator.

    ReplyDelete
  3. This comment has been removed by a blog administrator.

    ReplyDelete
  4. Hallo Pushpalanka, I need to know, what is wrong with my CustomUserStore implementation, when SamlSSO doesn't want to retrieve Claims for user from my CustomUserStore. I have this CustomUserStore implemented as JDBC type and mapped to domain name (MYDOMAIN).

    Claims are filled through WSO2 Identity server administration pages, write & read works fine. But when user from this custom user store (MYDOMAIN) is being logged in through SAML SSO, he doesn't get any claim. But If user from PRIMARY domain is logged in, he gets everything OK.

    Can you guide me where to find a problem?

    ReplyDelete
  5. I know, that my previous question is not directly related to this topic, but I would like to kindly ask for answer, if possible. Thank you in advance.

    ReplyDelete
    Replies
    1. Hi Pepa,

      Please refer this Stackoverflow question and answer (http://stackoverflow.com/questions/26850997/wso2-identity-server-5-0-0-fails-to-return-user-claims-in-samlresponse-for-user).

      If that does not help you, we should look at the custom implementation, whether it properly query and get back the results for user claims(UM_USER_ATTRIBUTE table in default schema).

      Thanks,
      Pushpalanka

      Delete
    2. Dear Pushpalanka, I think you've just made my day :) It works now. Who would have known that such small amount of code could do such investigation case. Have a nice day!


      Best regards, Pepa

      Delete
  6. Dear Pushpalanka,

    please, excuse me when I'm using this channel to solve my issues with WSO2 IS, but I think I have another issue related to claims. It is described here:
    http://stackoverflow.com/questions/27355228/wso2-is-5-0-0-claims-mapping-error

    Can you help me resolve this?

    Thank you in advance, Pepa

    ReplyDelete
  7. Hi,

    Can you please let me know how I can handle claims depending upon the SP. Means like I want to return the user role depending upon service provider frm where the request is coming.

    ReplyDelete
  8. Hi, How we can get DOB, Tiltle, PostalCode in SAML Post? Because my User Store from User Profile doesn't give me that option to save those values in order to make it available in SAML Post. Please guide.

    ReplyDelete
    Replies
    1. You can add them as new claim mappings, then those will be available at this level.
      Refer : https://docs.wso2.com/display/IS500/Adding+New+Claim+Mapping

      Delete
  9. Hello Pushpalanka,

    Is it possible to have a request parameter as a part of the SAML Post to identify targer_url? If yes, could you please explain it or provide any link / example etc to support your answer?

    Thanks,
    Anjana

    ReplyDelete
  10. Hello Pushpalanka,
    In my use case I want to log in through an external IDP via a federated authenticator. My credentials are authenticated against their user store, and I want to add claims retrieved from my user store as attributes in the SAML response to my application. In this case, according to my tests any custom claims handler shall not be invoked.

    My next attempt was to customize the federated authenticator (SAMLSSOAuthenticator) itself but I ran into further problems (http://stackoverflow.com/questions/33256571/wso2-identity-server-customize-samlssoauthenticator).

    Any advice would be much appreciated.
    Thanks,
    //John.

    ReplyDelete
  11. hi Pushpalanka,
    i have one question about wso2 IS. Hope you can help me.
    i want to add a new button on IS login page, when click on this button, it will redirect to my app to authenticate the user. after authenticate the user successfully, my app then redirect the user to the service provider's consumer url.

    thanks in advance.
    Xin~

    ReplyDelete
  12. Hi Chin,

    This sounds more like an IDP initiated SAML SSO flow. Usually we do not customize IS management console login page and guess you are talking about IS dashboard login page.. If there is a specfic problem you face, would able to do some help.
    Thanks,
    Pushpalanka

    ReplyDelete
  13. Hi Pushpalanka,
    I mean a SP initiated the SAML SSO, when sp sends the SAML request, i was redirected to the authencationpoint login page, what i want is to customize this login page(add one button as one option to link to my own app to authenticate the user), i want to implement multiple way login on this page.

    thanks,

    ReplyDelete
    Replies
    1. Hi Xin,

      Yes that is allowed. You can go ahead and change the login.jsp file within the authenticationendpoint. This link will help, https://docs.wso2.com/display/IS500/Customizing+Login+Pages

      Delete
  14. Hi Pushpalanka,
    i checked the link you provided, the content is not i want. The description below which comes from the is500 documention is what i want to implement, do you have any samples or some guides to me? thanks!

    Multi-option authentication
    The service provider can define how to authenticate users at the Identity Server, for authentication
    requests initiated by it. While doing that, each service provider can pick more than one authenticators -
    so, the end user will get multiple login options. This can be a combination of local authenticators and
    federated authenticators.

    ReplyDelete
    Replies
    1. This will help you https://docs.wso2.com/display/IS500/Configuring+Local+and+Outbound+Authentication+for+a+Service+Provider
      You don't have to go and change the login page. Based on the above configuration you do, WSO2 IS provides the login page with the above options to users..

      Delete
  15. Hello. I have the same use case as John. Any solution for that..?
    Thanks and best regards.
    Francesco

    ReplyDelete
    Replies
    1. I am also trying to do the same thing as you and John. Did you ever find any info?

      Delete
    2. If it's possible for you to make a mapping between federated userID and local user ID manually as in [1], this can be supported out of the box. Else we can have a custom claim retriever written to get and inject all the claims to SAML response. Let me know if you need further details.

      [1] - https://docs.wso2.com/display/IS530/Associating+User+Accounts

      Delete
    3. yes the federated user name (Which I have set as the subject) and the local userprincipalname do match. I did associate my login with the federated idp but the only way I can make it work is to have a 2 step authentication on the SP and then I have to still login twice.

      Delete
    4. Here is my overall setup. I have sharepoint pointed to a wso2 SP with incoming authentication as WS-Federation. i also have Azure AD setup as an external IdP. I have the authentication of the SP set to Federation and pointing to Azure AD. When I browse to sharepoint it directs me to Azure to login and that works but I also need to have attributes from the local PRIMARY user store to be sent back to sharepoint without having to also login again to the local basic authentication. so I need to somehow match up the user name from Azure to the local PRIMARY user store and get attributes to send back to sharepoint. Both the Azure username and the local username match.

      Delete
    5. You can switch from which attributes to be sent to the service provider using the option "Assert identity using mapped local subject identifier" as in [1]. When this is ticked the attributes will be sent from local user store. But this will neglect attributes from federated login.

      So the best option see here is to go for a custom claim handler as described here in this post. Inside handleClaimMapping you can set all the claims you are interested in. The relevant protocol response builders will then add those in the response.

      [1] - https://docs.wso2.com/display/IS510/Configuring+Local+and+Outbound+Authentication+for+a+Service+Provider?preview=/43986806/49974628/image2016-1-27%2019%3A59%3A52.png

      Delete
    6. I do not know where to find any of the code that controls any of this. When you say "Inside handleClaimMapping" what are you referring to? Where do I find and change it?
      One thing I keep seeing in the log is "isFederatedClaims = true" which I think is stopping it from pulling the local claims. I did check the box you spoke of but it is still not pulling the local claims.

      Delete
    7. I am referring to the sample custom code shared above in the blogpost.

      Delete
    8. OK I will regroup and look back through this. Thank you.

      Delete
    9. if (isFederatedClaims) {

      returningClaims = handleFederatedClaims(remoteClaims, spStandardDialect, stepConfig, context);

      } else {

      returningClaims = handleLocalClaims(spStandardDialect, stepConfig, context);

      }
      if (log.isDebugEnabled()) {
      logOutput(returningClaims, context);
      }
      return returningClaims;

      This is in the Default and I want it to be like this.

      if (isFederatedClaims) {

      returningClaims = handleLocalClaims(spStandardDialect, stepConfig, context);

      } else {

      returningClaims = handleLocalClaims(spStandardDialect, stepConfig, context);

      }
      if (log.isDebugEnabled()) {
      logOutput(returningClaims, context);
      }
      return returningClaims;

      So in both cases, it gets local claims. I have the DefaultClaimHandler.java file. And I can make that change. Sorry for being stupid but how do I make that into a jar? The above info says you need to do it but not how, and if it does, I am not reading it correctly.

      I just want local claims no matter what happens.

      Delete
    10. If you refer the "How?" section in the blogpost it should answer the question. :)

      In brief you have extend the default Class and write a new class overriding the method. Then you can build this as a separate OSGI bundle and put inside product. There is a configuration file change that is required too. Exact locations and file names are mentioned in the post.

      Delete
    11. I am not a developer. I do not know what an OSGI bundle is. I have gotten to the point of getting eclipse downloaded and there is some very limited documentation on how to create a project. i have downloaded the so called source code from wso2 but there is no file called DefaultClaimHandler.java anywhere in any of that source code that I can find. that is the How I am referring too. Is there any docs that you know of anywhere that would be a step by step process of compiling a new DefaultClaimHandler file to then put in the dropins folder. I have done a lot of php and perl scripting so I am not totally new to it. But I do not know what a class is or how to extend one if I did. I do not want to waste the time of anyone in this blog, so if you maybe know of a better place I can get good info on how to do the OSGI part, I will surely go away and leave you alone. You have been very helpful in at least verifying that the boxes I have been checking in the GUI should have been doing what I need them to, so thank you very much for that.

      Delete
    12. Hi Rod, I can help you get there. Please download the sample code from here [1] shared in the blogpost. You can open it in Eclipse by pointing to the pom.xml file within the sample. Once open, locate a class named 'CustomClaimHandler' and change the below line,

      public class CustomClaimHandler implements ClaimHandler --> public class CustomClaimHandler extends DefaultClaimHandler'

      Then in the class you can override the relevant methods, then build it within eclipse and put in dropins folder. The pom.xml file in the downloaded sample carries older versions as sample was for a older version. You will have to update that to jar file versions available in the version you use, most probably for IS 5.3.0. You can check for the versions from repository/components/plugins directory of the server. Hope this will help you.

      [1] - https://drive.google.com/file/d/0B1njqfOEx3g8SThYRVNKYXFmbkU/edit

      Delete
    13. Francesco AbrusciJune 23, 2017 at 11:13 AM

      Hello. Exactly as Rod, I need to send to SP only the local claims.
      In my case I found the following trick: I created a class that extends DefaultClaimHandler and declaring this class in "IS_HOME/repository/conf/security/application­authentication.xml".
      In my class, I use "user.setUserStoreDomain("MYSQL")" to force the federated user as local where MYSQL is an additional user store created previously. With JIT the user is automatically provisioned in this store and after that I can put in place the required attributes to send directly in wso2is console.
      Anything seems working pretty well. I'm using version 5.3.0 of IS so additional versioning adjustments is required in POM and for this I'm not confident that I'm really in synch with the new version. In my POM I use these:


      org.wso2.carbon
      org.wso2.carbon.core
      4.4.11


      org.wso2.carbon.identity
      org.wso2.carbon.claim.mgt
      5.0.7


      org.wso2.carbon
      org.wso2.carbon.user.api
      4.4.11


      org.wso2.carbon
      org.wso2.carbon.user.core
      4.4.11


      org.wso2.carbon
      org.wso2.carbon.utils
      4.4.11


      org.wso2.carbon.identity
      org.wso2.carbon.identity.application.authentication.framework
      5.0.7


      commons-logging
      commons-logging
      1.1.3




      Please Pushpalanka, can you tell me if it is correct ?....and what about the trick mentioned before....any additional idea ?....thanks so much....
      francesco

      Delete
    14. Hi Francesco,

      Assuming you are using IS 5.3.0 version, I checked the version of
      'org.wso2.carbon.identity.application.authentication.framework' and it should be 5.7.5 (not 5.0.7). So please double check the versions against the jar files located at 'wso2is-5.3.0/repository/components/plugins/' folder.

      Delete
    15. Hi Pushpalanka and thanks for your help.
      I changed in my pom file the version to 5.7.5 but the following error appear: "Missing artifact org.wso2.carbon.identity:org.wso2.carbon.identity.application.authentication.framework:jar:5.7.5"....can you hep me ?
      Thanks so much
      francesco

      Delete
    16. Hi Francesco,

      Please verify below points.
      - In the pom.xml file have the below repository defined. https://github.com/wso2/carbon-identity-framework/blob/v5.7.5/pom.xml#L90-L99 (That is the repository which has http://maven.wso2.org/nexus/content/repositories/releases/org/wso2/carbon/identity/framework/org.wso2.carbon.identity.application.authentication.framework/5.7.5/)

      - Change the groupID of the artifact to be 'org.wso2.carbon.identity.framework' In you comment it seems it is set to 'org.wso2.carbon.identity' which is different from what is there for 5.7.5 version.

      Delete
    17. Thanks Pushpalanka. Now it is ok. I noted also another version difference in my project against the IS 5.3.0 release about the or.wso2.carbon.claim.mgt
      Even in this case I see that the release is 5.7.5 (not 5.0.7). Adding the repository in this case does not make me able to find artifact for the latest version. My pom for this is as follow:

      org.wso2.carbon.identity
      org.wso2.carbon.claim.mgt
      5.0.7

      Apart the version, what is my mistake ..?
      Thanks again and best regards.
      francesco

      Delete
    18. It's the group id it seems.. It should be, org.wso2.carbon.identity.framework.

      Ref : https://github.com/wso2/carbon-identity-framework/blob/v5.7.5/components/claim-mgt/org.wso2.carbon.claim.mgt/pom.xml#L22-L27

      Delete
    19. Great Pushpalanka. It works like a charm....apologize for my poor knowledge on maven.
      Thanks a lot.
      Best regards
      francesco

      Delete
  16. Hi Pushpalanka, IT's really a nice article, I wanted to know do we have any provision to make settings to get HL7 compliant claims in SAML response?

    ReplyDelete
    Replies
    1. Yes, wso2 IS has a claim mapping functionality to where we can map local claims to any other format we wish to. Please refer https://docs.wso2.com/display/IS530/Configuring+Claims+for+a+Service+Provider

      Delete

Post a Comment

Popular posts from this blog

Signing SOAP Messages - Generation of Enveloped XML Signatures

How to send an HTML email in Java (Using Google SMTP Server)