This package offers some utilities to implement a client-side OAuth 2 authorization in Elm. It covers all 4 grant types:
Implicit: The most commonly used. The token is obtained directly as a result of a user redirection to an OAuth provider.
Authorization Code: The token is obtained as a result of an authentication, from a code obtained as a result of a user redirection to an OAuth provider.
Resource Owner Password Credentials: The token is obtained directly by exchanging the user credentials with an OAuth provider.
Client Credentials: The token is obtained directly by exchanging application credentials with an OAuth provider.
The following parts is a walkthrough the first 2 flows. The last 2 are actually pretty straightforward and can be seen (in terms of steps) as a subset of the Authorization Code flow.
elm package install truqu/elm-oauth2A complete example is available here (with the corresponding sources here)
import OAuth
import OAuth.Implicittype alias Model =
{ oauth :
{ clientId : String
, redirectUri : Url
}
-- [ ... ]
}
type Msg
= ClientIdSubmitted
-- [ ... ]
update : Msg -> Model -> ( Model, Cmd Msg )
update msg ({ oauth } as model) =
case msg of
ClientIdSubmitted ->
( model
, OAuth.Implicit.authorize
{ clientId = model.oauth.clientId
, redirectUri = model.oauth.redirectUri
, responseType = OAuth.Token
, scope = [ "email", "profile" ]
, state = Nothing
, url = authorizationEndpoint
}
)
-- [ ... ]init : () -> Url -> Key -> ( Model, Cmd Msg )
init _ origin navKey =
let
model =
{ oauth = { clientId = "", redirectUri = origin }
, error = Nothing
, token = Nothing
, profile = Nothing
}
in
case OAuth.Implicit.parse origin of
Ok { token } ->
( { model | token = Just token }
, getUserProfile profileEndpoint token
)
Err err ->
( { model | error = showParseErr err }
, Cmd.none
)getUserProfile : Url -> OAuth.Token -> Cmd Msg
getUserProfile endpoint token =
Http.send GotUserInfo <|
Http.request
{ method = "GET"
, body = Http.emptyBody
, headers = OAuth.use token []
, withCredentials = False
, url = Url.toString endpoint
, expect = Http.expectJson profileDecoder
, timeout = Nothing
}GitHub API v3 supports the Authorization Code Flow in order to obtain access tokens for a registered application. However, the implementation presents a flaw which makes it not compliant with the official RFC:
By default, GitHub's authorization server will respond with a x-www-form-urlencoded mime-type
when trying to exchange the authorization code against an access token (instead of a
application/json mime-type as specified in the official RFC).
This behavior can be changed by providing an extra Accept: application/json header with the
authenticate request. However, by doing so, GitHub's authorization server will encode the
scope of the response as a comma-separated list (instead of a space-separated list as
specified in the official RFC).
Hence, this library provides a way to work around this implementation quirks by adjusting the
authentication request before it gets sent. To achieve this, one may use the various decoders
now exposed in OAuth.Decode
to craft a custom transformation function for the authenticateWithOpts functions.
Here's a small example of how to work around GitHub's API v3 implementation:
adjustRequest : AdjustRequest ResponseToken
adjustRequest req =
let
headers =
Http.header "Accept" ("application/json") :: req.headers
expect =
Http.expectJson lenientResponseDecoder
in
{ req | headers = headers, expect = expect }
getToken : String -> Cmd ResponseToken
getToken code =
let
req =
OAuth.AuthorizationCode.authenticateWithOpts adjustRequest <|
OAuth.AuthorizationCode
{ credentials = { clientId = "clientId", secret = "secret" }
, code = code
, redirectUri = "redirectUri"
, scope = [ "whatever" ]
, state = Nothing
, url = "tokenEndpoint"
}
in
Http.send handleResponse req