개요
대부분의 사용자는 등록 양식에서 포기합니다: 사용자 이름, 기억해야 하는 비밀번호, 스팸함으로 들어가는 이메일 코드. KlugAuth를 쓰면 Apple로 계속하기 또는 Google로 계속하기를 탭하는 것으로 끝납니다.
KlugAuth는 각 플랫폼의 네이티브 로그인을 표시합니다. iOS와 macOS에서는 Face ID / Touch ID를 사용하는 Apple 시트, Google에는 Android 계정 시트(Credential Manager), iOS와 macOS의 Google에는 시스템 로그인 창을 사용합니다. 서버에서 검증할 수 있도록 공급자의 ID 토큰을 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 silicon Mac의 iPad 앱에서 Sign in with Apple
- Android(Credential Manager)와 iOS / macOS(시스템 로그인 창, Google SDK 없음)에서 Sign in with Google
- ID 토큰, 사용자 ID, 이메일, 이름. 갱신과 해제를 위한 Apple 인증 코드
- 재전송 공격 방지를 위한 nonce 도우미(Apple은 SHA-256, Google은 원본 값을 전달)
- 재방문 사용자를 위한 Apple 자격 증명 상태 확인(authorized / revoked)
- 성공, 취소, 오류 이벤트가 분리되어 있으며, 취소는 오류가 아닌 취소로 보고
지원 플랫폼
Android에서의 Sign in with Apple은 Apple의 웹 흐름과 작은 서버 페이지로 가능하며, 프로젝트의 일부로 제공해 드릴 수 있습니다. isSupported(Apple)와 isGoogleSupported가 기기가 제공하는 기능을 코드에 알려 줍니다.
시작하기
애플리케이션 디스크립터에 확장을 추가합니다:
<extensions>
<extensionID>com.klug.auth.KlugAuth</extensionID>
</extensions>
Sign in with Apple을 쓰려면 entitlement를 추가하세요(iOS: 디스크립터의 Entitlements, macOS: 서명 시):
<key>com.apple.developer.applesignin</key> <array><string>Default</string></array>
Google의 경우 Google Cloud 콘솔에서 OAuth 클라이언트를 만드세요: 웹 클라이언트(ID가 Android에서의 토큰 audience), 패키지 이름과 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 | Sign in with Apple을 사용할 수 있는 곳(iOS, macOS)에서 true입니다. |
| isGoogleSupported | Sign in with Google을 사용할 수 있는 곳(Android, iOS, macOS)에서 true입니다. |
| generateNonce() | 정적 메서드입니다. 새로운 무작위 nonce(16진수 32바이트)이며, 한 번만 사용하고 같은 값을 서버로 보내세요. |
| signInWithApple(nonce) | Apple의 로그인 시트를 엽니다. 시작하지 못하면(지원되지 않거나 이미 로그인이 진행 중) false를 반환합니다. |
| signInWithGoogle(clientId, nonce) | Google 로그인을 엽니다. Android: 웹 클라이언트 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 애플리케이션 1개에 대한 연간 라이선스이며 12개월간 모든 업데이트를 제공합니다. 결제 후 확장, ActionScript 라이브러리, 문서를 이메일로 보내 드리며, 보통 영업일 기준 1일 이내입니다. 라이선스 및 약관
요구 사항
| AIR SDK | 51.x (HARMAN) |
| iOS | iOS 13 이상 |
| Android | Android 6 이상, Google Play services |
| macOS | macOS 10.15 이상 |
| Apple | App ID의 Sign in with Apple capability |
| Google Cloud 콘솔의 OAuth 클라이언트 |