From 692a42bdf80d9717bd7ba0e22607ff760357ed01 Mon Sep 17 00:00:00 2001 From: Bram van den Heuvel Date: Sun, 5 Mar 2023 22:43:01 +0100 Subject: [PATCH] Add automated login --- src/Internal/Api/PreApi/Main.elm | 33 ++++- src/Internal/Api/PreApi/Objects/Login.elm | 135 +++++++++++++++++++++ src/Internal/Api/PreApi/Objects/Login.yaml | 45 +++++++ src/Internal/Credentials.elm | 2 +- 4 files changed, 210 insertions(+), 5 deletions(-) create mode 100644 src/Internal/Api/PreApi/Objects/Login.elm create mode 100644 src/Internal/Api/PreApi/Objects/Login.yaml diff --git a/src/Internal/Api/PreApi/Main.elm b/src/Internal/Api/PreApi/Main.elm index 837abfa..618bc9d 100644 --- a/src/Internal/Api/PreApi/Main.elm +++ b/src/Internal/Api/PreApi/Main.elm @@ -8,11 +8,13 @@ that the credentials type needs to know about before it can make a request. -} +import Internal.Api.PreApi.Objects.Login as L import Internal.Api.PreApi.Objects.Versions as V import Internal.Api.Request as R import Internal.Tools.Exceptions as X import Internal.Tools.LoginValues exposing (AccessToken(..)) import Internal.Tools.ValueGetter exposing (ValueGetter) +import Json.Encode as E import Task import Time @@ -30,10 +32,33 @@ accessToken baseUrl t = UsernameAndPassword { token } -> token , getValue = - "Automated login yet needs to be implemented." - |> X.NotSupportedYet - |> X.SDKException - |> Task.fail + case t of + UsernameAndPassword { username, password } -> + R.rawApiCall + { headers = R.NoHeaders + , method = "POST" + , baseUrl = baseUrl + , path = "/_matrix/client/v3/login" + , pathParams = [] + , queryParams = [] + , bodyParams = + [ [ ( "type", E.string "m.id.user" ) + , ( "user", E.string username ) + ] + |> E.object + |> R.RequiredValue "identifier" + , R.RequiredString "password" password + , R.RequiredString "type" "m.login.password" + ] + , timeout = Nothing + , decoder = \_ -> L.loggedInResponseDecoder + } + |> Task.map .accessToken + + _ -> + X.NoAccessToken + |> X.SDKException + |> Task.fail } diff --git a/src/Internal/Api/PreApi/Objects/Login.elm b/src/Internal/Api/PreApi/Objects/Login.elm new file mode 100644 index 0000000..4670aab --- /dev/null +++ b/src/Internal/Api/PreApi/Objects/Login.elm @@ -0,0 +1,135 @@ +module Internal.Api.PreApi.Objects.Login exposing + ( DiscoveryInformation + , HomeserverInformation + , IdentityServerInformation + , LoggedInResponse + , discoveryInformationDecoder + , encodeDiscoveryInformation + , encodeHomeserverInformation + , encodeIdentityServerInformation + , encodeLoggedInResponse + , homeserverInformationDecoder + , identityServerInformationDecoder + , loggedInResponseDecoder + ) + +{-| Automatically generated 'Login' + +Last generated at Unix time 1677859025 + +-} + +import Internal.Tools.DecodeExtra exposing (opField) +import Internal.Tools.EncodeExtra exposing (maybeObject) +import Json.Decode as D +import Json.Encode as E + + +{-| Information that overwrites the credential's base url and more. +-} +type alias DiscoveryInformation = + { mHomeserver : HomeserverInformation + , mIdentityServer : Maybe IdentityServerInformation + } + + +encodeDiscoveryInformation : DiscoveryInformation -> E.Value +encodeDiscoveryInformation data = + maybeObject + [ ( "m.homeserver", Just <| encodeHomeserverInformation data.mHomeserver ) + , ( "m.identity_server", Maybe.map encodeIdentityServerInformation data.mIdentityServer ) + ] + + +discoveryInformationDecoder : D.Decoder DiscoveryInformation +discoveryInformationDecoder = + D.map2 + (\a b -> + { mHomeserver = a, mIdentityServer = b } + ) + (D.field "m.homeserver" homeserverInformationDecoder) + (opField "m.identity_server" identityServerInformationDecoder) + + +{-| Used by clients to discover homeserver information. +-} +type alias HomeserverInformation = + { baseUrl : String + } + + +encodeHomeserverInformation : HomeserverInformation -> E.Value +encodeHomeserverInformation data = + maybeObject + [ ( "base_url", Just <| E.string data.baseUrl ) + ] + + +homeserverInformationDecoder : D.Decoder HomeserverInformation +homeserverInformationDecoder = + D.map + (\a -> + { baseUrl = a } + ) + (D.field "base_url" D.string) + + +{-| Used by clients to discover identity server information. +-} +type alias IdentityServerInformation = + { baseUrl : String + } + + +encodeIdentityServerInformation : IdentityServerInformation -> E.Value +encodeIdentityServerInformation data = + maybeObject + [ ( "base_url", Just <| E.string data.baseUrl ) + ] + + +identityServerInformationDecoder : D.Decoder IdentityServerInformation +identityServerInformationDecoder = + D.map + (\a -> + { baseUrl = a } + ) + (D.field "base_url" D.string) + + +{-| Confirmation that the user has successfully logged in. +-} +type alias LoggedInResponse = + { accessToken : String + , deviceId : String + , expiresInMs : Maybe Int + , refreshToken : Maybe String + , userId : String + , wellKnown : Maybe DiscoveryInformation + } + + +encodeLoggedInResponse : LoggedInResponse -> E.Value +encodeLoggedInResponse data = + maybeObject + [ ( "access_token", Just <| E.string data.accessToken ) + , ( "device_id", Just <| E.string data.deviceId ) + , ( "expires_in_ms", Maybe.map E.int data.expiresInMs ) + , ( "refresh_token", Maybe.map E.string data.refreshToken ) + , ( "user_id", Just <| E.string data.userId ) + , ( "well_known", Maybe.map encodeDiscoveryInformation data.wellKnown ) + ] + + +loggedInResponseDecoder : D.Decoder LoggedInResponse +loggedInResponseDecoder = + D.map6 + (\a b c d e f -> + { accessToken = a, deviceId = b, expiresInMs = c, refreshToken = d, userId = e, wellKnown = f } + ) + (D.field "access_token" D.string) + (D.field "device_id" D.string) + (opField "expires_in_ms" D.int) + (opField "refresh_token" D.string) + (D.field "user_id" D.string) + (opField "well_known" discoveryInformationDecoder) diff --git a/src/Internal/Api/PreApi/Objects/Login.yaml b/src/Internal/Api/PreApi/Objects/Login.yaml new file mode 100644 index 0000000..77711c8 --- /dev/null +++ b/src/Internal/Api/PreApi/Objects/Login.yaml @@ -0,0 +1,45 @@ +version: V_1 +name: Login +objects: + LoggedInResponse: + description: Confirmation that the user has successfully logged in. + fields: + access_token: + type: string + required: true + device_id: + type: string + required: true + expires_in_ms: + type: int + required: false + refresh_token: + type: string + required: false + user_id: + type: string + required: true + well_known: + type: DiscoveryInformation + required: false + DiscoveryInformation: + description: Information that overwrites the credential's base url and more. + fields: + m.homeserver: + type: HomeserverInformation + required: true + m.identity_server: + type: IdentityServerInformation + required: false + HomeserverInformation: + description: Used by clients to discover homeserver information. + fields: + base_url: + type: string + required: true + IdentityServerInformation: + description: Used by clients to discover identity server information. + fields: + base_url: + type: string + required: true diff --git a/src/Internal/Credentials.elm b/src/Internal/Credentials.elm index cb676f4..7c25ab0 100644 --- a/src/Internal/Credentials.elm +++ b/src/Internal/Credentials.elm @@ -161,7 +161,7 @@ updateWith credUpdate ((Credentials ({ cred, context } as data)) as credentials) -- Add new room Nothing -> jroom - |> Room.initFromJoinedRoom { nextBatch = output.nextBatch, roomId = roomId } + |> Room.initFromJoinedRoom { nextBatch = output.nextBatch, roomId = roomId } ) in cred