Bug 1147275 - Delete cached OAuth tokens when Firefox Account is removed. r=nalexander

========

a31f669a88
Author: vivek <vivekb.balakrishnan@gmail.com>
    Bug 1147275 - Part 3: Try to delete cached OAuth tokens when account is removed.

========

d33fbcaa80
Author: vivek <vivekb.balakrishnan@gmail.com>
Date:   Mon Apr 6 21:27:45 2015 +0300

    Bug 1147275 - Part 2: Include cached OAuth tokens in deleted account intent.

========

71230ab56a
Author: vivek <vivekb.balakrishnan@gmail.com>
Date:   Mon Apr 6 20:41:20 2015 +0300

    Bug 1147275 - Part 1: Add token deletion API to OAuth client.

========

93b6d074c1
Author: vivek <vivekb.balakrishnan@gmail.com>
Date:   Mon Apr 6 17:44:17 2015 +0300

    Bug 1147275 - Pre: Extract getOAuthServerURI.

--HG--
extra : rebase_source : de772d7102ebb29b7a058549e07a619dbecc085c
This commit is contained in:
vivek 2015-04-06 21:30:40 +03:00
Родитель 87ca3080b6
Коммит 7d82c01981
7 изменённых файлов: 120 добавлений и 16 удалений

Просмотреть файл

@ -32,6 +32,7 @@ public class FxAccountOAuthClient10 extends FxAccountAbstractClient {
protected static final String JSON_KEY_RESPONSE_TYPE = "response_type";
protected static final String JSON_KEY_SCOPE = "scope";
protected static final String JSON_KEY_STATE = "state";
protected static final String JSON_KEY_TOKEN = "token";
protected static final String JSON_KEY_TOKEN_TYPE = "token_type";
// access_token: A string that can be used for authorized requests to service providers.
@ -98,4 +99,31 @@ public class FxAccountOAuthClient10 extends FxAccountAbstractClient {
post(resource, requestBody, delegate);
}
public void deleteToken(final String token, final RequestDelegate<Void> delegate) {
final BaseResource resource;
try {
resource = new BaseResource(new URI(serverURI + "destroy"));
} catch (URISyntaxException e) {
invokeHandleError(delegate, e);
return;
}
resource.delegate = new ResourceDelegate<Void>(resource, delegate) {
@Override
public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) {
try {
delegate.handleSuccess(null);
return;
} catch (Exception e) {
delegate.handleError(e);
return;
}
}
};
final ExtendedJSONObject requestBody = new ExtendedJSONObject();
requestBody.put(JSON_KEY_TOKEN, token);
post(resource, requestBody, delegate);
}
}

Просмотреть файл

@ -52,7 +52,7 @@ public class FxAccountConstants {
* can be received only by Firefox channels sharing the same Android Firefox
* Account type.
* <p>
* See {@link org.mozilla.gecko.fxa.AndroidFxAccount#makeDeletedAccountIntent(android.content.Context, android.accounts.Account)}
* See {@link org.mozilla.gecko.fxa.AndroidFxAccount#makeDeletedAccountIntent()}
* for contents of the intent.
*
* See bug 790931 for additional information in the context of Sync.
@ -66,6 +66,8 @@ public class FxAccountConstants {
public static final String ACCOUNT_DELETED_INTENT_VERSION_KEY = "account_deleted_intent_version";
public static final String ACCOUNT_DELETED_INTENT_ACCOUNT_KEY = "account_deleted_intent_account";
public static final String ACCOUNT_OAUTH_SERVICE_ENDPOINT_KEY = "account_oauth_service_endpoint";
public static final String ACCOUNT_DELETED_INTENT_ACCOUNT_AUTH_TOKENS = "account_deleted_intent_auth_tokens";
/**
* This signing-level permission protects broadcast intents that should be

Просмотреть файл

@ -11,11 +11,13 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.background.ReadingListConstants;
import org.mozilla.gecko.background.common.GlobalConstants;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountUtils;
@ -73,6 +75,19 @@ public class AndroidFxAccount {
public static final String BUNDLE_KEY_STATE_LABEL = "stateLabel";
public static final String BUNDLE_KEY_STATE = "state";
// Services may request OAuth tokens from the Firefox Account dynamically.
// Each such token is prefixed with "oauth::" and a service-dependent scope.
// Such tokens should be destroyed when the account is removed from the device.
// This list collects all the known "oauth::" token types in order to delete them when necessary.
private static final List<String> KNOWN_OAUTH_TOKEN_TYPES;
static {
final List<String> list = new ArrayList<>();
if (AppConstants.MOZ_ANDROID_READING_LIST_SERVICE) {
list.add(ReadingListConstants.AUTH_TOKEN_TYPE);
}
KNOWN_OAUTH_TOKEN_TYPES = Collections.unmodifiableList(list);
}
public static final Map<String, Boolean> DEFAULT_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP;
static {
final HashMap<String, Boolean> m = new HashMap<String, Boolean>();
@ -281,6 +296,15 @@ public class AndroidFxAccount {
return accountManager.getUserData(account, ACCOUNT_KEY_TOKEN_SERVER);
}
public String getOAuthServerURI() {
// Allow testing against stage.
if (FxAccountConstants.STAGE_AUTH_SERVER_ENDPOINT.equals(getAccountServerURI())) {
return FxAccountConstants.STAGE_OAUTH_SERVER_ENDPOINT;
} else {
return FxAccountConstants.DEFAULT_OAUTH_SERVER_ENDPOINT;
}
}
private String constructPrefsPath(String product, long version, String extra) throws GeneralSecurityException, UnsupportedEncodingException {
String profile = getProfile();
String username = account.name;
@ -599,18 +623,29 @@ public class AndroidFxAccount {
/**
* Create an intent announcing that a Firefox account will be deleted.
*
* @param context
* Android context.
* @param account
* Android account being removed.
* @return <code>Intent</code> to broadcast.
*/
public static Intent makeDeletedAccountIntent(final Context context, final Account account) {
public Intent makeDeletedAccountIntent() {
final Intent intent = new Intent(FxAccountConstants.ACCOUNT_DELETED_ACTION);
final List<String> tokens = new ArrayList<>();
intent.putExtra(FxAccountConstants.ACCOUNT_DELETED_INTENT_VERSION_KEY,
Long.valueOf(FxAccountConstants.ACCOUNT_DELETED_INTENT_VERSION));
intent.putExtra(FxAccountConstants.ACCOUNT_DELETED_INTENT_ACCOUNT_KEY, account.name);
// Get the tokens from AccountManager. Note: currently, only reading list service supports OAuth. The following logic will
// be extended in future to support OAuth for other services.
for (String tokenKey : KNOWN_OAUTH_TOKEN_TYPES) {
final String authToken = accountManager.peekAuthToken(account, tokenKey);
if (authToken != null) {
tokens.add(authToken);
}
}
// Update intent with tokens and service URI.
intent.putExtra(FxAccountConstants.ACCOUNT_OAUTH_SERVICE_ENDPOINT_KEY, getOAuthServerURI());
// Deleted broadcasts are package-private, so there's no security risk include the tokens in the extras
intent.putExtra(FxAccountConstants.ACCOUNT_DELETED_INTENT_ACCOUNT_AUTH_TOKENS, tokens.toArray(new String[tokens.size()]));
return intent;
}

Просмотреть файл

@ -182,15 +182,7 @@ public class FxAccountAuthenticator extends AbstractAccountAuthenticator {
Logger.info(LOG_TAG, "Fetching oauth token with scope: " + scope);
final Responder responder = new Responder(response, fxAccount);
// Allow testing against stage.
final boolean usingStageAuthServer = FxAccountConstants.STAGE_AUTH_SERVER_ENDPOINT.equals(fxAccount.getAccountServerURI());
final String oauthServerUri;
if (usingStageAuthServer) {
oauthServerUri = FxAccountConstants.STAGE_OAUTH_SERVER_ENDPOINT;
} else {
oauthServerUri = FxAccountConstants.DEFAULT_OAUTH_SERVER_ENDPOINT;
}
final String oauthServerUri = fxAccount.getOAuthServerURI();
final String audience;
try {
@ -360,7 +352,8 @@ public class FxAccountAuthenticator extends AbstractAccountAuthenticator {
//
// Broadcast intents protected with permissions are secure, so it's okay
// to include private information such as a password.
final Intent intent = AndroidFxAccount.makeDeletedAccountIntent(context, account);
final AndroidFxAccount androidFxAccount = new AndroidFxAccount(context, account);
final Intent intent = androidFxAccount.makeDeletedAccountIntent();
Logger.info(LOG_TAG, "Account named " + account.name + " being removed; " +
"broadcasting secure intent " + intent.getAction() + ".");
context.sendBroadcast(intent, FxAccountConstants.PER_ACCOUNT_TYPE_PERMISSION);

Просмотреть файл

@ -4,7 +4,12 @@
package org.mozilla.gecko.fxa.receivers;
import java.util.concurrent.Executor;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClient;
import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClientException.FxAccountAbstractClientRemoteException;
import org.mozilla.gecko.background.fxa.oauth.FxAccountOAuthClient10;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.sync.FxAccountNotificationManager;
import org.mozilla.gecko.fxa.sync.FxAccountSyncAdapter;
@ -108,6 +113,47 @@ public class FxAccountDeletedService extends IntentService {
// Bug 1147275: Delete cached oauth tokens. There's no way to query all
// oauth tokens from Android, so this is tricky to do comprehensively. We
// can query, individually, for specific oauth tokens to delete, however.
final String oauthServerURI = intent.getStringExtra(FxAccountConstants.ACCOUNT_OAUTH_SERVICE_ENDPOINT_KEY);
final String[] tokens = intent.getStringArrayExtra(FxAccountConstants.ACCOUNT_DELETED_INTENT_ACCOUNT_AUTH_TOKENS);
if (oauthServerURI != null && tokens != null) {
final Executor directExecutor = new Executor() {
@Override
public void execute(Runnable runnable) {
runnable.run();
}
};
final FxAccountOAuthClient10 oauthClient = new FxAccountOAuthClient10(oauthServerURI, directExecutor);
for (String token : tokens) {
if (token == null) {
Logger.error(LOG_TAG, "Cached OAuth token is null; should never happen. Ignoring.");
continue;
}
try {
oauthClient.deleteToken(token, new FxAccountAbstractClient.RequestDelegate<Void>() {
@Override
public void handleSuccess(Void result) {
Logger.info(LOG_TAG, "Successfully deleted cached OAuth token.");
}
@Override
public void handleError(Exception e) {
Logger.error(LOG_TAG, "Failed to delete cached OAuth token; ignoring.", e);
}
@Override
public void handleFailure(FxAccountAbstractClientRemoteException e) {
Logger.error(LOG_TAG, "Exception during cached OAuth token deletion; ignoring.", e);
}
});
} catch (Exception e) {
Logger.error(LOG_TAG, "Exception during cached OAuth token deletion; ignoring.", e);
}
}
} else {
Logger.error(LOG_TAG, "Cached OAuth server URI is null or cached OAuth tokens are null; ignoring.");
}
}
public static void deletePickle(final Context context) {

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 540 B

После

Ширина:  |  Высота:  |  Размер: 540 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 2.2 KiB

После

Ширина:  |  Высота:  |  Размер: 2.2 KiB