Browse Source

switch from deprecated live_js api to REST API (oauth2+graph)

Andrii Zadorozhnii 8 years ago
parent
commit
71e5b18d84
4 changed files with 71 additions and 77 deletions
  1. 0 57
      priv/microsoft_sdk.dtl
  2. 1 0
      src/avz.erl
  3. 0 2
      src/facebook.erl
  4. 70 18
      src/microsoft.erl

+ 0 - 57
priv/microsoft_sdk.dtl

@@ -1,57 +0,0 @@
-<script>
-
-WL.init({ client_id: "{{client}}", redirect_uri: "{{redirect}}" });
-//WL.Event.subscribe("auth.login", onLogin);
-//WL.Event.subscribe("auth.sessionChange", onSessionChange);
-
-function microsoft_login2() {
-    var session = WL.getSession();
-    if (session) {
-        console.log("You are already signed in!");
-    } else {
-        WL.login({ scope: "wl.signin" });
-    }
-}
-
-function microsoft_login() {
-
-    WL.login({ "scope": "wl.basic" }).then(
-        function (response) {
-            showUserData();
-        },
-        function (response) {
-            log("Could not connect, status = " + response.status);
-        }
-    );
-
-}
-
-function showUserData() {
-    WL.api({ path: "/me", method: "GET" }).then(
-        function (response) {
-            winLogin(JSON.stringify(response));
-        },
-        function (response) {
-            log("API call failed: " + JSON.stringify(response.error).replace(/,/g, "\n"));
-        }
-    );
-}
-
-function onLogin() {
-    var session = WL.getSession();
-    if (session) {
-        console.log("You are signed in!");
-        console.log("{{event}}");
-    }
-}
-
-function onSessionChange() {
-    var session = WL.getSession();
-    if (session) {
-        winLogin(session);
-        console.log("Your session has changed.");
-        console.log(session);
-    }
-}
-
-</script>

+ 1 - 0
src/avz.erl

@@ -21,6 +21,7 @@ event(logout) -> wf:user(undefined), wf:redirect(?LOGIN_PAGE);
 event(to_login) -> wf:redirect(?LOGIN_PAGE);
 event({register, #avz_user{}=U}) -> kvs:put(U), login_user(U); % sample
 event({login, #avz_user{}=U, N}) -> Updated = merge(U,N), kvs:put(Updated), login_user(Updated); % sample
+event({error, E}) -> (?CTX#cx.module):event({login_failed, E});
 event({Method,Event}) -> Method:event({Method,Event});
 event(Ev) ->  wf:info(?MODULE,"Page Event ~p",[Ev]).
 

+ 0 - 2
src/facebook.erl

@@ -43,8 +43,6 @@ login_button() -> application:get_env(avz,facebook_button,
                         <<"Facebook">>], postback={facebook,loginClick} }}).
 
 sdk() ->
-    wf:wire(#api{name=setFbIframe, tag=fb}),
-    wf:wire(#api{name=fbAutoLogin, tag=fb}),
     wf:wire(#api{name=fbLogin, tag=fb}),
     [ #dtl{bind_script=false, file="facebook_sdk", ext="dtl", folder="priv/static/js",
         bindings=[{appid, ?FB_APP_ID}] } ].

+ 70 - 18
src/microsoft.erl

@@ -6,8 +6,22 @@
 -include_lib("avz/include/avz_user.hrl").
 -compile(export_all).
 -export(?API).
--define(CLIENT_ID,    "000000004C0FEEB0").
--define(REDIRECT_URI, "http://skyline.synrc.com:8000").
+
+-define(OAUTH_URI,      "https://login.microsoftonline.com/common/oauth2/v2.0").
+-define(API_URI,        "https://graph.microsoft.com/v1.0/").
+-define(AUTHORIZE,      ?OAUTH_URI ++ "/authorize").
+-define(ACCESS_TOKEN,   ?OAUTH_URI ++ "/token").
+-define(OAUTH_REDIRECT, application:get_env(avz, ms_redirect_uri, [])).
+-define(CLIENT_ID,      application:get_env(avz, ms_client_id, [])).
+-define(CLIENT_SECRET,  application:get_env(avz, ms_client_secret, [])).
+-define(SCOPE,          "https://graph.microsoft.com/user.read"). % only one working scope so far 
+
+% ms doesn't support sslv3
+-define(HTTP_OPTS, [{ssl, [{versions,['tlsv1.2']}]}]).
+
+authorize_url() -> 
+  Params = [{"client_id", ?CLIENT_ID},{"redirect_uri", ?OAUTH_REDIRECT},{"response_type", "code"},{"scope", ?SCOPE}],
+  oauth:uri(?AUTHORIZE, Params).
 
 api_event(_, Args, _)->
     JSArgs = string:tokens(Args,"\\\\"),
@@ -19,13 +33,12 @@ api_event(_, Args, _)->
     avz:login(microsoft, D).
 
 registration_data(Props, microsoft, Ori)->
-    wf:info(?MODULE,"Microsoft Login: ~p",[Props]),
     Id = proplists:get_value(<<"id">>, Props),
-    GivenName = proplists:get_value(<<"first_name">>, Props),
-    FamilyName = proplists:get_value(<<"last_name">>, Props),
+    GivenName = proplists:get_value(<<"givenName">>, Props),
+    FamilyName = proplists:get_value(<<"surname">>, Props),
     Email = email_prop(Props,microsoft),
-    Ori#avz_user{   id = Email,
-                display_name = proplists:get_value(<<"name">>, Props),
+    Ori#avz_user{ id = Email,
+                display_name = proplists:get_value(<<"displayName">>, Props),
                 email = Email,
                 names = GivenName,
                 surnames = FamilyName,
@@ -34,17 +47,56 @@ registration_data(Props, microsoft, Ori)->
                 sex = proplists:get_value(<<"gender">>, Props),
                 status = ok }.
 
-email_prop(Props, _) -> proplists:get_value(<<"id">>, Props).
+email_prop(Props, _) -> proplists:get_value(<<"userPrincipalName">>, Props).
+
+login_button()-> #link{
+  id=microsoftbtn, class=[btn, "btn-microsoft", "btn-large"], 
+  body=[#i{class=["icon-microsoft", "icon-large"]}, <<"Microsoft">>],
+  postback={microsoft, login}}.
+
+get_access_token(Code) ->
+  Params = [
+    {grant_type, "authorization_code"},
+    {code, binary_to_list(Code)},
+    {redirect_uri, ?OAUTH_REDIRECT},
+    {client_id, ?CLIENT_ID},
+    {client_secret, ?CLIENT_SECRET}
+  ],
+  Req = {?ACCESS_TOKEN, [], "application/x-www-form-urlencoded", oauth:uri_params_encode(Params)},
+
+  case httpc:request(post, Req, ?HTTP_OPTS, []) of 
+    {ok, {{"HTTP/1.1",200,"OK"},_, B1}} ->
+      ?AVZ_JSON:decode(list_to_binary(B1), [{object_format, proplist}]);
+    {error, E} -> avz:event({error, wf:jse(E)}), not_authorized;
+    {ok, {{"HTTP/1.1",_,_}, _, B}} ->
+      Fail = ?AVZ_JSON:decode(list_to_binary(B), [{object_format, proplist}]),
+      case proplists:get_value(<<"error">>, Fail, undefined) of undefined -> Fail;
+        _E ->
+          Desc = proplists:get_value(<<"error_description">>, Fail, undefined),
+          avz:event({error, wf:jse(Desc)}),
+          not_authorized end end.
 
-login_button()-> application:get_env(avz,microsoft_button,#panel{class=["btn-group"], body=
-    #link{id=microsoftbtn, class=[btn, "btn-microsoft", "btn-large"], 
-        body=[#i{class=["icon-microsoft", "icon-large"]}, <<"Microsoft">>],
-              actions= "$('#microsoftbtn').on('click', microsoft_login);" }}).
+api_call(Name, Props) ->
+  Token = proplists:get_value(<<"access_token">>, Props, undefined),
+  TokenType = proplists:get_value(<<"token_type">>, Props, undefined),
+  Authorization = [{"Authorization", string:join([wf:to_list(TokenType),wf:to_list(Token)]," ") }],
+  Req = {?API_URI ++ Name, Authorization},
 
-event(_) -> ok.
-callback() -> ok.
-sdk() ->
-    wf:wire(#api{name=winLogin, tag=plus}),
-    #dtl{bind_script=false, file="microsoft_sdk", ext="dtl", folder="priv/static/js",
-        bindings=[{event, microsoft_login},{client, ?CLIENT_ID},{redirect, ?REDIRECT_URI}]}.
+  case httpc:request(get, Req, ?HTTP_OPTS, []) of 
+    {ok, {{"HTTP/1.1",200,"OK"},Hh,Bh}} ->
+      {Usr} = ?AVZ_JSON:decode(list_to_binary(Bh)), Usr;
+    {ok, {{"HTTP/1.1",_,_},_,B}} ->
+      {Err} = ?AVZ_JSON:decode(list_to_binary(B)), avz:event({error, wf:jse(Err)}), api_error;
+    {error, Err} -> avz:event({error, wf:jse(Err)}), api_error
+  end.
 
+callback() -> 
+  Code = wf:q(<<"code">>),
+  case wf:user() of undefined when Code =/= undefined ->
+    case get_access_token(Code) of 
+      not_authorized -> skip;
+      Props -> UserData = api_call("me/", Props), avz:login(microsoft, UserData)
+    end;
+  _ -> skip end.
+sdk() -> [].
+event({microsoft, _}) -> wf:redirect(microsoft:authorize_url()).