Home | History | Annotate | Download | only in authenticator
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package com.example.android.samplesync.authenticator;
     18 
     19 import com.example.android.samplesync.Constants;
     20 import com.example.android.samplesync.client.NetworkUtilities;
     21 
     22 import android.accounts.AbstractAccountAuthenticator;
     23 import android.accounts.Account;
     24 import android.accounts.AccountAuthenticatorResponse;
     25 import android.accounts.AccountManager;
     26 import android.accounts.NetworkErrorException;
     27 import android.content.Context;
     28 import android.content.Intent;
     29 import android.os.Bundle;
     30 import android.text.TextUtils;
     31 import android.util.Log;
     32 
     33 /**
     34  * This class is an implementation of AbstractAccountAuthenticator for
     35  * authenticating accounts in the com.example.android.samplesync domain. The
     36  * interesting thing that this class demonstrates is the use of authTokens as
     37  * part of the authentication process. In the account setup UI, the user enters
     38  * their username and password. But for our subsequent calls off to the service
     39  * for syncing, we want to use an authtoken instead - so we're not continually
     40  * sending the password over the wire. getAuthToken() will be called when
     41  * SyncAdapter calls AccountManager.blockingGetAuthToken(). When we get called,
     42  * we need to return the appropriate authToken for the specified account. If we
     43  * already have an authToken stored in the account, we return that authToken. If
     44  * we don't, but we do have a username and password, then we'll attempt to talk
     45  * to the sample service to fetch an authToken. If that fails (or we didn't have
     46  * a username/password), then we need to prompt the user - so we create an
     47  * AuthenticatorActivity intent and return that. That will display the dialog
     48  * that prompts the user for their login information.
     49  */
     50 class Authenticator extends AbstractAccountAuthenticator {
     51 
     52     /** The tag used to log to adb console. **/
     53     private static final String TAG = "Authenticator";
     54 
     55     // Authentication Service context
     56     private final Context mContext;
     57 
     58     public Authenticator(Context context) {
     59         super(context);
     60         mContext = context;
     61     }
     62 
     63     @Override
     64     public Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
     65             String authTokenType, String[] requiredFeatures, Bundle options) {
     66         Log.v(TAG, "addAccount()");
     67         final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
     68         intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
     69         final Bundle bundle = new Bundle();
     70         bundle.putParcelable(AccountManager.KEY_INTENT, intent);
     71         return bundle;
     72     }
     73 
     74     @Override
     75     public Bundle confirmCredentials(
     76             AccountAuthenticatorResponse response, Account account, Bundle options) {
     77         Log.v(TAG, "confirmCredentials()");
     78         return null;
     79     }
     80 
     81     @Override
     82     public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
     83         Log.v(TAG, "editProperties()");
     84         throw new UnsupportedOperationException();
     85     }
     86 
     87     @Override
     88     public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
     89             String authTokenType, Bundle loginOptions) throws NetworkErrorException {
     90         Log.v(TAG, "getAuthToken()");
     91 
     92         // If the caller requested an authToken type we don't support, then
     93         // return an error
     94         if (!authTokenType.equals(Constants.AUTHTOKEN_TYPE)) {
     95             final Bundle result = new Bundle();
     96             result.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType");
     97             return result;
     98         }
     99 
    100         // Extract the username and password from the Account Manager, and ask
    101         // the server for an appropriate AuthToken.
    102         final AccountManager am = AccountManager.get(mContext);
    103         final String password = am.getPassword(account);
    104         if (password != null) {
    105             final String authToken = NetworkUtilities.authenticate(account.name, password);
    106             if (!TextUtils.isEmpty(authToken)) {
    107                 final Bundle result = new Bundle();
    108                 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
    109                 result.putString(AccountManager.KEY_ACCOUNT_TYPE, Constants.ACCOUNT_TYPE);
    110                 result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
    111                 return result;
    112             }
    113         }
    114 
    115         // If we get here, then we couldn't access the user's password - so we
    116         // need to re-prompt them for their credentials. We do that by creating
    117         // an intent to display our AuthenticatorActivity panel.
    118         final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
    119         intent.putExtra(AuthenticatorActivity.PARAM_USERNAME, account.name);
    120         intent.putExtra(AuthenticatorActivity.PARAM_AUTHTOKEN_TYPE, authTokenType);
    121         intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
    122         final Bundle bundle = new Bundle();
    123         bundle.putParcelable(AccountManager.KEY_INTENT, intent);
    124         return bundle;
    125     }
    126 
    127     @Override
    128     public String getAuthTokenLabel(String authTokenType) {
    129         // null means we don't support multiple authToken types
    130         Log.v(TAG, "getAuthTokenLabel()");
    131         return null;
    132     }
    133 
    134     @Override
    135     public Bundle hasFeatures(
    136             AccountAuthenticatorResponse response, Account account, String[] features) {
    137         // This call is used to query whether the Authenticator supports
    138         // specific features. We don't expect to get called, so we always
    139         // return false (no) for any queries.
    140         Log.v(TAG, "hasFeatures()");
    141         final Bundle result = new Bundle();
    142         result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
    143         return result;
    144     }
    145 
    146     @Override
    147     public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account,
    148             String authTokenType, Bundle loginOptions) {
    149         Log.v(TAG, "updateCredentials()");
    150         return null;
    151     }
    152 }
    153