웨어러블 기기에서 인증: 인증 관리자

Wear OS 앱은 호환 앱 없이도 독립형으로 실행할 수 있습니다. 따라서 Wear OS 앱은 인터넷에서 데이터에 액세스할 때 자체적으로 인증을 관리해야 합니다. 시계의 작은 화면 크기와 줄어든 입력 기능으로 Wear OS 앱에서 사용할 수 있는 인증 옵션이 제한됩니다.

이 가이드에서는 Wear OS 앱에 권장되는 인증 방식인 인증 관리자에 관한 안내를 제공합니다.

좋은 로그인 환경을 설계하는 방법을 자세히 알아보려면 로그인 UX 가이드를 참고하세요.

사전 고려사항

구현을 시작하기 전에 다음 사항을 고려하세요.

게스트 모드

모든 기능에 대해 인증을 요구해서는 안 됩니다. 대신 로그인하지 않아도 사용할 수 있는 기능을 최대한 많이 사용자에게 제공하세요.

사용자는 모바일 앱을 사용하지 않고 Wear 앱을 검색하고 설치할 수 있으므로 계정이 없거나 어떤 기능이 제공되는지 모를 수도 있습니다. 게스트 모드 기능이 앱의 기능을 정확하게 보여줘야 합니다.

일부 기기는 더 오래 잠금 해제된 상태로 유지될 수 있습니다.

Wear OS 5 이상을 실행하는 지원되는 기기에서 시스템은 사용자가 기기를 손목에 착용하고 있는지 감지합니다. 사용자가 손목 감지를 사용 중지한 후 기기를 손목에서 벗으면 시스템은 기기를 잠금 해제된 상태로 유지하는 기간을 평소보다 더 길게 유지합니다.

민감하거나 비공개일 수 있는 데이터를 표시하는 경우와 같이 앱에 더 높은 수준의 보안이 필요한 경우 먼저 손목 감지가 사용 설정되어 있는지 확인합니다.

val wristDetectionEnabled =
        isWristDetectionAutoLockingEnabled(applicationContext)

이 메서드의 반환 값이 false이면 사용자별 콘텐츠를 표시하기 전에 사용자에게 앱의 계정에 로그인하라는 메시지를 표시합니다.

인증 관리자

인증 관리자는 Wear OS 인증을 위해 권장되는 API입니다. 연결된 페어링된 휴대전화가 필요하지 않고 비밀번호를 기억할 필요 없이 사용자가 독립형 설정에서 Wear OS 애플리케이션에 로그인할 수 있는 더 안전한 환경을 제공합니다.

이 문서에서는 개발자가 호스팅하는 표준 인증 메커니즘을 사용하여 인증 관리자 솔루션을 구현하는 데 필요한 정보를 간략히 설명합니다.

  • 패스키
  • 비밀번호
  • 제휴 ID (예: Google 계정으로 로그인)

이 가이드에서는 허용되는 다른 Wear OS 인증 방법 (데이터 영역 토큰 공유OAuth)을 인증 관리자의 백업으로 이전하는 방법과 현재 지원 중단된 독립형 Google 로그인 버튼에서 삽입된 인증 관리자 버전으로 전환하는 방법에 관한 특별한 안내도 제공합니다.

Wear OS의 패스키

개발자는 Wear OS 인증 관리자 구현에 패스키를 구현하는 것이 좋습니다. 패스키는 최종 사용자 인증을 위한 새로운 업계 표준이며 사용자에게 몇 가지 중요한 이점을 제공합니다.

패스키가 더 쉽습니다.

  • 사용자는 로그인할 계정을 선택할 수 있습니다. 사용자 이름을 입력할 필요가 없습니다.
  • 사용자는 기기의 화면 잠금을 사용하여 인증할 수 있습니다.
  • 패스키가 생성되고 등록되면 사용자는 새 기기로 원활하게 전환하고 재등록할 필요 없이 즉시 사용할 수 있습니다.

패스키가 더 안전함

  • 개발자는 비밀번호를 저장하는 대신 서버에 공개 키만 저장합니다. 즉, 악의적인 행위자가 서버를 해킹할 가치가 훨씬 낮아지고, 침해 발생 시 정리해야 할 작업이 훨씬 줄어듭니다.
  • 패스키는 피싱 방지 보호 기능을 제공합니다. 패스키는 등록된 웹사이트와 앱에서만 작동합니다. 브라우저나 OS에서 인증을 처리하므로 사용자가 기만적인 사이트에서 속아 인증할 수 없습니다.
  • 패스키를 사용하면 SMS를 전송할 필요가 없어 인증 비용 효율성이 높아집니다.

패스키 구현

모든 구현 유형에 관한 설정 및 안내가 포함되어 있습니다.

설정

  1. 애플리케이션 모듈의 build.gradle 파일에서 타겟 API 수준을 35로 설정합니다.

    android {
        defaultConfig {
            targetSdkVersion(35)
        }
    }
    
  2. androidx.credentials 출시 참조의 최신 안정화 버전을 사용하여 앱 또는 모듈의 build.gradle 파일에 다음 행을 추가합니다.

    androidx.credentials:credentials:1.5.0
    androidx.credentials:credentials-play-services-auth:1.5.0
    

기본 제공되는 인증 방법

인증 관리자는 통합 API이므로 Wear OS의 구현 단계는 다른 기기 유형과 동일합니다.

모바일 안내를 사용하여 패스키 및 비밀번호 지원을 시작하고 구현하세요.

인증 관리자에 Google 계정으로 로그인 지원을 추가하는 단계는 모바일 개발을 염두에 두고 있지만 Wear OS에서도 동일한 단계를 따릅니다. 이 케이스에 관한 특별한 고려사항은 기존 Google 계정으로 로그인에서 전환 섹션을 참고하세요.

Wear OS에서는 사용자 인증 정보를 만들 수 없으므로 모바일 안내에 언급된 사용자 인증 정보 생성 메서드를 구현할 필요가 없습니다.

백업 인증 방법

Wear OS 앱에 허용되는 다른 두 가지 인증 방법은 OAuth 2.0 (두 가지 변형)과 모바일 인증 토큰 데이터 영역 공유입니다. 이러한 메서드는 Credential Manager API에 통합 지점이 없지만 사용자가 인증 관리자 화면을 닫을 경우 대체로 인증 관리자의 UX 흐름에 포함할 수 있습니다.

인증 관리자 화면 닫기 사용자 작업을 처리하려면 GetCredential 로직의 일부로 NoCredentialException를 포착하고 자체 맞춤 인증 UI로 이동합니다.

yourCoroutineScope.launch {
    try {
      val response = credentialManager.getCredential(activity, request)
      signInWithCredential(response.credential)
    } catch (e: GetCredentialCancellationException) {
      navigateToFallbackAuthMethods()
    }
}

그러면 맞춤 인증 UI에서 로그인 UX 가이드에 설명된 다른 허용되는 인증 방법을 제공할 수 있습니다.

데이터 영역 토큰 공유

휴대전화의 호환 앱은 Wearable Data Layer API를 사용하여 인증 데이터를 Wear OS 앱에 안전하게 전송할 수 있습니다. 사용자 인증 정보를 메시지 또는 데이터 항목으로 전송합니다.

이러한 유형의 인증은 일반적으로 사용자의 작업 없이도 가능합니다. 그러나 사용자에게 로그인 중임을 알리지 않고 인증을 실행해서는 안 됩니다. 닫을 수 있는 화면을 통해 계정이 모바일에서 전송 중임을 사용자에게 알릴 수 있습니다.

중요: 이 옵션은 모바일 앱이 설치된 경우에만 Android와 페어링된 시계에서 작동하므로 Wear OS 앱은 다른 인증 방법을 하나 이상 제공해야 합니다. 해당 모바일 앱이 없거나 Wear OS 기기가 iOS 기기와 페어링된 사용자를 위해 대체 인증 방법을 제공하세요.

다음 예와 같이 모바일 앱의 데이터 영역을 사용하여 토큰을 전달합니다.

val token = "..." // Auth token to transmit to the Wear OS device.
val dataClient: DataClient = Wearable.getDataClient(context)
val putDataReq: PutDataRequest = PutDataMapRequest.create("/auth").run {
    dataMap.putString("token", token)
    asPutDataRequest()
}
val putDataTask: Task<DataItem> = dataClient.putDataItem(putDataReq)

다음 예와 같이 Wear OS 앱에서 데이터 변경 이벤트를 수신 대기합니다.

val dataClient: DataClient = Wearable.getDataClient(context)
dataClient.addListener{ dataEvents ->
    dataEvents.forEach { event ->
        if (event.type == DataEvent.TYPE_CHANGED) {
            val dataItemPath = event.dataItem.uri.path ?: ""
            if (dataItemPath.startsWith("/auth")) {
                val token = DataMapItem.fromDataItem(event.dataItem).dataMap.getString("token")
                // Display an interstitial screen to notify the user that
                // they're being signed in.
                // Then, store the token and use it in network requests.
            }
        }
    }
}

웨어러블 데이터 영역 사용에 관한 자세한 내용은 Wear OS에서 데이터 보내기 및 동기화를 참고하세요.

OAuth 2.0 사용

Wear OS는 다음 섹션에 설명된 두 가지 OAuth 2.0 기반 흐름을 지원합니다.

  • RFC 7636에 정의된 PKCE (Proof Key for Code Exchange)로 승인 코드 부여
  • RFC 8628에 정의된 기기 승인 부여 (DAG)
PKCE(Proof Key for Code Exchange)

PKCE를 효과적으로 사용하려면 RemoteAuthClient를 사용하세요. 그런 다음 Wear OS 앱에서 OAuth 제공자에 인증 요청을 실행하려면 OAuthRequest 객체를 만듭니다. 이 객체는 토큰을 가져오기 위한 OAuth 엔드포인트 URL과 CodeChallenge 객체로 구성됩니다.

다음 코드는 인증 요청을 만드는 예를 보여줍니다.

val request = OAuthRequest.Builder(this.applicationContext)
    .setAuthProviderUrl(Uri.parse("https://...."))
    .setClientId(clientId)
    .setCodeChallenge(codeChallenge)
    .build()

인증 요청을 빌드한 후 sendAuthorizationRequest() 메서드를 사용하여 호환 앱으로 보냅니다.

val client = RemoteAuthClient.create(this)
client.sendAuthorizationRequest(request,
    { command -> command?.run() },
    object : RemoteAuthClient.Callback() {
        override fun onAuthorizationResponse(
            request: OAuthRequest,
            response: OAuthResponse
        ) {
            // Extract the token from the response, store it, and use it in
            // network requests.
        }

        override fun onAuthorizationError(errorCode: Int) {
            // Handle any errors.
        }
    }
)

이 요청은 호환 앱에 호출을 트리거하고, 이렇게 하면 사용자 휴대전화의 웹브라우저에 승인 UI가 표시됩니다. OAuth 2.0 제공자가 사용자를 인증하고 요청된 권한에 관한 사용자 동의를 얻습니다. 응답은 자동으로 생성된 리디렉션 URL로 전송됩니다.

승인이 성공 또는 실패한 후 OAuth 2.0 서버가 요청에 지정된 URL로 리디렉션됩니다. 사용자가 액세스 요청을 승인하면 응답에 승인 코드가 포함됩니다. 사용자가 요청을 승인하지 않으면 응답에 오류 메시지가 포함됩니다.

응답은 쿼리 문자열 형식이며 다음 예 중 하나와 유사합니다.

  https://wear.googleapis.com/3p_auth/com.your.package.name?code=xyz
  https://wear.googleapis-cn.com/3p_auth/com.your.package.name?code=xyz

이렇게 하면 사용자를 호환 앱으로 연결하는 페이지가 로드됩니다. 호환 앱은 응답 URL을 확인하고 onAuthorizationResponse API를 사용하여 Wear OS 앱에 응답을 전달합니다.

그러면 시계 앱은 승인 코드를 액세스 토큰으로 교환할 수 있습니다.

기기 승인 부여

기기 승인 부여를 사용하면 사용자는 다른 기기에서 인증 URI를 열게 됩니다. 그러면 승인 서버는 사용자에게 요청을 승인하거나 거부하도록 요청합니다.

이 과정을 더 쉽게 하려면 다음 예와 같이 RemoteActivityHelper를 사용하여 페어링된 사용자의 휴대기기에서 웹페이지를 엽니다.

// Request access from the authorization server and receive Device Authorization
// Response.
val verificationUri = "..." // Extracted from the Device Authorization Response.
RemoteActivityHelper.startRemoteActivity(
    this,
    Intent(Intent.ACTION_VIEW)
        .addCategory(Intent.CATEGORY_BROWSABLE)
        .setData(Uri.parse(verificationUri)),
    null
)
// Poll the authorization server to find out if the user completed the user
// authorization step on their mobile device.

iOS 앱을 사용하는 경우 브라우저에서 토큰을 승인하는 대신 범용 링크를 사용하여 앱에서 이 인텐트를 가로챕니다.

기존 Google 계정으로 로그인에서 전환

인증 관리자에는 Google 계정으로 로그인 버튼을 위한 전용 통합 지점이 있습니다. 이전에는 이 버튼을 앱의 인증 UX 어디에나 추가할 수 있었지만 인증 관리자에 포함되면서 이전 옵션은 지원 중단되었습니다.

// Define a basic SDK check.
fun isCredentialManagerAvailable(): Boolean {
 return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM
}

// Elsewhere in the code, use it to selectively disable the legacy option.
Button(
  onClick = {
    if (isCredentialManagerAvailable()) {
      Log.w(TAG, "Devices on API level 35 or higher should use
                  Credential Manager for Sign in with Google")
    } else {
      navigateToSignInWithGoogle()
    }},
  enabled = !isCredentialManagerAvailable(),
  label = { Text(text = stringResource(R.string.sign_in_with_google)) },
  secondaryLabel = { Text(text = "Disabled on API level 35+")
  }
)