안드로이드 많은 애플리케이션에서 로그인 화면이 존재하고 해당 애플리케이션 내에서 자체적으로 회원가입을 하여 로그인을 할 수 있습니다. 그 외에도 구글, 페이스북, 네이버, 카카오 등 타 서비스의 계정으로도 로그인을 할 수 있도록 하는데 이 때 쓰이는 기술이 OAuth 입니다.
OAuth는 간단하게 설명하면 자신의 서비스에서 제 3자의 서비스(페이스북, 구글, 네이버) 의 기능들을 이용하게 해줄 수 있는 기술입니다. 제 3자의 서비스의 계정 정보를 자신의 서비스에 기록하여 권한을 가져오도록 구현하면 편리하겠지만 서비스를 제공하는 측에서는 자신들의 회원 정보들을 주는게 많은 문제로 다가올 수 있기 때문에 허용하지 않을 것입니다. 이를 해결할 수 있는 것이 OAuth로 제 3자의 서비스의 회원 정보를 직접 노출하지 않고 해당 서비스의 권한을 부여할 수 있습니다.
OAuth의 구성들의 역할에 대해 먼저 이해를 한 후 어떤 로직을 통해서 권한을 부여해주고 원하는 기능들을 사용하도록 허용해주는지 확인해보면 좋을 것 같습니다. 로직들을 하나씩 설명하면서 Facebook 로그인 연동하는 과정까지 같이 보여드리도록 하겠습니다.
Client가 Resource Server에서 제공해주는 서비스를 이용하기 위해서는 미리 Client 정보를 등록하는 과정이 필요합니다. 서비스마다 등록하는 과정은 다르지만 대체적으로 Client를 서비스에 등록을 할 때 Client ID, Client Secret을 받게 되고 Authroized redirect URI를 별도로 제공 서비스에 알려주어야 합니다. Authorized redirect URI는 Authroization Server가 권한을 부여하는 과정에서 Authorization code를 보내게 되는데 보낼 곳에 대한 위치를 가리키게 됩니다.
위의 그림처럼 등록을 완료하면 Authorization Server는 Client ID, Client Secret, Authorization redirect URI 정보를 알게 되고 마찬가지로 Client 부분에서도 ID와 Secret 정보는 가지고 있습니다. 아래의 링크를 통해서 페이스북 서비스에 Tinder라는 애플리케이션을 등록해보겠습니다.
화면에 보이시는 것처럼 Tinder 앱을 등록하게 되면 자동으로 앱 ID와 앱 시크릿 코드가 발급되는 것을 확인할 수 있습니다. 이 때 앱 ID가 Client ID이고 앱 시크릿 코드가 Client Secret 정보입니다.
이후 Fackbook 로그인 설정 부분에 유효한 OAuth redirect URI를 설정하는 부분이 있습니다. Tinder 앱에서는 Firebase Authentication을 이용하여 로그인을 구현하기에 Firebase에서 제공해주는 유효한 URI를 사용하면 됩니다.
Firebase Authentication 화면에서 Facebook 로그인 제공업체를 추가한 후 앱 ID와 앱 비밀번호를 입력하고 redirect URI 정보는 Facebook 로그인 설정 부분에서도 입력합니다.
안드로이드 애플리케이션에서 Firebase를 추가할 때 google-services.json 구성파일을 추가하게 됩니다. 이 때 Authentication에 추가한 제공업체의 Client ID와 Secret 정보도 구성파일에 존재하게 되어 Client 앱에서도 ID와 Secret 정보를 가지기 때문이지 않을까 싶습니다. 개인적인 생각이라서 혹시 틀렸다면.. 댓글 부탁드려요 😇
Client 등록까지 완료가 되면 Client 애플리케이션에서는 Resource Server에서 제공해줄 수 있는 기능들 중 사용하려는 서비스에 대한 화면을 보여주게 됩니다. Tinder 애플리케이션에서는 Facebook 제공업체에서 제공해줄 수 있는 다양한 서비스들 중 로그인 기능을 사용하려고 합니다.
다음 화면처럼 Facebook 로그인 버튼을 보여주고 버튼을 클릭한다는 것은 사용자가 Facebook 로그인에 대한 동의로 보시면 됩니다. 버튼을 클릭하면 아래 그림과 같은 링크를 생성하게 됩니다.
- Resource Server : facebook.com
- client_id : 764763960808004
- scope : public_profile
- redirect_url : inderef468.firebaseapp.com/__/auth/handler
위 링크에서 scope는 Resource Server에서 제공해주는 서비스 중 어떤 것을 사용할 것인지를 나타냅니다. 즉, Resource Server에 접속하여 Client_ID가 764763960808004인 사용자 정보를 확인하여 Authorization code를 redirect_url로 보내라는 뜻입니다.만일 Facebook 로그인이 되어 있지 않다면 로그인 하라는 화면이 나타나게 되고 로그인 성공 시 Resource Server의 로그인 기능을 사용하도록 승인하게 되는 것입니다.
이제 Authorization Server는 user_id와 어떤 서비스를 허용받았는지를 가리키는 scope에 대한 정보도 알게 됩니다.
Resource Owner가 페이스북 로그인 서비스를 승인한 이후에는 Authorization Server도 해당 Resource Server를 이용할 수 있도록 승인을 해주어야 합니다. 이 때 Authorization Server에서 바로 Access Token을 발급하는 것이 아닌 Authorization Code를 주게 됩니다.
초반에 입력했던 Authorization Redirect URI 경로로 Authroizationi code 값을 전달하게 되고 내부적으로 Client에게도 Authorization code 값을 보내주게 됩니다.
이제 Client 애플리케이션은 Resource Owner를 통해서 요청하는 것이 아닌 직접 Resource Server에 접속합니다. 요청 시에 Authorization Server에 등록된 Client_ID와 Client_Secret 정보와 Authroization Server에서 승인 시에 발급해주었던 Authorization code 값도 비교하게 됩니다.
모든 정보가 같다면 다음과 같이 Authorization Server에서 Access Token을 발급한 후 Client 쪽에 전달하게 됩니다. Client 애플리케이션은 내부적으로 가지고 있어야 하며 Resoure Server에서 제공해주는 서비스 이용 시에 사용하게 됩니다. Tinder 애플리케이션에서는 Facebook 로그인 서비스를 이용하기에 Access Token을 발급받으면 해당 Tokenㅇ을 가지고 Firebase credential을 얻어와서 로그인을 하도록 해줄 수 있습니다.
facebookLoginButton.registerCallback(callbackManager, object: FacebookCallback<LoginResult> {
override fun onSuccess(result: LoginResult) {
// 로그인 성공, result에 AccessToken 포함
val credential = FacebookAuthProvider.getCredential(result.accessToken.token)
auth.signInWithCredential(credential).addOnCompleteListener(this@LoginActivity) {task ->
if(task.isSuccessful){
handleSuccessLogin()
} else{
Toast.makeText(this@LoginActivity, "페이스북 로그인이 실패했습니다.", Toast.LENGTH_LONG).show()
}
}
}
override fun onCancel() {}
override fun onError(error: FacebookException?) {
Toast.makeText(this@LoginActivity, "페이스북 로그인이 실패했습니다.",Toast.LENGTH_LONG).show()
}
})
구현은 정말 간단한데 내부적으로 동작 원리는 상당히 복잡하네요.
로그인 연동 부분은 문서들 보면서 구현은 몇 번 해봤지만, 어떻게 동작하는 지에 대해서 제대로 알지 못해서 시간 잡아서 정리해봤습니다. 대략적으로는 이해는 되지만, Authorization Redirect URI가 어떤 역할을 하고 Authorization code를 보내는 과정은 확 와닿진 않네요.. 다음에 추가적으로 정리해보겠습니다! 😎