packages feed

heddit-0.0.1: doc/auth.org

#+TITLE: Authenticating with OAuth

~heddit~ only supports authenticating via OAuth. You can use any of the applications types that can be registered on Reddit:

    - [[#script][Scripts]]
    - [[#web][Web applications]]
    - [[#installed][Installed applications]]
    - [[#app-only][Application-only]]

The first three application types take a user context. If you do not wish to have a user context, you can create an ~ApplicationOnly~ app. You must first register your application on Reddit before using ~heddit~, even for ~ApplicationOnly~ apps.

Although Reddit supports alternative OAuth flows for each application type, ~heddit~ currently only supports the default flows detailed below.

In brief, [[#script][scripts]] and [[#app-only][application-only]] apps can use ~loadClient~ and ~newClient~ to create a Reddit client. [[#web][web]] and [[#installed][installed]] apps can use ~newClient~ in which case OAuth refresh tokens will be unmanaged and will not persist after the end of your program, or ~newClientWithManager~, which will manage the tokens when provided with a ~TokenManager~.

~heddit~ will automatically refresh access tokens when they are close to expiration.

You must also provide a unique ~UserAgent~ when creating your ~Client~, as the code snippets below assume.

* Scripts
:PROPERTIES:
:CUSTOM_ID: script
:END:

*Script apps* are the simplest application to configure and use, but also require providing your Reddit username and password. Your credentials will be kept in memory in the ~Client~ that is created, so if this is not desirable, consider using a different application type. There is no callback process with script apps.

To create a script app, you need to provide your:
    - *client ID*
    - *client secret*
    - *username*
    - *password*

At the moment, ~heddit~ supports two ways of creating new ~ScriptApps~. The first is directly provide the relevant details in ~newClient~, as so:
#+begin_src haskell
main :: IO ()
main = do
     c <- newClient myAuthConfig
     ...
  where
    myAuthConfig = AuthConfig myClientID myScriptApp myUA
    myScriptApp = ScriptApp myClientSecret myPWFlow
    myPWFlow = PasswordFlow myUsername myPassword
#+end_src

The second way is to store your credentials and other details in an ~auth.ini~ file and use ~loadClient~. See the details of [[file:../src/Network/Reddit.hs::loadClient][loadClient]] for the location and format of these files.

* Web applications
:PROPERTIES:
:CUSTOM_ID: web
:END:

*Web apps* use a ~CodeFlow~ and require configuring an OAuth callback to generate the auth tokens. The steps outlined below largely apply to [[#installed][installed apps]] as well. This flow has several benefits:
    - you want to access the Reddit accounts of your users from a web or installed application
    - you are using a script and do not want to store your username and password in memory
    - you want to use delimited scopes with your script application (using a [[#script][script app]] with a ~PasswordFlow~ implicitly grants all scopes)

If you are not actually running a web service, you can still create a ~WebApp~ by registering the correct application on Reddit and running [[file:../examples/RefreshTokens.hs][the RefreshTokens example program]] to get the initial token.

The first step in completing a ~CodeFlow~ is to provide a valid redirect URI when creating your application. This will be used to generate a URL that your users can visit to authorize your application, resulting in a single-use code that will be used to create your ~Client~, e.g.:

#+begin_src haskell
getURL :: MonadIO m => URL -> ClientID -> m URL
getURL myRedirectURL myClientID = do
     -- The state should be unique to each user. You can confirm that it matches when you
     -- extract the params from Reddit's response
     state <- T.pack . show <$> randomRIO @Int (0, 65000)
     pure $ getAuthURL myRedirectURI Permanent [ Read, Save, Vote, Accounts ] clientID state
#+end_src

See [[file:../src/Network/Reddit.hs::getAuthURL][getAuthURL]] for details.

Once you have your ~CodeFlow~ using the code and your redirect URL, you can use this to create a ~Client~. If you use ~newClient~, ~heddit~ will fetch the initial refresh token and exchange it for an access token. The tokens will be stored in the ~Client~ that you create, but if you do not store them, your users will need to complete the authentication flow again.

#+begin_src haskell
myNewClient :: (MonadUnliftIO m, MonadCatch m) => Code -> m Client
myNewClient code = newClient authConfig
  where
    authConfig = AuthConfig myClientID app myUserAgent
    app        = WebApp myClientSecret codeFlow
    codeFlow   = CodeFlow redirectURI code
#+end_src

An alternative is to use ~getAuthURL~ to obtain the code, then get the refresh token using a similar method to what is demonstrated in ~examples/RefreshTokens.hs~. After getting the refresh token, you can use [[file:../src/Network/Reddit.hs::newClientWithManager][newClientWithManager]] and provide ~heddit~ with a record of monadic actions that will load and store your tokens. See the [[file:../src/Network/Reddit.hs::fileTokenManager][fileTokenManager]] for a simple example.

* Installed applications
:PROPERTIES:
:CUSTOM_ID: installed
:END:

*Installed apps* are similar to [[#web][web]], but do not take a secret and also follow the ~CodeFlow~.

* Application-only
:PROPERTIES:
:CUSTOM_ID: app-only
:END:

If you do not wish to use your Reddit ~Client~ with a user context, create an ~ApplicationOnly~ app. The process is indentical to [[#script][scripts]], but you do not provide a ~PasswordFlow~ to create the ~Client~. ~ApplicationOnly~ apps may also use ~loadClient~ with an ~auth.ini~ file. NB: many endpoints will not work, so be prepared for exceptions when working within this context.