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-oauth2Complete examples are available here. Resulting applications can be seen on the following links:
Throughout the library, you'll find terms referring to OAuth well-defined roles:
resource owner
An entity capable of granting access to a protected resource.
When the resource owner is a person, it is referred to as an
end-user.
client
An application making protected resource requests on behalf of the
resource owner and with its authorization. The term "client" does
not imply any particular implementation characteristics (e.g.,
whether the application executes on a server, a desktop, or other
devices).
authorization server
The server issuing access tokens to the client after successfully
authenticating the resource owner and obtaining authorization.
resource server
The server hosting the protected resources, capable of accepting
and responding to protected resource requests using access tokens.
NOTE: Usually, the authorization server and the resource server are a same entity, or comes from the same entity. So, a simplified vision of this roles can be:
resource owner
The end-user
client
Your Elm app
authorization server/resource server
Google, Facebook, Twitter or whatever OAuth provider you're talking to
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 each module 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 : Http.Request AuthenticationSuccess -> Http.Request AuthenticationSuccess
adjustRequest req =
let
headers =
Http.header "Accept" ("application/json") :: req.headers
expect =
Http.expectJson <| Json.map4 AuthenticationSuccess
defaultTokenDecoder
defaultRefreshTokenDecoder
defaultExpiresInDecoder
lenientScopeDecoder
in
{ req | headers = headers, expect = expect }
getToken : String -> Cmd AuthenticationSuccess
getToken code =
let
req =
adjustRequest <|
OAuth.AuthorizationCode.makeTokenRequest <|
OAuth.AuthorizationCode
{ credentials = { clientId = clientId, secret = Nothing }
, code = code
, redirectUri = redirectUri
, scope = scope
, state = state
, url = tokenEndpoint
}
in
Http.send handleResponse (Http.request req)Most authorization servers don't enable CORS on the authentication endpoints. For this reason, it's likely that the preflight OPTIONS requests sent by the browser return an invalid answer, preventing the browser from making the request at all.
Why is it so? The authorization request _usually_requires one's secret; thus making them rather impractical to do perform from a client-side application without exposing those secrets. As a security measure, most authorization servers choose to enforce that those requests are made server-side.
Generally, this is also what you want, unless you're dealing with a custom authorization server in some sort of isolated environment. OAuth 2.0 is a designed to cover all sort of delegation of permissions, the case of user-facing client-side applications is only one of them; some authorization flows are therefore not necessarily adapted to these cases. Usually, a client-side application will prefer the Implicit Flow over the others.