2020년 4월 20일 월요일

생체 인증을 위한 CheckboxPreference 만들기

개인적으로 만드는 app에서 생체 인증을 사용하고자 하여 
생체인증 enable/disable을 위한 CheckboxPreference로 옵션을 만들고 
옵션을 변경할 때마다 생체 인증을 하도록 간략히 만들어 봄

사실 너무 간단한 내용이라 안드로이드 가이드만 봐도 충분하다. 


내가 구현하고자 했던 것은
- 생체 인증을 enable/disable 하는 옵션
- 생체 인증을 통해서만 옵션을 enable<->disable 할 수 있음
- 생체 인증 실패 시에는 옵션 변경 없고 에러 발생 시 토스트 표시


안드로이드 생체인증 가이드를 기초하여 구현을 간략히 설명하면

1. 먼저 생체 인증을 위한 jetpack 라이브러리를 사용하고자 종속성을 추가한다.
 : 현재는 1.0.1 이다. 항상 Jetpack 라이브러리의 최신 버전을 확인해야 할 필요 있음.
dependencies {
        implementation 'androidx.biometric:biometric:1.0.1'
    }

2. app 설정에서 CheckboxPreference 추가

<CheckBoxPreference app:iconSpaceReserved="false"
 android:defaultValue="false"  
 android:key="pref_enableBiometricSignIn"   android:summary="@string/settings_summary_entry_enable_biometric_sign_in" 
 android:title="@string/settings_entry_enable_biometric_sign_in" />


3. CheckboxPreference의 onPreferenceChangeListener에서 BiometricPrompt 표시

val biometricCheckBox = findPreference("pref_enableBiometricSignIn") as CheckBoxPreference biometricCheckBox.onPreferenceChangeListener = OnPreferenceChangeListener { preference, newValue ->

 val promptInfo = BiometricPrompt.PromptInfo.Builder()
    .setTitle("Biometric login for app")
    .setSubtitle("Log in using your biometric credential")
    .setNegativeButtonText("Cancel")
    .build()
 val toBe = newValue as Boolean
 val biometricPrompt = BiometricPrompt(this, Executors.newSingleThreadExecutor(),
  object: BiometricPrompt.AuthenticationCallback() {
 
  override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
    super.onAuthenticationError(errorCode, errString) activity ? .runOnUiThread {
     Toast.makeText(activity ? .applicationContext, errString, Toast.LENGTH_SHORT).show()
    }
   }

   override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
    super.onAuthenticationSucceeded(result) activity ? .runOnUiThread {
     (preference as CheckBoxPreference).isChecked = toBe
    }
   }
  }) 

 biometricPrompt.authenticate(promptInfo) 
 false
}


안드로이드 가이드 코드를 거의 그대로 사용하였고 원하는 기능을 위해 아래 사항을 살짝 수정함

- 생체 인증에 성공 시에만 옵션의 check status를 변경하고자 OnPreferenceChangeListener 구현
 : 생체 인증 시에만 check status를 변경하고자 OnPreferenceChangeListener의 리턴값을 false로 전달
 : 생체인증 성공시에만 isChecked 값을 OnPreferenceChangeListener의 argument인 newValue로 설정

- 사용자가 옵션을 변경할 때마다 생체 인증 창을 띄우기 위해 Executors.newSingleThreadExecutor() 로 Executor 매번 생성
 : 솔직히 이게 맞는 것인지는 모르겠음 ㅜㅜ

- 생체 인증 중 지문이 틀릴 때마다 호출되는 onAuthenticationFailed() 를 override하지 않고 onAuthenticationError() 만 override하여 사용자가 Cancel 하였을 때만 처리함 
 : 사용자가 생체 인증을 여러 번 틀려 block될 경우 BiometricPrompt에 안내가 표시되고 OnAuthenticationError()가 호출되기만 하고 사용자가 직접 cancel을 눌러야함.
 : block된 상황에서는 BiometricPrompt를 닫고 다시 옵션을 누르더라도 생체 인증창이 뜨지 않고 OnAuthenticationFailed()가 호출됨. 

2020년 4월 15일 수요일

OAuth 2.0 Authorization Framework

https://archive.org/details/mastering-oauth-2.0/mode/2up


상식선에서 대충 정리함.
자세한 것들(header, field, flow 등)은 rfc 참고 필요


OAuth 2.0은 Authorization framework로서
HTTP 서비스에 대한 액세스 권한을 얻기 위해
애플리케이션 승인 상호 작용을 정의하여
타사 애플리케이션이 자체적으로 리소스와 HTTP 서비스에 접근할 수 있도록 함.


먼저 인증(authentication) 인가(authorization)의 차이점

https://hanee24.github.io/2018/04/21/authentication-authorization/

인증(authentication)은 자신이 누구라고 주장하는 사람을 확인하는 절차이다. 권한부여(authorization)는 가고 싶은 곳으로 가도록 혹은 원하는 정보를 얻도록 허용하는 과정이다.

OAuth 2.0의 Authorization Grant 종류

. Authorization code grant (인증 코드 기반 인가)
. Implicit grant (암시적 인가)
. Resource Owner Password Credentials Grant
. Client Credentials Grant


클라이언트별 신뢰성에 따라 권고되는 인가 절차가 다름
. 신뢰 클라이언트 => 인증 코드 기반 허가
. 비 신뢰 클라이언트 => 암시적 허가

각각의 인가 절차는 
인가 코드 허가 절차
 - 서버 사이드 플로우이며
 - 복잡, 안전, 장기간
암시적 허가 절차
 - 클라이언트 사이드 플로우
 - 단순, 보안성 낮음, 단기간


OAuth 2.0 기반 client 개발 절차

1. 클라이언트 앱 등록
2. 액세스 토큰 얻기
3. 액세스 토큰을 이용하여 보호된 리소스 접근
4. 액세스 토큰 갱신

클라이언트 앱 등록 시 서비스로부터 다음 정보를 받게 됨
. Client ID
. Client secret
. Redirection endpoint
. Authorization endpoint
. Token endpoint



참고로 spec에서 말하는 용어들은 다음과 같다.

  • Resource Owner : 사용자로서 리소스의 접근을 허가함.
  • Resource server : 리소스를 보호하는 서버로서 acces token을 통해 리소스 요청을 허가한다.
  • Authorization server : 사용자로의 인증, 인가를 통해 access token을 client에 발급하는 서버 
  • User-agent : 웹 브라우저

Implicit Grant, 암시적 허가 절차



  • Implicit Grant 절차는 일반적인 client가 redirection URI를 사용하여 access token을 가져올 때 사용된다. 
    • client는 일반적으로 JavaScript를 이용하여 browser 내에서 동작한다.
  • Redirection 기반이므로 client는 리소스를 제공자의 user-agent(웹브라우저)와 연동 가능해야 하고 authorization server로 전달되는 요청을 받을 수 있어야 한다.
  • Authorization 수행과 access token 획득을 위해 각각 요청하는 uthorization code grant 절차와 달리 본 절차는 client가 authorization 수행의 결과로 access token 획득하게 되고 access token은 redirection URI에 포함되어 전달되므로 노출될 수 있는 점을 고려해야 한다.   


     +----------+
     | Resource |
     |  Owner   |
     |          |
     +----------+
          ^
          |
         (B)
     +----|-----+          Client Identifier     +---------------+
     |         -+----(A)-- & Redirection URI --->|               |
     |  User-   |                                | Authorization |
     |  Agent  -|----(B)-- User authenticates -->|     Server    |
     |          |                                |               |
     |          |<---(C)--- Redirection URI ----<|               |
     |          |          with Access Token     +---------------+
     |          |            in Fragment
     |          |                                +---------------+
     |          |----(D)--- Redirection URI ---->|   Web-Hosted  |
     |          |          without Fragment      |     Client    |
     |          |                                |    Resource   |
     |     (F)  |<---(E)------- Script ---------<|               |
     |          |                                +---------------+
     +-|--------+
       |    |
      (A)  (G) Access Token
       |    |
       ^    v
     +---------+
     |         |
     |  Client |
     |         |
     +---------+

   Note: The lines illustrating steps (A) and (B) are broken into two
   parts as they pass through the user-agent.

                       Figure 4: Implicit Grant Flow


(A) 아래 client는 User-Agent(웹브라우저) 상에서 원하는 resource(정보, API) 접근 허가를 받고자 
identifier, requested scope, local state, redirection URI를 Authorization Server로 전달한다.
(B) 사용자가 User-Agent(웹브라우저)에서 인증(로그인 등) 하게 되면
(C) Authorization Server에서는 client에서 전달받은 redirection URI로 이동시킨다. 
     이때 URI에 access token이 (parameter 형태로) 포함된다.
(D)(E)는 client가 web hosted 기반인 상황에 해당하는 절차이고
(F) User-Agent(웹브라우저)에서는 URI 이동을 감지하여 access token을 추출한다.
(G) Client에 access token을 전달한다.

절차가 다르게 설명되어 있지만, Mastering OAuth2.0 책 삽화를 보면 이해하긴 쉬움.






Authorization Code Grant, 인증 코드 기반 허가 플로우

https://tools.ietf.org/html/rfc6749#section-4.1


  • Authorization Code Grant는 access token과 refresh token을 받기 위해 사용되며 신뢰할 수 있는 client를 대상으로 한다. 
  • Redirection 기반이므로 client는 리소스를 제공자의 user-agent(웹브라우저)와 연동 가능해야 하고 authorization server로 전달되는 요청을 받을 수 있어야 한다.


     +----------+
     | Resource |
     |   Owner  |
     |          |
     +----------+
          ^
          |
         (B)
     +----|-----+          Client Identifier      +---------------+
     |         -+----(A)-- & Redirection URI ---->|               |
     |  User-   |                                 | Authorization |
     |  Agent  -+----(B)-- User authenticates --->|     Server    |
     |          |                                 |               |
     |         -+----(C)-- Authorization Code ---<|               |
     +-|----|---+                                 +---------------+
       |    |                                         ^      v
      (A)  (C)                                        |      |
       |    |                                         |      |
       ^    v                                         |      |
     +---------+                                      |      |
     |         |>---(D)-- Authorization Code ---------'      |
     |  Client |          & Redirection URI                  |
     |         |                                             |
     |         |<---(E)----- Access Token -------------------'
     +---------+       (w/ Optional Refresh Token)

   Note: The lines illustrating steps (A), (B), and (C) are broken into
   two parts as they pass through the user-agent.

                     Figure 3: Authorization Code Flow

 

(A) 아래 client는 User-Agent(웹브라우저) 상에서 원하는 resource(정보, API) 접근 허가를 받고자 identifier, requested scope, local state, redirection URI를 Authorization Server로 전달한다.
(B) 사용자가 User-Agent(웹브라우저)에서 인증(로그인 등)을 하게 되면
(C) Authorization Server에서는 client에서 전달한 redirection URI로 이동시킨다.
     이때 URI에 authorization code가 (parameter 형태로) 포함된다.
     User-Agent(웹브라우저)에서는 redirection URI 변경을 감지하여 authorization code를 추출하여 Client에 전달한다.
(D) Client는 access token을 발급받기 위해 전달받은 authorization code와 redirection URI을 Authorization Server로 전달한다.
(E) Authorization Server는 client를 인증, authorization code 유효성 확인, redirection URI가 (C)에서 사용된 것과 동일한지를 확인한 뒤  access token를 (필요 시 refresh token도 함께) 전달한다.

Mastering OAuth2.0 책 삽화를 참고




액세스토큰
. 클라이언트가 보호된 리소스에 일시적으로 접근할 수 있도록 해주는 형식이 정해지지 않은 문자열.
. 특정 범위의 권한으로 일정 기간 접근 할 수 있도록 해주며 서비스 제공자가 액세스 토큰의 권한 범위와 유효기간을 결정한다.
. 서버로 전달은 주로 다음 방법을 통해 가능
 : 인가요청 헤더 필드에 담아서 전달
 : 인코딩된 폼의 파라미터로 전달
 : URI 질의 파라미터로 전달


액세스토큰 갱신(refresh)

. 리프레시 토큰 워크플로우
. 인가 프로세스를 다시 시작하여 갱신 가능함.

  +--------+                                           +---------------+
  |        |--(A)------- Authorization Grant --------->|               |
  |        |                                           |               |
  |        |<-(B)----------- Access Token -------------|               |
  |        |               & Refresh Token             |               |
  |        |                                           |               |
  |        |                            +----------+   |               |
  |        |--(C)---- Access Token ---->|          |   |               |
  |        |                            |          |   |               |
  |        |<-(D)- Protected Resource --| Resource |   | Authorization |
  | Client |                            |  Server  |   |     Server    |
  |        |--(E)---- Access Token ---->|          |   |               |
  |        |                            |          |   |               |
  |        |<-(F)- Invalid Token Error -|          |   |               |
  |        |                            +----------+   |               |
  |        |                                           |               |
  |        |--(G)----------- Refresh Token ----------->|               |
  |        |                                           |               |
  |        |<-(H)----------- Access Token -------------|               |
  +--------+           & Optional Refresh Token        +---------------+

               Figure 2: Refreshing an Expired Access Token
화질이 좋진 않지만 액세스토큰 갱신 시 절차를 나타낸 diagram임
위에서 말한 것과 같이 액세스토큰이 만료되는 경우
리프레시 토큰을 사용하여 갱신이 가능하지만
리프레시 토큰이 없다면 인가 절차를 다시 수행해야 함.



보안을 위해 고려해야 하는 사항들은 다음과 같다. 
(Mastering OAuth2.0 책 내용)


  • TLS를 사용하라
  • 최소한의 범위만을 요청하라
  • 암시적 그랜트 플로우를 사용할 때는 읽기 전용 권한만을 요청하라.
  • 사용자의 손이 미치지 않는 곳에 자격증명과 토큰을 보관하라 
  • 가능하면 항상 인가 코드 그랜트 플로우를 사용하라
  • 가능하면 항상 리프레시 토큰을 사용하라
  • 내장 브라우저 대신 네이티브 브라우저를 사용하라
  • 리다이렉션 엔드포인트에서 서드파티 스크림트를 사용하지 말라
  • 클라이언트 자격증명을 바꿔서 사용하라