# HG changeset patch # User Da Risk # Date 1348512986 -7200 # Node ID b8430fb9b6ddfab7432695222a6169aee0283cc1 # Parent 1d65e00847a1c8aedbf8260f0c3b0d7722574ac4 Add a method to authenticate via OAuth for Google accounts. diff -r 1d65e00847a1 -r b8430fb9b6dd AndroidManifest.xml --- 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"> - - + + + + . + + 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 ""; + } +} diff -r 1d65e00847a1 -r b8430fb9b6dd src/com/beem/project/beem/smack/sasl/SASLGoogleOAuth2Mechanism.java --- /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 . + + 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 props = new HashMap(); + 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 props = new HashMap(); + 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(""); + if (authenticationText != null + && authenticationText.trim().length() > 0) { + stanza.append(authenticationText); + } + stanza.append(""); + return stanza.toString(); + } + } +} diff -r 1d65e00847a1 -r b8430fb9b6dd src/com/beem/project/beem/smack/sasl/package-info.java --- /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 . + + 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;