hoauth2-tutorial-0.2: Tutorial for using hoauth2
Safe HaskellSafe-Inferred
LanguageHaskell2010

HOAuth2Tutorial

Description

If you're hurry, go check source code directly.

Configure your OAuth2 provider

Pick which OAuth2 provider you'd to use, e.g. Google, Github, Auth0 etc. Pretty much all standard OAuth2 provider has developer portal to guide developer to use oauth2 flow. So read it through if you're unfamiliar OAuth2 before. Often time, those documents will guide you how to create an Application which has credentials (e.g. client_id and client_secret for a web application), which will be used to authenticate your service (replying party) with server.

For some OIDC providers, you may even be able to find out those URLs from a well-known endpoint.

https://BASE_DOMAIN/.well-known/openid-configuration

In this tutorial, I choose Auth0, which is one of existing OAuth2/OIDC Providers in the market. This is the API Docs https://auth0.com/docs/api

Generate Authorization URL.

OAuth2 starts with authorization.

To generate an authorization URL, call method authorizationUrl, then call appendQueryParams to append additional query parameters, e.g. state, scope etc.

That method will also automatically append following query parameter to the authorization url.

client_id = xxx        -- client id of your Application credential you got previously
response_type = code   -- must be for authorization request
redirect_uri = xxx     -- where does the server (provider) send back the authorization code.
                       -- You have to config this when creating Application at previous step.

The generated URL looks like

https://DOMAIN/path/to/authorize?client_id=xxx&response_type=code&redirect_uri=xxx&state=xxx&scope=xxx&..

Notes: As of today, hoauth2 only supports Code Grant.

Redirect user to the Authorization URL

Now you need to have your user to navigate to that URL to kick off OAuth flow.

There are different ways to redirect user to the authorizeUrl.

e.g.

  1. Display as anchor link directly at UI so that user can click it.
  2. Create your own login endpoint, e.g. /login, which then 302 to the authorizeUrl.

In this tutorial, I choose the second option. For instance this is how indexH is implemented.

>>> setHeader "Location" (uriToText authorizeUrl)
>>> status status302

Obtain Access Token

When user navigates to authorizeUrl, user will be prompt for login against the OAuth provider.

After an successful login there, user will be redirect back to your Application's redirect_uri with code in the query parameter.

With this code, we could exchange for an Access Token.

Also you'd better to validate the state is exactly what you pass in the authorizeUrl. OAuth2 provider expects to send the exact state back in the redirect request.

To obtain an Access Token, you could call fetchAccessToken, which essentially takes the authorization code, make request to OAuth2 provider's /token endpoint to get an Access Token, plus some other information (see details at OAuth2Token).

fetchAccessToken returns ExceptT (OAuth2Error Errors) m OAuth2Token However Scotty, which is web framework I used to build this tutorial, requires error as Text hence the transform with oauth2ErrorToText

Once we got the OAuth2Token (which actually deserves an better name like TokenResponse), we could get the actual accessToken of out it, use which to make API requests to resource server (often time same as the authorization server)

Network.OAuth.OAuth2.HttpClient provides a few handy method to send such API request. For instance,

authGetJSON   -- Makes GET request and decode response as JSON, with access token appended in Authorization http header.
authPostJSON  -- Similar but does POST request

In this tutorial, it makes request to auth0UserInfoUri to fetch Auth0 user information so application knows who did the authorize.

The end

That's it! Congratulations make thus far!

If you're interested more of OAuth2, keep reading on https://www.oauth.com/, which provides a nice guide regarding what is OAuth2 and various use cases.

Synopsis

Configuration

randomStateValue :: ByteString Source #

You'll need to find out an better way to create state which is recommended in https://www.rfc-editor.org/rfc/rfc6749#section-10.12

auth0UserInfoUri :: URI Source #

Endpoint for fetching user profile using access token

data Auth0User Source #

Constructors

Auth0User 

Fields

Instances

Instances details
FromJSON Auth0User Source # 
Instance details

Defined in HOAuth2Tutorial

Generic Auth0User Source # 
Instance details

Defined in HOAuth2Tutorial

Associated Types

type Rep Auth0User :: Type -> Type #

Show Auth0User Source # 
Instance details

Defined in HOAuth2Tutorial

type Rep Auth0User Source # 
Instance details

Defined in HOAuth2Tutorial

type Rep Auth0User = D1 ('MetaData "Auth0User" "HOAuth2Tutorial" "hoauth2-tutorial-0.2-8sV0rrDdVolJKoRyhZj0la" 'False) (C1 ('MetaCons "Auth0User" 'PrefixI 'True) (S1 ('MetaSel ('Just "name") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Text) :*: (S1 ('MetaSel ('Just "email") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Text) :*: S1 ('MetaSel ('Just "sub") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Text))))

Web server

app :: IO () Source #

The scotty application

indexH :: IORef (Maybe Auth0User) -> ActionM () Source #

/ endpoint handler

loginH :: ActionM () Source #

/login endpoint handler

logoutH :: IORef (Maybe Auth0User) -> ActionM () Source #

/logout endpoint handler

callbackH :: IORef (Maybe Auth0User) -> ActionM () Source #

oauth2callback endpoint handler

Utilities

paramValue Source #

Arguments

:: Text

Parameter key

-> [Param]

All parameters

-> Either Text Text 

excepttToActionM :: Show a => ExceptT Text IO a -> ActionM a Source #

Lift ExceptT to ActionM which is basically the handler Monad in Scotty.