cancel
Showing results for 
Search instead for 
Did you mean: 

Logout from external SSO (CAS)

sorin_postelnic
Confirmed Champ
Confirmed Champ
Hello everyone!

In our custom Alfresco Share 4.2 application we are integrating with an external SSO Central Authentication System (CAS), and we have the following problem: whenever the user tries to logout from Share, they are redirected to the /share page, which is intercepted by the CAS filter which then automatically logs them back in (since they never logged-out from the CAS).

We are trying to intercept in JavaScript (in LogoutService.js) the XHR call to the "dologout" controller, so that after the dologout is performed, we can redirect the browser to the CAS logout page.

But this does not work, for the following reason: the SlingshotLogoutController is normally returning an HTTP 401 code; and then the serviceXhr function from Alfresco CoreXhr.js receives this 401 and redirects the browser to the /share page. (This is done because the serviceXhr function thinks that the user tried an AJAX call request while the session had expired, so they assumed that in this case the browser should be redirected to the Share homepage. But in the case of dologout we don't want the user to be redirected to /share, but instead we want to redirect the user to the CAS logout page.)

Is our only option just to modify the implementation of CoreXhr.serviceXhr() and check if the called URL was Alfresco.constants.URL_PAGECONTEXT + "dologout" to skip the redirection in that case?

Did any of you have to deal with integration with external SSO in the past?
6 REPLIES 6

afaust
Legendary Innovator
Legendary Innovator
I believe the best way to deal with this actually IS by modifying the CoreXhr handling. Since this is an Aikau module you can raise a ticket for this use case in <a href="https://github.com/Alfresco/Aikau/issues">the Aikau GitHub issue list</a>. I would suggest that the CoreXhr allow an additional configuration to the serviceXhr parameter / config object to specify properties to e.g. not redirect on 401 which would cause a failureCallback (if provided) to be triggered instead, which can then specifically handle the result. Then it is only a matter of providing a custom LogoutService that adds this failureCallback.

Regards
Axel

sorin_postelnic
Confirmed Champ
Confirmed Champ
In the end I performed the following modifications to /share/web/js/alfresco/core/CoreXhr.js (note that we are using Alfresco 4.2, so this differs a bit from the latest SVN code):

                  // HANDLE UN-AUTHORIZED
                  if (response.response && response.response.status === 401)
                  {
                     // HANDLE LOGOUT
                     if (config.isLogout) {
                        if (typeof config.logoutCallback === "function")
                        {
                           callbackScope = (config.logoutCallbackScope ? config.logoutCallbackScope : (config.callbackScope ? config.callbackScope : _this));
                           config.logoutCallback.call(callbackScope, response, config);
                        }
                        return;
                     }
                     // HANDLE SESSION TIMEOUT
                     var redirect = response.response.getHeader("Location");
                     if (redirect)
                     {
                        window.location.href = window.location.protocol + "//" + window.location.host + redirect;
                        return;
                     }
                     else
                     {
                        window.location.reload(true);
                        return;
                     }
                  }

And then I changed this in /share/web/js/alfresco/services/LogoutService.js :

      doLogout: function alfresco_services_LogoutService__doLogout() {
         this.serviceXhr({
            method: "POST",
            url: Alfresco.constants.URL_PAGECONTEXT + "dologout",
            isLogout: true,
            logoutCallback: function() {
               if (Alfresco.constants.CAS_SERVER_LOGOUT_URL) {
                  window.location.href = Alfresco.constants.CAS_SERVER_LOGOUT_URL;
               }
            },
            callbackScope: this
         });
      }

Then I configured Alfresco.constants.CAS_SERVER_LOGOUT_URL inside /share/site-webscripts/org/alfresco/components/head/resources.get.html.ftl :

<@markup id="alfrescoConstants">
   <@inlineScript group="template-common">
      <!– Alfresco web framework constants –>
      Alfresco.constants = Alfresco.constants || {};
      ……………
      Alfresco.constants.CAS_SERVER_URL = "${casServerUrl}";
      Alfresco.constants.CAS_SERVER_LOGOUT_URL = "${casServerLogoutUrl}";
      ……………

and for this I had to add it in /share/site-webscripts/org/alfresco/components/head/resources.get.js

// model items required by resources template
model.preferences = preferences.value;
model.casServerUrl = MyappnameShareConfiguration.getCasServerUrl();
model.casServerLogoutUrl = MyappnameShareConfiguration.getCasServerLogoutUrl();


In order for this to work, I had to define a bean in a new Spring context file /share/config/alfresco/web-extension/myappname-config-share-context.xml

   <bean id="presentationJavascriptCompatibleConfiguration" parent="baseScriptExtension"
        class="my.company.myappname.config.PresentationJavascriptCompatibleConfiguration">
      <property name="extensionName" value="MyappnameShareConfiguration" /> <!– This defines the name of the javascript object which will be accessible in the webscripts –>
      <property name="casServerUrl" value="${casServerUrl}" />
      <property name="casServerLogoutUrl" value="${casServerLogoutUrl}" />
   </bean>

Where PresentationJavascriptCompatibleConfiguration is this class:

package my.company.myappname.config;

import org.springframework.extensions.webscripts.processor.BaseProcessorExtension;

public class PresentationJavascriptCompatibleConfiguration extends BaseProcessorExtension {
    private String casServerUrl;
    private String casServerLogoutUrl;

    public String getCasServerUrl() {
        return this.casServerUrl;
    }
    public void setCasServerUrl(String casServerUrl) {
        this.casServerUrl = casServerUrl;
    }

    public String getCasServerLogoutUrl() {
        return this.casServerLogoutUrl;
    }
    public void setCasServerLogoutUrl(String casServerLogoutUrl) {
        this.casServerLogoutUrl = casServerLogoutUrl;
    }
}

and casServerUrl is defined in alfresco-global.properties:

casServerUrl=https://my.company.cas.server:port
casServerLogoutUrl=${casServerUrl}/cas/logout

idwright
Star Collaborator
Star Collaborator

You can just change the Aikau HEADER_USER_MENU_LOGOUT as shown below (share-header.get.js)

var logoutItem = widgetUtils.findObject(model.jsonModel, "id", "HEADER_USER_MENU_LOGOUT");
if (logoutItem != null)
{
    logoutItem.config.targetUrl += "?redirectURL=";
    logoutItem.config.targetUrl += "https://my.company.cas.serverSmiley Tongueort/cas/logout";
    logoutItem.config.targetUrl += "&redirectURLQueryKey=service&";
    logoutItem.config.targetUrl += "redirectURLQueryValue=https://my.company.server/";
}

Of course this does have the redirect locations hard-coded rather than in alfresco-global.properties but it looks like you're able to deal with that.

Note that this will only work for 4.2 (as will your code)

(See GitHub - wrighting/alfresco-cas: A project designed to show how to integrate Alfresco with CAS singl...  for details of later versions)

Dear Mr. Wrighting,

Will the "redirectURL" parameter make sure that Alfresco will first perform a normal Alfresco logout (clearing of Alfresco session cookies) and it will then do a redirect to my specified URL?

Yes, it will.

The 3 redirect parameters get used in the Surf Logout servlet so it logs out of Alfresco first then uses those 3 parameters to construct the URL to forward to.

That is very good to know, thank you.

I could not find any information like that in the Alfresco Share documentation, nor in the forums.