概览
大多数用户会在注册表单处放弃:用户名、必须记住的密码、落入垃圾邮件的邮箱验证码。使用 KlugAuth,他们只需点击通过 Apple 继续或通过 Google 继续即可完成。
KlugAuth 显示各平台的原生登录界面:iOS 和 macOS 上带 Face ID / Touch ID 的 Apple 登录界面,Google 在 Android 上使用的账户界面 (Credential Manager),以及 Google 在 iOS 和 macOS 上使用的系统登录窗口。它会返回服务商的身份令牌及您的 nonce,可直接在服务器上验证,另外还有在删除账户时撤销关联所需的 Apple 授权码(App Store 指南 5.1.1(v))。
不捆绑任何 Google SDK:iOS 和 macOS 上的 Google 登录在系统浏览器界面中使用带 PKCE 的标准 OAuth 流程,因此扩展保持小巧。
KlugAuth 运行在我们的应用的当前版本中,这就是我们的概念验证,用于通过 Apple 或 Google 进行注册、登录以及确认设备登出。
特性
- 自成一体:一个 ANE,无需添加核心库或依赖 ANE
- 在 iOS 13+、macOS 10.15+ 以及 Apple 芯片 Mac 上的 iPad 应用中使用 Apple 登录
- 在 Android (Credential Manager) 以及 iOS / macOS(系统登录窗口,无需 Google SDK)上使用 Google 登录
- 身份令牌、用户 ID、电子邮件和姓名;用于刷新和撤销的 Apple 授权码
- 用于防重放保护的 nonce 辅助工具(Apple 携带其 SHA-256,Google 携带原始值)
- 面向回访用户的 Apple 凭据状态检查(已授权 / 已撤销)
- 成功、取消和错误事件各自独立;取消会被如实报告,绝不算作错误
支持的平台
在 Android 上使用 Apple 登录可通过 Apple 的网页流程和一个小型服务器页面实现;我们可以在项目中提供该功能。isSupported(Apple)和 isGoogleSupported 会告诉您的代码设备提供哪些登录方式。
入门
将扩展添加到您的应用描述文件中:
<extensions>
<extensionID>com.klug.auth.KlugAuth</extensionID>
</extensions>
要使用 Apple 登录,请添加 entitlement(iOS:描述文件中的 Entitlements;macOS:签名时添加):
<key>com.apple.developer.applesignin</key> <array><string>Default</string></array>
对于 Google,请在 Google Cloud 控制台中创建 OAuth 客户端:一个 Web 客户端(其 ID 即 Android 上的令牌受众)、一个包含您的包名和 SHA-1 的 Android 客户端,以及一个供 iOS 和 macOS 使用的 iOS 客户端。
代码示例
五个完整流程,从按钮到您的服务器。每次调用都会立即返回;结果以 KlugAuthEvent 的形式到达。完整文档 →
import com.klug.auth.KlugAuth; import com.klug.auth.KlugAuthEvent; var auth:KlugAuth = KlugAuth.getInstance(); var nonce:String = ""; // a fresh one for every attempt appleButton.visible = auth.isSupported; // iOS 13+, macOS 10.15+, iPad apps on the Mac googleButton.visible = auth.isGoogleSupported; // Android, iOS 13+, macOS 10.15+ auth.addEventListener( KlugAuthEvent.APPLE_SUCCESS, onAppleSuccess ); auth.addEventListener( KlugAuthEvent.APPLE_CANCELED, onCanceled ); auth.addEventListener( KlugAuthEvent.APPLE_ERROR, onError ); auth.addEventListener( KlugAuthEvent.GOOGLE_SUCCESS, onGoogleSuccess ); auth.addEventListener( KlugAuthEvent.GOOGLE_CANCELED, onCanceled ); auth.addEventListener( KlugAuthEvent.GOOGLE_ERROR, onError );
function signInWithApple():void { nonce = KlugAuth.generateNonce(); // 32 random bytes as hex, used once if ( !auth.signInWithApple( nonce ) ) // false: not supported, or a sheet is already open showMessage( "Sign in with Apple is not available." ); // your app function } function onAppleSuccess( e:KlugAuthEvent ):void { // the token carries SHA-256 of the nonce: the server hashes the raw value and compares sendToServer( "apple", e.identityToken, nonce, e.fullName, e.authorizationCode ); saveAppleUserID( e.userID ); // stable for this Apple ID; your app function // e.email and the names arrive only on the FIRST authorization; // the e-mail is always inside the identity token } // your app function: the server verifies the token and signs the user in function sendToServer( provider:String, idToken:String, rawNonce:String, name:String, appleCode:String = "" ):void { var vars:URLVariables = new URLVariables(); vars.provider = provider; vars.id_token = idToken; vars.nonce = rawNonce; vars.name = name; if ( appleCode != "" ) vars.code = appleCode; // lets the server revoke the link later var req:URLRequest = new URLRequest( "https://your.server/auth" ); req.method = URLRequestMethod.POST; req.data = vars; var loader:URLLoader = new URLLoader(); loader.addEventListener( Event.COMPLETE, onServerAnswer ); // your app function loader.load( req ); }
import flash.system.Capabilities; // Android: the WEB client ID, which is the token audience on your server. // iOS and macOS: the iOS client ID, used in the system sign-in window. var googleClientId:String = Capabilities.manufacturer.indexOf( "Android" ) >= 0 ? "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com" : "YOUR_IOS_CLIENT_ID.apps.googleusercontent.com"; function signInWithGoogle():void { nonce = KlugAuth.generateNonce(); // Google puts the raw value into the token if ( !auth.signInWithGoogle( googleClientId, nonce ) ) // false: no client ID, or already open showMessage( "Google sign-in could not start." ); } function onGoogleSuccess( e:KlugAuthEvent ):void { // e.userID is the account e-mail; names arrive every time sendToServer( "google", e.identityToken, nonce, e.fullName ); }
function onCanceled( e:KlugAuthEvent ):void { // the user closed the sheet: not an error, simply stay on the start screen } function onError( e:KlugAuthEvent ):void { if ( e.type == KlugAuthEvent.GOOGLE_ERROR && e.errorCode == "NO_ACCOUNT" ) showMessage( "Please add a Google account to this device first." ); // Android else showMessage( "Sign-in failed: " + e.errorMessage ); // e.errorCode for your log }
auth.addEventListener( KlugAuthEvent.APPLE_CREDENTIAL_STATE, function( e:KlugAuthEvent ):void { // e.state: authorized | revoked | notFound | transferred | unknown if ( e.state == "revoked" || e.state == "notFound" ) signOut(); // your app function: the user removed the app from their Apple ID }); var savedID:String = loadAppleUserID(); // stored after APPLE_SUCCESS; your app function if ( auth.isSupported && savedID != "" ) auth.getCredentialState( savedID ); // Apple only: iOS and macOS
API 概览
| KlugAuth.getInstance() | 返回唯一实例。 |
| isSupported | 在支持 Apple 登录的平台上为 True(iOS、macOS)。 |
| isGoogleSupported | 在支持 Google 登录的平台上为 True(Android、iOS、macOS)。 |
| generateNonce() | 静态方法。生成全新的随机 nonce(32 字节,十六进制);仅使用一次,并将相同的值发送到您的服务器。 |
| signInWithApple(nonce) | 打开 Apple 登录界面。如果无法启动(不受支持,或已有登录正在进行),则返回 false。 |
| signInWithGoogle(clientId, nonce) | 打开 Google 登录。Android:Web 客户端 ID;iOS / macOS:iOS 客户端 ID。如果无法启动则返回 false。 |
| getCredentialState(userID) | 向 Apple 查询用户是否仍处于授权状态;答复以 APPLE_CREDENTIAL_STATE 形式到达。适用于 iOS 和 macOS。 |
| KlugAuthEvent | identityToken, authorizationCode, userID, email, givenName, familyName, fullName, state, errorCode, errorMessage. |
事件
| APPLE_SUCCESS | identityToken, authorizationCode, userID;email 和姓名仅在首次授权时提供。 |
| APPLE_CANCELED | 用户关闭了界面。 |
| APPLE_ERROR | 来自系统的 errorCode 和 errorMessage。 |
| APPLE_CREDENTIAL_STATE | userID 和 state:authorized · revoked · notFound · transferred · unknown |
| GOOGLE_SUCCESS | identityToken, userID(账户的电子邮件), email, givenName, familyName. |
| GOOGLE_CANCELED | 用户关闭了界面。 |
| GOOGLE_ERROR | errorCode 和 errorMessage;在 Android 上,当设备没有 Google 账户时为 NO_ACCOUNT。 |
购买 KlugAuth
一个 AIR 应用的年度许可,含 12 个月内的所有更新。付款后,您将通过电子邮件收到扩展、ActionScript 库和文档,通常在一个工作日内。许可与条款
要求
| AIR SDK | 51.x (HARMAN) |
| iOS | iOS 13 或更高版本 |
| Android | Android 6 或更高版本,Google Play 服务 |
| macOS | macOS 10.15 或更高版本 |
| Apple | 您的 App ID 上的 Sign in with Apple 功能 |
| Google Cloud 控制台中的 OAuth 客户端 |