Browse Source

Merge branch 'master' of github.com:synrc/avz into HEAD

Namdak Tonpa 6 years ago
parent
commit
245c2f7bb0
5 changed files with 101 additions and 8 deletions
  1. 19 1
      README.md
  2. 6 0
      priv/telegram_login.dtl
  3. 6 3
      src/avz.erl
  4. 8 4
      src/email.erl
  5. 62 0
      src/telegram.erl

+ 19 - 1
README.md

@@ -13,6 +13,7 @@ Supported Methods
 * Facebook
 * Facebook
 * Github
 * Github
 * Microsoft
 * Microsoft
+* Telegram
 
 
 API
 API
 ---
 ---
@@ -60,6 +61,7 @@ Authentication endpoints can be configured in your `sys.config` under avz applic
 Available settings listed below with test applications configured for each provider and will
 Available settings listed below with test applications configured for each provider and will
 call you back on `http://localhost:8000/login`.
 call you back on `http://localhost:8000/login`.
 
 
+
 ```erlang
 ```erlang
 {
 {
   ...
   ...
@@ -92,12 +94,28 @@ call you back on `http://localhost:8000/login`.
         % Microsoft Account Login
         % Microsoft Account Login
         {ms_client_id, "54385d15-f1e0-4fcf-9bf4-042d740e0df4"},
         {ms_client_id, "54385d15-f1e0-4fcf-9bf4-042d740e0df4"},
         {ms_client_secret, "jU0tStEzRdDPFwL9NdVGYxo"},
         {ms_client_secret, "jU0tStEzRdDPFwL9NdVGYxo"},
-        {ms_redirect_uri, "http://localhost:8000/login"}
+        {ms_redirect_uri, "http://localhost:8000/login"},
+        
+        % Telegram Login Widget
+        {tl_bot, "NYNJA_bot"},
+        {tl_bot_token, "548231922:AAHmXMMr38XGtH0tJMDUdiByheT2mZ7qkVI"}
+        {tl_auth_url, "http://127.0.0.1/login"},
+        {tl_request_access, "write"}
+        {tl_btn_size, "large"},
+        {tl_btn_radius, "20"},
   ]}
   ]}
   ...
   ...
 }
 }
 ```
 ```
 
 
+Telegram Notes
+---------------
+Login widget is displayed within the iframe, so the battle of `CPS` and `x-frame-options` is expected in different browsers.
+
+When setting a domain in BotFather with `/setdomain`, please note that telegram will cut the port part of your domain in the `X-Frame-Options` and `Content-Security-Policy` response headers. 
+
+So in fact you are limited to use 80 and 443 ports only.
+
 Credits
 Credits
 -------
 -------
 
 

+ 6 - 0
priv/telegram_login.dtl

@@ -0,0 +1,6 @@
+<script async src="https://telegram.org/js/telegram-widget.js?4" 
+    data-telegram-login = {{bot}}
+    data-size           = {{size}}
+    data-size           = {{radius}}
+    data-auth-url       = {{auth_url}}
+    data-request-access = {{request_access}}></script>

+ 6 - 3
src/avz.erl

@@ -35,12 +35,15 @@ api_event(Name, Args, Term)      -> wf:info(?MODULE,"Unknown API event: ~p ~p ~p
 login_user(User) -> wf:user(User), wf:redirect(?AFTER_LOGIN).
 login_user(User) -> wf:user(User), wf:redirect(?AFTER_LOGIN).
 login(_Key, [{error, E}|_Rest])-> wf:info(?MODULE,"Auth Error: ~p", [E]);
 login(_Key, [{error, E}|_Rest])-> wf:info(?MODULE,"Auth Error: ~p", [E]);
 login(Key, Args) ->
 login(Key, Args) ->
-
   LoginFun = fun(K) ->
   LoginFun = fun(K) ->
     Index = proplists:get_value(Key:index(K), Args),
     Index = proplists:get_value(Key:index(K), Args),
     case kvs:index(user,K,Index) of
     case kvs:index(user,K,Index) of
       [Exists|_] ->
       [Exists|_] ->
-        RegData = Key:registration_data(Args, Key, Exists),
+        Diff = tuple_size(Exists) - tuple_size(#user{}),
+        {It, UsrExt} = lists:split(tuple_size(#iterator{}), tuple_to_list(Exists)),
+        {_,Usr} = lists:split(Diff, UsrExt),
+
+        RegData = Key:registration_data(Args, Key, list_to_tuple(lists:append([It,Usr]))),
         (?CTX#cx.module):event({login, Exists, RegData}),
         (?CTX#cx.module):event({login, Exists, RegData}),
         true;
         true;
       _ -> false end end,
       _ -> false end end,
@@ -49,7 +52,7 @@ login(Key, Args) ->
 
 
   LoggedIn = lists:any(LoginFun, Keys),
   LoggedIn = lists:any(LoginFun, Keys),
 
 
-  if (LoggedIn =:= true) -> ok; true -> 
+  if (LoggedIn =:= true) -> true; true -> 
     RegData = Key:registration_data(Args, Key, #user{}),
     RegData = Key:registration_data(Args, Key, #user{}),
     (?CTX#cx.module):event({register, RegData})
     (?CTX#cx.module):event({register, RegData})
   end.
   end.

+ 8 - 4
src/email.erl

@@ -9,18 +9,22 @@
 
 
 registration_data(Props, email, Ori)->
 registration_data(Props, email, Ori)->
   Email = email_prop(Props, email),
   Email = email_prop(Props, email),
-  Ori#user{ display_name = Email,
+  
+  Ori#user{ display_name = proplists:get_value(<<"display_name">>, Props, Email),
             email = Email,
             email = Email,
+            names    = proplists:get_value(<<"first_name">>, Props,[]),
+            surnames = proplists:get_value(<<"last_name">>,  Props,[]),
             register_date = os:timestamp(),
             register_date = os:timestamp(),
             tokens = avz:update({email,Email},Ori#user.tokens),
             tokens = avz:update({email,Email},Ori#user.tokens),
-            status = ok,
+            status = proplists:get_value(<<"status">>, Props, ok),
+            type   = proplists:get_value(<<"type">>, Props,[]),
             password = avz:sha(proplists:get_value(<<"password">>,Props))}.
             password = avz:sha(proplists:get_value(<<"password">>,Props))}.
 
 
 index(K) -> wf:to_binary(K).
 index(K) -> wf:to_binary(K).
-email_prop(Props, _) -> binary_to_list(proplists:get_value(<<"email">>, Props)).
+email_prop(Props, _) -> proplists:get_value(<<"email">>, Props).
 
 
 login_button() -> #button{id=login, body= <<"Sign in">>, postback={email, loginemail}, source=[user,pass]}.
 login_button() -> #button{id=login, body= <<"Sign in">>, postback={email, loginemail}, source=[user,pass]}.
-event({email,loginemail}) -> avz:login(email, [{<<"email">>, list_to_binary(wf:q(user))}, {<<"password">>, wf:q(pass)}]);
+event({email,loginemail}) -> avz:login(email, [{<<"email">>, wf:q(user)}, {<<"password">>, wf:q(pass)}]);
 event(_) -> ok.
 event(_) -> ok.
 api_event(_,_,_) -> ok.
 api_event(_,_,_) -> ok.
 callback() -> ok.
 callback() -> ok.

+ 62 - 0
src/telegram.erl

@@ -0,0 +1,62 @@
+-module(telegram).
+-include_lib("avz/include/avz.hrl").
+-include_lib("nitro/include/nitro.hrl").
+-include_lib("kvs/include/user.hrl").
+-compile(export_all).
+-export(?API).
+
+-define(TL_BOT_NAME,    application:get_env(avz, tl_bot, [])).
+-define(TL_BOT_TOKEN,   application:get_env(avz, tl_bot_token, [])).
+-define(TL_AUTH_URL,    application:get_env(avz, tl_auth_url, [])).
+-define(TL_ACCESS,      application:get_env(avz, tl_request_access, "write")).
+-define(TL_BTN_SIZE,    application:get_env(avz, tl_btn_size, "large")).
+-define(TL_BTN_RADIUS,  application:get_env(avz, tl_btn_radius, "20")).
+
+-define(TL_USER, [<<"id">>, <<"first_name">>, <<"last_name">>,<<"username">>,<<"auth_date">>, <<"photo_url">>]).
+-define(ATTS, #{email => <<"id">>}).
+
+api_event(_,_,_) -> ok.
+
+registration_data(Props, telegram, Ori) -> 
+    Id = proplists:get_value(<<"id">>, Props),
+    UserName = binary_to_list(proplists:get_value(<<"username">>, Props)),
+    Email = email_prop(Props,telegram),
+    Ori#user{   username = re:replace(UserName, "\\.", "_", [{return, list}]),
+                display_name = proplists:get_value(<<"username">>, Props),
+                images = avz:update({tl_avatar,proplists:get_value(<<"photo_url">>, Props)},Ori#user.images),
+                names = proplists:get_value(<<"first_name">>, Props),
+                email = Email,
+                surnames = proplists:get_value(<<"last_name">>, Props),
+                tokens = avz:update({telegram,Id},Ori#user.tokens),
+                register_date = os:timestamp(),
+                status = ok }.
+
+index(K) -> maps:get(K, ?ATTS, wf:to_binary(K)).
+email_prop(Props, telegram) -> proplists:get_value(maps:get(email,?ATTS), Props).
+
+login_button() ->
+    #dtl{bind_script=false, file="telegram_login", ext="dtl", bindings=[
+        {bot,             ?TL_BOT_NAME},
+        {size,            ?TL_BTN_SIZE}, 
+        {radius,          ?TL_BTN_RADIUS},
+        {auth_url,        ?TL_AUTH_URL},
+        {request_access,  ?TL_ACCESS} ]}.
+
+event(E) -> ok.
+sdk() -> [].
+
+% HMAC-SHA-256 signature of the data-check-string with the SHA256 hash of the bot's token used as a secret key
+callback() ->
+    Hash = wf:q(<<"hash">>),
+
+    Rec  = lists:filter(fun({_, undefined}) -> false; (_) -> true end, [ {T, wf:q(T)} || T <- lists:sort(?TL_USER) ]),
+    Data = lists:join(<<"\n">>, [unicode:characters_to_nfkc_binary([K, <<"=">>, V]) || {K, V} <- Rec]),
+
+    case crypto:hmac(sha256, crypto:hash(sha256, ?TL_BOT_TOKEN), Data) of <<X:256/big-unsigned-integer>> ->
+        case list_to_binary(lists:flatten(io_lib:format("~64.16.0b", [X]))) of 
+          Hash ->
+              avz:login(telegram, Rec);
+          _ -> skip
+        end;
+        _ -> skip
+    end.