{"id":1031,"date":"2013-06-18T01:00:00","date_gmt":"2013-06-17T23:00:00","guid":{"rendered":"https:\/\/www.fussylogic.co.uk\/blog\/?p=1031"},"modified":"2014-10-10T12:08:26","modified_gmt":"2014-10-10T11:08:26","slug":"android-authenticators-i","status":"publish","type":"post","link":"https:\/\/www.fussylogic.co.uk\/blog\/?p=1031","title":{"rendered":"Android Authenticators I"},"content":{"rendered":"<p>I found a blog that gave an <a href=\"http:\/\/udinic.wordpress.com\/2013\/04\/24\/write-your-own-android-authenticator\/\">example<\/a> of how to make a custom authenticator for Android. I didn\u00e2\u20ac\u2122t find it very clear, so this article covers my understanding that I\u00e2\u20ac\u2122ve pulled this together from the <a href=\"https:\/\/github.com\/Udinic\/AccountAuthenticator\">example<\/a> given by that blog author, and from the Android documentation, and then building something that works.<\/p>\n<p>To make a <a href=\"http:\/\/developer.android.com\/reference\/android\/accounts\/AbstractAccountAuthenticator.html\">custom account type<\/a> available in Android\u00e2\u20ac\u2122s account page, your app must supply an authenticator <code>&lt;service&gt;<\/code> in your <code>AndroidManifest.xml<\/code> that includes the following intent filter:<\/p>\n<pre class=\"sourceCode xml\"><code class=\"sourceCode xml\">    <span class=\"kw\">&lt;intent-filter&gt;<\/span>\n        <span class=\"kw\">&lt;action<\/span><span class=\"ot\"> android:name=<\/span><span class=\"st\">&quot;android.accounts.AccountAuthenticator&quot;<\/span> <span class=\"kw\">\/&gt;<\/span>\n    <span class=\"kw\">&lt;\/intent-filter&gt;<\/span><\/code><\/pre>\n<p>Which in turn will need the following permission:<\/p>\n<pre class=\"sourceCode xml\"><code class=\"sourceCode xml\">    <span class=\"kw\">&lt;uses-permission<\/span><span class=\"ot\"> android:name=<\/span><span class=\"st\">&quot;android.permission.AUTHENTICATE_ACCOUNTS&quot;<\/span> <span class=\"kw\">\/&gt;<\/span><\/code><\/pre>\n<p>When the OS finds support for this <code>Intent<\/code> in an app, it will then go looking for information about how it should display it on its settings screen. That\u00e2\u20ac\u2122s done by including the following in that same <code>&lt;service&gt;<\/code> section:<\/p>\n<pre class=\"sourceCode xml\"><code class=\"sourceCode xml\">    <span class=\"kw\">&lt;meta-data<\/span>\n<span class=\"ot\">        android:name=<\/span><span class=\"st\">&quot;android.accounts.AccountAuthenticator&quot;<\/span>\n<span class=\"ot\">        android:resource=<\/span><span class=\"st\">&quot;@xml\/authenticator&quot;<\/span> <span class=\"kw\">\/&gt;<\/span><\/code><\/pre>\n<p>The actual account description is then in the XML resource file referenced in this <code>meta-data<\/code> section, <code>res\/xml\/authenticator.xml<\/code> in this case. That file contains the actual help that the OS settings module needs:<\/p>\n<pre class=\"sourceCode xml\"><code class=\"sourceCode xml\">    <span class=\"kw\">&lt;?xml<\/span> version=&quot;1.0&quot; encoding=&quot;utf-8&quot;<span class=\"kw\">?&gt;<\/span>\n    <span class=\"kw\">&lt;account-authenticator<\/span><span class=\"ot\"> xmlns:android=<\/span><span class=\"st\">&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;<\/span>\n<span class=\"ot\">        android:accountPreferences=<\/span><span class=\"st\">&quot;@xml\/authenticator_preferences&quot;<\/span>\n<span class=\"ot\">        android:accountType=<\/span><span class=\"st\">&quot;@string\/authenticator_account_type&quot;<\/span>\n<span class=\"ot\">        android:label=<\/span><span class=\"st\">&quot;@string\/authenticator_label&quot;<\/span>\n<span class=\"ot\">        android:icon=<\/span><span class=\"st\">&quot;@drawable\/ic_launcher&quot;<\/span>\n<span class=\"ot\">        android:smallIcon=<\/span><span class=\"st\">&quot;@drawable\/ic_launcher&quot;<\/span> <span class=\"kw\">\/&gt;<\/span><\/code><\/pre>\n<p>The most important setting above is the <code>android:accountType<\/code> attribute. That\u00e2\u20ac\u2122s the unique name that causes your interface rather than any other (Google, Facebook, or Twitter, say) to be used when the user picks this type when they click \u00e2\u20ac\u0153Add Account\u00e2\u20ac\u009d \u00e2\u20ac\u201c it\u00e2\u20ac\u2122s best defined as a resource reference because you\u00e2\u20ac\u2122ll want it available in the code later.<\/p>\n<p>Note that we\u00e2\u20ac\u2122ve also referred to another resource, <code>res\/xml\/authenticator_preferences.xml<\/code>. This file defines the user interface for configuring the authentication service as a whole \u00e2\u20ac\u201c this is not the same thing as configuring an individual account, this is settings for the account type.<\/p>\n<pre class=\"sourceCode xml\"><code class=\"sourceCode xml\">    <span class=\"kw\">&lt;PreferenceScreen<\/span><span class=\"ot\"> xmlns:android=<\/span><span class=\"st\">&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;<\/span> <span class=\"kw\">&gt;<\/span>\n        <span class=\"kw\">&lt;PreferenceCategory<\/span><span class=\"ot\"> android:title=<\/span><span class=\"st\">&quot;FussyLogic Authenticator&quot;<\/span> <span class=\"kw\">\/&gt;<\/span>\n        <span class=\"kw\">&lt;CheckBoxPreference<\/span>\n<span class=\"ot\">            android:key=<\/span><span class=\"st\">&quot;isDebug&quot;<\/span>\n<span class=\"ot\">            android:summary=<\/span><span class=\"st\">&quot;Connecting to a debug server instead of production server&quot;<\/span>\n<span class=\"ot\">            android:title=<\/span><span class=\"st\">&quot;Use debug server&quot;<\/span> <span class=\"kw\">\/&gt;<\/span>\n        <span class=\"kw\">&lt;SwitchPreference<\/span>\n<span class=\"ot\">            android:key=<\/span><span class=\"st\">&quot;logsVerbose&quot;<\/span>\n<span class=\"ot\">            android:summary=<\/span><span class=\"st\">&quot;Increase logging verbosity for LogCat output&quot;<\/span>\n<span class=\"ot\">            android:title=<\/span><span class=\"st\">&quot;Debug Logs&quot;<\/span> <span class=\"kw\">\/&gt;<\/span>\n    <span class=\"kw\">&lt;\/PreferenceScreen&gt;<\/span><\/code><\/pre>\n<p>These are just examples, you could use whatever you want. I\u00e2\u20ac\u2122m missing one piece of information however \u00e2\u20ac\u201c how you access these settings. I\u00e2\u20ac\u2122d be delighted if someone more knowledgeable can tell me, as the Android documentation is lacking. It certainly seems possible, the Skype app does it.<\/p>\n<p>The presence of the <code>Service<\/code>, and the above settings are enough to make Android offer your authenticator when the user adds an account. The authenticator <a href=\"http:\/\/developer.android.com\/guide\/components\/services.html\"><code>Service<\/code><\/a> code itself is short:<\/p>\n<pre class=\"sourceCode java\"><code class=\"sourceCode java\">    <span class=\"kw\">package uk.co.fussylogic.accounttester;<\/span>\n    \n    <span class=\"kw\">import android.app.Service;<\/span>\n    <span class=\"kw\">import android.content.Intent;<\/span>\n    <span class=\"kw\">import android.os.IBinder;<\/span>\n    \n    <span class=\"kw\">public<\/span> <span class=\"kw\">class<\/span> AuthenticatorService <span class=\"kw\">extends<\/span> Service {\n        <span class=\"fu\">@Override<\/span>\n        <span class=\"kw\">public<\/span> IBinder <span class=\"fu\">onBind<\/span>(Intent intent) {\n            <span class=\"kw\">if<\/span> (intent.<span class=\"fu\">getAction<\/span>().<span class=\"fu\">equals<\/span>(\n                    android.<span class=\"fu\">accounts<\/span>.<span class=\"fu\">AccountManager<\/span>.<span class=\"fu\">ACTION_AUTHENTICATOR_INTENT<\/span>))\n                <span class=\"kw\">return<\/span> <span class=\"kw\">null<\/span>;\n            \n            AbstractAccountAuthenticator authenticator =\n                <span class=\"kw\">new<\/span> <span class=\"fu\">FussyLogicAuthenticator<\/span>(<span class=\"kw\">this<\/span>);\n            <span class=\"kw\">return<\/span> authenticator.<span class=\"fu\">getIBinder<\/span>();\n        }\n\n        <span class=\"co\">\/\/ --------------------------------<\/span>\n\n        <span class=\"kw\">public<\/span> <span class=\"kw\">class<\/span> FussyLogicAuthenticator <span class=\"kw\">extends<\/span> AbstractAccountAuthenticator {\n            <span class=\"kw\">protected<\/span> Context mContext;\n\n            <span class=\"kw\">public<\/span> <span class=\"fu\">FussyLogicAuthenticator<\/span>(Context context) {\n                <span class=\"kw\">super<\/span>(context);\n                <span class=\"kw\">this<\/span>.<span class=\"fu\">mContext<\/span> = context;\n            }\n\n            <span class=\"co\">\/\/ ... implement abstract methods ...<\/span>\n        };\n    }<\/code><\/pre>\n<p>Android calls <code>onBind()<\/code> in a <code>Service<\/code> when another component wants to connect to it for a dialogue (this is in contrast to <code>onStartCommand()<\/code>, which is used when the service only undertakes actions, it doesn\u00e2\u20ac\u2122t return answers). The <code>IBinder<\/code> class defines the RPC interface \u00e2\u20ac\u201c that is, it describes the functions that may be called by the binding client. This is the case for all bound services. In the particular case of an authenticator service, the <code>IBinder<\/code> class must be one that describes the calls that the Android accounts system requires. Fortunately, we needn\u00e2\u20ac\u2122t worry about creating an appropriate <code>IBinder<\/code>, Android can provide an appropriate <code>IBinder<\/code> using its <code>AbstractAccountAuthenticator<\/code> class, which you can see above as the call to <code>FussyLogicAuthenticator.getIBinder()<\/code> \u00e2\u20ac\u201c that is a child class of <code>AbstractAccountAuthenticator<\/code>, which, being abstract, <em>we<\/em> have to subclass to provide a concrete implementation, <code>FussyLogicAuthenticator<\/code>. There are a number of abstract methods that we have to implement, that the <code>AbstractAccountAuthenticator.getIBinder()<\/code> will have described. For the most part, the pattern for implementing these abstract methods is the same:<\/p>\n<ul>\n<li>If we know the answer already, return a <code>Bundle<\/code> with it<\/li>\n<li>If we don\u00e2\u20ac\u2122t know the answer, and need to ask the user something in order to find the answer, we create an <code>Intent<\/code> that will start an <code>Activity<\/code> to interact with the user. We return that <code>Intent<\/code> via the <code>Bundle<\/code>, under the key, <code>KEY_INTENT<\/code>. It\u00e2\u20ac\u2122s the caller\u00e2\u20ac\u2122s responsibility to make use of that intent; if the caller is Android, it will do so.<\/li>\n<li>Alternatively, if no synchronous response is possible, but an asynchronous response <em>is<\/em> possible then it may return null and use the given <code>AccountAuthenticatorResponse<\/code> to return it later.<\/li>\n<\/ul>\n<p>There is some crossover between these last two, the <code>Intent<\/code> you create to interact with the user will likely store the <code>AccountAuthenticatorResponse<\/code> so that the interactive process has somewhere to return its results once it\u00e2\u20ac\u2122s done \u00e2\u20ac\u201c i.e.\u00c2\u00a0it\u00e2\u20ac\u2122s an asynchronous response, it\u00e2\u20ac\u2122s just that it\u00e2\u20ac\u2122s an interactive asynchronous response.<\/p>\n<p>It\u00e2\u20ac\u2122s not unreasonable to create your <code>AbstractAccountAuthenticator<\/code> child class as an inner-class to <code>AuthenticatorService<\/code>; as it won\u00e2\u20ac\u2122t need to be visible to any other part of the application other than via the <code>IBinder<\/code>.<\/p>\n<h2 id=\"addaccount\"><code>addAccount()<\/code><\/h2>\n<p>Let\u00e2\u20ac\u2122s see that process in action in the <code>addAccount()<\/code> implementation. <code>addAccount()<\/code> is called (via the hoops we jumped through above) in response to the <code>Add Account<\/code> option being selected on the Android settings page.<\/p>\n<pre class=\"sourceCode java\"><code class=\"sourceCode java\">    <span class=\"fu\">@Override<\/span>\n    <span class=\"kw\">public<\/span> Bundle <span class=\"fu\">addAccount<\/span>(AccountAuthenticatorResponse response,\n            String accountType, String authTokenType,\n            String[] requiredFeatures, Bundle options)\n            <span class=\"kw\">throws<\/span> NetworkErrorException {\n        <span class=\"co\">\/\/ We absolutely cannot add an account without some information<\/span>\n        <span class=\"co\">\/\/ from the user; so we&#39;re definitely going to return an Intent<\/span>\n        <span class=\"co\">\/\/ via KEY_INTENT<\/span>\n        <span class=\"dt\">final<\/span> Bundle bundle = <span class=\"kw\">new<\/span> <span class=\"fu\">Bundle<\/span>();\n\n        <span class=\"co\">\/\/ We&#39;re going to use a LoginActivity to talk to the user (mContext<\/span>\n        <span class=\"co\">\/\/ we&#39;ll have noted on construction).<\/span>\n        <span class=\"dt\">final<\/span> Intent intent = <span class=\"kw\">new<\/span> <span class=\"fu\">Intent<\/span>(mContext, LoginActivity.<span class=\"fu\">class<\/span>);\n        \n        <span class=\"co\">\/\/ We can configure that activity however we wish via the<\/span>\n        <span class=\"co\">\/\/ Intent.  We&#39;ll set ARG_IS_ADDING_NEW_ACCOUNT so the Activity<\/span>\n        <span class=\"co\">\/\/ knows to ask for the account name as well<\/span>\n        intent.<span class=\"fu\">putExtra<\/span>(LoginActivity.<span class=\"fu\">ARG_ACCOUNT_TYPE<\/span>, accountType);\n        intent.<span class=\"fu\">putExtra<\/span>(LoginActivity.<span class=\"fu\">ARG_AUTH_TYPE<\/span>, authTokenType);\n        intent.<span class=\"fu\">putExtra<\/span>(LoginActivity.<span class=\"fu\">ARG_IS_ADDING_NEW_ACCOUNT<\/span>, <span class=\"kw\">true<\/span>);\n\n        <span class=\"co\">\/\/ It will also need to know how to send its response to the<\/span>\n        <span class=\"co\">\/\/ account manager; LoginActivity must derive from<\/span>\n        <span class=\"co\">\/\/ AccountAuthenticatorActivity, which will want this key set<\/span>\n        intent.<span class=\"fu\">putExtra<\/span>(AccountManager.<span class=\"fu\">KEY_ACCOUNT_AUTHENTICATOR_RESPONSE<\/span>,\n                response);\n\n        <span class=\"co\">\/\/ Wrap up this intent, and return it, which will cause the<\/span>\n        <span class=\"co\">\/\/ intent to be run<\/span>\n        bundle.<span class=\"fu\">putParcelable<\/span>(AccountManager.<span class=\"fu\">KEY_INTENT<\/span>, intent);\n        <span class=\"kw\">return<\/span> bundle;\n    }<\/code><\/pre>\n<p>The <code>Intent<\/code> will be run, which will in turn trigger our Activity, in this case called <code>LoginActivity<\/code>. I used Eclipse-ADT\u00e2\u20ac\u2122s template, \u00e2\u20ac\u0153Login Activity\u00e2\u20ac\u009d, which you can get from <em>Right-Click on source directory -&gt; New -&gt; Other -&gt; Android -&gt; Android Activity<\/em>, and then changed it to inherit from <code>AccountAuthenticatorActivity<\/code>. Most of it is just UI work, only gathering credentials, sanity-checking them and supplying a \u00e2\u20ac\u0153login\u00e2\u20ac\u009d button. The important thing for us is what happens when that login button is clicked. The work of that is done in a separate thread, so the GUI stays responsive. That\u00e2\u20ac\u2122s done like this (these are snippets only, you\u00e2\u20ac\u2122ll have to put them in the right places yourself):<\/p>\n<pre class=\"sourceCode java\"><code class=\"sourceCode java\">    <span class=\"co\">\/\/ ... in onCreate(), let the base class handle the<\/span>\n    <span class=\"co\">\/\/ KEY_ACCOUNT_AUTHENTICATOR_RESPONSE decode<\/span>\n    <span class=\"kw\">super<\/span>.<span class=\"fu\">onCreate<\/span>(bundle);\n    <span class=\"co\">\/\/ ... in onCreate() most likely ...<\/span>\n    mAccountManager = AccountManager.<span class=\"fu\">get<\/span>(<span class=\"fu\">getBaseContext<\/span>());\n    mAccountName = <span class=\"fu\">getIntent<\/span>().<span class=\"fu\">getStringExtra<\/span>(ARG_ACCOUNT_NAME);\n    mAccountType = <span class=\"fu\">getIntent<\/span>().<span class=\"fu\">getStringExtra<\/span>(ARG_ACCOUNT_TYPE);\n    mAuthType = <span class=\"fu\">getIntent<\/span>().<span class=\"fu\">getStringExtra<\/span>(ARG_AUTH_TYPE);\n    <span class=\"co\">\/\/ and your UI fields for password, and account name editing<\/span>\n\n    <span class=\"co\">\/\/ ... get here in response to a login click ...<\/span>\n    <span class=\"fu\">showProgress<\/span>(<span class=\"kw\">true<\/span>);\n    UserLoginTask mAuthTask = <span class=\"kw\">new<\/span> <span class=\"fu\">UserLoginTask<\/span>();\n    mAuthTask.<span class=\"fu\">execute<\/span>((Void) <span class=\"kw\">null<\/span>);\n\n    <span class=\"co\">\/\/ ... elsewhere ...<\/span>\n    <span class=\"kw\">public<\/span> <span class=\"kw\">class<\/span> UserLoginTask <span class=\"kw\">extends<\/span> AsyncTask&lt;Void, Void, Intent&gt; {\n        <span class=\"fu\">@Override<\/span>\n        <span class=\"kw\">protected<\/span> Intent <span class=\"fu\">doInBackground<\/span>(Void<span class=\"kw\">... <\/span>params) {\n            <span class=\"dt\">final<\/span> Intent res = <span class=\"kw\">new<\/span> <span class=\"fu\">Intent<\/span>();\n            String authToken;\n\n            <span class=\"co\">\/\/ Call some external method which handles your<\/span>\n            <span class=\"co\">\/\/ application-specific login, returning a token<\/span>\n            <span class=\"kw\">try<\/span> {\n                authToken = <span class=\"fu\">fetchTokenFromCredentials<\/span>(mAccountName, mPassword, mAuthTokenType);\n\n                res.<span class=\"fu\">putExtra<\/span>(AccountManager.<span class=\"fu\">KEY_ACCOUNT_NAME<\/span>, mAccountName);\n                res.<span class=\"fu\">putExtra<\/span>(AccountManager.<span class=\"fu\">KEY_ACCOUNT_TYPE<\/span>, mAccountType);\n                res.<span class=\"fu\">putExtra<\/span>(AccountManager.<span class=\"fu\">KEY_AUTHTOKEN<\/span>, authToken);\n                <span class=\"co\">\/\/ We&#39;ll add an extra one for us<\/span>\n                res.<span class=\"fu\">putExtra<\/span>(PARAM_USER_PASS, mPassword);\n            } <span class=\"kw\">catch<\/span> (Exception e) {\n                res.<span class=\"fu\">putExtra<\/span>(KEY_ERROR_MESSAGE, e.<span class=\"fu\">getMessage<\/span>());\n            }\n\n            <span class=\"kw\">return<\/span> res;\n        }\n\n        <span class=\"fu\">@Override<\/span>\n        <span class=\"kw\">protected<\/span> <span class=\"dt\">void<\/span> <span class=\"fu\">onPostExecute<\/span>(<span class=\"dt\">final<\/span> Intent intent) {\n            <span class=\"co\">\/\/ Complete, clear the reference<\/span>\n            mAuthTask = <span class=\"kw\">null<\/span>;\n\n            <span class=\"co\">\/\/ If we got an error message, put it on the UI<\/span>\n            <span class=\"kw\">if<\/span> (intent.<span class=\"fu\">hasExtra<\/span>(KEY_ERROR_MESSAGE)) {\n                mLoginStatusMessageView.<span class=\"fu\">setError<\/span>(intent.<span class=\"fu\">getStringExtra<\/span>(KEY_ERROR_MESSAGE));\n                mLoginStatusMessageView.<span class=\"fu\">requestFocus<\/span>();\n            } <span class=\"kw\">else<\/span> {\n                <span class=\"fu\">finishLogin<\/span>(intent);\n                <span class=\"co\">\/\/ Close the activity, we&#39;re done<\/span>\n                <span class=\"fu\">finish<\/span>();\n            }\n        }\n\n        <span class=\"fu\">@Override<\/span>\n        <span class=\"kw\">protected<\/span> <span class=\"dt\">void<\/span> <span class=\"fu\">onCancelled<\/span>() {\n            mAuthTask = <span class=\"kw\">null<\/span>;\n            <span class=\"co\">\/\/ Stop whatever UI noise you were making<\/span>\n            <span class=\"fu\">showProgress<\/span>(<span class=\"kw\">false<\/span>);\n        }\n    }<\/code><\/pre>\n<p>We\u00e2\u20ac\u2122re missing <code>finishLogin()<\/code> to send the result back to the account manager.<\/p>\n<pre class=\"sourceCode java\"><code class=\"sourceCode java\">    <span class=\"kw\">private<\/span> <span class=\"dt\">void<\/span> <span class=\"fu\">finishLogin<\/span>(Intent intent) {\n        String accountPassword = intent.<span class=\"fu\">getStringExtra<\/span>(PARAM_USER_PASS);\n        <span class=\"dt\">final<\/span> Account account = <span class=\"kw\">new<\/span> <span class=\"fu\">Account<\/span>(\n                mAccountName,\n                intent.<span class=\"fu\">getStringExtra<\/span>(AccountManager.<span class=\"fu\">KEY_ACCOUNT_TYPE<\/span>));\n        String authtoken = intent.<span class=\"fu\">getStringExtra<\/span>(AccountManager.<span class=\"fu\">KEY_AUTHTOKEN<\/span>);\n\n        <span class=\"kw\">if<\/span> (<span class=\"fu\">getIntent<\/span>().<span class=\"fu\">getBooleanExtra<\/span>(ARG_IS_ADDING_NEW_ACCOUNT, <span class=\"kw\">false<\/span>)) {\n            <span class=\"co\">\/\/ Creating the account<\/span>\n            <span class=\"co\">\/\/ Password is optional to this call, safer not to send it really.<\/span>\n            mAccountManager.<span class=\"fu\">addAccountExplicitly<\/span>(account, accountPassword, <span class=\"kw\">null<\/span>);\n        } <span class=\"kw\">else<\/span> {\n            <span class=\"co\">\/\/ Password change only<\/span>\n            mAccountManager.<span class=\"fu\">setPassword<\/span>(account, accountPassword);\n        }\n        <span class=\"co\">\/\/ set the auth token we got (Not setting the auth token will cause<\/span>\n        <span class=\"co\">\/\/ another call to the server to authenticate the user)<\/span>\n        mAccountManager.<span class=\"fu\">setAuthToken<\/span>(account, mAuthType, authtoken);\n\n        <span class=\"co\">\/\/ Our base class can do what Android requires with the<\/span>\n        <span class=\"co\">\/\/ KEY_ACCOUNT_AUTHENTICATOR_RESPONSE extra that onCreate has<\/span>\n        <span class=\"co\">\/\/ already grabbed<\/span>\n        <span class=\"fu\">setAccountAuthenticatorResult<\/span>(intent.<span class=\"fu\">getExtras<\/span>());\n        <span class=\"co\">\/\/ Tell the account manager settings page that all went well<\/span>\n        <span class=\"fu\">setResult<\/span>(RESULT_OK, intent);\n    }<\/code><\/pre>\n<p>Lots of code here, but nothing too scary. All we\u00e2\u20ac\u2122re really doing is passing parameters into an <code>Activity<\/code> via an <code>Intent<\/code>, having that <code>Activity<\/code> add its own parameters that it requests from the user, then have all of those used in an <code>AsyncTask<\/code> to perform the actual logon without blocking the UI, and finally to pass the result of that logon back to the calling <code>Activity<\/code> via another <code>Intent<\/code>.<\/p>\n<p>The most important part of all that is in <code>finishLogin()<\/code>, which is the part that sends something back to the OS. The key calls are:<\/p>\n<ul>\n<li><code>mAccountManager.addAccountExplicitly()<\/code> et al. These calls add the credentials and authentication token to the locally held account manager object \u00e2\u20ac\u201c which in turn passes them to the OS. These are all that are required to make the account appear in the Android account list.<\/li>\n<li><code>setAccountAuthenticatorResult()<\/code> tells the caller the response to the original act of creating this activity. This is done via the <code>AccountAuthenticatorResponse<\/code> that got extracted from the <code>KEY_ACCOUNT_AUTHENTICATOR_RESPONSE<\/code> extra when the activity started.<\/li>\n<li><code>setResult()<\/code> returns the successful result and the full response data.<\/li>\n<\/ul>\n<p>It\u00e2\u20ac\u2122s not yet clear to me why there are two result return paths.<\/p>\n<p>Let\u00e2\u20ac\u2122s look more closely at what gets returned in the <code>Intent<\/code>\u00e2\u20ac\u2122s extra data, that isn\u00e2\u20ac\u2122t internal to our <code>Activity<\/code>:<\/p>\n<ul>\n<li><code>AccountManager.KEY_ACCOUNT_NAME<\/code><\/li>\n<li><code>AccountManager.KEY_ACCOUNT_TYPE<\/code><\/li>\n<li><code>AccountManager.KEY_AUTHTOKEN<\/code><\/li>\n<\/ul>\n<p>Only the authentication token was unknown at the start. Pretty simple in the end.<\/p>\n<h2 id=\"getauthtoken\"><code>getAuthToken()<\/code><\/h2>\n<p>Now, onto usage. Now that we have a method of creating an account of our own custom type, using our own custom credentials, logging in with them and storing an authentication token to remember that login, how will our application fetch that token for its own use?<\/p>\n<p>Back in our <code>AbstractAccountAuthenticator<\/code> is an abstract method we need to implement. <code>getAuthToken()<\/code> is the day-to-day work horse. When an application wants to make use of an account\u00e2\u20ac\u2122s remote service, it (indirectly) calls this <code>getAuthToken()<\/code> (it\u00e2\u20ac\u2122s important to note that this is different from the client-side <code>getAuthToken()<\/code> we\u00e2\u20ac\u2122ll see later).<\/p>\n<pre class=\"sourceCode java\"><code class=\"sourceCode java\">    <span class=\"fu\">@Override<\/span>\n    <span class=\"kw\">public<\/span> Bundle <span class=\"fu\">getAuthToken<\/span>(AccountAuthenticatorResponse response,\n            Account account, String authTokenType, Bundle options)\n            <span class=\"kw\">throws<\/span> NetworkErrorException {\n\n        <span class=\"co\">\/\/ We can add rejection of a request for a token type we<\/span>\n        <span class=\"co\">\/\/ don&#39;t support here<\/span>\n\n        <span class=\"co\">\/\/ Get the instance of the AccountManager that&#39;s making the<\/span>\n        <span class=\"co\">\/\/ request<\/span>\n        <span class=\"dt\">final<\/span> AccountManager am = AccountManager.<span class=\"fu\">get<\/span>(mContext);\n\n        <span class=\"co\">\/\/ See if there is already an authentication token stored<\/span>\n        String authToken = am.<span class=\"fu\">peekAuthToken<\/span>(account, authTokenType);\n\n        <span class=\"co\">\/\/ If we have no token, use the account credentials to fetch<\/span>\n        <span class=\"co\">\/\/ a new one, effectively another logon<\/span>\n        <span class=\"kw\">if<\/span> (TextUtils.<span class=\"fu\">isEmpty<\/span>(authToken)) {\n            <span class=\"dt\">final<\/span> String password = am.<span class=\"fu\">getPassword<\/span>(account);\n            <span class=\"kw\">if<\/span> (password != <span class=\"kw\">null<\/span>) {\n                authToken = <span class=\"fu\">fetchTokenFromCredentials<\/span>(account.<span class=\"fu\">name<\/span>, password, authTokenType)\n            }\n        }\n\n        <span class=\"co\">\/\/ If we either got a cached token, or fetched a new one, hand<\/span>\n        <span class=\"co\">\/\/ it back to the client that called us.<\/span>\n        <span class=\"kw\">if<\/span> (!TextUtils.<span class=\"fu\">isEmpty<\/span>(authToken)) {\n            <span class=\"dt\">final<\/span> Bundle result = <span class=\"kw\">new<\/span> <span class=\"fu\">Bundle<\/span>();\n            result.<span class=\"fu\">putString<\/span>(AccountManager.<span class=\"fu\">KEY_ACCOUNT_NAME<\/span>, account.<span class=\"fu\">name<\/span>);\n            result.<span class=\"fu\">putString<\/span>(AccountManager.<span class=\"fu\">KEY_ACCOUNT_TYPE<\/span>, account.<span class=\"fu\">type<\/span>);\n            result.<span class=\"fu\">putString<\/span>(AccountManager.<span class=\"fu\">KEY_AUTHTOKEN<\/span>, authToken);\n            <span class=\"kw\">return<\/span> result;\n        }\n\n        <span class=\"co\">\/\/ If we get here, then we don&#39;t have a token, and we don&#39;t have<\/span>\n        <span class=\"co\">\/\/ a password that will let us get a new one (or we weren&#39;t able<\/span>\n        <span class=\"co\">\/\/ to use the password we do have).  We need to fetch<\/span>\n        <span class=\"co\">\/\/ information from the user, we do that by creating an Intent<\/span>\n        <span class=\"co\">\/\/ to an Activity child class.<\/span>\n        <span class=\"dt\">final<\/span> Intent intent = <span class=\"kw\">new<\/span> <span class=\"fu\">Intent<\/span>(mContext, LoginActivity.<span class=\"fu\">class<\/span>);\n\n        <span class=\"co\">\/\/ We want to give the Activity the information we want it to<\/span>\n        <span class=\"co\">\/\/ return to the AccountManager.  We&#39;ll cover that with the<\/span>\n        <span class=\"co\">\/\/ KEY_ACCOUNT_AUTHENTICATOR_RESPONSE parameter.<\/span>\n        intent.<span class=\"fu\">putExtra<\/span>(AccountManager.<span class=\"fu\">KEY_ACCOUNT_AUTHENTICATOR_RESPONSE<\/span>,\n                response);\n        <span class=\"co\">\/\/ We&#39;ll also give it the parameters we&#39;ve already looked up, or<\/span>\n        <span class=\"co\">\/\/ were given.<\/span>\n        intent.<span class=\"fu\">putExtra<\/span>(LoginActivity.<span class=\"fu\">ARG_IS_ADDING_NEW_ACCOUNT<\/span>, <span class=\"kw\">false<\/span>);\n        intent.<span class=\"fu\">putExtra<\/span>(LoginActivity.<span class=\"fu\">ARG_ACCOUNT_NAME<\/span>, account.<span class=\"fu\">name<\/span>);\n        intent.<span class=\"fu\">putExtra<\/span>(LoginActivity.<span class=\"fu\">ARG_ACCOUNT_TYPE<\/span>, account.<span class=\"fu\">type<\/span>);\n        intent.<span class=\"fu\">putExtra<\/span>(LoginActivity.<span class=\"fu\">ARG_AUTH_TYPE<\/span>, authTokenType);\n\n        <span class=\"co\">\/\/ Remember that we have to return a Bundle, not an Intent, but<\/span>\n        <span class=\"co\">\/\/ we can tell the caller to run our intent to get its<\/span>\n        <span class=\"co\">\/\/ information with the KEY_INTENT parameter in the returned<\/span>\n        <span class=\"co\">\/\/ Bundle<\/span>\n        <span class=\"dt\">final<\/span> Bundle bundle = <span class=\"kw\">new<\/span> <span class=\"fu\">Bundle<\/span>();\n        bundle.<span class=\"fu\">putParcelable<\/span>(AccountManager.<span class=\"fu\">KEY_INTENT<\/span>, intent);\n        <span class=\"kw\">return<\/span> bundle;\n    }<\/code><\/pre>\n<p>Most of that is commentary. There is unshown detail hidden in the <code>fetchTokenFromCredentials()<\/code> method \u00e2\u20ac\u201c as with our <code>addAccount()<\/code> <code>Activity<\/code>, that is specific to your application, logon however you wish.<\/p>\n<p>All the above has essentially supplied a server-side implementation (this is the Android authentication service \u00e2\u20ac\u0153server\u00e2\u20ac\u009d rather than whatever server out on the Internet we\u00e2\u20ac\u2122re wrapping) of a custom account type for Android\u00e2\u20ac\u2122s <code>AccountManager<\/code> system to use. Next time we\u00e2\u20ac\u2122ll look at how we make use of that account in our app \u00e2\u20ac\u201c the client side.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I found a blog that gave an example of how to make a custom authenticator for Android. I didn\u00e2\u20ac\u2122t find it very clear, so this article covers my understanding that I\u00e2\u20ac\u2122ve pulled this together from the example given by that blog author, and from the Android documentation, and then building something that works. To make\u2026 <span class=\"read-more\"><a href=\"https:\/\/www.fussylogic.co.uk\/blog\/?p=1031\">Read More &raquo;<\/a><\/span><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[9,70,69,42,6],"_links":{"self":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1031"}],"collection":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1031"}],"version-history":[{"count":11,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1031\/revisions"}],"predecessor-version":[{"id":1280,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1031\/revisions\/1280"}],"wp:attachment":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1031"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1031"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1031"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}