Visão geral
A maioria dos usuários desiste no formulário de cadastro: um nome de usuário, uma senha que precisam lembrar, um código por e-mail que cai no spam. Com o KlugAuth, eles tocam em Continue with Apple ou Continue with Google e pronto.
O KlugAuth exibe o login nativo de cada plataforma: a tela da Apple com Face ID / Touch ID no iOS e macOS, a tela de contas do Android (Credential Manager) para o Google e a janela de login do sistema para o Google no iOS e macOS. Ele devolve o token de identidade do provedor junto com o seu nonce, pronto para verificação no seu servidor, além do código de autorização da Apple de que você precisa para revogar o vínculo quando uma conta é excluída (diretriz 5.1.1(v) da App Store).
Nenhum SDK do Google é incluído: o login com Google no iOS e macOS usa o fluxo OAuth padrão com PKCE na tela de navegador do sistema, por isso a extensão continua pequena.
O KlugAuth roda na versão atual de nossos apps, nossa prova de conceito, para cadastro, login e confirmação de logout de um dispositivo com Apple ou Google.
Recursos
- Autossuficiente: uma ANE, sem biblioteca principal nem ANEs de dependência para adicionar
- Sign in with Apple no iOS 13+, macOS 10.15+ e apps iPad em Macs com Apple silicon
- Sign in with Google no Android (Credential Manager) e no iOS / macOS (janela de login do sistema, sem SDK do Google)
- Token de identidade, ID do usuário, e-mail e nome; código de autorização da Apple para renovação e revogação
- Auxiliar de nonce para proteção contra replay (a Apple carrega o SHA-256 dele, o Google o valor bruto)
- Verificação do estado da credencial Apple (autorizada / revogada) para usuários recorrentes
- Eventos separados de sucesso, cancelamento e erro; o cancelamento é informado, nunca como erro
Plataformas compatíveis
O Sign in with Apple no Android é possível com o fluxo web da Apple e uma pequena página no servidor; podemos fornecê-la como parte de um projeto. isSupported (Apple) e isGoogleSupported informam ao seu código o que o dispositivo oferece.
Primeiros passos
Adicione a extensão ao descritor do seu aplicativo:
<extensions>
<extensionID>com.klug.auth.KlugAuth</extensionID>
</extensions>
Para o Sign in with Apple, adicione a entitlement (iOS: Entitlements no descritor; macOS: na assinatura):
<key>com.apple.developer.applesignin</key> <array><string>Default</string></array>
Para o Google, crie clientes OAuth no console do Google Cloud: um cliente web (seu ID é a audience do token no Android), um cliente Android com o nome do seu pacote e o SHA-1, e um cliente iOS para iOS e macOS.
Exemplos de código
Cinco fluxos completos, dos botões ao seu servidor. Cada chamada retorna imediatamente; os resultados chegam como KlugAuthEvents. Documentação completa →
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
Visão geral da API
| KlugAuth.getInstance() | Retorna a instância única. |
| isSupported | Verdadeiro onde o Sign in with Apple está disponível (iOS, macOS). |
| isGoogleSupported | Verdadeiro onde o Sign in with Google está disponível (Android, iOS, macOS). |
| generateNonce() | Estático. Um nonce aleatório novo (32 bytes em hexadecimal); use-o uma vez e envie o mesmo valor ao seu servidor. |
| signInWithApple(nonce) | Abre a tela de login da Apple. Retorna false se não conseguiu iniciar (sem suporte, ou um login já em andamento). |
| signInWithGoogle(clientId, nonce) | Abre o login do Google. Android: o ID do cliente web; iOS / macOS: o ID do cliente iOS. Retorna false se não conseguiu iniciar. |
| getCredentialState(userID) | Pergunta à Apple se o usuário ainda está autorizado; a resposta chega como APPLE_CREDENTIAL_STATE. iOS e macOS. |
| KlugAuthEvent | identityToken, authorizationCode, userID, email, givenName, familyName, fullName, state, errorCode, errorMessage. |
Eventos
| APPLE_SUCCESS | identityToken, authorizationCode, userID; email e nomes somente na primeira autorização. |
| APPLE_CANCELED | O usuário fechou a tela. |
| APPLE_ERROR | errorCode e errorMessage do sistema. |
| APPLE_CREDENTIAL_STATE | userID e state: authorized · revoked · notFound · transferred · unknown |
| GOOGLE_SUCCESS | identityToken, userID (o e-mail da conta), email, givenName, familyName. |
| GOOGLE_CANCELED | O usuário fechou a tela. |
| GOOGLE_ERROR | errorCode e errorMessage; no Android, NO_ACCOUNT quando o dispositivo não tem conta Google. |
Compre o KlugAuth
Uma licença anual para um app AIR, com todas as atualizações por 12 meses. Após o pagamento, você recebe a extensão, a biblioteca ActionScript e a documentação por e-mail, normalmente em até um dia útil. Licença e termos
- Licença para um app AIR
- Todas as atualizações por 12 meses
- Sem suporte
- Licença para um app AIR
- Todas as atualizações por 12 meses
- Resposta por e-mail em até 7 dias
- Licença para um app AIR
- Todas as atualizações por 12 meses
- Resposta por e-mail em até 48 horas
Requisitos
| AIR SDK | 51.x (HARMAN) |
| iOS | iOS 13 ou posterior |
| Android | Android 6 ou posterior, Google Play Services |
| macOS | macOS 10.15 ou posterior |
| Apple | Capability Sign in with Apple no seu App ID |
| Clientes OAuth no console do Google Cloud |