맞춤 계정 유형 만들기

지금까지 Google에서 정의한 계정과 사용자를 사용하여 Google API에 액세스하는 방법에 관해 알아봤습니다. 하지만 고유한 온라인 서비스가 있다면 이 서비스에 Google 계정이나 사용자가 없을 것입니다. 이런 경우 어떻게 할까요? 사용자의 기기에 새 계정 유형을 설치하는 일은 비교적 간단합니다. 이 과정에서는 내장된 계정과 같은 방식으로 작동하는 맞춤 계정 유형을 만드는 방법을 설명합니다.

맞춤 계정 코드 구현하기

가장 먼저 필요한 것은 사용자로부터 사용자 인증 정보를 가져올 방법입니다. 단순하게는 이름과 비밀번호를 요청하는 대화상자일 수도 있고 일회용 비밀번호나 생체 인식 스캔 같은 좀 더 이색적인 절차일 수도 있습니다. 어느 방법을 사용하든 다음 기능을 위한 코드를 구현하는 것은 개발자의 책임입니다.

  1. 사용자로부터 사용자 인증 정보 수집
  2. 서버에서 사용자 인증 정보 인증
  3. 기기에 사용자 인증 정보 저장

일반적으로 하나의 활동으로 위의 세 요건 모두를 처리할 수 있습니다. 이 활동을 인증자 활동이라고 하겠습니다.

인증자 활동은 AccountManager 시스템과 상호작용해야 하므로 일반적인 활동에는 없는 특정 요건이 있습니다. 올바른 처리를 수월하게 하기 위해 Android 프레임워크는 기본 클래스 AccountAuthenticatorActivity를 제공하며 이 클래스를 확장하여 자체 맞춤 인증자를 만들 수 있습니다.

인증자 활동의 처음 두 요건인 사용자 인증 정보 수집과 인증을 처리하는 방법은 완전히 개발자에게 달려 있습니다. (이 방법이 한 가지뿐이었다면 결국 '맞춤' 계정 유형이 필요 없었을 것입니다.) 세 번째 요건은 다소 단순한 표준 구현입니다.

Kotlin

    Account(username, your_account_type).also { account ->
        accountManager.addAccountExplicitly(account, password, null)
    }
    

자바

    final Account account = new Account(username, your_account_type);
    accountManager.addAccountExplicitly(account, password, null);
    

스마트한 보안 접근 방식

AccountManager는 암호화 서비스나 키체인이 아니라는 것을 이해해야 합니다. 개발자가 전달한 그대로 계정 사용자 인증 정보를 일반 텍스트로 저장합니다. 루트에서만 액세스 가능한 데이터베이스에 저장하기 때문에 이는 대부분의 기기에서 특별히 우려할 사안이 아닙니다. 그러나 루팅된 기기에서는 기기 adb 액세스 권한이 있는 누구나 사용자 인증 정보를 읽을 수 있습니다.

이 점을 고려하여 사용자의 실제 비밀번호를 AccountManager.addAccountExplicitly()에 전달해서는 안 됩니다. 대신 공격자의 입장에서 사용이 제한적인, 암호학적으로 안전한 토큰을 저장해야 합니다. 사용자 인증 정보가 가치 있는 것을 보호할 때는 비슷한 무언가를 수행할 것을 세심하게 고려해 보세요.

주의: 보안 코드에 관해서는 TV 프로그램 '호기심 해결사(Mythbusters)'에서 말하는 '집에서는 따라하지 마세요' 규칙을 따르세요. 보안 전문가에게 문의한 후에 맞춤 계정 코드를 구현하세요.

이제 보안 면책조항은 처리되었으므로 본격적으로 작업하겠습니다. 이미 맞춤 계정 코드의 핵심을 구현했으므로 이제 남은 것은 연결입니다.

AbstractAccountAuthenticator 확장하기

AccountManager가 맞춤 계정 코드와 함께 작동하려면 AccountManager가 예상하는 인터페이스를 구현하는 클래스가 필요합니다. 이 클래스가 인증자 클래스입니다.

인증자 클래스를 만드는 가장 쉬운 방법은 AbstractAccountAuthenticator를 확장하고 추상 메서드를 구현하는 것입니다. 이전 과정을 실습해 왔다면 AbstractAccountAuthenticator의 추상 메서드에 익숙할 것입니다. 이전 과정에서 호출하여 계정 정보와 승인 토큰을 가져온 메서드의 반대쪽에 있는 메서드입니다.

인증자 클래스를 제대로 구현하려면 다수의 개별 코드가 필요합니다. 첫째, AbstractAccountAuthenticator에는 재정의해야 하는 추상 메서드가 7개 있습니다. 둘째, "android.accounts.AccountAuthenticator"인텐트 필터를 애플리케이션 매니페스트에 추가해야 합니다(다음 섹션에서 설명함). 마지막으로, 맞춤 계정 유형의 이름과 시스템에서 이 유형의 계정 옆에 표시할 아이콘을 정의하는 두 XML 리소스를 제공해야 합니다.

성공적인 인증자 클래스 및 XML 파일을 구현하는 단계별 안내를 AbstractAccountAuthenticator 문서에서 확인할 수 있습니다. SampleSyncAdapter 샘플 앱에서 샘플 구현도 제공됩니다.

SampleSyncAdapter 코드를 읽어 보면 일부 메서드가 번들로 인텐트를 반환한다는 것을 확인할 수 있습니다. 이 인텐트는 맞춤 인증자 활동을 실행하는 데 사용될 것과 동일한 인텐트입니다. 인증자 활동에 특수한 초기화 매개변수가 필요하면 Intent.putExtra()를 사용하여 인텐트에 매개변수를 연결하면 됩니다.

인증자 서비스 만들기

이제 만든 인증자 클래스를 배치할 장소가 필요합니다. 계정 인증자는 여러 애플리케이션에 사용할 수 있어야 하고 백그라운드에서 작동해야 하므로 기본적으로 Service 내에서 실행해야 합니다. 이를 인증자 서비스라고 부르겠습니다.

인증자 서비스는 아주 단순할 수 있습니다. 인증자 클래스의 인스턴스를 onCreate()에서 만들고 onBind()에서 getIBinder()를 호출하기만 하면 됩니다. SampleSyncAdapter에는 인증자 서비스의 좋은 예가 포함되어 있습니다.

<service> 태그를 매니페스트 파일에 추가하고 AccountAuthenticator 인텐트의 인텐트 필터를 추가하며 계정 인증자를 선언해야 합니다.

    <service ...>
       <intent-filter>
          <action android:name="android.accounts.AccountAuthenticator" />
       </intent-filter>
       <meta-data android:name="android.accounts.AccountAuthenticator"
                 android:resource="@xml/authenticator" />
    </service>
    

서비스 배포하기

완료되었습니다. 이제 시스템에서 'Google' 및 'Corporate' 같은 모든 주요 계정 유형과 함께 개발자의 계정 유형이 인식됩니다. 계정 및 동기화 설정 페이지를 사용하여 계정을 추가할 수 있으며 맞춤 유형의 계정을 요청하는 앱은 다른 계정 유형의 경우처럼 열거하고 인증할 수 있습니다.

물론 이러한 모든 과정은 계정 서비스가 실제로 기기에 설치되어 있다는 것을 가정합니다. 앱 하나만 서비스에 액세스한다면 간단합니다. 앱에 서비스를 번들하기만 하면 됩니다. 그러나 계정 서비스를 둘 이상의 앱에서 사용하기를 바란다면 좀 복잡해집니다. 모든 앱에 서비스를 번들해서 사본 여러 개가 사용자 기기의 공간을 차지하도록 만들어서는 안 됩니다.

한 가지 해결책은 특수 목적의 소규모 APK 하나에 서비스를 배치하는 것입니다. 앱이 맞춤 계정 유형을 사용하려고 하면 기기를 확인하여 맞춤 계정 서비스를 사용할 수 있는지 알아볼 수 있습니다. 맞춤 계정 서비스를 사용할 수 없는 경우 서비스를 다운로드하도록 사용자를 Google Play로 안내할 수 있습니다. 이 방식은 처음에는 상당히 번거로워 보일 수도 있지만 맞춤 계정을 사용하는 앱별로 사용자 인증 정보를 다시 입력하는 대안과 비교하면 아주 간편합니다.