GDK Glassware 身份验证

如果您的 GDK Glassware 需要针对网络服务对用户进行身份验证,GDK 会提供一个 API,供用户在安装 Glassware 时输入其凭据。

通过使用此 API,您可以为 Glass 用户提供一致的用户体验,并避免实现您自己的自定义身份验证方案的开销。

创建 Google API 服务帐号

正确设置身份验证后,Web 应用的后端会在用户通过您的服务进行身份验证后,使用 镜像 API 将用户的帐号信息推送到 Glass。

为了访问此 API,请创建一个 Google API 项目,然后为“服务帐号”(而不是“Web 应用”)创建客户端 ID。通过使用服务帐号,用户不必单独向您的应用授予将其凭据推送到 Glass 的权限,并且不会再次看到 OAuth 权限页面和您自己的身份验证页面。

如需创建此帐号,请执行以下操作:

  1. 转到 Google Developers Console
  2. 点击 Create Project 按钮,然后输入所需信息。
  3. 创建项目后,请记下项目编号,稍后您将会用到。
  4. API 和身份验证下,点击 API 并为新项目启用 Google 镜像 API
  5. API 和身份验证下,点击凭据,然后点击创建新的客户端 ID。勾选标有服务帐号的复选框,为项目创建新的 OAuth 2.0 客户端 ID。
  6. 弹出式窗口会通知您私钥正在下载到您的计算机,并为您提供该私钥的密码。关闭此窗口后,您将无法下载此私钥或再次查看密码。如果这些信息丢失,您必须创建一个新密钥。
  7. 记下服务帐号的电子邮件地址,稍后您需要调用该地址以进行 API 调用。

提供关于 Glassware 的元数据

准备提交 Glassware 时,您需要提供以下信息。这样,我们您就可以设置您的 Glassware,使其在您实现时正确通过身份验证。

  • 您的身份验证网址,当用户在 MyGlass 中开启您的 Glassware 时,系统会将用户重定向到此网址。
  • 帐号类型(您在 Glass 设备上调用 Android AccountManager API 时使用的字符串)
  • AndroidManifest.xml 中应用的软件包名称
  • 您在上面创建的项目的 Google API 数字项目 ID
  • 要在 MyGlass 上上传的 APK。对于测试,当从 MyGlass 开启 Glass 时,您只需提供此 APK 一次即可处理初始下载;之后,您可以通过覆盖设备上的 APK 在本地进行迭代和调试。请注意,此 APK 需要满足以下条件:
    • 它必须 Zip-align。
    • 在此之后,您不得更改软件包名称或私有签名密钥(Android 软件包管理器不允许进行其中任何一项升级)。
    • 该文件必须小于 50 MB。
    • 必须使用最新版 GDK 进行编译。

实现身份验证流程

下图显示了 GDK Glassware 的基本身份验证流程:

如需实现身份验证流程,请执行以下操作:

  1. 当用户在 MyGlass 中开启 Glassware 时,系统会将其重定向到您的身份验证网址。这些请求包含稍后需要用到的名为 userToken 的查询参数。

  2. 用户在身份验证页面上输入其凭据。

  3. 您的服务器会验证用户的凭据。如果凭据有效,请对 mirror.accounts.insert 方法进行 镜像 API 调用。此方法要求您在构建镜像服务对象时指定 https://www.googleapis.com/auth/glass.thirdpartyauth 范围。有关使用原始 HTTP 或 Java 进行此 API 调用的示例,请参阅帐号创建示例

    您在下面提供的参数和请求正文与您直接向设备创建帐号时提供的信息相同。

    属性名称 说明
    features[] 字符串列表 功能列表(请参阅 AccountManager.hasFeatures)。
    password 字符串 帐号密码(请参阅 AccountManager.getPassword)。我们建议您不要在此字段中存储用户的实际密码,而要使用其存储长期的私密数据(如刷新令牌)。
    userData[] 对象列表 与帐号关联的一对或多对用户数据(请参阅 AccountManager.getUserData)。
    userData[].key 字符串 与特定用户数据键值对关联的键。
    userData[].value 字符串 与特定用户数据键值对关联的值。
    authTokens[] 对象列表 与帐号关联的一个或多个身份验证令牌(请参阅 AccountManager.getAuthToken)。
    authTokens[].type 字符串 身份验证令牌的类型。
    authTokens[].authToken 字符串 身份验证令牌。
  4. 收到 mirror.account.insert 请求后,镜像 API 会将该帐号推送到用户的 Glass 设备,现在您可以使用 AccountManager 类访问该帐号。

请按照以下准则实现方便用户使用的身份验证流程:

  • 针对移动设备优化流程。
  • 如果您的流程包含作用域,并且用户取消这些作用域,应提供精心设计的错误消息。
  • 请确保 Google Glass 中实际使用了您请求的范围。
  • 如果用户帐号可以关联,请务必将其关联。
  • 应尽可能将用户数据备份到云端。

为了保持 Glassware 身份验证的一致性,请使用以下身份验证流程之一:

无帐号镜像或混合

  1. 在 MyGlass 中开启后,您的身份验证网址会在弹出式窗口中打开。
  2. 这会直接将用户转到要接受的范围。
  3. 用户接受或取消范围后,请关闭弹出式窗口。

使用帐号镜像

  1. 在 MyGlass 中开启后,您的身份验证网址会在弹出式窗口中打开。
    • 如果用户已登录您的服务,请将用户直接转到范围。
    • 如果用户未登录,则显示登录字段,允许用户登录您的服务,然后将他们转到范围。
    • 如果用户没有帐号,请提供用于创建帐号的链接。在安装流程中,用户必须能够创建帐号。
  2. 用户接受范围。
    • 如果您的 Glassware 具有可配置的设置,请将用户引导至设置页面,同时选择合理的默认设置。
    • 如果您的 Glassware 没有可配置的设置,请将用户转到确认页面。如果不需要其他配置,请关闭弹出式窗口。

混合使用帐号

  1. 在 MyGlass 中开启后,您的身份验证网址会在弹出式窗口中打开。
    • 如果用户已登录您的服务,请将用户直接转到范围。
    • 如果用户未登录,则显示登录字段,允许用户登录,然后将他们转到范围。
    • 如果用户没有帐号,请提供用于创建帐号的链接。
  2. 用户接受范围。
  3. 向 镜像 API 发送请求,以插入 GDK 帐号。
    • 将用户引导至已选定合理默认值的设置页面。
    • 向用户发送确认页面。如果不需要其他配置,请关闭弹出式窗口。

通过帐号和自定义范围镜像或混合

  1. 在 MyGlass 中开启后,您的身份验证网址会在弹出式窗口中打开。
    • 如果用户已登录您的服务,请将用户转到您的内部范围
    • 如果用户未登录,则显示登录字段,允许用户登录,然后将他们发送到您的内部范围
    • 如果用户没有帐号,请提供用于创建帐号的链接。
  2. 当用户接受您的自定义范围时,将用户引导至 Google 的范围。
  3. 向 镜像 API 发送请求,以插入 GDK 帐号。
    • 将用户引导至已选定合理默认值的设置页面。
    • 向用户发送确认页面。如果不需要其他配置,请关闭弹出式窗口。

通过 Android/iPhone 应用进行镜像或混合

  1. 在 MyGlass 中开启后,您的身份验证网址会在弹出式窗口中打开。
  2. 这会直接将用户转到要接受的范围。
  3. 用户接受范围后:
    • 如果用户有配套应用并通过身份验证,请关闭弹出式窗口。
    • 否则,请将用户引导至插页式广告,引导他们从 Google Play 商店或 iOS 商店下载应用
  4. 安装应用并进行身份验证后,关闭弹出式窗口

GDK 和无帐号

此流程只需开启 MyGlass 中的 Glassware 即可。

拥有帐号的 GDK

  1. 在 MyGlass 中开启后,您的身份验证网址会在弹出式窗口中打开。
    • 如果用户已登录您的服务,请将用户转到确认屏幕。
    • 如果用户未登录,则显示登录字段,允许其登录,然后将用户转到确认屏幕。
    • 如果用户没有帐号,请提供用于创建帐号的链接。
  2. 用户接受范围。
  3. 向 镜像 API 发送请求,以插入 GDK 帐号。
  4. 显示确认屏幕,并在显示一小段时间后将其关闭。

帐号创建示例

请尽可能使用 镜像 API 的客户端库。这样即可更轻松地调用 mirror.accounts.insert 来创建帐号。

原始 HTTP 示例

以下示例仅显示了请求的网址以及预期的 JSON 正文示例。代表服务帐号发出原始 HTTP 请求要复杂得多(如需了解详情,请参阅针对服务器到服务器应用使用 OAuth 2.0),因此我们建议您尽可能使用我们的某个 Google API 客户端库

请求方法和网址:

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

请求正文:

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

将请求网址中的 {userToken} 替换为实现身份验证流程第 1 步中传递给您的身份验证网址的令牌。

Java 示例

以下示例展示了如何使用 Java 客户端库调用 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();
  }
}

在 Glass 上检索帐号

在 Glass 上检索和使用 Account 对象与使用标准 Android AccountManager 类似。

  1. AndroidManifest.xml 文件中声明以下清单权限:

    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
    
  2. 检索 Glassware 的帐号:

    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. 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);