This package offers some utilities to implement a client-side OAuth 2 authorization in Elm. It covers all 4 grant types:
Implicit (Recommended): The most commonly used. The token is obtained directly as a result of a user redirection to an OAuth provider. The implicit grant type is used to obtain access tokens (it does not support the issuance of refresh tokens) and is optimized FOR PUBLIC CLIENTS.
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. The authorization code grant type is used to obtain both access tokens and refresh tokens and is optimized FOR CONFIDENTIAL CLIENTS such as the device operating system or a highly privileged application.
Resource Owner Password Credentials: The token is obtained directly by exchanging the user credentials with an OAuth provider. The resource owner password credentials grant type is suitable in cases WHERE THE RESOURCE OWNER HAS A TRUST RELATIONSHIP WITH THE CLIENT.
Client Credentials: The token is obtained directly by exchanging application credentials with an OAuth provider. The client credentials grant type MUST ONLY BE USED BY CONFIDENTIAL CLIENTS.
elm install truqu/elm-oauth2import OAuth
import OAuth.Implicit
import Url exposing (Url)
import Browser.Navigation as Navigation exposing (Key)type alias Model =
{ redirectUri : Url
, error : Maybe String
, token : Maybe OAuth.Token
, state : String
}
type Msg = SignInRequested { clientId : String, authorizationEndpoint : String }init : { randomBytes : String } -> Url -> Key -> ( Model, Cmd Msg )
init { randomBytes } origin _ =
let
model =
{ redirectUri = { origin | query = Nothing, fragment = Nothing }
, error = Nothing
, token = Nothing
, state = randomBytes
}
in
case OAuth.Implicit.parseToken origin of
OAuth.Implicit.Empty ->
( model, Cmd.none )
OAuth.Implicit.Success { token, state } ->
if state /= Just model.state then
( { model | error = Just "'state' mismatch, request likely forged by an adversary!" }
, Cmd.none
)
else
( { model | token = Just token }
, getUserInfo config token
)
OAuth.Implicit.Error error ->
( { model | error = Just <| errorResponseToString error }
, Cmd.none
)update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
SignInRequested { clientId, authorizationEndpoint } ->
let
auth =
{ clientId = clientId
, redirectUri = model.redirectUri
, scope = []
, state = Just model.state
, url = authorizationEndpoint
}
in
( model
, auth |> OAuth.Implicit.makeAuthorizationUrl |> Url.toString |> Navigation.load
)Complete examples are available here. Resulting applications can be seen on the following links: