enabledSaslMechanisms;
protected ConnectionConfiguration(Builder,?> builder) {
+ authzid = builder.authzid;
username = builder.username;
password = builder.password;
callbackHandler = builder.callbackHandler;
@@ -361,6 +368,17 @@ public abstract class ConnectionConfiguration {
return resource;
}
+ /**
+ * Returns the optional XMPP address to be requested as the SASL authorization identity.
+ *
+ * @return the authorization identifier.
+ * @see RFC 6120 § 6.3.8. Authorization Identity
+ * @since 4.2
+ */
+ public EntityBareJid getAuthzid() {
+ return authzid;
+ }
+
/**
* Returns true if an available presence should be sent when logging in while reconnecting.
*
@@ -425,6 +443,7 @@ public abstract class ConnectionConfiguration {
private String[] enabledSSLProtocols;
private String[] enabledSSLCiphers;
private HostnameVerifier hostnameVerifier;
+ private EntityBareJid authzid;
private CharSequence username;
private String password;
private Resourcepart resource;
@@ -803,6 +822,25 @@ public abstract class ConnectionConfiguration {
return getThis();
}
+ /**
+ * Set the XMPP address to be used as authorization identity.
+ *
+ * In XMPP, authorization identities are bare jids. In general, callers should allow the server to select the
+ * authorization identifier automatically, and not call this. Note that setting the authzid does not set the XMPP
+ * service domain, which should typically match.
+ * Calling this will also SASL CRAM, since this mechanism does not support authzid.
+ *
+ *
+ * @param authzid The BareJid to be requested as the authorization identifier.
+ * @return a reference to this builder.
+ * @see RFC 6120 § 6.3.8. Authorization Identity
+ * @since 4.2
+ */
+ public B setAuthzid(EntityBareJid authzid) {
+ this.authzid = authzid;
+ return getThis();
+ }
+
public abstract C build();
protected abstract B getThis();
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java b/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java
index 9c22cce01..faf90a291 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java
@@ -25,6 +25,7 @@ import org.jivesoftware.smack.sasl.SASLMechanism;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
import org.jxmpp.jid.DomainBareJid;
+import org.jxmpp.jid.EntityBareJid;
import javax.security.auth.callback.CallbackHandler;
@@ -171,26 +172,27 @@ public final class SASLAuthentication {
*
* @param username the username that is authenticating with the server.
* @param password the password to send to the server.
- * @throws XMPPErrorException
- * @throws SASLErrorException
- * @throws IOException
- * @throws SmackException
- * @throws InterruptedException
+ * @param authzid the authorization identifier (typically null).
+ * @throws XMPPErrorException
+ * @throws SASLErrorException
+ * @throws IOException
+ * @throws SmackException
+ * @throws InterruptedException
*/
- public void authenticate(String username, String password)
+ public void authenticate(String username, String password, EntityBareJid authzid)
throws XMPPErrorException, SASLErrorException, IOException,
SmackException, InterruptedException {
- currentMechanism = selectMechanism();
+ currentMechanism = selectMechanism(authzid);
final CallbackHandler callbackHandler = configuration.getCallbackHandler();
final String host = connection.getHost();
final DomainBareJid xmppServiceDomain = connection.getXMPPServiceDomain();
synchronized (this) {
if (callbackHandler != null) {
- currentMechanism.authenticate(host, xmppServiceDomain, callbackHandler);
+ currentMechanism.authenticate(host, xmppServiceDomain, callbackHandler, authzid);
}
else {
- currentMechanism.authenticate(username, host, xmppServiceDomain, password);
+ currentMechanism.authenticate(username, host, xmppServiceDomain, password, authzid);
}
final long deadline = System.currentTimeMillis() + connection.getPacketReplyTimeout();
while (!authenticationSuccessful && saslException == null) {
@@ -312,7 +314,7 @@ public final class SASLAuthentication {
return lastUsedMech.getName();
}
- private SASLMechanism selectMechanism() throws SmackException {
+ private SASLMechanism selectMechanism(EntityBareJid authzid) throws SmackException {
Iterator it = REGISTERED_MECHANISMS.iterator();
final List serverMechanisms = getServerMechanisms();
if (serverMechanisms.isEmpty()) {
@@ -330,6 +332,12 @@ public final class SASLAuthentication {
if (!configuration.isEnabledSaslMechanism(mechanismName)) {
continue;
}
+ if (authzid != null) {
+ if (!mechanism.authzidSupported()) {
+ LOGGER.fine("Skipping " + mechanism + " because authzid is required by not supported by this SASL mechanism");
+ continue;
+ }
+ }
if (serverMechanisms.contains(mechanismName)) {
// Create a new instance of the SASLMechanism for every authentication attempt.
return mechanism.instanceForAuthentication(connection);
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java b/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java
index 9dd01c91d..198985e49 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java
@@ -25,6 +25,7 @@ import org.jivesoftware.smack.util.StringTransformer;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.stringencoder.Base64;
import org.jxmpp.jid.DomainBareJid;
+import org.jxmpp.jid.EntityBareJid;
import javax.security.auth.callback.CallbackHandler;
@@ -34,9 +35,9 @@ import javax.security.auth.callback.CallbackHandler;
* {@link #getName()} -- returns the common name of the SASL mechanism.
*
* Subclasses will likely want to implement their own versions of these methods:
- * {@link #authenticate(String, String, DomainBareJid, String)} -- Initiate authentication stanza using the
+ * {@link #authenticate(String, String, DomainBareJid, String, EntityBareJid)} -- Initiate authentication stanza using the
* deprecated method.
- * {@link #authenticate(String, DomainBareJid, CallbackHandler)} -- Initiate authentication stanza
+ * {@link #authenticate(String, DomainBareJid, CallbackHandler, EntityBareJid)} -- Initiate authentication stanza
* using the CallbackHandler method.
* {@link #challengeReceived(String, boolean)} -- Handle a challenge from the server.
*
@@ -102,6 +103,12 @@ public abstract class SASLMechanism implements Comparable {
*/
protected String authenticationId;
+ /**
+ * The authorization identifier (authzid).
+ * This is always a bare Jid, but can be null.
+ */
+ protected EntityBareJid authorizationId;
+
/**
* The name of the XMPP service
*/
@@ -116,7 +123,7 @@ public abstract class SASLMechanism implements Comparable {
/**
* Builds and sends the auth stanza to the server. Note that this method of
* authentication is not recommended, since it is very inflexible. Use
- * {@link #authenticate(String, DomainBareJid, CallbackHandler)} whenever possible.
+ * {@link #authenticate(String, DomainBareJid, CallbackHandler, EntityBareJid)} whenever possible.
*
* Explanation of auth stanza:
*
@@ -149,24 +156,27 @@ public abstract class SASLMechanism implements Comparable {
* called "mail3.example.com"; it's "serv-name" would be "example.com", it's "host" would be
* "mail3.example.com". If the service is not replicated, or the serv-name is identical to
* the host, then the serv-name component MUST be omitted
- *
- * digest-uri verification is needed for ejabberd 2.0.3 and higher
- *
+ *
+ * digest-uri verification is needed for ejabberd 2.0.3 and higher
+ *
* @param username the username of the user being authenticated.
* @param host the hostname where the user account resides.
* @param serviceName the xmpp service location - used by the SASL client in digest-uri creation
* serviceName format is: host [ "/" serv-name ] as per RFC-2831
* @param password the password for this account.
+ * @param authzid the optional authorization identity.
* @throws SmackException If a network error occurs while authenticating.
* @throws NotConnectedException
* @throws InterruptedException
*/
- public final void authenticate(String username, String host, DomainBareJid serviceName, String password)
+ public final void authenticate(String username, String host, DomainBareJid serviceName, String password, EntityBareJid authzid)
throws SmackException, NotConnectedException, InterruptedException {
this.authenticationId = username;
this.host = host;
this.serviceName = serviceName;
this.password = password;
+ this.authorizationId = authzid;
+ assert(authorizationId == null || authzidSupported());
authenticateInternal();
authenticate();
}
@@ -184,14 +194,17 @@ public abstract class SASLMechanism implements Comparable {
* @param host the hostname where the user account resides.
* @param serviceName the xmpp service location
* @param cbh the CallbackHandler to obtain user information.
+ * @param authzid the optional authorization identity.
* @throws SmackException
* @throws NotConnectedException
* @throws InterruptedException
*/
- public void authenticate(String host, DomainBareJid serviceName, CallbackHandler cbh)
+ public void authenticate(String host, DomainBareJid serviceName, CallbackHandler cbh, EntityBareJid authzid)
throws SmackException, NotConnectedException, InterruptedException {
this.host = host;
this.serviceName = serviceName;
+ this.authorizationId = authzid;
+ assert(authorizationId == null || authzidSupported());
authenticateInternal(cbh);
authenticate();
}
@@ -283,6 +296,10 @@ public abstract class SASLMechanism implements Comparable {
return saslMechansim;
}
+ public boolean authzidSupported() {
+ return false;
+ }
+
protected abstract SASLMechanism newInstance();
protected static byte[] toBytes(String string) {
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SCRAMSHA1Mechanism.java b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SCRAMSHA1Mechanism.java
index 217ace4e9..0a6d6d6f1 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SCRAMSHA1Mechanism.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SCRAMSHA1Mechanism.java
@@ -38,7 +38,6 @@ public class SCRAMSHA1Mechanism extends SASLMechanism {
public static final String NAME = "SCRAM-SHA-1";
private static final int RANDOM_ASCII_BYTE_COUNT = 32;
- private static final String DEFAULT_GS2_HEADER = "n,,";
private static final byte[] CLIENT_KEY_BYTES = toBytes("Client Key");
private static final byte[] SERVER_KEY_BYTES = toBytes("Server Key");
private static final byte[] ONE = new byte[] { 0, 0, 0, 1 };
@@ -77,7 +76,7 @@ public class SCRAMSHA1Mechanism extends SASLMechanism {
clientRandomAscii = getRandomAscii();
String saslPrepedAuthcId = saslPrep(authenticationId);
clientFirstMessageBare = "n=" + escape(saslPrepedAuthcId) + ",r=" + clientRandomAscii;
- String clientFirstMessage = DEFAULT_GS2_HEADER + clientFirstMessageBare;
+ String clientFirstMessage = getGS2Header() + clientFirstMessageBare;
state = State.AUTH_TEXT_SENT;
return toBytes(clientFirstMessage);
}
@@ -105,6 +104,11 @@ public class SCRAMSHA1Mechanism extends SASLMechanism {
}
}
+ @Override
+ public boolean authzidSupported() {
+ return true;
+ }
+
@Override
protected byte[] evaluateChallenge(byte[] challenge) throws SmackException {
final String challengeString = new String(challenge);
@@ -148,7 +152,7 @@ public class SCRAMSHA1Mechanism extends SASLMechanism {
// Parsing and error checking is done, we can now begin to calculate the values
// First the client-final-message-without-proof
- String clientFinalMessageWithoutProof = "c=" + Base64.encode(DEFAULT_GS2_HEADER) + ",r=" + rvalue;
+ String clientFinalMessageWithoutProof = "c=" + Base64.encode(getGS2Header()) + ",r=" + rvalue;
// AuthMessage := client-first-message-bare + "," + server-first-message + "," +
// client-final-message-without-proof
@@ -209,6 +213,14 @@ public class SCRAMSHA1Mechanism extends SASLMechanism {
return null;
}
+ private final String getGS2Header() {
+ String authzidPortion = "";
+ if (authorizationId != null) {
+ authzidPortion = "a=" + authorizationId;
+ }
+ return "n," + authzidPortion + ",";
+ }
+
private static Map parseAttributes(String string) throws SmackException {
if (string.length() == 0) {
return Collections.emptyMap();
diff --git a/smack-core/src/test/java/org/jivesoftware/smack/sasl/DigestMd5SaslTest.java b/smack-core/src/test/java/org/jivesoftware/smack/sasl/DigestMd5SaslTest.java
index 19e3936c7..6cfe83e51 100644
--- a/smack-core/src/test/java/org/jivesoftware/smack/sasl/DigestMd5SaslTest.java
+++ b/smack-core/src/test/java/org/jivesoftware/smack/sasl/DigestMd5SaslTest.java
@@ -27,6 +27,7 @@ import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.util.StringUtils;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
+import org.jxmpp.jid.EntityBareJid;
public class DigestMd5SaslTest extends AbstractSaslTest {
@@ -37,9 +38,12 @@ public class DigestMd5SaslTest extends AbstractSaslTest {
super(saslMechanism);
}
- protected void runTest() throws NotConnectedException, SmackException, InterruptedException, XmppStringprepException {
- saslMechanism.authenticate("florian", "irrelevant", JidCreate.domainBareFrom("xmpp.org"), "secret");
-
+ protected void runTest(boolean useAuthzid) throws NotConnectedException, SmackException, InterruptedException, XmppStringprepException {
+ EntityBareJid authzid = null;
+ if (useAuthzid) {
+ authzid = JidCreate.entityBareFrom("shazbat@xmpp.org");
+ }
+ saslMechanism.authenticate("florian", "irrelevant", JidCreate.domainBareFrom("xmpp.org"), "secret", authzid);
byte[] response = saslMechanism.evaluateChallenge(challengeBytes);
String responseString = new String(response);
String[] responseParts = responseString.split(",");
@@ -51,6 +55,11 @@ public class DigestMd5SaslTest extends AbstractSaslTest {
String value = keyValue[1].replace("\"", "");
responsePairs.put(key, value);
}
+ if (useAuthzid) {
+ assertMapValue("authzid", "shazbat@xmpp.org", responsePairs);
+ } else {
+ assert(!responsePairs.containsKey("authzid"));
+ }
assertMapValue("username", "florian", responsePairs);
assertMapValue("realm", "xmpp.org", responsePairs);
assertMapValue("digest-uri", "xmpp/xmpp.org", responsePairs);
@@ -58,6 +67,6 @@ public class DigestMd5SaslTest extends AbstractSaslTest {
}
private static void assertMapValue(String key, String value, Map map) {
- assertEquals(map.get(key), value);
+ assertEquals(value, map.get(key));
}
}
diff --git a/smack-core/src/test/java/org/jivesoftware/smack/sasl/core/SCRAMSHA1MechanismTest.java b/smack-core/src/test/java/org/jivesoftware/smack/sasl/core/SCRAMSHA1MechanismTest.java
index 93c0cb79c..4903b9354 100644
--- a/smack-core/src/test/java/org/jivesoftware/smack/sasl/core/SCRAMSHA1MechanismTest.java
+++ b/smack-core/src/test/java/org/jivesoftware/smack/sasl/core/SCRAMSHA1MechanismTest.java
@@ -48,7 +48,7 @@ public class SCRAMSHA1MechanismTest extends SmackTestSuite {
}
};
- mech.authenticate(USERNAME, "unusedFoo", JidTestUtil.DOMAIN_BARE_JID_1, PASSWORD);
+ mech.authenticate(USERNAME, "unusedFoo", JidTestUtil.DOMAIN_BARE_JID_1, PASSWORD, null);
AuthMechanism authMechanism = con.getSentPacket();
assertEquals(SCRAMSHA1Mechanism.NAME, authMechanism.getMechanism());
assertEquals(CLIENT_FIRST_MESSAGE, saslLayerString(authMechanism.getAuthenticationText()));
diff --git a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLDigestMD5Mechanism.java b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLDigestMD5Mechanism.java
index d7038e6ea..39d7dc430 100644
--- a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLDigestMD5Mechanism.java
+++ b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLDigestMD5Mechanism.java
@@ -25,6 +25,11 @@ public class SASLDigestMD5Mechanism extends SASLJavaXMechanism {
public static final String NAME = DIGESTMD5;
+ @Override
+ public boolean authzidSupported() {
+ return true;
+ }
+
public String getName() {
return NAME;
}
diff --git a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLExternalMechanism.java b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLExternalMechanism.java
index eff4697c4..f36bfd383 100644
--- a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLExternalMechanism.java
+++ b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLExternalMechanism.java
@@ -46,6 +46,11 @@ public class SASLExternalMechanism extends SASLJavaXMechanism {
public static final String NAME = EXTERNAL;
+ @Override
+ public boolean authzidSupported() {
+ return true;
+ }
+
@Override
public String getName() {
return EXTERNAL;
diff --git a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLGSSAPIMechanism.java b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLGSSAPIMechanism.java
index 2a95dd8eb..b82ac46a9 100644
--- a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLGSSAPIMechanism.java
+++ b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLGSSAPIMechanism.java
@@ -34,6 +34,11 @@ public class SASLGSSAPIMechanism extends SASLJavaXMechanism {
System.setProperty("java.security.auth.login.config","gss.conf");
}
+ @Override
+ public boolean authzidSupported() {
+ return true;
+ }
+
@Override
public String getName() {
return NAME;
diff --git a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLJavaXMechanism.java b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLJavaXMechanism.java
index 8a2848a3f..d9692b794 100644
--- a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLJavaXMechanism.java
+++ b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLJavaXMechanism.java
@@ -53,8 +53,12 @@ public abstract class SASLJavaXMechanism extends SASLMechanism {
throws SmackException {
String[] mechanisms = { getName() };
Map props = getSaslProps();
+ String authzid = null;
+ if (authorizationId != null) {
+ authzid = authorizationId.toString();
+ }
try {
- sc = Sasl.createSaslClient(mechanisms, null, "xmpp", getServerName().toString(), props,
+ sc = Sasl.createSaslClient(mechanisms, authzid, "xmpp", getServerName().toString(), props,
new CallbackHandler() {
@Override
public void handle(Callback[] callbacks) throws IOException,
diff --git a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLPlainMechanism.java b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLPlainMechanism.java
index 83ee3e437..278d60f1d 100644
--- a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLPlainMechanism.java
+++ b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLPlainMechanism.java
@@ -29,6 +29,11 @@ public class SASLPlainMechanism extends SASLJavaXMechanism {
return NAME;
}
+ @Override
+ public boolean authzidSupported() {
+ return true;
+ }
+
@Override
public int getPriority() {
return 400;
diff --git a/smack-sasl-javax/src/test/java/org/jivesoftware/smack/sasl/javax/SASLDigestMD5Test.java b/smack-sasl-javax/src/test/java/org/jivesoftware/smack/sasl/javax/SASLDigestMD5Test.java
index 125aa3699..bb2aac7c5 100644
--- a/smack-sasl-javax/src/test/java/org/jivesoftware/smack/sasl/javax/SASLDigestMD5Test.java
+++ b/smack-sasl-javax/src/test/java/org/jivesoftware/smack/sasl/javax/SASLDigestMD5Test.java
@@ -30,6 +30,11 @@ public class SASLDigestMD5Test extends DigestMd5SaslTest {
@Test
public void testDigestMD5() throws NotConnectedException, SmackException, InterruptedException, XmppStringprepException {
- runTest();
+ runTest(false);
+ }
+
+ @Test
+ public void testDigestMD5Authzid() throws NotConnectedException, SmackException, InterruptedException, XmppStringprepException {
+ runTest(true);
}
}
diff --git a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java
index 1c0a8370a..e72ba840e 100644
--- a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java
+++ b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java
@@ -83,6 +83,11 @@ public class SASLDigestMD5Mechanism extends SASLMechanism {
return new SASLDigestMD5Mechanism();
}
+ @Override
+ public boolean authzidSupported() {
+ return true;
+ }
+
@Override
public void checkIfSuccessfulOrThrow() throws SmackException {
@@ -141,7 +146,14 @@ public class SASLDigestMD5Mechanism extends SASLMechanism {
String responseValue = calcResponse(DigestType.ClientResponse);
// @formatter:off
// See RFC 2831 2.1.2 digest-response
+ String authzid;
+ if (authorizationId == null) {
+ authzid = "";
+ } else {
+ authzid = ",authzid=\"" + authorizationId + '"';
+ }
String saslString = "username=\"" + authenticationId + '"'
+ + authzid
+ ",realm=\"" + serviceName + '"'
+ ",nonce=\"" + nonce + '"'
+ ",cnonce=\"" + cnonce + '"'
diff --git a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLExternalMechanism.java b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLExternalMechanism.java
index 3f6670f49..3869fc7f2 100644
--- a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLExternalMechanism.java
+++ b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLExternalMechanism.java
@@ -40,6 +40,10 @@ public class SASLExternalMechanism extends SASLMechanism {
@Override
protected byte[] getAuthenticationText() throws SmackException {
+ if (authorizationId != null) {
+ return toBytes(authorizationId.toString());
+ }
+
if (StringUtils.isNullOrEmpty(authenticationId)) {
return null;
}
@@ -67,4 +71,9 @@ public class SASLExternalMechanism extends SASLMechanism {
// No check performed
}
+ @Override
+ public boolean authzidSupported() {
+ return true;
+ }
+
}
diff --git a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLPlainMechanism.java b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLPlainMechanism.java
index 770c29040..5bbf1b35c 100644
--- a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLPlainMechanism.java
+++ b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLPlainMechanism.java
@@ -34,7 +34,13 @@ public class SASLPlainMechanism extends SASLMechanism {
@Override
protected byte[] getAuthenticationText() throws SmackException {
// concatenate and encode username (authcid) and password
- byte[] authcid = toBytes('\u0000' + authenticationId);
+ String authzid;
+ if (authorizationId == null) {
+ authzid = "";
+ } else {
+ authzid = authorizationId.toString();
+ }
+ byte[] authcid = toBytes(authzid + '\u0000' + authenticationId);
byte[] passw = toBytes('\u0000' + password);
return ByteUtils.concact(authcid, passw);
@@ -59,4 +65,9 @@ public class SASLPlainMechanism extends SASLMechanism {
public void checkIfSuccessfulOrThrow() throws SmackException {
// No check performed
}
+
+ @Override
+ public boolean authzidSupported() {
+ return true;
+ }
}
diff --git a/smack-sasl-provided/src/test/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Test.java b/smack-sasl-provided/src/test/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Test.java
index 75f1c8d03..c68eb346c 100644
--- a/smack-sasl-provided/src/test/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Test.java
+++ b/smack-sasl-provided/src/test/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Test.java
@@ -29,6 +29,11 @@ public class SASLDigestMD5Test extends DigestMd5SaslTest {
@Test
public void testDigestMD5() throws NotConnectedException, SmackException, InterruptedException, XmppStringprepException {
- runTest();
+ runTest(false);
+ }
+
+ @Test
+ public void testDigestMD5Authzid() throws NotConnectedException, SmackException, InterruptedException, XmppStringprepException {
+ runTest(true);
}
}
diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java
index 664ce777f..4cc705fc6 100644
--- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java
+++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java
@@ -376,7 +376,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
protected synchronized void loginInternal(String username, String password, Resourcepart resource) throws XMPPException,
SmackException, IOException, InterruptedException {
// Authenticate using SASL
- saslAuthentication.authenticate(username, password);
+ saslAuthentication.authenticate(username, password, config.getAuthzid());
// If compression is enabled then request the server to use stream compression. XEP-170
// recommends to perform stream compression before resource binding.