the Chromium logo

The Chromium Projects

Writing a SPNEGO Authenticator for Chrome on Android

As described in Kerberos for Chrome on Android, in Chrome M46 third parties can enable SPNEGO authentication in Chrome for Android. To do this they must provide a SPNEGO Authenticator. This page describes the interface between Chrome and the SPNEGO Authenticator.

Basics

The SPNEGO Authenticator is provided by an Android Service. This must be incorporated in an app, provided by the third party, installed on the user’s device. The app is responsible for managing any accounts used for SPNEGO authentication, and for all communication with the SPNEGO server.

The SPNEGO Authenticator is an Android AccountAuthenticator. As such it must follow the pattern described in AbstractAccountAuthenticator. In particular it must implement an authenticator class derived from AbstractAccountAuthenticator.

The SPNEGO Authenticator must define a new account type. Its name should be derived from the writer’s domain name (e.g. com.example.spnego). As explained in Kerberos for Chrome on Android, the account type must be defined to use customTokens. The account type must support the “SPNEGO” feature (HttpNegotiateConstants.SPNEGO_FEATURE).

Interface to Chrome

Chrome finds the SPNEGO authenticator through the Android account type it provides. This is defined by the authenticator, and passed to Chrome through the AuthAndroidNegotiateAccountType policy.

The interface to Chrome is through the Android account management framework, and in particular through AbstractAccountManager.getAuthToken. Chrome, in org.chromium.net.HttpNegotiateConstants, defines some additional keys and values that are used in the arguments to getAuthToken, and in the returned result bundle.

getAuthToken arguments

When getAuthToken is called the authTokenType will be "SPNEGO:HOSTBASED:<spn>" where <spn> is the principal for the request. This will always be a host based principal (in the current implementation; future versions may allow other types of principal, but if they do so they will use a different prefix. SPNEGO Authenticators should check the prefix).

The options bundle will contain the keys:

[KEY_CALLER_PID](http://developer.android.com/reference/android/accounts/AccountManager.html#KEY_CALLER_PID)

[KEY_CALLER_UID](http://developer.android.com/reference/android/accounts/AccountManager.html#KEY_CALLER_UID)

HttpNegotiateConstants.KEY_CAN_DELEGATE - True if delegation is allowed,
false otherwise.

If this is the second or later round of multi-round authentication sequence it will also contain.

HttpNegotiateConstants.KEY_INCOMING_AUTH_TOKEN - The incoming token from the
WWW-Authenticate header, Base64 encoded.

HttpNegotiateConstants.KEY_SPNEGO_CONTEXT - The SPNEGO context provided by
the authenticator on the previous round. This is a bundle. Chrome treats
this as an opaque object and simply preservers it between rounds.

getAuthToken result bundle

The final result bundle of getAuthToken (returned either as the return value of getAuthToken, or through the AccountAuthenticatorResponse) should contain the account name, account type, and token as defined in the Android documentation. The token should be Base64 encoded. In addition the bundle should contain the keys:

HttpNegotiateConstants.KEY_SPNEGO_RESULT - the SPNEGO result code. This
should be one of the values defined in
[HttpNegotiateConstants](https://code.google.com/p/chromium/codesearch#chromium/src/net/android/java/src/org/chromium/net/HttpNegotiateConstants.java&q=HttpNeg&sq=package:chromium&l=10).

HttpNegotiateConstants.KEY_SPNEGO_CONTEXT - a context to be returned to the
authenticator in the next authentication round. This is only required if
authentication is incomplete.

Implementation recommendations

Each account provided by an SPNEGO Account Authenticator should correspond
to a single user principal (account) provided by single key distribution
center.

The account authenticator should not store any passwords. Instead it should
store TGTs for the user principles, and require the users to re-enter their
passwords (or other authentication data) when their TGT expires.

The account authenticator should keep a list of authorized applications (or
application signatures) for each account, and refuse to provide service
tokens to other applications. The list of authorized applications may be:

    Built into the account authenticator.

    Configurable by the system administrator

    Configurable by the user. In this case the account authenticator might
    choose to allow the user to authorize new applications dynamically when
    they first request access.

The authenticator can get the uid of the calling app using the KEY_CALLER_UID field of the options bundle, and then identify the requesting application using context.getPackageManager().getNameForUid() or similar.

This is required to ensure that malicious apps run by the user cannot use the user’s credentials to access services in unintended ways. This is particularly important since using the custom tokens option (as described above) disables Android’s own signature check when getting auth tokens.

Unless it is built into the account authenticator, the system administrator
or user will need to be able to configure the location of the key
distribution center.

Error codes displayed in Chrome

In addition to the error codes that can be forwarded from the authenticator app, the following errors can be displayed by Chrome when trying to authenticate a request:

Please search for the cr_net_auth tag in logcat to have more information about the cause of these errors.

Use, and testing, with Chrome

Chrome defines a number of policies for controlling the use of SPNEGO authentication. In particular to enable SPNEGO authentication the AuthServerWhitelist must not be empty, and the AuthAndroidNegotiateAccountType must match the account type provided by the SPNEGO authenticator.

To simplify testing of SPNEGO authentication Chrome on Android supports command line options corresponding to these policies. These are “--auth-server-whitelist=<whitelist>” and “--auth-spnego-account-type=<account type>”. To set these, add them to the command line on the device (requires a rooted device).