Yesod.Auth.OAuth2

Hackage Stackage Nightly Stackage LTS CI

OAuth2 AuthPlugins for Yesod.

Usage

import Yesod.Auth
import Yesod.Auth.OAuth2.GitHub

instance YesodAuth App where
    -- ...

    authPlugins _ = [oauth2GitHub clientId clientSecret]

clientId :: Text
clientId = "..."

clientSecret :: Text
clientSecret = "..."

Some plugins, such as GitHub and Slack, have scoped functions for requesting additional information:

oauth2SlackScoped [SlackBasicScope, SlackEmailScope] clientId clientSecret

Working with Extra Data

We put the minimal amount of user data possible in credsExtra – just enough to support you parsing or fetching additional data yourself.

For example, if you work with GitHub and GitHub user profiles, you likely already have a model and a way to parse the /user response. Rather than duplicate all that in our library, we try to make it easy for you to re-use that code yourself:

authenticate creds = do
    let
        -- You can run your own FromJSON parser on the response we already have
        eGitHubUser :: Either String GitHubUser
        eGitHubUser = getUserResponseJSON creds

        -- Avert your eyes, simplified example
        Just accessToken = getAccessToken creds
        Right githubUser = eGitHubUser

    -- Or make followup requests using our access token
    runGitHub accessToken $ userRepositories githubUser

    -- Or store it for later
    insert User
        { userIdent = credsIdent creds
        , userAccessToken = accessToken
        }

NOTE: Avoid looking up values in credsExtra yourself; prefer the provided get functions. The data representation itself is no longer considered public API.

Local Providers

If we don’t supply a “Provider” (e.g. GitHub, Google, etc) you need, you can write your own using our provided Prelude:

import Yesod.Auth.OAuth2.Prelude

pluginName :: Text
pluginName = "mysite"

oauth2MySite :: YesodAuth m => Text -> Text -> AuthPlugin m
oauth2MySite clientId clientSecret =
    authOAuth2 pluginName oauth2 $ \manager token -> do
        -- Fetch a profile using the manager and token, leave it a ByteString
        userResponse <- -- ...

        -- Parse it to your preferred identifier, e.g. with Data.Aeson
        userId <- -- ...

        -- See authGetProfile for the typical case

        pure Creds
            { credsPlugin = pluginName
            , credsIdent = userId
            , credsExtra = setExtra token userResponse
            }
  where
    oauth2 = OAuth2
        { oauth2ClientId = clientId
        , oauth2ClientSecret = Just clientSecret
        , oauth2AuthorizeEndpoint = "https://mysite.com/oauth/authorize"
        , oauth2TokenEndpoint = "https://mysite.com/oauth/token"
        , oauth2RedirectUri = Nothing
        }

The Prelude module is considered public API, though we may build something higher-level that is more convenient for this use-case in the future.

Development & Tests

stack setup
stack build --dependencies-only
stack build --pedantic --test

Please also run HLint and Weeder before submitting PRs.

Example

This project includes an executable that runs a server with (almost) all supported providers present.

To use:

  1. cp .env.example .env and edit in secrets for providers you wish to test

    Be sure to include http://localhost:3000/auth/page/{plugin}/callback as a valid Redirect URI when configuring the OAuth application.

  2. Build with the example: stack build ... --flag yesod-auth-oauth2:example

  3. Run the example stack exec yesod-auth-oauth2-example

  4. Visit the example: $BROWSER http://localhost:3000

  5. Click the log-in link for the provider you configured

If successful, you will be presented with a page that shows the credential and User response value.


CHANGELOG | LICENSE

Changes

Unreleased

v0.7.2.0

v0.7.1.3

  • Add support (with caveats) for relative approots @cptrodolfox

v0.7.1.2

  • Support hoauth2-2.9.

v0.7.1.1

  • Support mtl-2.3, which no longer re-exports Control.Monad

v0.7.1.0

  • Add AzureADv2 provider

v0.7.0.3

  • Support hoauth-2.7. This change is only breaking in the unlikely case of users using something other than fetchAccessToken or fetchAccessToken2

v0.7.0.2

v0.7.0.1

  • Support hoauth-2.2 and 2.3

v0.7.0.0

  • Support hoauth2-2.0

    The OAuth2 type’s fields have changed. If you are not defining your own Local Providers (i.e. you’re not constructing any OAuth2 values) you should not be affected by this change. If you are, you should update to the new field names.

v0.6.3.4

  • Remove dependencies upper bounds

v0.6.3.1

  • Relax dependencies bounds

v0.6.3.0

  • Expose onDispatchError and generic OtherDispatchError for passthrough log
  • Don’t throw exceptions; handle all errors through the set-message-redirect path
  • Respect onErrorHtml for said error-handling
  • Support custom widget in Google plugin @jmorag

v0.6.2.3

  • Allow bytestring-0.11 and cryptonite 0.28
  • Test with GHC 8.10 on CI

v0.6.2.2

  • Consistent dependencies bounds in all targets

v0.6.2.1

  • Adjust lower bounds on cryptonite

v0.6.2.0

  • Filter + from state tokens

    This decreases entropy in the token slightly, but ensures that providers performing unexpected +/space/%20 encoding (e.g. ClassLink) still function.

    See #140.

  • Add ClassLink provider

v0.6.1.7

  • Relax upper bounds on hoauth2 and http-client

v0.6.1.6

  • Revert back to Authorization-header-only fetchAccessToken function
  • Add authOAuth2' and authOAuth2Widget', which use fetchAccessToken2

v0.6.1.5

  • Update to GHC-8.8, and hoauth2-1.14
  • Drop CI-backed support for GHC-8.4

v0.6.1.4

  • Tighten upper bound on hoauth2

v0.6.1.3

  • Replace System.Random state token generation with cryptonite
  • Allow aeson-1.5 and hoauth2-1.14
  • Add WordPress.com provider @nbloomf

v0.6.1.2

  • Don’t report our own errors like OAuth2 ErrorResponses

v0.6.1.1

  • Added AzureAD provider
  • COMPATIBILITY: Use hoauth2-1.8.1
  • COMPATIBILITY: Test with GHC 8.6.3, and not 8.2

v0.6.1.0

  • Allow http-client-0.6

v0.6.0.0

  • Remove deprecated Github module

v0.5.3.0

  • Allow aeson-1.4 and hoauth2-1.8

v0.5.2.0

  • InvalidProfileResponse was replaced with different, situation-specific constructors; the exception type is considered internal API, but end-users may see them in logs, or if they (unexpectedly) escape our error-handling
  • Errors during log-in no longer result in 4XX or 5XX responses; they now redirect to LoginR with the exception details logged and something user-appropriate displayed via setMessage

v0.5.1.0

  • Added GitLab provider
  • Added properly-named GitHub module, deprecated Github
  • Store refreshToken in credsExtra

v0.5.0.0

  • COMPATIBILITY: Allow and require yesod-1.6
  • COMPATIBILITY: Stop testing GHC 8.0 on CI

v0.4.1.0

  • Check for errors in callback query params, as described in the spec

v0.4.0.1

  • COMPATIBILITY: Allow http-types-0.12

v0.4.0.0

  • COMPATIBILITY: Allow aeson-1.3

  • COMPATIBILITY: Dropped a lot of information from credsExtra:

    TL;DR: you’ll no longer find things like username or email as keys in the credsExtra map. Instead, you’ll find the encoded profile response we received and the OAuth access token. You can/should do your own decoding or make your own follow-up requests to get extra data about your users.

    This reduced a lot of complexity, likely duplication between our decoding and yours, and (I think) makes the library easier to use.

  • COMPATIBILITY: Support GHC-8.2

  • COMPATIBILITY: Drop (claimed, but never tested) support for GHC-7.8 & 7.10

  • LICENSE: fixed vague licensing (MIT now)

v0.3.1

  • Internal project cleanup

v0.3.0

  • COMPATIBILITY: Use hoauth2-1.3

v0.2.4

  • FIX: Update Nylas provider
  • NEW: Battle.Net provider
  • NEW: Bitbucket provider
  • NEW: Salesforce provider

v0.2.1

  • FIX: Fix collision in GitHub email / public_email extras value

v0.2.0

v0.1.10

  • FIX: location is optional in GitHub response

v0.1.9

v0.1.8

  • COMPATIBILITY: Allow aeson-0.11 (@k-bx)

v0.1.7

  • NEW: Prefer primary email in GitHub provider
  • NEW: Include public_email in GitHub extras response
  • REMOVED: Remove Twitter provider

v0.1.6

  • NEW: Nicer error message on invalid code (@silky)

v0.1.5

  • FIX: Incorrect state parameter handling

v0.1.4

  • FIX: Use newer Nylas endpoint

v0.1.3

  • NEW: EveOnline provider (@Drezil)
  • NEW: Nylas provider (@bts)

v0.1.2

  • NEW: A more different Google provider (@ssaavedra)

v0.1.1

  • NEW: Twitter provider

v0.1.0

  • REMOVED: Google provider, use Yesod.Auth.GoogleEmail2
  • CHANGED: Learn was renamed to Upcase
  • COMPATIBILITY: Drop support for GHC-6
  • COMPATIBILITY: Support GHC-7.10

v0.0.12

  • COMPATIBILITY: Allow transformers-0.4 (@snoyberg)

v0.0.11

v0.0.10

v0.0.9

v0.0.8

  • FIX: Username may be missing in GitHub responses (@skade)

v0.0.7

  • NEW: Scope support in GitHub provider (@skade)

v0.0.6

  • NEW: GitHub provider (@freiric)
  • COMPATIBILITY: flag-driven network/network-uri dependency

v0.0.5.1

  • DOCUMENTATION: fix data declaration, allows Haddocks to build

v0.0.5

  • COMPATIBILITY: Allow yesod-core-1.3 and target yesod-auth-1.3 (@maxcan)
  • COMPATIBILITY: Target haouth2-0.4 (@katyo)

v0.0.4

  • COMPATIBILITY: Allow text-1.*
  • COMPATIBILITY: Allow lifted-base-0.2.*

v0.0.3

  • FIX: replace error crash with throwIO exception

v0.0.2

  • Various documentation fixes.

v0.0.1

Initial version. Maintainer-ship taken over by @pbrisbin.