profile
viewpoint
If you are wondering where the data of this site comes from, please visit https://api.github.com/users/lucca65/events. GitMemory does not store any data, but only uses NGINX to cache data for a period of time. The idea behind GitMemory is simply to give users a better reading experience.
Julien Lucca lucca65 Cambiatus São Paulo, Brasil http://lucca65.github.io λ functional programming, blockchain, buddhism and philosophy

cambiatus/frontend 16

Cambiatus Web based frontend using Elm

lbighetti/guia.elm-lang.pt 16

Portuguese version of guide.elm-lang.org 🇧🇷 🇵🇹 🇦🇴 🇲🇿 🇬🇼 🇨🇻 🇸🇹

cambiatus/backend 11

Cambiatus public GraphQL API

cambiatus/contracts 7

Cambiatus EOSIO Smart Contracts

lucca65/coffeechain 6

☕️⛓ Compre café usando Blockchain! Repo para o workshop de blockchain da CampusParty 2018

lucca65/ex_coin 6

💰Simple cryptocurrency implementation using Elixir

lucca65/rails_semantic_base 2

A Semantic UI powered Rails app with some of my favorite gems

lucca65/simple_forum 2

Simple Forum Elixir Exercise

lucca65/ashes 1

🔥 Phoenix ashes for new projects

Pull request review commentcambiatus/backend

Session table

 defmodule Cambiatus.Auth.Ecdsa do   """    def verify_signature(account, signature, phrase) do-    {"app", :publicKeyPoints}-    |> NodeJS.call([signature, phrase])-    |> case do-      {:ok, %{"publicKey" => pub_points, "signature" => signature_points}} ->-        public_key = %EllipticCurve.PublicKey.Data{-          curve: %EllipticCurve.Curve.Data{-            A: 0,-            B: 7,-            G: %EllipticCurve.Utils.Point{-              x:-                55_066_263_022_277_343_669_578_718_895_168_534_326_250_603_453_777_594_175_500_187_360_389_116_729_240,-              y:-                32_670_510_020_758_816_978_083_085_130_507_043_184_471_273_380_659_243_275_938_904_335_757_337_482_424,-              z: 1-            },-            N:-              115_792_089_237_316_195_423_570_985_008_687_907_852_837_564_279_074_904_382_605_163_141_518_161_494_337,-            P:-              115_792_089_237_316_195_423_570_985_008_687_907_853_269_984_665_640_564_039_457_584_007_908_834_671_663,-            name: :secp256k1,-            oid: [1, 3, 132, 0, 10]-          },-          point: %EllipticCurve.Utils.Point{-            x: String.to_integer(pub_points["x"]),-            y: String.to_integer(pub_points["y"]),-            z: 1-          }-        }+    pub_key =+      account+      |> EosjsAuthWrapper.get_account_info()+      |> get_pub_key() -        signature_elixir = %EllipticCurve.Signature.Data{-          r: String.to_integer(signature_points["r"]),-          s: String.to_integer(signature_points["s"])-        }+    EosjsAuthWrapper.verify(signature, phrase, pub_key)+    |> case do+      {:ok, result} -> result+      {:error, _} -> false+    end+  end -        EllipticCurve.Ecdsa.verify?(phrase, signature_elixir, public_key) &&-          compare_public_keys(pub_points["string"], account)+  def sign(signature, priv_key) do+    EosjsAuthWrapper.sign(signature, priv_key)+  end -      {:error, _} ->-        false-    end+  def sign_with_random(phrase) do+    EosjsAuthWrapper.gen_rand_signature(phrase)   end -  @doc """-  Verify public key associated to the signature private key and the public key for the account-  """-  defp compare_public_keys(public_key_a, account) do-    {"app", :accountToPublicKey}-    |> NodeJS.call([account])+  defp get_pub_key(account) do

In my opinion private functions help hide the implementation details for the main functions of the module. In this case get_pub_key is a helper functions and it shouldn't be called outside off this module.

venomnert

comment created time in an hour

Pull request review commentcambiatus/backend

Session table

 defmodule Cambiatus.Auth.Ecdsa do   """    def verify_signature(account, signature, phrase) do-    {"app", :publicKeyPoints}-    |> NodeJS.call([signature, phrase])-    |> case do-      {:ok, %{"publicKey" => pub_points, "signature" => signature_points}} ->-        public_key = %EllipticCurve.PublicKey.Data{-          curve: %EllipticCurve.Curve.Data{-            A: 0,-            B: 7,-            G: %EllipticCurve.Utils.Point{-              x:-                55_066_263_022_277_343_669_578_718_895_168_534_326_250_603_453_777_594_175_500_187_360_389_116_729_240,-              y:-                32_670_510_020_758_816_978_083_085_130_507_043_184_471_273_380_659_243_275_938_904_335_757_337_482_424,-              z: 1-            },-            N:-              115_792_089_237_316_195_423_570_985_008_687_907_852_837_564_279_074_904_382_605_163_141_518_161_494_337,-            P:-              115_792_089_237_316_195_423_570_985_008_687_907_853_269_984_665_640_564_039_457_584_007_908_834_671_663,-            name: :secp256k1,-            oid: [1, 3, 132, 0, 10]-          },-          point: %EllipticCurve.Utils.Point{-            x: String.to_integer(pub_points["x"]),-            y: String.to_integer(pub_points["y"]),-            z: 1-          }-        }+    pub_key =+      account+      |> EosjsAuthWrapper.get_account_info()+      |> get_pub_key() -        signature_elixir = %EllipticCurve.Signature.Data{-          r: String.to_integer(signature_points["r"]),-          s: String.to_integer(signature_points["s"])-        }+    EosjsAuthWrapper.verify(signature, phrase, pub_key)+    |> case do+      {:ok, result} -> result+      {:error, _} -> false+    end+  end -        EllipticCurve.Ecdsa.verify?(phrase, signature_elixir, public_key) &&-          compare_public_keys(pub_points["string"], account)+  def sign(signature, priv_key) do+    EosjsAuthWrapper.sign(signature, priv_key)+  end -      {:error, _} ->-        false-    end+  def sign_with_random(phrase) do+    EosjsAuthWrapper.gen_rand_signature(phrase)

Agreed, I made the necessary update.

venomnert

comment created time in 2 hours

Pull request review commentcambiatus/backend

Session table

 defmodule Cambiatus.Auth.Ecdsa do   """    def verify_signature(account, signature, phrase) do-    {"app", :publicKeyPoints}-    |> NodeJS.call([signature, phrase])-    |> case do-      {:ok, %{"publicKey" => pub_points, "signature" => signature_points}} ->-        public_key = %EllipticCurve.PublicKey.Data{-          curve: %EllipticCurve.Curve.Data{-            A: 0,-            B: 7,-            G: %EllipticCurve.Utils.Point{-              x:-                55_066_263_022_277_343_669_578_718_895_168_534_326_250_603_453_777_594_175_500_187_360_389_116_729_240,-              y:-                32_670_510_020_758_816_978_083_085_130_507_043_184_471_273_380_659_243_275_938_904_335_757_337_482_424,-              z: 1-            },-            N:-              115_792_089_237_316_195_423_570_985_008_687_907_852_837_564_279_074_904_382_605_163_141_518_161_494_337,-            P:-              115_792_089_237_316_195_423_570_985_008_687_907_853_269_984_665_640_564_039_457_584_007_908_834_671_663,-            name: :secp256k1,-            oid: [1, 3, 132, 0, 10]-          },-          point: %EllipticCurve.Utils.Point{-            x: String.to_integer(pub_points["x"]),-            y: String.to_integer(pub_points["y"]),-            z: 1-          }-        }+    pub_key =+      account+      |> EosjsAuthWrapper.get_account_info()+      |> get_pub_key() -        signature_elixir = %EllipticCurve.Signature.Data{-          r: String.to_integer(signature_points["r"]),-          s: String.to_integer(signature_points["s"])-        }+    EosjsAuthWrapper.verify(signature, phrase, pub_key)+    |> case do+      {:ok, result} -> result+      {:error, _} -> false+    end+  end -        EllipticCurve.Ecdsa.verify?(phrase, signature_elixir, public_key) &&-          compare_public_keys(pub_points["string"], account)+  def sign(signature, priv_key) do+    EosjsAuthWrapper.sign(signature, priv_key)+  end

I have made the update

venomnert

comment created time in 2 hours

Pull request review commentcambiatus/frontend

Feature/support subdomains

 encodeCreateCommunityData c =         , ( "description", Encode.string c.description )         , ( "inviter_reward", Eos.encodeAsset c.inviterReward )         , ( "invited_reward", Eos.encodeAsset c.invitedReward )+        , ( "min_balance", Eos.encodeAsset c.minBalance )         , ( "has_objectives", Eos.encodeEosBool c.hasObjectives )         , ( "has_shop", Eos.encodeEosBool c.hasShop )         , ( "has_kyc", Eos.encodeEosBool c.hasKyc )         ]  +createCommunityDataDecoder : Decoder CreateCommunityData+createCommunityDataDecoder =+    Decode.succeed CreateCommunityData+        |> required "cmm_asset" Eos.decodeAsset+        |> required "creator" Eos.nameDecoder+        |> required "logo" Decode.string+        |> required "name" Decode.string+        |> required "description" Decode.string+        |> required "inviter_reward" Eos.decodeAsset+        |> required "invited_reward" Eos.decodeAsset+        |> required "min_balance" Eos.decodeAsset+        |> required "has_objectives" Eos.eosBoolDecoder+        |> required "has_shop" Eos.eosBoolDecoder+        |> required "has_kyc" Eos.eosBoolDecoder++

The old flow for creating a community was like this:

  1. User sees empty form
  2. User fills in form
  3. User clicks "Save"
  4. If the symbol field is actually a valid symbol, we subscribe to GraphQL's newCommunity (otherwise, literally do nothing)
  5. We wait until the subscription port sends us something back.
  6. a. If it sends "responded", just redirect to the community page (and the flow is over) b. If it sends "starting", we ask the user for authentication
  7. Only now we validate the form
  8. a. If the form is not valid, we show the errors to the user (and the flow starts from 2.) b. If the form is valid, we send the Eos transaction
  9. a. If the Eos response was valid, redirect to community (we should remove this, so we only redirect when the subscription tells us to) b. Otherwise, show errors and start from 2.

The new flow is like this:

  1. User sees empty form
  2. User fills in form
  3. User clicks "Save"
  4. a. If the form is not valid, we show errors (and the flow starts from 2.) b. If the form is valid, we ask the user for authentication
  5. We subscribe to newCommunity
  6. We wait until the subscription port sends us something back
  7. a. If it sends "responded", redirect to the community (and the flow is over) b. If it sends "starting" we send the Eos transaction
  8. a. If the Eos response was valid, redirect to community (we should remove this, so we only redirect when the subscription tells us to) b. Otherwise, show errors and start from 2.

So the old flow validates the model very late (after subscribing to newCommunity and asking the user for authentication - IMO it should be before those), while the new one validates it as soon as the user clicks "Save". Because of that, we have the validated information on the SubmittedForm msg, not only just before sending the Eos transaction. So we didn't need the decoder before because we only validated the data when sending the Eos transaction - which means it would take longer to show errors to the user.

The result ends up being the same, but the process is a little different, and better for the user. By the way, minBalance already was in CreateCommunityData, it just wasn't included in the encoder. I'm still convinced minBalance should be in CreateCommunityData - that's an abstraction we make throughout the app (we don't have a Community and a Token, the token information is already included in Community). Having min_balance in the encoder doesn't seem to break anything, but we could make it so the encoder actually returns two encoders (in a tuple), one for the community and one for the token - I think this makes sense, because when we're creating a new community, we're also creating a new token anyway

NeoVier

comment created time in 5 hours

startedOneGraph/graphiql-explorer

started time in 6 hours

Pull request review commentcambiatus/frontend

Feature/support subdomains

+module Page.Community.Settings.Currency exposing+    ( Model+    , Msg+    , init+    , jsAddressToMsg+    , msgToString+    , receiveBroadcast+    , update+    , view+    )++import Community+import Eos+import Eos.Account as Eos+import Html exposing (Html, button, div, form, span, text)+import Html.Attributes exposing (class, disabled)+import Html.Events exposing (onSubmit)+import Json.Decode as Decode+import Json.Encode as Encode+import Page+import Ports+import RemoteData+import Route+import Session.LoggedIn as LoggedIn+import Session.Shared exposing (Translators)+import UpdateResult as UR+import View.Feedback as Feedback+import View.Form.Input as Input++++-- MODEL+++type alias Model =+    { inviterReward : String+    , invitedReward : String+    , minimumBalance : String+    , isLoading : Bool+    , errors : List ( Field, String )+    }+++type alias ValidModel =+    { inviterReward : Float+    , invitedReward : Float+    , minimumBalance : Float+    }+++init : LoggedIn.Model -> ( Model, Cmd Msg )+init loggedIn =+    ( { inviterReward = ""+      , invitedReward = ""+      , minimumBalance = ""+      , isLoading = True+      , errors = []+      }+    , LoggedIn.maybeInitWith CompletedLoadCommunity .selectedCommunity loggedIn+    )++++-- UPDATE+++type Msg+    = Ignored+    | EnteredInviterReward String+    | EnteredInvitedReward String+    | EnteredMinimumBalance String+    | ClickedSubmit+    | GotSubmitResponse (Result Encode.Value ValidModel)+    | CompletedLoadCommunity Community.Model+++type alias UpdateResult =+    UR.UpdateResult Model Msg (LoggedIn.External Msg)+++update : Msg -> Model -> LoggedIn.Model -> UpdateResult+update msg model ({ shared } as loggedIn) =+    case msg of+        Ignored ->+            UR.init model++        EnteredInviterReward inviterReward ->+            { model | inviterReward = inviterReward }+                |> withSymbolValidation validateInviterReward InviterReward loggedIn+                |> UR.init++        EnteredInvitedReward invitedReward ->+            { model | invitedReward = invitedReward }+                |> withSymbolValidation validateInvitedReward InvitedReward loggedIn+                |> UR.init++        EnteredMinimumBalance minimumBalance ->+            { model | minimumBalance = minimumBalance }+                |> withSymbolValidation validateMinimumBalance MinimumBalance loggedIn+                |> UR.init++        ClickedSubmit ->+            case loggedIn.selectedCommunity of+                RemoteData.Success community ->+                    case validateModel community.symbol model of+                        Ok validatedModel ->+                            if LoggedIn.hasPrivateKey loggedIn then+                                { model | isLoading = True }+                                    |> UR.init+                                    |> UR.addPort (savePort validatedModel loggedIn community)++                            else+                                UR.init model+                                    |> UR.addExt+                                        (Just ClickedSubmit+                                            |> LoggedIn.RequiredAuthentication+                                        )++                        Err withError ->+                            UR.init withError++                _ ->+                    UR.init model+                        |> UR.logImpossible msg [ "CommunityNotLoaded" ]++        GotSubmitResponse (Ok validModel) ->+            { model | isLoading = False }+                |> UR.init+                |> (case loggedIn.selectedCommunity of+                        RemoteData.Success community ->+                            UR.addExt+                                ({ community+                                    | inviterReward = validModel.inviterReward+                                    , invitedReward = validModel.invitedReward+                                    , minBalance = Just validModel.minimumBalance+                                 }+                                    |> LoggedIn.CommunityLoaded+                                    |> LoggedIn.ExternalBroadcast+                                )+                                >> UR.addExt+                                    (LoggedIn.ShowFeedback Feedback.Success+                                        (shared.translators.t "community.create.success")+                                    )+                                >> UR.addCmd (Route.pushUrl shared.navKey Route.CommunitySettings)++                        _ ->+                            UR.logImpossible msg [ "WithoutCommunity" ]+                   )++        GotSubmitResponse (Err val) ->+            { model | isLoading = False }+                |> UR.init+                |> UR.addExt (LoggedIn.ShowFeedback Feedback.Failure (shared.translators.t "community.error_saving"))+                |> UR.logDebugValue msg val++        CompletedLoadCommunity community ->+            { model+                | inviterReward = String.fromFloat community.inviterReward+                , invitedReward = String.fromFloat community.invitedReward+                , minimumBalance =+                    Maybe.map String.fromFloat community.minBalance+                        |> Maybe.withDefault "0"+                , isLoading = False+            }+                |> UR.init+++withSymbolValidation : (Eos.Symbol -> Model -> Result String a) -> Field -> LoggedIn.Model -> Model -> Model+withSymbolValidation fn field loggedIn_ model_ =+    case loggedIn_.selectedCommunity of+        RemoteData.Success community ->+            fn community.symbol model_+                |> (\result -> setErrors field result model_)++        _ ->+            model_+++savePort : ValidModel -> LoggedIn.Model -> Community.Model -> Ports.JavascriptOutModel Msg+savePort validModel loggedIn community =+    let+        authorization =+            { actor = loggedIn.accountName+            , permissionName = Eos.samplePermission+            }++        asset amount =+            { amount = amount+            , symbol = community.symbol+            }+    in+    { responseAddress = ClickedSubmit+    , responseData = encodeValidModel validModel+    , data =+        Eos.encodeTransaction+            [ { accountName = loggedIn.shared.contracts.community+              , name = "update"+              , authorization = authorization+              , data =+                    { asset = asset 0+                    , logo = community.logo+                    , name = community.name+                    , description = community.description+                    , inviterReward = asset validModel.inviterReward+                    , invitedReward = asset validModel.invitedReward+                    , hasObjectives = Eos.boolToEosBool community.hasObjectives+                    , hasShop = Eos.boolToEosBool community.hasShop+                    }+                        |> Community.encodeUpdateData+              }+            , { accountName = loggedIn.shared.contracts.token+              , name = "update"+              , authorization = authorization+              , data =+                    { maxSupply = asset 21000000.0+                    , minBalance = asset validModel.minimumBalance+                    }+                        |> Community.encodeUpdateTokenData+              }

What about updating inviterReward and invitedReward though?

NeoVier

comment created time in 6 hours

pull request commentcambiatus/frontend

Feature/support subdomains

looks that for some browsers it will not work at all. Do you think its possible to have a flag to disable it somehow? I mean, I can use chrome, but without some mechanism to disable it, how can we test stuff that is safari only? Somehow safari is the new IE :facepalm:

I guess we could have a flag passed in on yarn start (or an env variable?). We'd need to store that in Shared, and then instead of redirecting when the user selects a community, we just load the correct one.

Also, I've noticed that the redirect is happening using the community name, and it fails on certain communities that have spaces or special characters on them like ' or 水. Are you needing some type of modification on the backend right?

Yep, right now I'm just using the community's name, I'm just waiting until subdomain is a thing in the backend

NeoVier

comment created time in 7 hours

Pull request review commentcambiatus/frontend

Feature/support subdomains

 encodeCreateCommunityData c =         , ( "description", Encode.string c.description )         , ( "inviter_reward", Eos.encodeAsset c.inviterReward )         , ( "invited_reward", Eos.encodeAsset c.invitedReward )+        , ( "min_balance", Eos.encodeAsset c.minBalance )         , ( "has_objectives", Eos.encodeEosBool c.hasObjectives )         , ( "has_shop", Eos.encodeEosBool c.hasShop )         , ( "has_kyc", Eos.encodeEosBool c.hasKyc )         ]  +createCommunityDataDecoder : Decoder CreateCommunityData+createCommunityDataDecoder =+    Decode.succeed CreateCommunityData+        |> required "cmm_asset" Eos.decodeAsset+        |> required "creator" Eos.nameDecoder+        |> required "logo" Decode.string+        |> required "name" Decode.string+        |> required "description" Decode.string+        |> required "inviter_reward" Eos.decodeAsset+        |> required "invited_reward" Eos.decodeAsset+        |> required "min_balance" Eos.decodeAsset+        |> required "has_objectives" Eos.eosBoolDecoder+        |> required "has_shop" Eos.eosBoolDecoder+        |> required "has_kyc" Eos.eosBoolDecoder++

To create a new community, we use the newCommunity GraphQL subscription. So, if you look at Community.Editor.Msg, we fire a Msg when the user has SubmittedForm, which parses the model into a CreateCommunityData and, if it's valid, subscribes to the GraphQL newCommunity subscription, which then fires the StartedCreatingCommunity Msg, which takes that CreateCommunityData. To pass in CreateCommunityData to StartedCreatingCommunity, I'm using the port's responseData field, so we need to encode the CreateCommunityData when adding the port, and decode it on jsAddressToMsg to pass it to StartedCreatingCommunity.

I see two approaches we can take on this scenario:

  • Do as I described above. This is nice because we don't have the impossible state where the user has submitted the form, and we started creating the community, but the form data isn't valid (e.g. invitedReward isn't a valid Float). The bad thing is we need to encode and then decode the data (which is why we need min_balance when encoding, and why we need this decoder).
  • Use the Model data. This is nice because we don't have to encode and then decode CreateCommunityData, but we'd have to parse the form data again, which leads to impossible states.

I still prefer passing data through responseData in the port, so we could pass in an object like so

{
  createCommunityData : { /* Regular CreateCommunityData, as it was before this PR, without `min_balance` */ },
  minBalance: 0
}

That would mean we can keep the encoder without min_balance, and avoid those impossible states

NeoVier

comment created time in 7 hours

issue commentcambiatus/backend

Improve Authentication

As discussed on the call I will address in my second PR to improve-auth. Which will solve the above case and allow a single user to create sessions across multiple devices.

lucca65

comment created time in 8 hours

Pull request review commentcambiatus/backend

Session table

 defmodule Cambiatus.Commune do     |> join(:left, [c, a], v in Validator, on: v.action_id == c.action_id)     |> where(       [c, a, o, v],-      o.community_id == ^community_id and v.validator_id == ^account and a.is_completed == false and+      o.community_id == ^community_id and+        v.validator_id == ^account and+        a.is_completed == false and+        not (a.usages > 0 and a.usages_left == 0) and+        (is_nil(a.deadline) or fragment("NOW()") < a.deadline) and

As discussed on our call this branch will update the main feature branch improve-auth.

venomnert

comment created time in 8 hours

Pull request review commentcambiatus/backend

Session table

 defmodule Cambiatus.Auth.Ecdsa do   """    def verify_signature(account, signature, phrase) do-    {"app", :publicKeyPoints}-    |> NodeJS.call([signature, phrase])-    |> case do-      {:ok, %{"publicKey" => pub_points, "signature" => signature_points}} ->-        public_key = %EllipticCurve.PublicKey.Data{-          curve: %EllipticCurve.Curve.Data{-            A: 0,-            B: 7,-            G: %EllipticCurve.Utils.Point{-              x:-                55_066_263_022_277_343_669_578_718_895_168_534_326_250_603_453_777_594_175_500_187_360_389_116_729_240,-              y:-                32_670_510_020_758_816_978_083_085_130_507_043_184_471_273_380_659_243_275_938_904_335_757_337_482_424,-              z: 1-            },-            N:-              115_792_089_237_316_195_423_570_985_008_687_907_852_837_564_279_074_904_382_605_163_141_518_161_494_337,-            P:-              115_792_089_237_316_195_423_570_985_008_687_907_853_269_984_665_640_564_039_457_584_007_908_834_671_663,-            name: :secp256k1,-            oid: [1, 3, 132, 0, 10]-          },-          point: %EllipticCurve.Utils.Point{-            x: String.to_integer(pub_points["x"]),-            y: String.to_integer(pub_points["y"]),-            z: 1-          }-        }+    pub_key =+      account+      |> EosjsAuthWrapper.get_account_info()+      |> get_pub_key() -        signature_elixir = %EllipticCurve.Signature.Data{-          r: String.to_integer(signature_points["r"]),-          s: String.to_integer(signature_points["s"])-        }+    EosjsAuthWrapper.verify(signature, phrase, pub_key)+    |> case do+      {:ok, result} -> result+      {:error, _} -> false+    end+  end -        EllipticCurve.Ecdsa.verify?(phrase, signature_elixir, public_key) &&-          compare_public_keys(pub_points["string"], account)+  def sign(signature, priv_key) do+    EosjsAuthWrapper.sign(signature, priv_key)+  end -      {:error, _} ->-        false-    end+  def sign_with_random(phrase) do+    EosjsAuthWrapper.gen_rand_signature(phrase)   end -  @doc """-  Verify public key associated to the signature private key and the public key for the account-  """-  defp compare_public_keys(public_key_a, account) do-    {"app", :accountToPublicKey}-    |> NodeJS.call([account])+  defp get_pub_key(account) do

not sure about this function returning nil bro, do you think we can improve it somehow?

I have made the fix for this

the main reason why I don't like to use private functions is that you loose stack trace if you run into an error on them

I agree but the question I have is, how do we utilize helper functions in modules without polluting the api?

venomnert

comment created time in 8 hours

Pull request review commentcambiatus/backend

Session table

 defmodule Cambiatus.Auth.Ecdsa do   """    def verify_signature(account, signature, phrase) do-    {"app", :publicKeyPoints}-    |> NodeJS.call([signature, phrase])-    |> case do-      {:ok, %{"publicKey" => pub_points, "signature" => signature_points}} ->-        public_key = %EllipticCurve.PublicKey.Data{-          curve: %EllipticCurve.Curve.Data{-            A: 0,-            B: 7,-            G: %EllipticCurve.Utils.Point{-              x:-                55_066_263_022_277_343_669_578_718_895_168_534_326_250_603_453_777_594_175_500_187_360_389_116_729_240,-              y:-                32_670_510_020_758_816_978_083_085_130_507_043_184_471_273_380_659_243_275_938_904_335_757_337_482_424,-              z: 1-            },-            N:-              115_792_089_237_316_195_423_570_985_008_687_907_852_837_564_279_074_904_382_605_163_141_518_161_494_337,-            P:-              115_792_089_237_316_195_423_570_985_008_687_907_853_269_984_665_640_564_039_457_584_007_908_834_671_663,-            name: :secp256k1,-            oid: [1, 3, 132, 0, 10]-          },-          point: %EllipticCurve.Utils.Point{-            x: String.to_integer(pub_points["x"]),-            y: String.to_integer(pub_points["y"]),-            z: 1-          }-        }+    pub_key =+      account+      |> EosjsAuthWrapper.get_account_info()+      |> get_pub_key() -        signature_elixir = %EllipticCurve.Signature.Data{-          r: String.to_integer(signature_points["r"]),-          s: String.to_integer(signature_points["s"])-        }+    EosjsAuthWrapper.verify(signature, phrase, pub_key)+    |> case do+      {:ok, result} -> result+      {:error, _} -> false+    end+  end -        EllipticCurve.Ecdsa.verify?(phrase, signature_elixir, public_key) &&-          compare_public_keys(pub_points["string"], account)+  def sign(signature, priv_key) do+    EosjsAuthWrapper.sign(signature, priv_key)+  end -      {:error, _} ->-        false-    end+  def sign_with_random(phrase) do+    EosjsAuthWrapper.gen_rand_signature(phrase)

same here, if the objetive is to have less lengthy words you can use aliases Fixed this by utilizing alias

also can you send a little explanation on the motivation behind random? This function is utilized to test invalid signatures

venomnert

comment created time in 8 hours

Pull request review commentcambiatus/backend

Session table

 defmodule Cambiatus.Auth.Ecdsa do   """    def verify_signature(account, signature, phrase) do-    {"app", :publicKeyPoints}-    |> NodeJS.call([signature, phrase])-    |> case do-      {:ok, %{"publicKey" => pub_points, "signature" => signature_points}} ->-        public_key = %EllipticCurve.PublicKey.Data{-          curve: %EllipticCurve.Curve.Data{-            A: 0,-            B: 7,-            G: %EllipticCurve.Utils.Point{-              x:-                55_066_263_022_277_343_669_578_718_895_168_534_326_250_603_453_777_594_175_500_187_360_389_116_729_240,-              y:-                32_670_510_020_758_816_978_083_085_130_507_043_184_471_273_380_659_243_275_938_904_335_757_337_482_424,-              z: 1-            },-            N:-              115_792_089_237_316_195_423_570_985_008_687_907_852_837_564_279_074_904_382_605_163_141_518_161_494_337,-            P:-              115_792_089_237_316_195_423_570_985_008_687_907_853_269_984_665_640_564_039_457_584_007_908_834_671_663,-            name: :secp256k1,-            oid: [1, 3, 132, 0, 10]-          },-          point: %EllipticCurve.Utils.Point{-            x: String.to_integer(pub_points["x"]),-            y: String.to_integer(pub_points["y"]),-            z: 1-          }-        }+    pub_key =+      account+      |> EosjsAuthWrapper.get_account_info()+      |> get_pub_key() -        signature_elixir = %EllipticCurve.Signature.Data{-          r: String.to_integer(signature_points["r"]),-          s: String.to_integer(signature_points["s"])-        }+    EosjsAuthWrapper.verify(signature, phrase, pub_key)+    |> case do+      {:ok, result} -> result+      {:error, _} -> false+    end+  end -        EllipticCurve.Ecdsa.verify?(phrase, signature_elixir, public_key) &&-          compare_public_keys(pub_points["string"], account)+  def sign(signature, priv_key) do+    EosjsAuthWrapper.sign(signature, priv_key)+  end

This used to for testing the auth process

venomnert

comment created time in 8 hours

Pull request review commentcambiatus/backend

Session table

 defmodule Cambiatus.Auth.Ecdsa do   """    def verify_signature(account, signature, phrase) do-    {"app", :publicKeyPoints}-    |> NodeJS.call([signature, phrase])-    |> case do-      {:ok, %{"publicKey" => pub_points, "signature" => signature_points}} ->-        public_key = %EllipticCurve.PublicKey.Data{-          curve: %EllipticCurve.Curve.Data{-            A: 0,-            B: 7,-            G: %EllipticCurve.Utils.Point{-              x:-                55_066_263_022_277_343_669_578_718_895_168_534_326_250_603_453_777_594_175_500_187_360_389_116_729_240,-              y:-                32_670_510_020_758_816_978_083_085_130_507_043_184_471_273_380_659_243_275_938_904_335_757_337_482_424,-              z: 1-            },-            N:-              115_792_089_237_316_195_423_570_985_008_687_907_852_837_564_279_074_904_382_605_163_141_518_161_494_337,-            P:-              115_792_089_237_316_195_423_570_985_008_687_907_853_269_984_665_640_564_039_457_584_007_908_834_671_663,-            name: :secp256k1,-            oid: [1, 3, 132, 0, 10]-          },-          point: %EllipticCurve.Utils.Point{-            x: String.to_integer(pub_points["x"]),-            y: String.to_integer(pub_points["y"]),-            z: 1-          }-        }+    pub_key =+      account+      |> EosjsAuthWrapper.get_account_info()+      |> get_pub_key() -        signature_elixir = %EllipticCurve.Signature.Data{-          r: String.to_integer(signature_points["r"]),-          s: String.to_integer(signature_points["s"])-        }+    EosjsAuthWrapper.verify(signature, phrase, pub_key)

I have made the update

venomnert

comment created time in 8 hours

Pull request review commentcambiatus/backend

Session table

 defmodule CambiatusWeb.Resolvers.Commune do         context: %{current_user: current_user}       }) do     query = Commune.claim_analysis_query(community_id, current_user.account)++    query =+      if Map.has_key?(args, :filter) do+        args+        |> Map.get(:filter)+        |> Enum.reduce(query, fn+          {:direction, direction}, query ->+            Claim.ordered(query, direction)+        end)+      else+        query+        |> Claim.ordered(:desc)+      end

As discussed on our call this branch will update the main feature branch improve-auth.

venomnert

comment created time in 8 hours

Pull request review commentcambiatus/backend

Session table

 defmodule Cambiatus.Auth do     |> netlink(invitation_id)   end -  def sign_in_v2(account, signature, phrase) do-    account-    |> Ecdsa.verify_signature(signature, phrase)+  def gen_auth_phrase(user) do+    user+    |> create_phrase()+    |> create_session()+  end++  def create_phrase(user) do+    phrase = Phrase.generate()+    token = AuthToken.gen_token(%{phrase: phrase})++    %UserToken{}+    |> UserToken.changeset(%{phrase: phrase, token: token, context: "auth"}, user)+    |> Repo.insert()     |> case do-      true ->-        account-        |> Accounts.get_user()-        |> case do-          nil ->-            {:error, :not_found}+      {:ok, _} -> {:ok, {user, phrase}}+      {:error, _err} -> {:error, "Phrase exists already"}+    end+  end -          user ->-            {:ok, user}-        end+  def create_session({:error, _} = err), do: err++  def create_session({:ok, {user, phrase}}) do+    token = AuthToken.gen_token(%{account: user.account})++    %UserToken{}+    |> UserToken.changeset(%{token: token, context: "session"}, user)+    |> Repo.insert()+    |> case do+      {:ok, _} -> {:ok, {phrase, token}}+      {:error, _err} -> {:error, "Session exists already"}+    end+  end++  def verify(token) do+    token+    |> verify_session_token()+    |> get_account_from_token()+  end++  def verify_session_token(token) do+    get_user_token(%{token: token, filter: :session})+    |> case do+      user_token when is_map(user_token) ->+        {AuthToken.verify(user_token.token, 2 * 24 * 3600), user_token}++      nil ->+        {:error, "Session not found"}+    end+  end++  def get_account_from_token({{:ok, _}, user_token}) do+    Cambiatus.Accounts.get_user(user_token.user_id)+  end++  def get_account_from_token({{:error, _}, _}) do+    {:error, "Invalid session"}+  end++  def verify_session_token(account, token) do+    %{account: account, filter: :session}+    |> get_user_token()+    |> Map.values()+    |> Enum.member?(token)+    |> case do+      true ->+        AuthToken.verify(token, 2 * 24 * 3600)        false ->-        {:error, "Invalid signature"}+        delete_user_token(%{account: account, filter: :auth})+        {:error, "No session found"}     end   end +  def verify_signature(account, signature) do+    result =+      case get_user_token(%{account: account, filter: :auth}) do+        %{phrase: phrase} ->+          if Ecdsa.verify_signature(account, signature, phrase) do+            {:ok, Accounts.get_user(account)}+          else+            {:error, "Invalid signature"}+          end++        nil ->+          {:error, "No phrase found"}+      end++    delete_user_token(%{account: account, filter: :auth})+    result+  end++  def get_user_token(%{account: account, filter: :session}) do+    from("user_tokens",+      where: [context: "session", user_id: ^account],+      select: [:context, :user_id, :token]

I have extracted this into it's own module Auth.Session

venomnert

comment created time in 9 hours

pull request commentcambiatus/frontend

Feature/support subdomains

Is there anything I need to change on my hosts configuration or something similar? the app gets redirected to app.localhost:3000 and my browser is unable to load anything

It works out of the box for me. It might be something related to the browser you're using (it works for me on Firefox and Chromium), but you can try editing your hosts file. What kind of errors are you getting? Is it a browser thing or an app thing? If it's from the app, is there something on the console?

The only thing I haven't figured out is accessing the app from another device (I used to do this with the IP address yarn start gives you) - and I guess it's at least not straightforward

NeoVier

comment created time in 9 hours

push eventcambiatus/frontend

Henrique Buss

commit sha 6a11d6ca7b3ac720b06794d4e3217d059b04b7cb

Fix disabled logic on image upload

view details

push time in 10 hours

issue commentcambiatus/backend

Improve Authentication

@lucca65 I need your advise with this edge case here, when the client doesn't save their session token after sign in.

  • This prevents the client from creating a new phrase because the session stills exists from sign in.
  • And they can't sign unless they have their session token

One solutions is to allow phrase creation even if a session already exists.

image

lucca65

comment created time in 12 hours

PR opened cambiatus/backend

Reviewers
Feature/improve auth

What issue does this PR close

Updated main feature branch with master

Changes Proposed ( a list of new changes introduced by this PR)

How to test ( a list of instructions on how to test this PR)

+26 -2

0 comment

3 changed files

pr created time in 13 hours

Pull request review commentcambiatus/backend

Session table

+defmodule Cambiatus.Auth.UserToken do+  @moduledoc false++  use Ecto.Schema+  import Ecto.Changeset++  alias Cambiatus.Accounts.User++  schema "user_tokens" do+    field(:phrase, :string)+    field(:token, :binary)+    field(:context, :string)+    belongs_to(:user, User, references: :account, type: :string)++    timestamps(updated_at: false)+  end++  @required_fields ~w(token context)a+  @optional_fields ~w(phrase)a

Made the necessary format

venomnert

comment created time in 13 hours

Pull request review commentcambiatus/backend

Session table

 defmodule CambiatusWeb.Resolvers.Accounts do   end    def sign_in(_, %{account: account, signature: signature}, %{context: context}) do-    %{phrase: phrase, token: token} = context+    %{token: token} = context -    case Auth.sign_in_v2(account, signature, phrase) do-      {:error, reason} ->-        CambiatusWeb.AuthToken.invalidate(token)-        {:error, message: "Sign In failed", details: Cambiatus.Error.from(reason)}--      {:ok, user} ->-        {:ok, %{user: user, token: CambiatusWeb.AuthToken.sign(user)}}+    with {:ok, _token_data} <- Auth.verify_session_token(account, token),+         {:ok, user} <- Auth.verify_signature(account, signature) do+      {:ok, %{user: user, token: token}}+    else+      error -> error

Made the update

venomnert

comment created time in 13 hours

startedhail2u/html-best-practices

started time in 21 hours

starteddmjio/miso

started time in a day

starteddvf/blockchain

started time in 2 days

startedamericanexpress/react-albus

started time in 2 days

startedgcanti/fp-ts

started time in 2 days

startedrevelcw/react-hooks-helper

started time in 2 days

pull request commentcambiatus/frontend

Feature/support subdomains

@lucca65 as this is a pretty big pull request, and to have better organization, I've created a feature branch for this, and I'll develop the other features needed for this in other PRs, let me know what you think

NeoVier

comment created time in 2 days

create barnchcambiatus/frontend

branch : feature/support-subdomains-umbrella

created branch time in 2 days