{"id":2653,"date":"2018-07-30T13:42:52","date_gmt":"2018-07-30T11:42:52","guid":{"rendered":"http:\/\/hasselba.ch\/blog\/?p=2653"},"modified":"2018-07-30T13:42:52","modified_gmt":"2018-07-30T11:42:52","slug":"dropping-dominos-http-task-3-websso-integration-part-1","status":"publish","type":"post","link":"https:\/\/hasselba.ch\/blog\/?p=2653","title":{"rendered":"Dropping Domino\u2019s HTTP task (3): WebSSO Integration (Part 1)"},"content":{"rendered":"<p>To integrate the new HTTP stack into the existing environment, we can use LTPA tokens. These tokens are cookies which store the authentication information and allow to share them betweeen different participating Domino servers. A users must log on only once, and existing applications and data\/views can be accessed without a relogin.<\/p>\n<p>Validating an existing LTPA token with Spring can be done with our own <a href=\"https:\/\/github.com\/spring-projects\/spring-security\/blob\/master\/web\/src\/main\/java\/org\/springframework\/security\/web\/authentication\/preauth\/AbstractPreAuthenticatedProcessingFilter.java\" target=\"_blank\" rel=\"noopener\">PreAuthentificationFilter<\/a> which checks for an existing LTPA token and extracts the authentication details from the cookie and creates a new <a href=\"https:\/\/docs.oracle.com\/javase\/7\/docs\/api\/java\/security\/Principal.html\" target=\"_blank\" rel=\"noopener\">Principal<\/a> instance.<\/p>\n<pre><code>\r\nimport org.springframework.beans.factory.annotation.Value;\r\nimport org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;\r\n\r\npublic class LtpaPreAuthenticatedFilter extends AbstractPreAuthenticatedProcessingFilter {\r\n\r\n   @Value(\"${ltpa.secret}\")\r\n   private String ltpaSecret;\r\n\r\n   @Value(\"${ltpa.cookieName}\")\r\n   private String ltpaCookieName;\r\n\r\n   @Override\r\n   protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {\r\n\r\n      Cookie[] cookies = request.getCookies();\r\n      if( cookies == null ) {\r\n         return null;\r\n      }\r\n\r\n      for( int i= 0; i&lt;cookies.length ; i++ ){\r\n         String name = cookies[i].getName();\r\n         String value = cookies[i].getValue();\r\n\r\n         if( ltpaCookieName.equalsIgnoreCase(name) ){\r\n            DominoLtpaToken ltpaToken = new DominoLtpaToken( value, ltpaSecret );\r\n\r\n            if( ltpaToken.isValid() ){\r\n               return ltpaToken.getDistinguishedName();\r\n            }\r\n         }\r\n      }\r\n\r\n      return null;\r\n   }\r\n\r\n   @Override\r\n   protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {\r\n      \/\/ is required to return an empty string\r\n      return \"\";\r\n   }\r\n\r\n}\r\n<\/code><\/pre>\n<p>The filter implements two methods, one for extraction of the principal, and the other for the credentials (which we don&#8217;t have with LTPA tokens). In the <i>getPreAuthenticatedPrincipal<\/i> method, existinig LTPA tokens are searched, then the user extracted and the token validated.<\/p>\n<p>The secret of the LTPA token and the name are stored in <em>application.properties:<\/em><\/p>\n<p><a href=\"https:\/\/hasselba.ch\/blog\/wp-content\/uploads\/2018\/07\/Greenshot-2018-07-30-12.37.59.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2654\" src=\"https:\/\/hasselba.ch\/blog\/wp-content\/uploads\/2018\/07\/Greenshot-2018-07-30-12.37.59.png\" alt=\"\" width=\"246\" height=\"76\" \/><\/a><\/p>\n<p><a href=\"https:\/\/hasselba.ch\/blog\/wp-content\/uploads\/2018\/07\/application.properties.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2655\" src=\"https:\/\/hasselba.ch\/blog\/wp-content\/uploads\/2018\/07\/application.properties.png\" alt=\"\" width=\"359\" height=\"114\" srcset=\"https:\/\/hasselba.ch\/blog\/wp-content\/uploads\/2018\/07\/application.properties.png 359w, https:\/\/hasselba.ch\/blog\/wp-content\/uploads\/2018\/07\/application.properties-300x95.png 300w\" sizes=\"auto, (max-width: 359px) 100vw, 359px\" \/><\/a><\/p>\n<p>The second part is implementing a AuthenticationUserDetailsService. This service is for getting additional details for the authenticated user, for example the ACL roles or groups the user belongs to.<\/p>\n<pre><code>import java.util.Collection;\r\nimport java.util.HashSet;\r\n\r\nimport org.springframework.security.core.GrantedAuthority;\r\nimport org.springframework.security.core.userdetails.AuthenticationUserDetailsService;\r\nimport org.springframework.security.core.userdetails.User;\r\nimport org.springframework.security.core.userdetails.UserDetails;\r\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\r\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;\r\n\r\npublic class LtpaUserDetailsService implements AuthenticationUserDetailsService&lt;PreAuthenticatedAuthenticationToken&gt; {\r\n\r\n   @Override\r\n   public UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token)\r\n      throws UsernameNotFoundException {\r\n\r\n      String userName=(String)token.getPrincipal();\r\n\r\n      Collection&lt;GrantedAuthority&gt; authorities = new HashSet&lt;GrantedAuthority&gt;() ;\r\n      authorities.add(new LtpaUserAuthority());\r\n\r\n      User user = new User(userName,\"\",authorities);\r\n\r\n      return user;\r\n    }\r\n\r\n}<\/code><\/pre>\n<p>In our case, we are just adding an <em>LtpaUserAuthority<\/em> to the user information. Don&#8217;t worry about the usage of the <em>LtpaUserAuthority<\/em>. We come back to this in another post.<\/p>\n<pre><code>import org.springframework.security.core.GrantedAuthority;\r\n\r\npublic class LtpaUserAuthority implements GrantedAuthority {\r\n\r\n   private static final long serialVersionUID = 1L;\r\n\r\n   @Override\r\n   public String getAuthority() {\r\n      return \"ROLE_USER_LTPA\";\r\n   }\r\n\r\n}<\/code><\/pre>\n<p>In the last step we have to update the <em>SecurityConfig.java <\/em>to activate the filter:<\/p>\n<pre><code>@EnableWebSecurity\r\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\r\n\r\n   @Configuration\r\n   @Order(1)\r\n   static class DominoLtpaSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {\r\n\r\n      @Bean\r\n      public AuthenticationUserDetailsService&lt;PreAuthenticatedAuthenticationToken&gt; authenticationUserDetailsService() {\r\n         return new LtpaUserDetailsService();\r\n      }\r\n\r\n      @Bean\r\n      public PreAuthenticatedAuthenticationProvider preAuthenticatedAuthenticationProvider() {\r\n         PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();\r\n\r\n         provider.setPreAuthenticatedUserDetailsService(authenticationUserDetailsService());\r\n         provider.setUserDetailsChecker(new AccountStatusUserDetailsChecker());\r\n\r\n         return provider;\r\n      }\r\n\r\n      @Override\r\n      protected void configure(AuthenticationManagerBuilder auth) throws Exception {\r\n         auth.authenticationProvider(preAuthenticatedAuthenticationProvider());\r\n      }\r\n\r\n      @Bean\r\n      public AbstractPreAuthenticatedProcessingFilter preAuthenticatedProcessingFilter() throws Exception {\r\n         LtpaPreAuthenticatedFilter filter = new LtpaPreAuthenticatedFilter();\r\n         filter.setAuthenticationManager(authenticationManager());\r\n         return filter;\r\n      }\r\n\r\n      @Override\r\n      protected void configure(HttpSecurity http) throws Exception {\r\n         http.addFilter(preAuthenticatedProcessingFilter())\r\n         .authorizeRequests()\r\n         .antMatchers(\"\/**\").permitAll() ;\r\n      }\r\n   }\r\n  ...\r\n}<\/code><\/pre>\n<p>This includes the filter in any request. Now, the Principal contains the user name stored in the LTPA token.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>To integrate the new HTTP stack into the existing environment, we can use LTPA tokens. These tokens are cookies which store the authentication information and allow to share them betweeen different participating Domino servers. A users must log on only &hellip; <a href=\"https:\/\/hasselba.ch\/blog\/?p=2653\">Weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[89,82,81],"tags":[31,16,127,12],"class_list":["post-2653","post","type-post","status-publish","format-standard","hentry","category-java","category-server","category-web","tag-java","tag-server","tag-spring-boot","tag-web"],"_links":{"self":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2653","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2653"}],"version-history":[{"count":5,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2653\/revisions"}],"predecessor-version":[{"id":2660,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2653\/revisions\/2660"}],"wp:attachment":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2653"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2653"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2653"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}