카테고리

분류 전체보기 (510)
문학 (128)
찬양 콘티(Continuity) (80)
Business as heritage (6)
IT . Web (149)
Photo (127)
etc. (20)
Total357,714
Today33
Yesterday98
*20121219 국치가 회복될 사건이 올 때까지 블로그 양쪽은 조의를 표하는 검정색입니다.
Tistory 로고 이미지 티스토리 가입하기!









블로그 이미지

MS 플랫폼 기반 웹 개발은 점점 좋아지고 있다. NuGet 패키지 관리자 창에서 다양한 오픈소스 라이브러리들을 인기순, 최신순 등으로 정렬해 살핀 다음 클릭 한번으로 내가 작업 중인 솔루션에 적용할 수 있다. MS가 개발한 OAuth(Microsoft.Web.WebPages.OAuth)를 이용하면 AppID / AppSecret 입력만으로 내 웹사이트에 페이스북 로그인을 연동하는 것도 가능하다.


그런데 함정이 있다. 인증을 알아서 처리해 주는 것은 고마운데, 너무 알아서 해버리는 바람에 유연성이 없다. 처음 페이스북의 앱 사용 허가 다이얼로그로 페이지가 redirect될 때, &scope= 파라미터에 email,user_about,me 등을 입력하는 것으로 최소한의 기본 정보(이름, 성별 등) 외에 어떤 정보에 대한 권한(permission) 을 추가 요청할 것인지를 결정하게 되는데 이 추가 파라미터를 임의로 설정할 수 없는 구조인 것이다.

구글링을 상당히 많이 했으나, 만족스러운 수준의 예제를 발견하기 어려웠다. 심지어 아래와 같은 접근을 하는 사람도 있었다. 그나마 괜찮은 힌트들이 모여 있는 글은 여기. - http://forums.asp.net/t/1837700.aspx/1


var fb= new Dictionary<string, object>();

fb.Add("scope", "email");


OAuthWebSecurity.RegisterFacebookClient(

    appId: "",

    appSecret: "",

    displayName:"FaceBook",

    extraData:fb

);


미안하지만 최초 등록 시 넘겨주는 extraData 콜렉션은 이런 용도가 아니다. 다만 화면에 여러 종류의 소셜 로그인 방식을 보여줄 때 원하는 이미지로 포장해서 보여준다던가 하고 싶을 때 관련 리소스를 입력해 주는 용도일 뿐이다.


프로젝트 생성 시 자동으로 구현되는 메서드는 아래와 같은 모양이다.


[HttpPost]

[AllowAnonymous]

[ValidateAntiForgeryToken]

public ActionResult ExternalLogin(string provider, string returnUrl)

{

return new ExternalLoginResult(provider, Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));

}


위 코드의 ExternalLoginResult가 실행되면 현재 컨트롤러의 httpContext를 페이스북 인증 주소로 redirect시켜 버리는데, 개발자는 컨트롤러의 OnActionExecuting,  OnActionExecuted, OnResultExecuting, OnResultExecuted 이벤트 등에서 이를 제어할 수 없다. OnResultExecuted 시점에서야 redirect location이 설정된 것을 파악할 수 있는데, 앞선 OnResultExecuting에서 이미 response는 처리되고 그 뒤에는 헤더나 내용을 건드려 봤자 사용자의 웹 브라우저가 이미 관심을 끈 뒤기 때문에 손을 놓을 수밖에 없다.


그래서 ExternalLoginResult를 대신해서 요청 주소를 만들어 봤다.

"http://" + Request.Url.Host + "/Home/ExternalLoginCallback?__provider__=facebook”


인증 다이얼로그로 잘 연결되기는 하지만, 앱 실행 버튼 클릭 후 내 사이트로 되돌아 온 뒤 Login Failed 메시지가 출력된다.

기존에 ExternalLoginResult가 만들어 내는 주소랑 비교해 봤더니 파라미터 하나가 다르다. 바로 __sid__.

이것이 세션 아이디인가 하여 시도해 봤더니 얄짤 없었다.


결국 마지막 선택. __sid__를 인증하는 과정에 문제가 있는 것 같은데 생성 규칙을 알 수 없으니 리플렉터로 dll을 까봤다. 까보니 역시 싶었다.


string parameterValue = Guid.NewGuid().ToString("N");

redirect_url += parameterValue;

byte[] input = MachineKeyUtil.Protect(Encoding.UTF8.GetBytes(GetUsername(this.HttpContext)), new string[] { "DotNetOpenAuth.AspNet.AntiXsrfToken.v1", "Token: " + parameterValue });

HttpCookie cookie2 = new HttpCookie("__csid__", HttpServerUtility.UrlTokenEncode(input));

cookie2.HttpOnly = true;

HttpCookie cookie = cookie2;

if (FormsAuthentication.RequireSSL)

{

    cookie.Secure = true;

}

this.HttpContext.Response.Cookies.Add(cookie);


이런 식으로 쿠키를 심어 놓고 콜백 메서드에서 검증을 시도하니 당연히 실패라고 인식하는 것이다.


[AllowAnonymous]

public ActionResult ExternalLoginCallback(string returnUrl)

{

    AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));

    //시드가 틀리면 실패.

    ...

}


결국 해결책은 올바른 시드를 심고 페이스북 다이얼로그 주소로 넘어가는 것이다. 원래 방식으로 인증을 마친 다음 그래프 API로 email을 달라고 한번 더(비효율적이긴 하지만) 요청하면 되는 것 아닌가 싶기도 하지만, 그래프 API도 최초 앱 사용 승인 시 허용된 permission 내에서만 값을 읽게 해주기 때문에 소용이 없었다.


최종적인 ExternalLogin 메서드의 구조는 다음과 같다. 내용이 많으므로 MachineKeyUtil과 같은 두어 개의 internal class는 담지 않았다.


string redirect_url = "http://" + Request.Url.Host + "/Home/ExternalLoginCallback?__provider__=facebook&__sid__=";


#region sid를 얻기 위해 MS dll 참조

string parameterValue = Guid.NewGuid().ToString("N");

redirect_url += parameterValue;

byte[] input = MachineKeyUtil.Protect(Encoding.UTF8.GetBytes(GetUsername(this.HttpContext)), new string[] { "DotNetOpenAuth.AspNet.AntiXsrfToken.v1", "Token: " + parameterValue });

HttpCookie cookie2 = new HttpCookie("__csid__", HttpServerUtility.UrlTokenEncode(input));

cookie2.HttpOnly = true;

HttpCookie cookie = cookie2;

if (FormsAuthentication.RequireSSL)

{

    cookie.Secure = true;

}

this.HttpContext.Response.Cookies.Add(cookie);

#endregion


return Redirect("https://www.facebook.com/dialog/oauth?client_id=" + fbclientAppId + "&redirect_uri=" + Server.UrlEncode(redirect_url.ToString()) + "&scope=email");


콜백의 AuthenticationResult에서 result.UserName이 이메일이 되고 result.ExtraData["name"]이 사용자 이름이 된다.

저작자 표시
신고
Posted by One of Remnants

댓글을 달아 주세요

달력

« » 2017.10
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        

최근에 받은 트랙백

글 보관함