Skip to content
This repository has been archived by the owner on Oct 27, 2020. It is now read-only.

Commit

Permalink
REPO-2575: allow sending 'AlfTicket' scheme in WWW-Authenticate header
Browse files Browse the repository at this point in the history
"401 response with www-authenticate header causes browser native login prompt to be shown."

By sending:

    WWW-Authenticate: AlfTicket realm="..."

We can avoid making the browser pop up a Basic auth dialogue box. This
is particularly useful for apps built for the browser that talk directly
to the Alfresco public APIs at the backend.

To use this feature, set alfresco.restApi.basicAuthScheme=false
  • Loading branch information
Matt Ward committed Oct 9, 2017
1 parent 1742e74 commit dfb270a
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 26 deletions.
@@ -1,28 +1,28 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api;

import java.util.Collections;
Expand Down Expand Up @@ -67,6 +67,7 @@ public class PublicApiAuthenticatorFactory extends RemoteUserAuthenticatorFactor
private TenantAuthentication tenantAuthentication;
private Set<String> validAuthenticatorKeys = Collections.emptySet();
private Set<String> outboundHeaderNames;
private boolean useBasicAuth = true;

public void setAuthenticatorKeyHeader(String authenticatorKeyHeader)
{
Expand All @@ -92,6 +93,23 @@ public void setOutboundHeaders(Set<String> outboundHeaders)
this.outboundHeaderNames = outboundHeaders;
}

/**
* Whether to suggest that users use Basic auth. If set to true, then a
* 401 (unauthorized) response will contain a WWW-Authentication header
* specifying the scheme "Basic". If this is set to false, then
* the scheme "AlfTicket" will be used.
* <p>
* Set this to false to avoid getting Basic auth dialogue popups in browsers
* when using the public API directly, for example.
*
* @see <a href="https://issues.alfresco.com/jira/browse/REPO-2575">REPO-2575</a>
* @param useBasicAuth
*/
public void setUseBasicAuth(boolean useBasicAuth)
{
this.useBasicAuth = useBasicAuth;
}

public void setTenantAuthentication(TenantAuthentication service)
{
this.tenantAuthentication = service;
Expand Down Expand Up @@ -232,7 +250,9 @@ public Boolean execute() throws Exception
if (!authorized)
{
servletRes.setStatus(401);
servletRes.setHeader("WWW-Authenticate", "Basic realm=\"Alfresco " + servletReq.getTenant() + " tenant\"");
String scheme = useBasicAuth ? "Basic" : "AlfTicket";
String challenge = scheme + " realm=\"Alfresco " + servletReq.getTenant() + " tenant\"";
servletRes.setHeader("WWW-Authenticate", challenge);
}
}
}
Expand Down
24 changes: 24 additions & 0 deletions src/main/resources/alfresco/project.properties
@@ -0,0 +1,24 @@
################################################################################
# Remote API property defaults
# 9th October 2017
################################################################################


# Whether to send a "basic auth" challenge along with a 401 response (not authorized)
#
# If set to true, then a header will be sent similar to:
#
# WWW-Authenticate: Basic realm="..."
#
# If set to false, then a header will be sent with an AlfTicket challenge:
#
# WWW-Authenticate: AlfTicket realm="..."
#
# This latter case is particularly useful when building a web-browser based client
# that communicates directly with the Alfresco Public API - using the AlfTicket
# challenge allows the client to completely control the login behaviour, whereas
# allowing a Basic auth challenge to be sent results in the Basic Authentication
# browser dialogue being popped-up without the client app being involved.
#
# See issue REPO-2575 for details.
alfresco.restApi.basicAuthScheme=true
1 change: 1 addition & 0 deletions src/main/resources/alfresco/public-rest-context.xml
Expand Up @@ -95,6 +95,7 @@
<property name="remoteUserMapper">
<ref bean="RemoteUserMapper" />
</property>
<property name="useBasicAuth" value="${alfresco.restApi.basicAuthScheme}"/>
</bean>

<bean id="apiBootstrapBean" class="org.alfresco.rest.framework.core.ApiBootstrap">
Expand Down
38 changes: 38 additions & 0 deletions src/test/java/org/alfresco/rest/api/tests/AuthenticationsTest.java
Expand Up @@ -31,6 +31,7 @@
import org.alfresco.rest.AbstractSingleNetworkSiteTest;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.People;
import org.alfresco.rest.api.PublicApiAuthenticatorFactory;
import org.alfresco.rest.api.model.LoginTicket;
import org.alfresco.rest.api.model.LoginTicketResponse;
import org.alfresco.rest.api.sites.SiteEntityResource;
Expand All @@ -41,6 +42,7 @@
import org.alfresco.rest.api.tests.client.data.Folder;
import org.alfresco.rest.api.tests.util.RestApiUtil;
import org.apache.commons.codec.binary.Base64;
import org.junit.Before;
import org.junit.Test;

import java.util.Collections;
Expand All @@ -56,7 +58,43 @@ public class AuthenticationsTest extends AbstractSingleNetworkSiteTest
{
private static final String TICKETS_URL = "tickets";
private static final String TICKETS_API_NAME = "authentication";
private PublicApiAuthenticatorFactory authFactory;

@Before
public void setUpAuthTest()
{
authFactory = (PublicApiAuthenticatorFactory) applicationContext.getBean("publicapi.authenticator");
}

@Test
public void canDisableBasicAuthChallenge() throws Exception
{
authFactory.setUseBasicAuth(false);

// Expect to be challenged for an AlfTicket (REPO-2575)
testAuthChallenge("AlfTicket");
}

@Test
public void canEnableBasicAuthChallenge() throws Exception
{
authFactory.setUseBasicAuth(true);

// Expect to be challenged for Basic auth.
testAuthChallenge("Basic");
}

private void testAuthChallenge(String expectedScheme) throws Exception
{
// Unauthorized call
setRequestContext(null);

HttpResponse response = getAll(SiteEntityResource.class, getPaging(0, 100), null, 401);
String authenticateHeader = response.getHeaders().get("WWW-Authenticate");
assertNotNull("Expected an authentication challenge", authenticateHeader);
String authScheme = authenticateHeader.split(" ")[0]; // Other parts may contain, e.g. realm="..."
assertEquals(expectedScheme, authScheme);
}

/**
* Tests login (create ticket), logout (delete ticket), and validate (get ticket).
Expand Down

0 comments on commit dfb270a

Please sign in to comment.