Add a method to authenticate via OAuth for Google accounts.
authorDa Risk <da_risk@beem-project.com>
Mon, 24 Sep 2012 20:56:26 +0200
changeset 990 b8430fb9b6dd
parent 989 1d65e00847a1
child 991 9a579769bb05
Add a method to authenticate via OAuth for Google accounts.
AndroidManifest.xml
src/com/beem/project/beem/service/auth/AccountAuthenticator.java
src/com/beem/project/beem/smack/sasl/SASLGoogleOAuth2Mechanism.java
src/com/beem/project/beem/smack/sasl/package-info.java
--- a/AndroidManifest.xml	Sun Sep 23 03:22:37 2012 +0200
+++ b/AndroidManifest.xml	Mon Sep 24 20:56:26 2012 +0200
@@ -3,8 +3,8 @@
 	package="com.beem.project.beem" android:versionCode="12"
 	android:versionName="0.1.7"	android:installLocation="auto">
 
-    <uses-sdk android:minSdkVersion="5" android:targetSdkVersion="7" />
-    <uses-feature name="android.hardware.touchscreen" required="false" />
+	<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="7" />
+	<uses-feature name="android.hardware.touchscreen" required="false" />
 	
 	<permission android:permissionGroup="android.permission-group.NETWORK"
 		android:label="BeemService" android:description="@string/BeemServiceDescription"
@@ -13,6 +13,8 @@
 	<uses-permission android:name="android.permission.VIBRATE"/>
 	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+	<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
+	<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
 	<uses-permission android:name="com.beem.project.beem.BEEM_SERVICE"/>
 	
 	<supports-screens android:largeScreens="true"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/beem/project/beem/service/auth/AccountAuthenticator.java	Mon Sep 24 20:56:26 2012 +0200
@@ -0,0 +1,145 @@
+/*
+    BEEM is a videoconference application on the Android Platform.
+
+    Copyright (C) 2009-2012 by Frederic-Charles Barthelery,
+                               Nikita Kozlov,
+                               Vincent Veronis.
+
+    This file is part of BEEM.
+
+    BEEM is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    BEEM 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with BEEM.  If not, see <http://www.gnu.org/licenses/>.
+
+    Please send bug reports with examples or suggestions to
+    contact@beem-project.com or http://www.beem-project.com/
+
+*/
+package com.beem.project.beem.service.auth;
+
+import java.io.IOException;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.content.Context;
+import android.util.Log;
+
+import org.apache.harmony.javax.security.auth.callback.Callback;
+import org.apache.harmony.javax.security.auth.callback.CallbackHandler;
+import org.apache.harmony.javax.security.auth.callback.NameCallback;
+import org.apache.harmony.javax.security.auth.callback.PasswordCallback;
+import org.apache.harmony.javax.security.auth.callback.UnsupportedCallbackException;
+import org.apache.harmony.javax.security.sasl.RealmCallback;
+import org.jivesoftware.smack.util.StringUtils;
+
+/**
+ * The AccountAuthenticator use an Android Account to authenticate.
+ */
+public class AccountAuthenticator implements CallbackHandler {
+    private static final String GOOGLE_TOKEN_TYPE = "oauth2:https://www.googleapis.com/auth/googletalk";
+    private static final String TAG = AccountAuthenticator.class.getSimpleName();
+
+    private AccountManager accountMgr;
+    private Account account;
+
+    /**
+     * Create an AccountAuthenticator.
+     *
+     * @param context the Android context
+     * @param account the account to use
+     *
+     */
+    public AccountAuthenticator(final Context context, final Account account) {
+	accountMgr = AccountManager.get(context);
+	this.account = account;
+    }
+
+    @Override
+    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+	String tmpJid = account.name;
+	String service = StringUtils.parseServer(tmpJid);
+
+	for (int i = 0; i < callbacks.length; i++) {
+	    if (callbacks[i] instanceof NameCallback) {
+		String authenticationId = StringUtils.parseName(tmpJid);
+		if (useFullJid(account)) {
+		    authenticationId = tmpJid;
+		}
+		NameCallback ncb = (NameCallback) callbacks[i];
+		ncb.setName(authenticationId);
+	    } else if (callbacks[i] instanceof PasswordCallback) {
+		PasswordCallback pcb = (PasswordCallback) callbacks[i];
+		// skip if password is asked for PKCS11 (SSL keystore)
+		String prompt = pcb.getPrompt();
+		if (prompt != null && prompt.startsWith("PKCS11 Password:"))
+		    continue;
+		String password;
+		if (useToken(account))
+		    password = getToken();
+		else
+		    password = accountMgr.getPassword(account);
+		if (password == null)
+		    password = "";
+		pcb.setPassword(password.toCharArray());
+	    } else if (callbacks[i] instanceof RealmCallback) {
+		RealmCallback rcb = (RealmCallback) callbacks[i];
+		rcb.setText(service);
+	    } else {
+		throw new UnsupportedCallbackException(callbacks[i]);
+	    }
+	}
+    }
+
+    /**
+     * Test if the accout use the full jid to authenticate.
+     *
+     * @param accountt the account to test
+     *
+     * @return true if the account use full jid false otherwise
+     */
+    private boolean useFullJid(Account accountt) {
+	String type = accountt.type;
+	return "com.google".equals(type);
+    }
+
+    /**
+     * Test if the account use authentication token.
+     *
+     * @param accountt the account to test
+     *
+     * @return true if the account use token false otherwise
+     */
+    private boolean useToken(Account accountt) {
+	String type = accountt.type;
+	return "com.google".equals(type);
+    }
+
+    /**
+     * Get a authentication token from the Account.
+     *
+     * @return the token or en empty string if an error occurs
+     */
+    private String getToken() {
+	try {
+	    return accountMgr.blockingGetAuthToken(account, GOOGLE_TOKEN_TYPE, true);
+	} catch (OperationCanceledException e) {
+	    Log.v(TAG, "Token request canceled", e);
+	} catch (AuthenticatorException e) {
+	    Log.d(TAG, "Unable to get token", e);
+	} catch (IOException e) {
+	    Log.d(TAG, "Unable to get token", e);
+	}
+	return "";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/beem/project/beem/smack/sasl/SASLGoogleOAuth2Mechanism.java	Mon Sep 24 20:56:26 2012 +0200
@@ -0,0 +1,141 @@
+/*
+    BEEM is a videoconference application on the Android Platform.
+
+    Copyright (C) 2009-2012 by Frederic-Charles Barthelery,
+                               Nikita Kozlov,
+                               Vincent Veronis.
+
+    This file is part of BEEM.
+
+    BEEM is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    BEEM 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with BEEM.  If not, see <http://www.gnu.org/licenses/>.
+
+    Please send bug reports with examples or suggestions to
+    contact@beem-project.com or http://www.beem-project.com/
+
+*/
+package com.beem.project.beem.smack.sasl;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import de.measite.smack.Sasl;
+
+import org.apache.harmony.javax.security.auth.callback.CallbackHandler;
+import org.apache.harmony.javax.security.sasl.SaslException;
+import org.jivesoftware.smack.SASLAuthentication;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.sasl.SASLMechanism;
+import org.jivesoftware.smack.util.Base64;
+
+/**
+ * An implementation of the SASL OAuth2 mechanism made by Google.
+ * The extension is described here :
+ * https://developers.google.com/talk/jep_extensions/oauth
+ */
+public class SASLGoogleOAuth2Mechanism extends SASLMechanism {
+
+    /**
+     * The name of the Google Oauth mechanism.
+     */
+    public static final String MECHANISM_NAME = "X-OAUTH2";
+
+    /**
+     * Create a SASLGoogleOAuth2Mechanism.
+     *
+     * @param saslAuthentication the smack SASLAuthentication.
+     *
+     */
+    public SASLGoogleOAuth2Mechanism(final SASLAuthentication saslAuthentication) {
+	super(saslAuthentication);
+    }
+
+    @Override
+    public void authenticate(String username, String host, String password) throws IOException, XMPPException {
+	//Since we were not provided with a CallbackHandler, we will use our own with the given
+	//information
+
+	//Set the authenticationID as the username, since they must be the same in this case.
+	this.authenticationId = username;
+	this.password = password;
+	this.hostname = host;
+
+	String[] mechanisms = {"PLAIN" };
+	Map<String, String> props = new HashMap<String, String>();
+	sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, this);
+	authenticate();
+    }
+
+    @Override
+    public void authenticate(String username, String host, CallbackHandler cbh) throws IOException, XMPPException {
+	String[] mechanisms = {"PLAIN" };
+	Map<String, String> props = new HashMap<String, String>();
+	sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, cbh);
+	authenticate();
+    }
+
+    @Override
+    protected void authenticate() throws IOException, XMPPException {
+	String authenticationText = null;
+	try {
+	    if (sc.hasInitialResponse()) {
+		byte[] response = sc.evaluateChallenge(new byte[0]);
+		authenticationText = Base64.encodeBytes(response, Base64.DONT_BREAK_LINES);
+	    }
+	} catch (SaslException e) {
+	    throw new XMPPException("SASL authentication failed", e);
+	}
+
+	// Send the authentication to the server
+	getSASLAuthentication().send(new GoogleOAuthMechanism(authenticationText));
+    }
+
+    @Override
+    protected String getName() {
+	return MECHANISM_NAME;
+    }
+
+    /**
+     * Initiating SASL authentication by select a mechanism.
+     */
+    public static class GoogleOAuthMechanism extends Packet {
+	private final String authenticationText;
+
+	/**
+	 * Create a GoogleOAuthMechanism.
+	 *
+	 * @param authenticationText the authentification token
+	 *
+	 */
+	public GoogleOAuthMechanism(final String authenticationText) {
+	    this.authenticationText = authenticationText;
+	}
+
+	@Override
+	public String toXML() {
+	    StringBuilder stanza = new StringBuilder();
+	    stanza.append("<auth mechanism=\"").append(MECHANISM_NAME);
+	    stanza.append("\" xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" "
+		    + "auth:service=\"oauth2\" "
+		    + "xmlns:auth=\"http://www.google.com/talk/protocol/auth\">");
+	    if (authenticationText != null
+		    && authenticationText.trim().length() > 0) {
+		stanza.append(authenticationText);
+	    }
+	    stanza.append("</auth>");
+	    return stanza.toString();
+	}
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/beem/project/beem/smack/sasl/package-info.java	Mon Sep 24 20:56:26 2012 +0200
@@ -0,0 +1,31 @@
+/*
+    BEEM is a videoconference application on the Android Platform.
+
+    Copyright (C) 2009-2012 by Frederic-Charles Barthelery,
+                               Nikita Kozlov,
+                               Vincent Veronis.
+
+    This file is part of BEEM.
+
+    BEEM is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    BEEM 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with BEEM.  If not, see <http://www.gnu.org/licenses/>.
+
+    Please send bug reports with examples or suggestions to
+    contact@beem-project.com or http://www.beem-project.com/
+
+*/
+
+/**
+ * This package contains implementations of different SASL mechanism.
+ */
+package com.beem.project.beem.smack.sasl;