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
).
EncodeDecode.Authorization
You'll usually get this from a JSON file, often via getAuthorizations
.
{ 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.
ResponseToken
An alias for OAuth.ResponseToken
.
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.
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
.