billstclair/elm-mastodon/6.0.2

NOTE: the latest version is 24.0.0

Mastodon.Login

Support for creating an App and logging in to a server.

= FetchAccount
(Task ( String, Error ) ( String, String, Account ))
| Redirect (Task ( String, Error ) ( String, App, Cmd msg ))

The result type of the loginTask function.

It's either a task to fetch the logged-in user's Mastodon.Entity.Account or a task to redirect to the authorization server to get a code.

The two Strings in FetchAccount are the server name and token. The String in Redirect is the server name.

{ client_name : String
, server : String
, applicationUri : String
}
-> Maybe String

Get a token and, if possible, the logged in Account from the server.

The Maybe String arg is a login token, called maybeToken below, if it is not Nothing.

Returns a Task to either fetch an Account for a known authorization token, or to redirect to the authentication server to create a code with which to mint the token.

If maybeToken is not Nothing, will attempt to get an Account using that token. If that fails, will return an error. If maybeToken is Nothing, will create a new App, and redirect to do authentication. When the application is restarted, with a code and state in the URL query, call getTokenTask to use those to mint a new token, and to use that to get an Account.

Usually, you will get a Token from persistent localStorage, pass that here, and successfully receive the Account back. No redirects necessary. But the redirects must happen at least once, and then whenever the authorization token expires. This is step 8 below. Only if that fails will this do step 3, or, if there was no persisted client id and secret, steps 1 through 3.

Currently, permission for all scopes is requested. Maybe the scopes should be passed as a parameter.

The full login procedure is as follows:

  1. Send "POST /api/v1/apps" to the Mastodon server, via a PostApp request.

  2. Receive the returned client_id and client_secret

  3. Redirect to <server_url>/oauth/authorize?client_id=<client_id>&redirect_uri=<client_uri>&response_type=code&scope=<scopes>&state=<state>

  4. The user enters login authorization information to the server web site.

  5. The server web site redirects to <redirect_uri>?code=<code>&state=<state>

  6. The <state> encodes the server uri, <client_id>, <client_secret>, and <scopes>. Use that to POST to <server_url>/oauth/token:

    POST /oauth/token HTTP/1.1
    Host: <server_url>
    Authorization: Basic `(Base64.encode (<client_id> ++ ":" ++ <client_secret>))`
    Content-Type: application/x-www-form-urlencoded
    
    
    
    
    grant_type=authorization_code&code=<code>&redirect_uri=<client_uri>
  7. Receive back token information, JSON-encoded:

    { "access_token":"cptLSO8ff7zKbBXlTTyH15bnxQS5b9erVUWi_n0_EGd",
      "token_type":"Bearer",
      "scope":"write,read,follow,push"
      "created_at":1561845912
    }
  8. Use the token_type and access_token to authenticate a request for the user's Account.

{ code : String
, state : String
}
-> Task ( String, Error ) ( String, Authorization, Account )

Continue after being restarted with a code and state in the URL query.

This continues from step 6 in the full login procedure description, which is included with the documentation for loginTask above.

Your application will usually persist the Authorization, so you can use it the next time the user starts the application, as a parameter to loginTask.

The String in the Task is the server name, e.g. "mastodon.social".

Internal functions.

encodeStateString : String -> App -> String

Convert a server domain and a Mastodon.Entity.App into a state string.

You will rarely, if ever, call this explicitly. It is exposed for debugging.

decodeStateString : String -> Result String ( String, App )

Decode the string created by encodeStateString.

You will rarely, if ever, call this explicitly. It is exposed for debugging.

appToAuthorizeUrl : String -> App -> String

Compute URL to redirect to for authentication.

Args are:

appToAuthorizeUrl server app

Returns:

https://<server>/oauth/authorize
   ?client_id=<app.client_id>
   &redirect_uri=<app.redirect_uri>
   &response_type=code
   &scope=<all scopes>
   &state=<encodeStateString server app>

You will call this only when a token expires, and you need to mint a new one from a previously created App:

appToAuthorizeUrl server app
    |> Browser.Navigation.load