billstclair / elm-oauth-middleware / OAuthMiddleware

Client side of OAuth Authorization Code Grant Flow.

Your top-level Elm program must be created with Navigation.program or Navigation.programWithFlags. You can ignore incoming Location changes, but your init function needs to pass the initial Location to receiveTokenAndState, in case you're here because of a redirect back from your OAuth callback server (at redirectUri).

Types


type alias Authorization =
EncodeDecode.Authorization

You'll usually get this from a JSON file, often via getAuthorizations.


type alias TokenAuthorization =
{ authorization : Authorization
, scope : List String
, state : Maybe String
, redirectBackUri : String 
}

Configuration for sending a request to the authorization server.

authorizationUri and tokenUri are provided by the OAuth authentication service.

authorize sends a request to authorizationUri, encoding the other parameters into the state sent there. The redirectUri server, implemented by the server directory of this package, uses the tokenUri as a key into its table containing valid values for redirectBackUri and the client secret that will be sent to the tokenUri.

clientId, redirectUri, scope, and state are all standard parts of the OAuth protocol, except state is optional here, and encoded so it comes back just as it went out.

clientId is assigned by the OAuth resource. It also provides a list of possible scope values. You can use any subset of those.

redirectUri is sent to the OAuth authorization web server. If the user successfullly logs in, an authorization code will be sent there as a Uri query parameter.

state is any string you'd like. It is passed to the authorization web server, which passes it to the redirectUri, and it is passed back to your application via the redirectBackUri.

redirectBackUri is a Uri to redirect to AFTER the redirectUri exchanges the received code for an access token. It will resume your Elm application, this time with an access token in its hand.


type alias ResponseToken =
ResponseToken

An alias for OAuth.ResponseToken.


type TokenState
    = TokenAndState ResponseToken (Maybe String)
    | TokenErrorAndState String (Maybe String)
    | TokenDecodeError String
    | NoToken

The result of parsing a Url.Url that may have come from a redirect from a callback server, as implemented in the server directory of this project.

For a TokenAndState or TokenErrorAndState, the state you passed is the (Maybe String).

You will rarely need to look inside a ResponseToken. Just pass it to use to add it to the headers for a request to the protected resource.

TokenDecodeError means that there was a properly-named query string, but an error occurred while decoding it.

NoToken means that there was no query parameter that looked like a token or an error message about getting a token. In other words, this invocation of your webapp was not due to a redirection from the callback server.

Client-side functions

getAuthorization : (Result Http.Error Authorization -> msg) -> Basics.Bool -> String -> OAuth.AuthorizationCode.RequestParts msg

Get a JSON file encoding an Authorization.

getAuthorization useCache url

If useCache is true, will use the browser's cache, meaning that it may not immediately notice changes to the file on the server.

The JSON format is as follows. You'll change it with information from your OAuth provider, and for your redirect server, and store it in a convenient place on the same server serving your compiled Elm code.

The scopes field is an object, mapping your internal name for each scope to the actual OAuth provider name. For most OAuth providers, the two will be identical, but Google, for example, uses long URL-looking strings for scope names, so it's convenient to have a shorter name your application can use. This field isn't used by any of the OAuthMiddleware code, except the example, so you can safely set it to {}, if you prefer to just encode the scope strings as constants in your Elm code.

Your client secret is stored with the redirect server, and never leaves that server machine.

{ "name": "Gmail",
  "authorizationUri": "https://accounts.google.com/o/oauth2/auth",
  "tokenUri": "https://accounts.google.com/o/oauth2/token",
  "apiUri": "https://www.googleapis.com/gmail/v1/users/",
  "clientId": "<Your OAuth clientid>",
  "redirectUri": "<Your redirect server Uri>",
  "scopes": {"<Your scope name>": "<OAuth provider's scope name>"}
}

The example contains more information about this, and a sample authorizations file.

getAuthorizations : (Result Http.Error (List Authorization) -> msg) -> Basics.Bool -> String -> OAuth.AuthorizationCode.RequestParts msg

Get a JSON file encoding an Authorization list.

getAuthorizations useCache url

If useCache is true, will use the browser's cache, meaning that it may not immediately notice changes to the file on the server.

locationToRedirectBackUri : Url -> String

Convert a Url.Url into a string suitable for the redirectBackUri in a TokenAuthorization.

authorize : TokenAuthorization -> Maybe Url

Compute an authorization request Url.

This will cause the authorization server to ask the user to login. If successful, it will send the received code and TokenAuthorization.state to the TokenAuthorization.authorization.redirectUri for generation of a token to send back to the TokenAuthorization.redirectBackUri. Your code at that Uri will receive an encoded ResponseToken on the responseTokenQuery parameter, or an error string on the responseTokenQueryError parameter. Use decodeResponseToken to turn the responseTokenQuery string into an ResponseToken, which you can use to do authenticated requests, just as if you had called OAuth.TokenAuthorizationCode.authenticate yourself, but hiding the client secret on the redirect server.

A return value of Nothing means that either the authorization uri or the redirect uri could not be parsed as valid uris.

Otherwise, navigating to the the returned Url will go to the the OAuth provider's authentication page, followed by token fetching by the redirect server at redirectUri. The redirect server will navigate back with query args that you can process with receiveTokenAndState.

receiveTokenAndState : Url -> TokenState

Parse a returned ResponseToken from a Url.Url.

Note that the scope in the returned TokenAndState ResponseToken is not guaranteed to match what you requested. The RFC 6749 Authorization Code Grant flow does not specify a returned scope from the OAuth token server, so if your redirectUri server receives no scope, or an empty scope, it will send back the list of scopes you requested. The spec does not guarantee that you'll get all the scopes you requested in your call to authorize, so this may be incorrect. It appears that GitHub returns proper scopes, so they will be as granted in that case.

use : ResponseToken -> List Http.Header -> List Http.Header

Use a token to add authenticatication to a request header.

A thin wrapper around OAuth.useToken.