Android용 앱 플립

OAuth 2.0을 구현하면 선택적으로 OAuth 기반 App Flip를 구성할 수 있습니다. 이렇게 하면 Android 사용자가 인증 시스템의 계정을 Google 계정에 더 빠르게 연결할 수 있습니다. 다음 섹션에서는 smart home 작업의 App Flip를 설계하고 구현하는 방법을 설명합니다.

디자인 가이드라인

이 섹션에서는 앱 플립 계정 연결 동의 화면의 설계 요구사항 및 권장사항을 설명합니다. Google에서 앱을 호출하면 앱에서 사용자에게 동의 화면을 표시합니다.

요구사양

  1. 사용자의 계정이 Google Home이나 Google 어시스턴트와 같은 특정 Google 제품이 아니라 Google에 연결되어 있음을 알려야 합니다.

권장사항

다음을 수행하는 것이 좋습니다.

  1. Google의 개인정보처리방침을 표시합니다. 동의 화면에 Google 개인정보처리방침 링크를 포함합니다.

  2. 공유할 데이터. 명확하고 간결한 표현을 사용하여 Google에 필요한 데이터와 그 이유를 사용자에게 알립니다.

  3. 명확한 클릭 유도 문구. 동의 화면에 '동의 및 링크'와 같은 명확한 클릭 유도 문구를 표시합니다. 사용자가 계정을 연결하기 위해 Google과 공유해야 하는 데이터를 파악해야 하기 때문입니다.

  4. 취소 기능. 사용자가 연결하지 않기로 결정한 경우 뒤로 돌아가거나 취소할 수 있는 방법을 제공합니다.

  5. 연결 해제 기능 사용자가 연결을 해제할 수 있는 메커니즘(예: 플랫폼의 계정 설정 URL)을 제공합니다. 또는 사용자가 연결된 계정을 관리할 수 있는 Google 계정 링크를 포함할 수 있습니다.

  6. 사용자 계정 변경 기능. 사용자가 계정을 전환할 수 있는 방법을 제안합니다. 이 방법은 사용자가 여러 계정을 사용하는 경향이 있는 경우에 특히 유용합니다.

    • 사용자가 계정을 전환하기 위해 동의 화면을 닫아야 하는 경우, 사용자가 OAuth 연결암시적 흐름을 사용하여 원하는 계정에 로그인할 수 있도록 복구 가능한 오류를 Google에 전송합니다.
  7. 로고를 포함합니다. 동의 화면에 회사 로고를 표시합니다. 스타일 가이드라인을 사용해 로고를 배치합니다. Google 로고도 표시하려면 로고 및 상표를 참조하세요.

이 그림은 사용자 동의 화면을 설계할 때 따라야 할 개별 요구사항과 권장사항을 설명하는 동의 화면의 예를 보여줍니다.
그림 1: 계정 연결 동의 화면 디자인 가이드라인

OAuth 기반 앱 플립 설정

다음 섹션에서는 OAuth 기반 앱 플립을 위한 기본 요건과 작업 콘솔에서 앱 플립 프로젝트를 구성하는 방법을 설명합니다.

스마트 홈 작업 만들기 및 OAuth 2.0 서버 설정하기

App Flip을 구성하려면 먼저 다음을 실행해야 합니다.

Actions 콘솔에서 앱 플립 구성

다음 섹션에서는 Actions 콘솔에서 App Flip을 구성하는 방법을 설명합니다.

  1. OAuth 클라이언트 정보 아래에서 모든 입력란을 작성합니다. 앱 플립이 지원되지 않으면 일반 OAuth가 대체로 사용됩니다.
  2. 계정 연결에 앱 사용 (선택사항)에서 Android에서 사용 설정을 선택합니다.
  3. 다음 입력란을 작성합니다.
    • 애플리케이션 ID. 애플리케이션 ID는 앱에 설정하는 고유 ID입니다.
    • 앱 서명. Android 앱을 설치하려면 먼저 공개 키 인증서로 '서명'해야 합니다. 앱 서명을 얻는 방법에 관한 자세한 내용은 클라이언트 인증을 참조하세요.
    • 승인 인텐트. 이 필드에 인텐트 작업을 지정하는 문자열을 입력합니다.
  4. 원하는 경우 클라이언트를 구성하려면 범위를 추가하고 클라이언트 구성 (선택사항)에서 범위 추가를 클릭합니다.
  5. 저장을 클릭합니다.

Android 앱에 App Flip 구현하기

앱 플립을 구현하려면 Google에서 딥 링크를 허용하도록 앱의 사용자 승인 코드를 수정해야 합니다.

OAuth 기반 앱 플립 연결 (앱 플립)은 Android 앱을 Google 계정 연결 흐름에 삽입합니다. 기존 계정 연결 흐름에서는 사용자가 브라우저에 사용자 인증 정보를 입력해야 합니다. App Flip을 사용하면 Android 앱에 대한 사용자 로그인을 지연시켜 기존 승인을 활용할 수 있습니다. 사용자가 앱에 로그인한 경우 계정을 연결하기 위해 사용자 인증 정보를 다시 입력하지 않아도 됩니다. Android 앱에서 앱 플립을 구현하려면 코드를 최소한으로 변경해야 합니다.

이 문서에서는 앱 플립을 지원하도록 Android 앱을 수정하는 방법을 알아봅니다.

샘플 사용해 보기

앱 플립 연결 샘플 앱은 Android에서 앱 플립과 호환되는 계정 연결 통합을 보여줍니다. 이 앱을 사용하여 Google 모바일 앱에서 수신되는 앱 플립 인텐트에 응답하는 방법을 확인할 수 있습니다.

샘플 앱은 Android용 앱 플립 테스트 도구와 통합되도록 사전 구성되어 있으며, Google과 계정 연결을 구성하기 전에 Android 앱과 앱 플립의 통합을 확인할 수 있습니다. 이 앱은 앱 플립이 사용 설정되었을 때 Google 모바일 앱에서 트리거되는 인텐트를 시뮬레이션합니다.

사용 방법

앱 플립 통합을 실행하려면 다음 단계를 따라야 합니다.

  1. Google 앱은 패키지 이름을 사용하여 기기에 앱이 설치되어 있는지 확인합니다.
  2. Google 앱은 패키지 서명 검사를 사용하여 설치된 앱이 올바른 앱인지 확인합니다.
  3. Google 앱은 앱에서 지정된 활동을 시작하기 위한 인텐트를 빌드합니다. 이 인텐트에는 연결에 필요한 추가 데이터가 포함됩니다. 또한, Android 프레임워크를 통해 이 인텐트를 해결하여 앱이 앱 플립을 지원하는지도 확인합니다.
  4. 앱은 Google 앱에서 발생한 요청인지 검증합니다. 이를 위해 앱은 패키지 서명과 제공된 클라이언트 ID를 확인합니다.
  5. 앱이 OAuth 2.0 서버에서 승인 코드를 요청합니다. 이 과정이 끝나면 승인 코드나 오류가 Google 앱에 반환됩니다.
  6. Google 앱이 결과를 검색하고 계정 연결을 계속합니다. 승인 코드가 제공되면 토큰 교환은 브라우저 기반 OAuth 연결 흐름에서와 동일한 방식으로 서버 간에 이루어집니다.

앱 플립을 지원하도록 Android 앱 수정

앱 플립을 지원하려면 Android 앱에서 다음과 같이 코드를 변경합니다.

  1. 앱 플립 인텐트 필드에 입력한 값과 일치하는 작업 문자열로 AndroidManifest.xml 파일에 <intent-filter>을 추가합니다.

    <activity android:name="AuthActivity">
      <!-- Handle the app flip intent -->
      <intent-filter>
        <action android:name="INTENT_ACTION_FROM_CONSOLE"/>
        <category android:name="android.intent.category.DEFAULT"/>
      </intent-filter>
    </activity>
    
  2. 호출 앱의 서명을 검증합니다.

    private fun verifyFingerprint(
            expectedPackage: String,
            expectedFingerprint: String,
            algorithm: String
    ): Boolean {
    
        callingActivity?.packageName?.let {
            if (expectedPackage == it) {
                val packageInfo =
                    packageManager.getPackageInfo(it, PackageManager.GET_SIGNATURES)
                val signatures = packageInfo.signatures
                val input = ByteArrayInputStream(signatures[0].toByteArray())
    
                val certificateFactory = CertificateFactory.getInstance("X509")
                val certificate =
                    certificateFactory.generateCertificate(input) as X509Certificate
                val md = MessageDigest.getInstance(algorithm)
                val publicKey = md.digest(certificate.encoded)
                val fingerprint = publicKey.joinToString(":") { "%02X".format(it) }
    
                return (expectedFingerprint == fingerprint)
            }
        }
        return false
    }
    
  3. 인텐트 매개변수에서 클라이언트 ID를 추출하고 클라이언트 ID가 예상값과 일치하는지 확인합니다.

    private const val EXPECTED_CLIENT = "<client-id-from-actions-console>"
    private const val EXPECTED_PACKAGE = "<google-app-package-name>"
    private const val EXPECTED_FINGERPRINT = "<google-app-signature>"
    private const val ALGORITHM = "SHA-256"
    ...
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        val clientId = intent.getStringExtra("CLIENT_ID")
    
        if (clientId == EXPECTED_CLIENT &&
            verifyFingerprint(EXPECTED_PACKAGE, EXPECTED_FINGERPRINT, ALGORITHM)) {
    
            // ...authorize the user...
        }
    }
    
  4. 승인이 완료되면 결과 승인 코드를 Google에 다시 반환합니다.

    // Successful result
    val data = Intent().apply {
        putExtra("AUTHORIZATION_CODE", authCode)
    }
    setResult(Activity.RESULT_OK, data)
    finish()
    
  5. 오류가 발생하면 대신 오류 결과를 반환합니다.

    // Error result
    val error = Intent().apply {
        putExtra("ERROR_TYPE", 1)
        putExtra("ERROR_CODE", 1)
        putExtra("ERROR_DESCRIPTION", "Invalid Request")
    }
    setResult(-2, error)
    finish()
    

출시 인텐트의 콘텐츠

앱을 실행하는 Android 인텐트에는 다음 필드가 포함됩니다.

  • CLIENT_ID(String): Google client_id이(가) 앱에 등록되어 있습니다.
  • SCOPE(String[]): 요청된 범위 목록입니다.
  • REDIRECT_URI (String): 리디렉션 URL입니다.

응답 데이터의 콘텐츠

Google 앱으로 반환된 데이터는 setResult()를 호출하여 앱에서 설정됩니다. 이 데이터에는 다음이 포함됩니다.

  • AUTHORIZATION_CODE(String): 승인 코드 값입니다.
  • resultCode(int): 프로세스의 성공 또는 실패를 전달하고 다음 값 중 하나를 가져옵니다.
    • Activity.RESULT_OK: 성공을 나타냅니다. 승인 코드가 반환됩니다.
    • Activity.RESULT_CANCELLED: 사용자가 프로세스를 취소했다는 신호입니다. 이 경우 Google 앱은 승인 URL을 사용하여 계정 연결을 시도합니다.
    • -2: 오류가 발생했음을 나타냅니다. 아래에서 다양한 유형의 오류를 설명합니다.
  • ERROR_TYPE (int): 다음 값 중 하나를 사용하는 오류 유형입니다.
    • 1: 복구 가능한 오류: Google 앱에서 승인 URL을 사용하여 계정 연결을 시도합니다.
    • 2: 복구할 수 없는 오류: Google 앱에서 계정 연결을 취소합니다.
    • 3: 요청 매개변수가 잘못되었거나 누락되었습니다.
  • ERROR_CODE(int): 오류의 특성을 나타내는 정수입니다. 각 오류 코드의 의미를 확인하려면 오류 코드 표를 참조하세요.
  • ERROR_DESCRIPTION (String, 선택사항): 사람이 읽을 수 있는 상태 메시지로서 오류를 설명합니다.

resultCode == Activity.RESULT_OK일 때 AUTHORIZATION_CODE의 값이 필요합니다. 그 외의 경우에는 AUTHORIZATION_CODE 값이 비어 있어야 합니다. resultCode == -2이면 ERROR_TYPE 값이 채워집니다.

오류 코드 표

아래 표에는 다양한 오류 코드와 각 코드가 복구 가능한 오류인지 또는 복구 불가능한 오류인지가 표시됩니다.

오류 코드 의미 복구 가능 Unrecoverable
1 INVALID_REQUEST
2 NO_INTERNET_CONNECTION
3 OFFLINE_MODE_ACTIVE
4 CONNECTION_TIMEOUT
5 INTERNAL_ERROR
6 AUTHENTICATION_SERVICE_UNAVAILABLE
8 CLIENT_VERIFICATION_FAILED
9 INVALID_CLIENT
10 INVALID_APP_ID
11 INVALID_REQUEST
12 AUTHENTICATION_SERVICE_UNKNOWN_ERROR
13 AUTHENTICATION_DENIED_BY_USER
14 CANCELLED_BY_USER
15 FAILURE_OTHER
16 USER_AUTHENTICATION_FAILED

모든 오류 코드의 경우 setResult를 통해 오류 결과를 반환하여 적절한 대체가 트리거되도록 해야 합니다.

기기에서 앱 플립 테스트

이제 콘솔과 앱에서 작업을 만들고 앱 플립을 구성했으므로 휴대기기에서 앱 플립을 테스트할 수 있습니다. Google 어시스턴트 앱 또는 Google Home 앱을 사용하여 앱 플립을 테스트할 수 있습니다.

어시스턴트 앱에서 App Flip을 테스트하려면 다음 단계를 따르세요.

  1. Actions 콘솔로 이동하여 프로젝트를 선택합니다.
  2. 상단 탐색 메뉴에서 테스트를 클릭합니다.
  3. 어시스턴트 앱에서 계정 연결 흐름을 트리거합니다.
    1. Google 어시스턴트 앱을 엽니다.
    2. 설정을 클릭합니다.
    3. 어시스턴트 탭에서 홈 컨트롤을 클릭합니다.
    4. Add(+)를 클릭합니다.
    5. 제공업체 목록에서 작업을 선택합니다. 목록에 '[test]'가 접두사로 붙습니다. 목록에서 [test] 작업을 선택하면 앱이 열립니다.
    6. 앱이 실행되었는지 확인하고 승인 흐름을 테스트합니다.

Home 앱에서 App Flip을 테스트하려면 다음 단계를 따르세요.

  1. Actions 콘솔로 이동하여 프로젝트를 선택합니다.
  2. 상단 탐색 메뉴에서 테스트를 클릭합니다.
  3. Home 앱에서 계정 연결 흐름을 트리거합니다.
    1. Google Home 앱을 엽니다.
    2. 화면의 + 버튼을 클릭합니다.
    3. 기기 설정을 클릭합니다.
    4. 이미 설정된 기기가 있나요?를 클릭합니다.
    5. 제공업체 목록에서 스마트 홈 작업을 선택합니다. 목록에 '[test]'가 접두사로 붙습니다. 목록에서 [test] 작업을 선택하면 앱이 열립니다.
    6. 앱이 실행되었는지 확인하고 승인 흐름을 테스트합니다.