如果您的 GDK Glassware 需要针对某项 Web 服务对用户进行身份验证,GDK 会提供一个 API,让用户可以在安装您的 Glassware 时输入其凭据。
通过使用此 API,您可以为 Glass 用户提供一致的用户体验,并避免实现自己的自定义身份验证方案所产生的开销。
创建 Google API 服务账号
正确设置身份验证后,在用户通过您的服务进行身份验证后,您的 Web 应用后端会使用 Mirror API 将用户的账号信息推送到 Google Glass。
如需访问此 API,请创建一个 Google API 项目,然后为“服务账号”(而不是“网站应用”)创建客户端 ID。使用服务账号后,用户无需单独向您的应用授予将其凭据推送到 Glass 的权限,也不会再次看到 OAuth 权限页面和您自己的身份验证页面。
如需创建此账号,请执行以下操作:
- 前往 Google Developers Console。
- 点击创建项目按钮,然后输入所需信息。
- 创建项目后,记下项目编号,您稍后会用到它。
- 在 API 和身份验证下,点击 API,然后为您的新项目启用 Google Mirror API。
- 在 API 和身份验证下,点击凭据,然后点击创建新的客户端 ID。选中标有服务账号的复选框,为项目创建新的 OAuth 2.0 客户端 ID。
- 系统会弹出一个窗口,告知您私钥正在下载到您的计算机,并为您提供该私钥的密码。关闭此窗口后,您将无法再下载此私钥或查看密码。如果它们丢失,您必须创建新的密钥。
- 记下服务账号的电子邮件地址,您稍后需要使用该地址进行 API 调用。
提供有关您的 Glassware 的元数据
准备提交 Glassware 时,您需要提供以下信息。这样,我们就可以在您实现 Glassware 时将其设置为正确进行身份验证。
- 您的身份验证网址,用户在 MyGlass 中打开您的 Glassware 时将被重定向到此网址。
- 账号类型(您在 Glass 设备上调用 Android
AccountManager
API 时将使用的字符串) AndroidManifest.xml
中的应用的软件包名称- 您在上文中创建的项目的数字 Google API 项目 ID
- 要上传到 MyGlass 的 APK。进行测试时,您只需提供一次此 APK 即可在通过 MyGlass 开启您的 Glassware 时处理初始下载;之后,您可以通过在设备上覆盖 APK 来本地迭代和调试。请注意,此 APK 需要满足以下条件:
- 它必须是按 ZIP 对齐的。
- 之后,您不得对软件包名称或私钥进行任何更改(如果发生这两种更改,Android 软件包管理器将不允许升级)。
- 必须小于 50 MB。
- 必须使用最新版本的 GDK 进行编译。
实现身份验证流程
下图显示了 GDK Glassware 的基本身份验证流程:
如需实现身份验证流程,请执行以下操作:
当用户在 MyGlass 中开启您的 Glassware 时,系统会将其重定向到您的身份验证网址。这些请求包含一个名为
userToken
的查询参数,您需要在稍后使用该参数。用户在您的身份验证页面上输入其凭据。
您的服务器会验证用户的凭据。如果凭据有效,请对
mirror.accounts.insert
方法进行 Mirror API 调用。使用此方法时,您需要在构建 Mirror 服务对象时指定https://www.googleapis.com/auth/glass.thirdpartyauth
作用域。账号创建示例中显示了使用原始 HTTP 或 Java 进行此 API 调用的示例。您在下面提供的参数和请求正文包含的信息与您直接在设备上创建账号时提供给 Android
AccountManager
的信息相同。属性名称 值 说明 features[]
字符串列表 功能列表(请参阅 AccountManager.hasFeatures
)。password
字符串 账号密码(请参阅 AccountManager.getPassword
)。我们建议您不要在此字段中存储用户的实际密码,而应将其用于存储长期有效的私密数据,例如刷新令牌。userData[]
对象列表 与账号关联的一对或多对用户数据(请参阅 AccountManager.getUserData
)。userData[].key
字符串 与特定用户数据键值对关联的键。 userData[].value
字符串 与特定用户数据键值对关联的值。 authTokens[]
对象列表 与账号关联的一个或多个身份验证令牌(请参阅 AccountManager.getAuthToken
)。authTokens[].type
字符串 身份验证令牌的类型。 authTokens[].authToken
字符串 身份验证令牌。 收到
mirror.account.insert
请求后,Mirror API 会将账号推送到用户的 Google Glass 设备,您现在可以使用AccountManager
类访问该账号。
建议的身份验证流程
请遵循以下准则实现人性化的身份验证流程:
- 针对移动设备优化流程。
- 如果您的流程具有作用域,并且用户取消了这些流程,请提供设计良好的错误消息。
- 确保您请求的镜框范围实际上在您的 Glassware 中使用。
- 如果可以关联某个用户账号,请确保关联该账号。
- 请尽可能将用户数据备份到云端。
为确保 Glassware 身份验证的一致性,请使用以下身份验证流程之一:
无需账号即可使用镜像模式或混合模式
- 在 MyGlass 中开启 Wi-Fi 后,系统会在弹出式窗口中打开您的身份验证网址。
- 这会直接将用户发送到要接受的镜重。
- 用户接受或取消相应范围后,关闭弹出式窗口。
使用账号镜像
- 在“我的 Google 眼镜”中开启此功能后,系统会在弹出式窗口中打开您的身份验证网址。
- 如果用户已登录您的服务,请直接将用户发送到镜重。
- 如果用户未登录,请显示登录字段,允许用户登录您的服务,然后将用户发送到镜重。
- 如果用户没有账号,请提供用于创建账号的链接。用户必须能够在安装流程过程中创建账号。
- 用户接受范围。
- 如果您的 Glassware 具有可配置的设置,请将用户定向到已选择合理默认值的设置页面。
- 如果您的 Glassware 没有可配置的设置,请将用户定向至确认页面。如果无需进行其他配置,请关闭弹出式窗口。
混合搭配一个账号
- 在“我的 Google 眼镜”中开启此功能后,系统会在弹出式窗口中打开您的身份验证网址。
- 如果用户已登录您的服务,请直接将用户发送到镜重。
- 如果用户未登录,则显示登录字段,允许用户登录,然后将其发送到镜重。
- 如果用户没有账号,请提供用于创建账号的链接。
- 用户接受权限范围。
- 向 Mirror API 发送请求以插入 GDK 账号。
- 将用户定向至已选择合理默认值的设置页面。
- 向用户发送确认页面。如果无需进行其他配置,请关闭弹出式窗口。
使用账号和自定义镜像范围的镜像或混合
- 在“我的 Google 眼镜”中开启此功能后,系统会在弹出式窗口中打开您的身份验证网址。
- 如果用户已登录您的服务,请将用户发送到您的内部镜重
- 如果用户未登录,则显示登录字段,允许其登录,然后将其发送到您的内部范围
- 如果用户没有账号,请提供用于创建账号的链接。
- 当用户接受您的自定义范围后,将用户发送到 Google 的范围。
- 向 Mirror API 发送请求以插入 GDK 账号。
- 将用户定向至已选择合理默认值的设置页面。
- 向用户发送确认页面。如果不需要其他配置,请关闭弹出式窗口。
镜像或混合使用 Android/iPhone 应用
- 在“我的 Google 眼镜”中开启此功能后,系统会在弹出式窗口中打开您的身份验证网址。
- 这会直接将用户发送到要接受的镜重。
- 用户接受范围后:
- 如果用户拥有配套应用并已通过身份验证,请关闭弹出式窗口。
- 如果不是,请将用户引导至插页式广告,引导他们从 Google Play 商店或 iOS 商店下载应用
- 安装应用并进行身份验证后,关闭弹出式窗口
GDK 且没有账号
在此流程中,只需在“我的 Google 眼镜”中开启相应 Glassware 即可。
登录 GDK
- 在“我的 Google 眼镜”中开启此功能后,系统会在弹出式窗口中打开您的身份验证网址。
- 如果用户已登录您的服务,请将其转到确认界面。
- 如果用户未登录,则显示登录字段,允许用户登录,然后将他们发送到确认屏幕。
- 如果用户没有账号,请提供用于创建账号的链接。
- 用户接受权限范围。
- 向 Mirror API 发送请求以插入 GDK 账号。
- 显示确认屏幕,并在显示一小段时间后关闭该屏幕。
账号创建示例
请尽可能使用 Mirror 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
。
在
AndroidManifest.xml
文件中声明以下清单权限:<uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.USE_CREDENTIALS" />
检索玻璃器皿的账号:
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.
从
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);