# Account Manager

The Account Manager service (`com.palm.service.accounts`) provides central account and credentials management on the device. Synergy services are services on the webOS message bus that provide a connection to an outside data source (i.e., for log in and syncing) and manage the caching of credentials and configuration data on the device. To interact with the Accounts service, Synergy services need to register an Account template file (read at Account service start-up) that contains callbacks and metadata. Synergy applications can use the Account service when interacting with external account providers such as Facebook, Google, Yahoo, and LinkedIn. Third-party applications can use the service to interface with their own providers and integrate with the Palm Accounts app which provides a unified and standard Account UI.

Providers give users capabilities such as contacts, calendar, blogging, email, and so on. For a single user, each of these capabilities can have its own provider account. In addition, each capability can have its own protocol implementation, such as Gdata, Exchange, POP, IMAP, and so on.

webOS supports a number of built-in providers and their capabilities which HP or 3rd parties may extend in the future. Providers determine the types of accounts and credentials that exist on the device which can vary by capability. Account objects stored on the device (proxies for real provider accounts) are a combination of credentials for a particular provider, with one or more capabilities enabled. For a given provider and user, only one Account object can exist in the db8 database. Using the Account service methods, apps and services can list account information, create new accounts, and alter existing accounts.

In this section

To integrate with the Account Manager service, applications need to create, configure or incorporate the following:

- Design Points—Things to keep in mind when you are developing your Account and Synergy services.

- Account Manager Service Interface (APIs)—The Accounts service (`com.palm.service.accounts`) allows users to list, create, and alter localized accounts. Note that this applies to Account objects in the db8 database on the device, not creating or deleting an actual provider (Facebook, Google, etc.) account. The user must already have a provider account.

  Once one capability is enabled, the Account service can enable all provider capabilities for the user. For example: if the user has Google accounts for contacts, messaging, mail, and calendar, the Accounts service can enable all of them.

  This service also handles the reading and writing of account credentials and other sensitive data. See the Account Manager API for more information.

- Synergy Service —Your app needs to provide a Synergy service on the bus that implements a connection with an outside data source. This service can also implement methods the Account Manager calls when an account is created or altered.

- Account Template—Your Synergy service needs to register an Account template file containing callbacks and metadata with the Accounts service. The Accounts service invokes these callbacks to validate credentials and notify the service about account creation or modification. This file is read and registered each time the Accounts service starts up.

  For 3rd-parties intstalling a service in a packaged file, your template file must be named `account_template.json`. See the Template data type for more information about this file's configuration.

- Accounts UI library—Your JavaScript app should include the Accounts UI library (`accounts.ui`) to handle common UI tasks such as launch screens, account preferences, and account listings. This library provides the baseline functionality for the standalone Accounts app.

- Custom Validator—This is optional. Most account templates can utilize the standard username/password Accounts UI when logging in. If not, the Accounts Framework allows template authors to specify a custom UI to display in lieu of the standard login UI.

See also: Account Manager Data Types, and "Creating a Synergy Contacts Package."

## Design points

- The Accounts preferences app and all PIM (Personal Information Manager) apps share an Accounts UI component. PIM apps need only implement their app-specific preferences UI (e.g. calendar color).

- The Accounts UI will talk to the Accounts service (`com.palm.service.accounts`) to coordinate account creation, modification, and removal.

- The Accounts Service triggers various procedures for account actions, including:

  - On account creation, begins initial syncs.
  - On capability removal, deletes PIM data.
  - On username/password change, calls all validators to generate custom authorization tokens.

- The Accounts Service acts as a proxy for credentials read/write access for white-listed services. For example: the IMAP service asks the account service for the gmail authorization token when doing a gmail sync.

## Account Manager Service Interface (APIs)

You can call Activity Manager methods asynchronously in one of two ways.

1. Through the "serviceReques"t function which takes 2 parameters:

   1. The Account Manager URI (Uniform Resource Identifier)
   2. A JSON (JavaScript Object Notation) object containing parameters, callback functions, and other data.

   this.controller.serviceRequest("palm://com.palm.service.accounts/", { method: "getAccountInfo", parameters: { "accountId": "2+PO" }, onSuccess: function(e) { Mojo.Log.info("getAccountInfo success, Account="+JSON.stringify(e));}, onFailure: function(e) { Mojo.Log.info("getAccountInfo failure Err code=" + e.errorCode + "Err text=" + e.text); } });

2. Through the new Foundation library API for calling Palm services.

   Palm's JavaScript Foundation libraries are a loadable framework that both Mojo and JavaScript service applications can use.

   For example:

       var libraries = MojoLoader.require({ name: "foundations", version: "1.0" });
       var PalmCall = libraries["foundations"].Comms.PalmCall;
       PalmCall.call("palm://com.palm.service.accounts/", "getAccountInfo", {"accountId": "2+PO"});

   This also requires your app to have the following two tags in your index.html file:

        <script src="/usr/palm/frameworks/mojo/mojo.js" type="text/javascript" x-mojo-version="1"></script>
        <script src="/usr/palm/frameworks/mojoloader.js" type="text/javascript"></script>

Depending on outcome, both calls return a JSON result indicating success or failure. Your code can then parse the result. Your app needs to take into account the call's asynchronous nature (results may be delayed) and not block user interaction while awaiting results.

### About JSON

JSON is a lightweight data-interchange text format based on a subset of the JavaScript programming language that is easy for humans to read and write and for machines to parse and generate. Like XML, JSON contains name/value pair collections and ordered list of values (i.e., arrays).

For more information on JSON, see the following websites:

- JSON objects, values and format
- JSON schemas

A primer on how JSON data objects are formatted is beyond the scope of this document—consult the two links above for more information. However, in brief:

- Curly brackets ('{', '}') are used to enclose objects.
- Angle brackets ('[', ']') are used to enclose arrays.
- Colons (':') are used as delimiters in name/value pairs.
- Quotes (") are used for string values. Numeric values are not quoted.

### Account Manager Service API:

- createAccount—Create an account object in the db8 database.
- deleteAccount—Delete an account object in the db8 database.
- getAccountInfo—Retrieve a specific account object from the db8 database.
- modifyAccount—Modify an account object in the db8 database.

¡

### createAccount

Creates an Account object in the db8 database that serves as a proxy for a real provider (i.e., Facebook, Google) account. The caller can specify the provider capabilities (email, calendar, etc.) they want to enable for the account. Callers can pass configuration data and credentials for one or more provider accounts. An account can have multiple named credentials objects. For example, it could have objects named "common", "smtp", and "imap". With these, the SMTP and IMAP services could keep their own authorization tokens, and all other Synergy services could use the "common" credentials. Typically, credential and configuration information is returned from a validate user login API. Configuration information is passed to the Synergy service's onCreate method and credentials are kept in encrypted storage.

This method does the initial writing of account credentials to encrypted storage; after this, you can use the writeCredentials method to alter them.

To avoid duplicates, usernames should be "normalized," that is, incomplete usernames (i.e., "johndoe") should be turned into fully-qualified, unambigous usernames (i.e., "johndoe@gmail.com").

#### Syntax

       {
           "templateId"   : string,
           "capabilityProviders" : [ {
                                         "id"         : string,
                                         "capability" : string
                                      }
                                     ],
            "username"     : string,
            "alias"        : string,
            "credentials"  : {
                               <any string> : CredentialsObj object,
                                ...
                             },
            "config"       : any,
            "sync"         : boolean
       }

#### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
templateId | Yes | string | ID of the template file (i.e., "com.palm.google") stored in `/usr/palm/public/accounts/` for the account's Synergy service. See the Template data type for more information.
capabilityProviders | No | array of inline objects | See two elements below.
capabilityProviders[0-n].id | No | string | Capability ID in template, i.e., "com.palm.gdata.calendar"
capabilityProviders[0-n].capability | No | string | Capability name, i.e., "CONTACTS"
username | Yes | string | User name.
alias | No | string | Alias name. Account name in UI.
credentials | Yes | CredentialsObj object(s) | One or more named credential objects. See example.
config | Yes | any | Opaque config objects passed to Synergy service.
sync | No | boolean | Automatic backup flag. Default is `true`.

#### Return

    {
        "returnValue" : boolean,
        "errorCode"   : string,
        "errorText"   : string,
        "result"      : Account
    }

Argument | Required | Type | Description
--- | --- | --- | ---
returnValue | Yes | boolean | `true` (success) or `false` (failure).
errorCode | No | string | Returned on failure.
errorText | No | string | Returned on failure. Text describing error.
result | Yes | Account | Created Account object.

Example with multiple credential objects

       this.controller.serviceRequest("palm://com.palm.service.accounts/", {
          method: "createAccount",
          parameters: {"templateId"          : "com.palm.sample",
                       "capabilityProviders" : [{"id":"com.palm.sample.contacts", "capability":"CONTACTS"}],
                       "username"            : "Souffle",
                       "alias"               : "puffy",
                       "credentials"         : {"common":{ "password" : "laddw99", "authToken":"8ukkk9##99"}, "smtp":{ "password" : "lawyer99", "authToken":"8u0009##99"}},
                       "config"              : {  "ip":"12.225.172.8" }
            },
         onSuccess: function(e) { Mojo.Log.info("Account object = "+JSON.stringify(e)); },
         onFailure: function(e) { Mojo.Log.info("createAccount failure: errorCode = " + e.errorCode + ", errorText = "+e.errorText); }
      });

¡

### deleteAccount

Given an account ID, deletes an account object in db8. The account's Synergy service is notified through its `onDelete` and `onEnabled` (with "enabled = false") methods. The Synergy should use the onEnabled method to clean up all sync data associated with the account.

#### Syntax

     {
        "accountID"  : string
     }

#### Parameter

Argument | Required | Type | Description
--- | --- | --- | ---
accountId | Yes | string | db8 Account ID

#### Return

    {
        "returnValue" : boolean,
        "errorCode"   : string,
        "errorText"   : string
    }

Argument | Required | Type | Description
--- | --- | --- | ---
returnValue | Yes | boolean | `true` (success) or `false` (failure)
errorCode | No | string | Returned on failure.
errorText | No | string | Returned on failure. Text describing error.

Example

       this.controller.serviceRequest("palm://com.palm.service.accounts/", {
          method: "deleteAccount",
          parameters: {
                "accountId": "2+PO"
            },
         onSuccess: function(){ Mojo.Log.info("deleteAccount success"); },
         onFailure: function(e) { Mojo.Log.info("deleteAccount failure errorCode = " + e.errorCode + " errorText = "+e.errorText); }
      });

¡

### getAccountInfo

Given an account ID, returns a db8 Account object combined with a Template object.

#### Syntax

    {
       "accountId" : string
    }

#### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
accountId | Yes | string | db8 account ID.

#### Return

    {
        "returnValue" : boolean,
        "errorCode"   : string,
        "errorText"   : string,
        "result"      : Combined Account and Template object
    }

Argument | Required | Type | Description
--- | --- | --- | ---
returnValue | Yes | boolean | `true` (success) or `false` (failure).
errorCode | No | string | Returned on failure.
errorText | No | string | Returned on failure. Text describing error.
result | No | Combined Account and Template object | Account object combined with Template object.

Example

       this.controller.serviceRequest("palm://com.palm.service.accounts/", {
          method: "getAccountInfo",
          parameters: {
                "accountId": "2+31",
            },
         onSuccess: function(e) { Mojo.Log.info("getAccountInfo success, Account="+JSON.stringify(e));},
         onFailure: function(e) { Mojo.Log.info("getAccountInfo failure errorCode = " + e.errorCode + " errorText = "+e.errorText); }
      });

Example Output

    Account=
    {"result":{"_kind": "com.palm.account:1",
            "_id": "2+31",
            "_rev": 258,
            "_sync": true,
            "beingDeleted": false,
            "capabilityProviders": [
                {
                    "_id": "2+32",
                    "capability": "CONTACTS",
                    "id": "com.palm.palmprofile.contacts",
                    "db_kinds": {
                        "contact": "com.palm.contact.palmprofile:1"
                    }
                },
                {
                    "_id": "2+33",
                    "capability": "CALENDAR",
                    "id": "com.palm.palmprofile.calendar"
                },
                {
                    "_id": "2+34",
                    "capability": "TASKS",
                    "id": "com.palm.palmprofile.tasks"
                },
                {
                    "_id": "2+35",
                    "capability": "MEMOS",
                    "id": "com.palm.palmprofile.memos"
                },
                {
                    "_id": "2+36",
                    "capability": "MESSAGING",
                    "id": "com.palm.palmprofile.sms",
                    "capabilitySubtype": "SMS",
                    "loc_name": "SMS Account",
                    "loc_shortName": "Text",
                    "icon": {
                        "loc_32x32": "\/usr\/palm\/public\/accounts\/com.palm.palmprofile\/images\/sms-32x32.png",
                        "loc_48x48": "\/usr\/palm\/public\/accounts\/com.palm.palmprofile\/images\/sms-48x48.png",
                        "splitter": "\/usr\/palm\/public\/accounts\/com.palm.palmprofile\/images\/sms-32x32.png"
                    }
                }
            ],
            "templateId": "com.palm.palmprofile",
            "username": "Dr. Skipped Firstuse",
            "loc_name": "Palm Profile",
            "icon": {
                "loc_32x32": "\/usr\/palm\/public\/accounts\/com.palm.palmprofile\/images\/mypalm-32x32.png",
                "loc_48x48": "\/usr\/palm\/public\/accounts\/com.palm.palmprofile\/images\/mypalm-48x48.png"
            }
        }}

### modifyAccount

Can be used to change Account object properties including capabilities. Properties are overwritten.

If you add or delete a capability, the capability's Synergy service onEnabled method is called.

#### Syntax

    {
       "accountId"  : string,
       "object"     : any
    }

#### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
accountId | Yes | string | db8 account ID.
object | Yes | any | Account properties and new values.

#### Return

    {
       "returnValue"  : boolean,
       "errorCode"    : string,
       "errorText"    : string
    }

Argument | Required | Type | Description
--- | --- | --- | ---
returnValue | Yes | boolean | `true` (success) or `false` (failure)
errorCode | No | string | Returned on failure.
errorText | No | string | Returned on failure. Text describing error.

Example

       this.controller.serviceRequest("palm://com.palm.service.accounts/", {
          method: "modifyAccount",
          parameters: {"accountId":"2+Ph",
                       "object": {"username":"ChuckWagon99", "alias": "huggybeer"}},
         onSuccess: function(){ Mojo.Log.info("modifyAccount success"); },
         onFailure: function(e) { Mojo.Log.info("modifyAccount failure errorCode = " + e.errorCode + " errorText = "+e.errorText); }
      });

## Synergy Service

A Synergy service that interacts with the Account service needs to implement a number of callbacks that are invoked to validate login or when an account object in db8 storage is created or altered. These are named in the Synergy service's Account template file that the Account service reads on start-up. The Account service invokes these callbacks to validate credentials and notify the Synergy service about account creation or modification.

Note: Third-party services that interact with the Account Manager are required to be packaged (via `palm-package`) with an `account-template.json` file containing a template object along with accompanying metadata such as account icons and localized text.

Most Synergy services will want to store account-wide configuration data upon account creation, and clean up that data upon deletion. Creation is not the time to start syncing, and deletion is not the time to stop syncing.
When a capability is enabled, Synergy services should start syncing and schedule future syncs with the Activity Manager. When a capability is disabled, Synergy services should stop syncing, delete associated data, stop future syncs, and delete sync state.

Except where noted, all callbacks are per-capability, meaning that you can either elect to have one capability provider handle everything, or each capability provider can implement its own behaviors (or a mix of the two).

For each account capability, you can have the same or different callbacks. See the capability data type for required capability implementation fields.

### Synergy Service Configuration Objects

Validators and custom validation UI can emit a configuration object that can be used to contain server settings and other account-wide, capability-independent configuration data. Unlike credentials, this is not kept in encrypted storage. Synergy services can read this data to know how to sync and validators and custom validation UI can emit new values to modify this data when the user changes login info.

Synergy services should use this for data that needs to persist even after a capability has been disabled (in case it is ever re-enabled), and delete it when the associated account is deleted (`onDelete` is called). Configuration data should not be used to store sync state or other information that should be deleted when a capability is disabled.

Synergy services are expected to persist configuration data when they are passed in to `onCreate` and clean them up in `onDelete`. Multiple services supporting different capabilities for a single account should cooperate in managing configuration objects—either each service could maintain its own portion, or one service could maintain it all.

### Required Directory Structure

Synergy services should use the following hierarchy to organize device files. The root is assumed to be the first party root (`"/usr/palm/public/"`) or third-party root (`"/media/cryptofs/apps/usr/palm/accounts"`).

    com.x.y/
        com.x.y.json
        images/
            xy-32x32.png
            xy-48x48.png
        resources/
            es_US/
                com.x.y.json (localized complete copy of base .json file)
                images/ (for localized images)
                    xy-32x32.png
                    xy-48x48.png

### Account Manager Invoked Synergy Service Callbacks

Note that the names listed below are NOT the names of methods your Synergy service implements, they are field names in a capability object. Bus address and method names are the value for these fields, which can be anything you want.

- onCapabilitiesChanged—Called when account capabilities (email, blog, calendar, etc.) change. This is implemented at the top-level, not per capability.

- onCreate—Called when account created.

- onDelete—Called when account deleted.

- onEnabled—Called when each capability is enabled or disabled which can happen when an account is created, deleted, or modified.

- validator—Called to validate username/password and generate credentials and configuration information.

- Synergy Service Error Codes—Error codes Synergy services should return on failure.

### onCapabilitiesChanged

The Account Manager calls this Synergy service method when an account's capability (email, blogging, contacts, etc.) is enabled or disabled. This can happen during initial account creation or when an account is subsequently modified (createAccount or modifyAccount). The new state of enabled capability providers is passed. This is useful for templates where a single unified Synergy service handles all syncing; in that case, it's easier to re-do all syncing in one step rather than using multiple 'onEnabled` handlers.

#### Syntax

    {
       "accountId"           : string,
       "capabilityProviders" : [ {
                                    id      : string,
                                    enabled : boolean
                                  }
                               ]
    }

#### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
accountId | Yes | string | db8 account ID.
capabilityProviders | Yes | array of inline objects | Array of object identifying capabilities and if they are enabled or disabled.
capabilityProviders[0-n].id | Yes | string | Capability ID, i.e., "com.palm.facebook.calendar".
capabilityProviders[0-n].enabled | Yes | boolean | Is capability enabled or disabled flag.

#### Return

Argument | Type | Description
--- | --- | ---
returnValue | boolean | `true` (success) or `false` (failure)

### onCreate

The Account Manager calls this Synergy service method when an account is created. After this, the Account Manager calls the Synergy service's
onCapabilitiesChanged and onEnabled methods for each capability. When called, these handlers should save any passed configuration information.

Note that `onCreate` is not called again for restored accounts as it is assumed the data has been recreated from backup. Therefore, any `onCreate` persisted data must be backed up to restore the account properly.

#### Syntax

    {
       "accountId" : string,
       "config"    :  any
    }

#### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
accountId | Yes | string | db8 account ID.
config | No | any | Opaque configuration objects containing any information the Synergy service wants such as IP addresses, ports, etc.

#### Return

Argument | Type | Description
--- | --- | ---
returnValue | boolean | `true` (success) or `false` (false)

### onDelete

The Account Manager calls this when an entire account is being deleted. All capability providers that declared a handler are called. Handlers should delete any account-wide configuration. To proceed, all `onDelete` calls must succeed.

#### Syntax

    {
       "accountId" : string
    }

#### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
accountId | Yes | string | db8 account ID.

#### Return

Argument | Type | Description
--- | --- | ---
returnValue | boolean | `true` (success) or `false` (failure)

### onEnabled

The Account Manager call this Synergy service method when a capability is enabled or disabled. When enabled, notified Synergy services should start a sync and schedule future syncs. When disabled, notified Synergy services should disable syncing and delete associated data. Account-wide configuration should remain on disk, and only deleted when `onDelete` is called.

#### Syntax

    {
       "enabled"              : boolean,
       "accountId"            : string,
       "capabilityProviderId" : string
    }

#### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
enabled | Yes | boolean | Capability enabled flag.
accountId | Yes | string | db8 account ID.
capabilityProviderId | Yes | string | Capability Provider ID, i.e., "com.palm.facebook.calendar"

#### Return

Argument | Type | Description
--- | --- | ---
returnValue | boolean | `true` (success) or `false` (failure)

### validator

This API is called to validate a user's login (username and password). Typically, this is done over the cloud. It can return user credentials (passwords, authentication tokens) for an account and configuration information (i.e., server name, port, etc.). Credentials and configuration information can then be used in creating a localized user account object.

Validators should not modify existing account state.

#### Syntax

    {
       "username"    : string,
       "password"    : string,
       "templateId"  : string,
       "config"      : any array,
       "accountId"   : string
    }

#### Parameters

Argument | Required | Type | Description
--- | --- | --- | ---
username | Yes | string | User name.
password | Yes | string | Password.
templateId | No | string | ID of the template file (i.e., "com.palm.google"). See the Template data type for more information on this file.
config | No | any array | Array of opaque configuration objects.
accountId | No | string | Only passed if modifying existing credentials.

#### Return

    {
       "returnValue"  : boolean,
       "errorCode"    : string,
       "errorText"    : string,
       "templateId"   : string,
       "username"     : string,
       "alias"        : string,
       "credentials"  : any,
       "config"       : any
    }

Argument | Required | Type | Description
--- | --- | --- | ---
returnValue | Yes | boolean | `true` (success) or `false` (failure).
errorText | No | string | Returned if validation fails and returnValue is false.
errorCode | No | string | Returned on failure. Has the form: "errInvalidParamater", for example: "errFileNotFound".
templateId | No | string | Returned if changing.
username | No | string | Returned if changing.
alias | No | string | Optional replacement for the account name, defaults to template's `"loc_name"`.
credentials | No | any | Array of opaque, named credentials objects.
config | No | any | Amended opaque config objects (only if changing).

Example

Parameters

    { "username":"HoLeeSmokes",
       "password": "Batman",
       "accountId": "2+Qn",
       "templateId": "com.palm.service.sample.contacts"
    }

Return

    {  "returnValue":true,
       "templateId":"com.palm.service.sample.contacts",
       "credentials":{"common":{"secret":"shhhh","authToken":"xxxToken7&%"}},
       "config":{"server":"sample.com","port":443}
    }

### Synergy Service Error Codes

The following table contains a set of common, standardized error codes that Synergy services should return. All of the codes are localized on the UI side. If a condition does not match any of these error codes, a Synergy service should return:

    {
        "errorCode": UNKNOWN_ERROR,
        "errorText": "&lt;English error description message&gt;"
    }

The UI ignores `errorText` if there is a UI-localized `errorCode` version and is communicated mostly for debugging purposes.

Error | Number | Description
--- | --- | ---
UNKNOWN_ERROR | -3141601 | Generic fallback error.
401_UNAUTHORIZED | -3141602 | Same as HTTP 401: Invalid credentials.
408_TIMEOUT | -3141603 | Same as HTTP 408: Request timeout.
500_SERVER_ERROR | -3141604 | Same as HTTP 500: General server error.
503_SERVICE_UNAVAILABLE | -3141605 | Same as HTTP 503: Server is (temporarily) unavailable.
412_PRECONDITION_FAILED | -3141606 | Same as HTTP 412: Precondition failed. The client request is not suitable for current configuration.
400_BAD_REQUEST | -3141607 | Same as HTTP 400. Bad request.
HOST_NOT_FOUND | -3141608 | Host not found.
CONNECTION_TIMEOUT | -3141609 | Connection timeout.
CONNECTION_FAILED | -3141610 | Miscellaneous connection failed.
NO_CONNECTIVITY | -3141611 | No connectivity.
SSL_CERT_EXPIRED | -3141612 | SSL certificate expired.
SSL_CERT_UNTRUSTED | -3141613 | SSL certificate not trusted.
SSL_CERT_INVALID | -3141614 | SSL certificate invalid.
SSL_CERT_HOSTNAME_MISMATCH | -3141615 | SSL certificate hostname mismatch.
DUPLICATE_ACCOUNT | -3141616 | Attempted, but refused to add a duplicate account.
UNSUPPORTED_CAPABILITY | -3141617 | Operation failed due to an unsupported capability. This error maps to a specific, user-friendly message.

Example

If credentials are invalid, a validator method should return the following to the UI:

    {
        "returnValue": false,
        "errorCode": 401_UNAUTHORIZED
    }

Note that since this is a standardized error, `errorText` is not required.

## Custom Validator

Most account templates can utilize the standard username/password Accounts UI when logging in. If not, the Accounts Framework allows template authors to specify a custom UI to display in lieu of the standard login UI. The custom UI is integrated into the account creation flow using Cross-App Push, and the custom validation UI is expected to return a result using popScene(result).

### Custom Validator Checklist

- Specify a CustomValidatorObj in place of a bus address string for your "validator" in your account template. The appId and name must refer to an app and scene.

The validation scene must accept as input the following arguments:

          MyAssistant( { initialTemplate, allTemplates } )

"initialTemplate" is optional and is only passed in if known. "allTemplates" is the list of known account templates as returned by "com.palm.service.accounts/listAccountTemplates"

- The validation scene must determine the following:

- The template to use when creating the account.
- The username is unique for this template.
- Handle optional config from the UI.
- The default validator login result (including credentials and, optionally, config).

- Login results for all other validators (because the password is not returned, custom validation UI must do this). You can use AccountsUI.multiLogin() for this:

          // returns a future which will be done after validating the login against an
          // array of validator service methods.  The final result looks like:
          //
          // { defaultResult: &lt;SINGLE_RESULT&gt;, otherResults: [ SINGLE_RESULT, ... ] }
          //
          // (where SINGLE_RESULT=
          // { id: &lt;validator id>, result:&lt;Validator Response&gt; -OR- exception: &lt;Validator Error&gt; }
          // )
          //
          // If the default validator fails, the Future will error out. Other failures are
          // individually reported in 'otherResults'.

- Validation UI should return a result that looks like this:

      {
          template: <a href="new-in-2.0/synergy/account-manager-data-types.html#Template">Template</a> object,
          username: string,
          defaultResult: ResultObj,
          otherResults: array of ResultObj (optional),
          config: &lt;dictionary of server settings in addition to those in ResultObjs&gt;
      }

Where TemplateObj is a Template object as returned by listAccountTemplates and ResultObj is:

        {
            item: {
                    id: string (capability provider ID),
                   validator: string (bus address of validator)
                  },
            result: { (response from the validator call)
                     returnValue,
                     credentials,
                     config
                    }
       }