Facebook 사용자 액세스 토큰은 서버 측에서 어떻게 사용해야합니까?
머리말
저는 HTTP (s)를 통해 상기 서비스와 인터페이스 할 여러 웹 서비스와 소수의 클라이언트 (웹 앱, 모바일 등)를 개발 중입니다. 내 현재 작업 항목은 제품에 대한 인증 및 권한 부여 솔루션을 설계하는 것입니다. 저는 인증을 위해 Facebook, Google, Microsoft, Twitter 등과 같은 외부 ID 공급자를 활용하기로 결정했습니다.
"내 서버에 요청이 올 때 사용자가 누구인지 어떻게 알 수 있으며 어떻게 확신 할 수 있습니까?"라는 문제를 해결하려고합니다. 아래에 더 많은 질문이 있습니다.
요구 사항
- 외부 ID에 의존하여 내가 누구를 상대하고 있는지 표시합니다 ( 'userId'는 본질적으로 내가 신경 쓰는 전부입니다).
시스템은 토큰 기반 인증을 사용해야합니다 (예 : 쿠키 또는 기본 인증과 반대).
저는 이것이 느슨한 결합을 제공하면서 여러 클라이언트와 서버에 걸쳐 확장하는 데 적합한 선택이라고 생각합니다.
워크 플로우
토큰 기반 인증에 대한 읽고 이해 한 결과, 다음은 워크 플로를 상상하는 방식입니다. 지금 은 웹 브라우저 에서 Facebook에 집중 해 보겠습니다 . 내 가정은 아직 확인하지 않았지만 다른 외부 ID 공급자도 비슷한 기능을 가져야한다는 것입니다.
작성 시점에서 Facebook 로그인 버전 2.2를 기반으로 다음을 기반으로합니다.
- 클라이언트 : JavaScript SDK를 사용하여 Facebook에 로그인을 시작 합니다.
- Facebook : 사용자가 앱 권한을 인증하고 승인합니다 (예 : 사용자의 공개 프로필에 액세스하기 위해).
- Facebook : 사용자의 액세스 토큰, ID 및 서명 된 요청이 포함 된 클라이언트에 응답을 보냅니다 .
- 클라이언트 : 브라우저 세션에 사용자 액세스 토큰 저장 ( SDK에서 편리하게 처리 )
- 클라이언트 : 인증 헤더의 사용자 액세스 토큰 + 사용자의 ID (잠재적으로 사용자 정의 헤더)에있는 사용자의 액세스 토큰을 전송하여 내 웹 서비스에 보안 리소스를 요청합니다.
- 서버 : 요청 헤더에서 사용자 액세스 토큰을 읽고 Facebook에서 제공하는 debug_token 그래프 API에 요청을 전송하여 확인을 시작합니다.
- Facebook : 사용자 액세스 토큰 정보 (appId 및 userId 포함)로 서버에 다시 응답합니다.
- 서버 : appId를 예상 (자체가 알고 있음)과 비교하고 userId를 클라이언트 요청에서 전송 된 것과 비교하여 토큰 확인을 완료합니다 .
- 서버 : 요청 된 리소스로 클라이언트에 응답합니다 (행복한 권한 부여 경로 가정).
서버에 대한 후속 요청에 대해 5-9 단계가 반복 될 것이라고 상상하고 있습니다 (사용자의 액세스 토큰이 유효하지만 만료되지 않았거나 FB 측에서 취소됨, 앱 권한 변경 등).
다음은 단계를 진행하는 데 도움이되는 다이어그램입니다. 이 시스템은 단일 페이지 애플리케이션 (SPA) 이 아닙니다 . 언급 된 웹 서비스는 기본적으로 클라이언트에 JSON 데이터를 제공하는 API 엔드 포인트입니다. HTML / JS / CSS를 제공하지 않습니다 (웹 클라이언트 서버 제외).
질문
가장 먼저, 서문과 요구 사항을 기반으로 설명 된 접근 방식에 눈에 띄는 갭 / 피트 폴이 있습니까?
클라이언트 요청마다 액세스 토큰 (위의 6-8 단계)을 확인하기 위해 Facebook에 아웃 바운드 요청을 수행 해야합니까?
적어도 클라이언트 요청에서 오는 액세스 토큰을 확인해야한다는 것을 알고 있습니다. 그러나 첫 번째 이후의 후속 검증에 권장되는 접근 방식은 나에게 알려지지 않았습니다. 전형적인 패턴이 있다면 그것에 대해 듣고 싶습니다. 내 요구 사항에 따라 응용 프로그램에 따라 달라질 수 있음을 이해합니다. 그러나 나는 아직 무엇을 찾아야할지 모르겠습니다. 기본 아이디어가 생기면 실사를 할 것입니다.
예를 들어 가능한 생각 :
첫 번째 확인이 완료된 후 액세스 토큰 + userId 쌍을 해시하고 액세스 토큰과 동일한 만료 기간으로 분산 캐시 (모든 웹 서버에서 액세스 가능)에 저장합니다. 클라이언트의 후속 요청에 따라 액세스 토큰 + userId 쌍을 해시하고 캐시에 존재하는지 확인합니다. 있는 경우 요청이 승인됩니다. 그렇지 않으면 Facebook 그래프 API에 문의하여 액세스 토큰을 확인하십시오. HTTPS를 사용하는 경우이 전략이 실현 가능할 것이라고 가정하고 있습니다. 그러나 성능은 어떻게 비교됩니까?
이 StackOverflow 질문 에서 허용되는 답변 은 Facebook 사용자 토큰의 첫 번째 확인이 완료된 후 사용자 지정 액세스 토큰을 만드는 것이 좋습니다. 그런 다음 사용자 지정 토큰이 후속 요청을 위해 클라이언트로 전송됩니다. 그러나 이것이 위의 솔루션보다 더 복잡한 지 궁금합니다. 이를 위해서는 내 ID 공급자를 구현해야합니다 (애초에 외부 ID 공급자를 사용하고 싶기 때문에 피하고 싶은 것…). 이 제안에 어떤 장점이 있습니까?
(언급 위의 단계 # 3의 응답에 signedRequest 필드의 존재 여기에 ) 서명 요청 매개 변수에 해당 여기 '게임 캔버스 로그인'흐름에?
전자가 문서에서 후자에 연결되기 때문에 그것들은 동등한 것으로 암시 된 것 같습니다. 하지만 게임 페이지에 언급 된 검증 전략 이 웹 문서 의 '수동으로 로그인 흐름 구축' 페이지 에 언급되지 않은 것에 놀랐습니다 .
# 3에 대한 답이 '예'인 경우 서명을 해독하고 서버 측에서 사용될 것으로 예상되는 것과 비교하는 동일한 신원 확인 전략이 가능합니까?
여기 에서 권장하는대로 액세스 토큰을 확인하기 위해 debug_token 그래프 API (위의 6 단계)에 대한 아웃 바운드 호출을 수행하는 대신 이것이 활용 될 수 있는지 궁금합니다 .
Of course, in order to make the comparison on the server-side, the signed request portion would need to be sent along with the request to the server (step #5 above). In addition to feasibility without sacrificing security, I’m wondering how the performance would compare to making the outbound call.
While I’m at it, in what scenario / for what purpose, would you persist a user's access token to a database for example? I don’t see a scenario where I would need to do this, however, I may be overlooking something. I’m curious was some common scenarios might be to spark some thoughts.
Thanks!
From what you describe I'd suggest to use a server-side login flow as described in
so that the token is already on your server, and doesn't need to be passed from the client. If you're using non-encrypted connections, this could be a security risk (e.g. for man-in-the-middle attacks).
The steps would be:
(1) Logging people in
You need to specify the permission you want to gather from the users in the scope
parameter. The request can be triggered just via a normal link:
GET https://www.facebook.com/dialog/oauth?
client_id={app-id}
&redirect_uri={redirect-uri}
&response_type=code
&scope={permission_list}
See
(2) Confirm the identitity
GET https://graph.facebook.com/oauth/access_token?
client_id={app-id}
&redirect_uri={redirect-uri}
&client_secret={app-secret}
&code={code-parameter}
(3) Inspect the access token
You can inspect the token as you already said in your question via
GET /debug_token?input_token={token-to-inspect}
&access_token={app-token-or-admin-token}
This should only be done server-side, because otherwise you'd make you app access token visible to end users (not a good idea!).
See
(4) Extending the access token
Once you got the (short-lived) token, you can do a call to extend the token as described in
like the following:
GET /oauth/access_token?grant_type=fb_exchange_token
&client_id={app-id}
&client_secret={app-secret}
&fb_exchange_token={short-lived-token}
(5) Storing of access tokens
Concerning the storing of the tokens on the server, FB suggests to do so:
(6) Handling expired access tokens
As FB doesn't notify you if a token has expired (and if you don't save the expiry date and compare this to the current timestamp before making a call), it's possible that you receive error messages from FB if the token got invalid (after max. 60 days). The error code will be 190
:
{
"error": {
"message": "Error validating access token: Session has expired at unix
time SOME_TIME. The current unix time is SOME_TIME.",
"type": "OAuthException",
"code": 190
}
}
See
If the access token becomes invalid, the solution is to have the person log in again, at which point you will be able to make API calls on their behalf once more. The login flow your app uses for new people should determine which method you need to adopt.
- I dont' see any glaring gaps / pit falls, but I'm not a security expert.
- Once your server has verified the given token (step 8), as you said:
The accepted answer in this StackOverflow question recommends creating a custom access token after the first verification of the Facebook user token is complete. The custom token would then be sent to the client for subsequent requests. I’m wondering if this is more complex than the above solution, however. This would require implementing my own Identity Provider (something I want to avoid because I want to use external identity providers in the first place…). Is there any merit to this suggestion?
IMHO is the way to go. I would use https://jwt.io/ which allows you to encode values (the userId for example) using a secret key. Then your client attach this token to every request. So you can verify the request without need to a third party (you don't need database queries neither). The nice thing here is there is no need to store the token on your DB.
You can define an expiration date on the token, to force the client authenticate with the third party again when you want.
- Let's say you want your server be able to do some action without the client interaction. For example: Open graph stories. In this scenario because you need to publish something in the name of the user you would need the access token stored on your DB.
(I can not help with the 3 and 4 questions, sorry).
'program tip' 카테고리의 다른 글
Eclipse + EGit : 작업 공간에 프로젝트 복제 (0) | 2020.11.22 |
---|---|
사용자 지정 사용자 데이터베이스를 사용한 웹 API 토큰 인증 (0) | 2020.11.22 |
봄과 빈혈 도메인 모델 (0) | 2020.11.22 |
앱 타나 스튜디오의 배경색은 어떻게 변경하나요? (0) | 2020.11.22 |
Scala Hoogle과 동등합니까? (0) | 2020.11.22 |