Browse Source

Basic Telegram Login Widget (redirect URL type) integration.

Andrii Zadorozhnii 7 years ago
parent
commit
7912d1d73c
3 changed files with 87 additions and 1 deletions
  1. 19 1
      README.md
  2. 6 0
      priv/telegram_login.dtl
  3. 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>

+ 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.