Authentication for GDK Glassware

If your GDK Glassware needs to authenticate users against a web service, the GDK provides an API that lets the user enter their credentials when they install your Glassware.

By using this API, you provide a consistent user experience for Glass users and avoid the overhead of implementing your own custom authentication schemes.

Creating a Google API service account

When authentication is set up correctly, your web app's back end uses the Mirror API to push users' account information to Glass after they authenticate with your service.

In order to access this API, create a Google API project and then create a client ID for a "service account" (and not a "web application"). By using a service account, users do not have to separately grant your application permission to push their credentials to Glass and won't be presented with both the OAuth permissions page and your own authentication page again.

To create this account:

  1. Go to the Google Developers Console.
  2. Click the Create Project button and enter the requested information.
  3. Once your project is created, make a note of the Project Number, which you will need later.
  4. Under APIs & auth, click APIs and enable the Google Mirror API for your new project.
  5. Under APIs & auth, click Credentials, then click Create New Client ID. Check the box labeled Service account to create a new OAuth 2.0 client ID for the project.
  6. A popup window will inform you that the private key is being downloaded to your computer and provides you with the password for that private key. Once you close this window, you will not be able to download this private key or see the password again. If they are ever lost, you must create a new one.
  7. Make a note of the service account's e-mail address, which you will need later to make the API call.

Providing metadata about your Glassware

When you are ready to submit your Glassware, you will need to provide the following information. This allows us to set up your Glassware to be authenticated correctly when you implement it.

  • Your authentication URL, which users are redirected to when they turn on your Glassware in MyGlass.
  • The account type (the string you will be using when you call the Android AccountManager APIs on the Glass device)
  • The package name of your application from your AndroidManifest.xml
  • The numeric Google API project ID of the project that you created above
  • The APK to upload on MyGlass. For testing, you only need to provide this APK once to handle the initial download when your Glassware is turned on from MyGlass; after that, you can iterate and debug locally by overwriting the APK on your device. Note that this APK needs to meet the following criteria:
    • It must be zip-aligned.
    • You must not make any changes to the package name or private signing key after this (the Android package manager does not allow upgrades if either of these changes).
    • It must be smaller than 50 megabytes.
    • It must be compiled using the latest version of the GDK.

Implementing the authentication flow

The following diagram shows the basic authentication flow for GDK Glassware:

To implement the authentication flow:

  1. When users turn on your Glassware in MyGlass, they are redirected to your authentication URL. These requests include a query parameter named userToken that you need to use later.

  2. The user enters their credentials on your authentication page.

  3. Your server validates the user's credentials. If the credentials are valid, make a Mirror API call to the mirror.accounts.insert method. This method requires that you specify the https://www.googleapis.com/auth/glass.thirdpartyauth scope when you build your Mirror service object. Examples of making this API call using either raw HTTP or Java are shown in the account creation examples.

    The parameters and request body that you provide below represent the same information that you would provide to Android's AccountManager if you were creating the account directly on the device.

    Property name Value Description
    features[] list of strings A list of features (see AccountManager.hasFeatures).
    password string The account password (see AccountManager.getPassword). We recommend that you not store the user's actual password in this field, but instead use it to store long-lived private data like a refresh token.
    userData[] list of objects One or more pairs of user data associated with the account (see AccountManager.getUserData).
    userData[].key string The key associated with a particular user data key-value pair.
    userData[].value string The value associated with a particular user data key-value pair.
    authTokens[] list of objects One or more auth tokens associated with the account (see AccountManager.getAuthToken).
    authTokens[].type string The type of the auth token.
    authTokens[].authToken string The auth token.

  4. Upon receiving the mirror.account.insert request, the Mirror API pushes the account to the user's Glass device(s), where you can now access it using the AccountManager class.

Follow these guidelines to implement a user-friendly authentication flow:

  • Optimize your flow for mobile devices.
  • If your flow has a scope and the user cancels them, have a well-designed error message.
  • Ensure the scopes that you request are actually being used in your Glassware.
  • If a user account can be connected, ensure that you connect it.
  • Where possible, user data should back up to the cloud.

To maintain consistency in Glassware authentication, use one of the following authentication flows:

Mirror or hybrid without an account

  1. After toggling on in MyGlass, your authentication URL opens in a pop-up.
  2. This directly sends the user to scopes to accept.
  3. After the user accepts or cancels the scopes, close the pop-up.

Mirror with an account

  1. After toggling on in MyGlass, your authentication URL opens in a pop-up.
    • If the user is already signed in to your service, send the user directly to scopes.
    • If the user is not signed in, show the sign-in fields, allow them to sign in to your service, and then send them to scopes.
    • If the user does not have an account, provide a link to create an account. Users must have a way to create an accoutn as part of the install flow process.
  2. User accepts scopes.
    • If your Glassware has configurable settings, send the user to the settings page with reasonable defaults selected.
    • If your Glassware does not have configurable settings, send the user to a confirmation page. Close the pop-up if no additional configuration is required.

Hybrid with an account

  1. After toggling on in MyGlass, your authentication URL opens in a pop-up.
    • If the user is already signed in to your service, send the user directly to scopes.
    • If the user is not signed in, show sign in fields, allow them to sign in, and then send them to scopes.
    • If the user does not have an account, provide a link to create an account.
  2. User accepts scopes.
  3. Send a request to the Mirror API to insert the GDK Account.
    • Send the user to the settings page with reasonable defaults selected.
    • Send the user a confirmation page. Close the pop-up if no additional configuration is required.

Mirror or hybrid with an account and custom scopes

  1. After toggling on in MyGlass, your authentication URL opens in a pop-up.
    • If the user is already signed in to your service, send user to your internal scopes
    • If the user is not signed in, show sign in fields, allow them to sign in, and then send them to your internal scopes
    • If the user does not have an account, provide a link to create an account.
  2. When the user accepts your custom scopes, send user to Google's scopes.
  3. Send a request to the Mirror API to insert the GDK Account.
    • Send the user to the settings page with reasonable defaults selected.
    • Send the user a confirmation page. Close the pop-up if no additional configuration is required.

Mirror or hybrid with an Android/iPhone app

  1. After toggling on in MyGlass, your authentication URL opens in a pop-up.
  2. This directly sends the user to scopes to accept.
  3. After the user accepts scopes:
    • If the user has the companion app and is authenticated, close the pop-up window.
    • If not, send the user to an interstitial that directs them to download the app from the Google Play store or iOS store
  4. After installing the app and authenticating, close the pop-up window

GDK and no account

Toggling the Glassware on in MyGlass is all that is required for this flow.

GDK with an account

  1. After toggling on in MyGlass, your authentication URL opens in a pop-up.
    • If the user is already signed in to your service, send the user to the confirmation screen.
    • If the user is not signed in, display the sign in fields, allow them to sign in, and then send them to the confirmation screen.
    • If the user does not have an account, provide a link to create an account.
  2. User accepts scopes.
  3. Send a request to the Mirror API to insert the GDK Account.
  4. Show the confirmation screen, and close the screen after showing it for a short period of time.

Account creation examples

Use the client libraries for the Mirror API when possible. This makes calling mirror.accounts.insert to create the account easier.

Raw HTTP example

The example below only shows the URL of the request and an example of the JSON body that it expects. Making raw HTTP requests on behalf of a service account is much more complicated (see Using OAuth 2.0 for Server to Server Applications for the full details), so we recommend that you use one of our Google API client libraries if possible to make this easier.

Request method and URL:

POST https://www.googleapis.com/mirror/v1/accounts/{userToken}/com.example.myapp/username%40email.com

Request body:

{
    "features": ["a", "b", "c"],
    "userData": [
        { "key": "realName", "value": "Rusty Shackleford" },
        { "key": "foo", "value": "bar" }
    ],
    "authTokens": [
        { "type": "your_token_type", "authToken": "zT419Ma3X2pBr0L..." }
    ]
}

Replace {userToken} in the request URL with the token that was passed to your authentication URL in step 1 of Implementing the authentication flow.

Java Example

This example shows how to use the Java client library to call mirror.accounts.insert

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.mirror.Mirror;
import com.google.api.services.mirror.model.Account;
import com.google.api.services.mirror.model.AuthToken;
import com.google.common.collect.Lists;
...

/** Email of the Service Account */
private static final String SERVICE_ACCOUNT_EMAIL =
    "<some-id>@developer.gserviceaccount.com";

/** Path to the Service Account's Private Key file */
private static final String SERVICE_ACCOUNT_PKCS12_FILE_PATH =
    "/path/to/<public_key_fingerprint>-privatekey.p12";

/** The account type, usually based on your company or app's package. */
private static final String ACCOUNT_TYPE = "com.example.myapp";

/** The Mirror API scopes needed to access the API. */
private static final String MIRROR_ACCOUNT_SCOPES =
    "https://www.googleapis.com/auth/glass.thirdpartyauth";

/**
 * Build and returns a Mirror service object authorized with the service accounts.
 *
 * @return Mirror service object that is ready to make requests.
 */
public static Mirror getMirrorService() throws GeneralSecurityException,
    IOException, URISyntaxException {
  HttpTransport httpTransport = new NetHttpTransport();
  JacksonFactory jsonFactory = new JacksonFactory();
  GoogleCredential credential = new GoogleCredential.Builder()
      .setTransport(httpTransport)
      .setJsonFactory(jsonFactory)
      .setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
      .setServiceAccountScopes(MIRROR_ACCOUNT_SCOPES)
      .setServiceAccountPrivateKeyFromP12File(
          new java.io.File(SERVICE_ACCOUNT_PKCS12_FILE_PATH))
      .build();
  Mirror service = new Mirror.Builder(httpTransport, jsonFactory, null)
      .setHttpRequestInitializer(credential).build();
  return service;
}

/**
 * Creates an account and causes it to be synced up with the user's Glass.
 * This example only supports one auth token; modify it if you need to add
 * more than one, or to add features, user data, or the password field.
 *
 * @param mirror the service returned by getMirrorService()
 * @param userToken the user token sent to your auth callback URL
 * @param accountName the account name for this particular user
 * @param authTokenType the type of the auth token (chosen by you)
 * @param authToken the auth token
 */
public static void createAccount(Mirror mirror, String userToken, String accountName,
    String authTokenType, String authToken) {
  try {
    Account account = new Account();
    List<AuthToken> authTokens = Lists.newArrayList(
        new AuthToken().setType(authTokenType).setAuthToken(authToken));
    account.setAuthTokens(authTokens);
    mirror.accounts().insert(
        userToken, ACCOUNT_TYPE, accountName, account).execute();
  } catch (IOException e) {
    e.printStackTrace();
  }
}

Retrieving Accounts on Glass

Retrieving and using Account objects on Glass is similar to using the standard Android AccountManager.

  1. Declare the following manifest permissions in your AndroidManifest.xml file:

    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
    
  2. Retrieve the Glassware's accounts:

    AccountManager accountManager = AccountManager.get(mContext);
    // Use your Glassware's account type.
    Account[] accounts = accountManager.getAccountsByType("com.example");
    
    // Pick an account from the list of returned accounts.
    
  3. Retrieve an auth token from the Account:

    // Your auth token type.
    final String AUTH_TOKEN_TYPE = "oauth2:https://www.example.com/auth/login";
    
    accountManager.getAuthToken(account, AUTH_TOKEN_TYPE, null, activity, new AccountManagerCallback<Bundle>() {
        public void run(AccountManagerFuture<Bundle> future) {
            try {
                String token = future.getResult().getString(AccountManager.KEY_AUTHTOKEN);
                // Use the token.
            } catch (Exception e) {
                // Handle exception.
            }
        }
    }, null);