OAuth 2.0 public clients utilizing the Authorization Code Grant are susceptible to the authorization code interception attack. A possible mitigation against the threat is to use a technique called Proof Key for Code Exchange (PKCE, pronounced "pixy") when supported by the target authorization server. See also RFC 7636.
To get started, have a look at the live-demo and its corresponding source code
+-----------------+
| Auth Server |
+-------+ | +-------------+ |
| |--(1)- Auth Request --->| | | |
| | + code_challenge | | Auth | |
| | | | Endpoint | |
| |<-(2)-- Auth Code ------| | | |
| Elm | | +-------------+ |
| App | | |
| | | +-------------+ |
| |--(3)- Token Request -->| | | |
| | + code_verifier | | Token | |
| | | | Endpoint | |
| |<-(4)- Access Token --->| | | |
+-------+ | +-------------+ |
+-----------------+
See also the Authorization Code flow for details about the basic version of this flow.
An opaque type representing a code verifier. Typically constructed from a high quality entropy.
case codeVerifierFromBytes entropy of
Nothing -> {- ...-}
Just codeVerifier -> {- ... -}
An opaque type representing a code challenge. Typically constructed from a CodeVerifier
.
let codeChallenge = mkCodeChallenge codeVerifier
codeVerifierFromBytes : Bytes -> Maybe CodeVerifier
Construct a code verifier from a byte sequence generated from a high quality randomness source (i.e. cryptographic).
Ideally, the byte sequence should be 32 or 64 bytes, and it must be at least 32 bytes and at most 90 bytes.
codeVerifierToString : CodeVerifier -> String
Convert a code verifier to its string representation.
mkCodeChallenge : CodeVerifier -> CodeChallenge
Construct a CodeChallenge
to send to the authorization server. Upon receiving the authorization code, the client can then
the associated CodeVerifier
to prove it is the rightful owner of the authorization code.
codeChallengeToString : CodeChallenge -> String
Convert a code challenge to its string representation.
makeAuthorizationUrl : Authorization -> Url
Redirects the resource owner (user) to the resource provider server using the specified authorization flow.
{ clientId : String
, url : Url
, redirectUri : Url
, scope : List String
, state : Maybe String
, codeChallenge : CodeChallenge
}
Request configuration for an authorization (Authorization Code & Implicit flows)
clientId
(REQUIRED):
The client identifier issues by the authorization server via an off-band mechanism.
url
(REQUIRED):
The authorization endpoint to contact the authorization server.
redirectUri
(OPTIONAL):
After completing its interaction with the resource owner, the authorization
server directs the resource owner's user-agent back to the client via this
URL. May be already defined on the authorization server itself.
scope
(OPTIONAL):
The scope of the access request.
state
(RECOMMENDED):
An opaque value used by the client to maintain state between the request
and callback. The authorization server includes this value when redirecting
the user-agent back to the client. The parameter SHOULD be used for preventing
cross-site request forgery.
codeChallenge
(REQUIRED):
A challenge derived from the code verifier that is sent in the
authorization request, to be verified against later.
parseCode : Url -> AuthorizationResult
Parse the location looking for a parameters set by the resource provider server after redirecting the resource owner (user).
Returns AuthorizationResult Empty
when there's nothing.
AuthorizationResultWith AuthorizationError AuthorizationSuccess
Describes errors coming from attempting to parse a url after an OAuth redirection
{ error : OAuth.ErrorCode
, errorDescription : Maybe String
, errorUri : Maybe String
, state : Maybe String
}
Describes an OAuth error as a result of an authorization request failure
error
(REQUIRED):
A single ASCII error code.
errorDescription
(OPTIONAL)
Human-readable ASCII text providing additional information, used to assist the client developer in
understanding the error that occurred. Values for the errorDescription
parameter MUST NOT
include characters outside the set %x20-21 / %x23-5B / %x5D-7E
.
errorUri
(OPTIONAL):
A URI identifying a human-readable web page with information about the error, used to
provide the client developer with additional information about the error. Values for the
errorUri
parameter MUST conform to the URI-reference syntax and thus MUST NOT include
characters outside the set %x21 / %x23-5B / %x5D-7E
.
state
(REQUIRED if state
was present in the authorization request):
The exact value received from the client
{ code : String
, state : Maybe String
}
The response obtained as a result of an authorization
code
(REQUIRED):
The authorization code generated by the authorization server. The authorization code MUST expire
shortly after it is issued to mitigate the risk of leaks. A maximum authorization code lifetime of
10 minutes is RECOMMENDED. The client MUST NOT use the authorization code more than once. If an
authorization code is used more than once, the authorization server MUST deny the request and
SHOULD revoke (when possible) all tokens previously issued based on that authorization code. The
authorization code is bound to the client identifier and redirection URI.
state
(REQUIRED if state
was present in the authorization request):
The exact value received from the client
String
A simple type alias to ease readability of type signatures
makeTokenRequest : (Result Http.Error AuthenticationSuccess -> msg) -> Authentication -> RequestParts msg
Builds a the request components required to get a token from an authorization code
let req : Http.Request AuthenticationSuccess
req = makeTokenRequest toMsg authentication |> Http.request
{ credentials : Credentials
, code : String
, codeVerifier : CodeVerifier
, redirectUri : Url
, url : Url
}
Request configuration for an AuthorizationCode authentication
credentials
(REQUIRED):
Only the clientId is required. Specify a secret if a Basic OAuth
is required by the resource provider.
code
(REQUIRED):
Authorization code from the authorization result
codeVerifier
(REQUIRED):
The code verifier proving you are the rightful recipient of the
access token.
url
(REQUIRED):
Token endpoint of the resource provider
redirectUri
(REQUIRED):
Redirect Uri to your webserver used in the authorization step, provided
here for verification.
{ clientId : String
, secret : Maybe String
}
Describes at least a clientId
and if define, a complete set of credentials
with the secret
. The secret is so-to-speak optional and depends on whether the
authorization server you interact with requires a Basic authentication on top of
the authentication request. Provides it if you need to do so.
{ clientId = "<my-client-id>"
, secret = Just "<my-client-secret>"
}
{ token : OAuth.Token
, refreshToken : Maybe OAuth.Token
, expiresIn : Maybe Basics.Int
, scope : List String
}
The response obtained as a result of an authentication (implicit or not)
token
(REQUIRED):
The access token issued by the authorization server.
refreshToken
(OPTIONAL):
The refresh token, which can be used to obtain new access tokens using the same authorization
grant as described in Section 6.
expiresIn
(RECOMMENDED):
The lifetime in seconds of the access token. For example, the value "3600" denotes that the
access token will expire in one hour from the time the response was generated. If omitted, the
authorization server SHOULD provide the expiration time via other means or document the default
value.
scope
(OPTIONAL, if identical to the scope requested; otherwise, REQUIRED):
The scope of the access token as described by Section 3.3.
{ error : OAuth.ErrorCode
, errorDescription : Maybe String
, errorUri : Maybe String
}
Describes an OAuth error as a result of a request failure
error
(REQUIRED):
A single ASCII error code.
errorDescription
(OPTIONAL)
Human-readable ASCII text providing additional information, used to assist the client developer in
understanding the error that occurred. Values for the errorDescription
parameter MUST NOT
include characters outside the set %x20-21 / %x23-5B / %x5D-7E
.
errorUri
(OPTIONAL):
A URI identifying a human-readable web page with information about the error, used to
provide the client developer with additional information about the error. Values for the
errorUri
parameter MUST conform to the URI-reference syntax and thus MUST NOT include
characters outside the set %x21 / %x23-5B / %x5D-7E
.
{ method : String
, headers : List Http.Header
, url : String
, body : Http.Body
, expect : Http.Expect a
, timeout : Maybe Basics.Float
, tracker : Maybe String
}
Parts required to build a request. This record is given to Http.request
in order to create a new request and may be adjusted at will.
defaultAuthenticationSuccessDecoder : Json.Decode.Decoder AuthenticationSuccess
Json decoder for a positive response. You may provide a custom response decoder using other decoders from this module, or some of your own craft.
defaultAuthenticationSuccessDecoder : Decoder AuthenticationSuccess
defaultAuthenticationSuccessDecoder =
D.map4 AuthenticationSuccess
tokenDecoder
refreshTokenDecoder
expiresInDecoder
scopeDecoder
defaultAuthenticationErrorDecoder : Json.Decode.Decoder AuthenticationError
Json decoder for an errored response.
case res of
Err (Http.BadStatus { body }) ->
case Json.decodeString OAuth.AuthorizationCode.defaultAuthenticationErrorDecoder body of
Ok { error, errorDescription } ->
doSomething
_ ->
parserFailed
_ ->
someOtherError
makeAuthorizationUrlWith : OAuth.ResponseType -> Dict String String -> Authorization -> Url
Like makeAuthorizationUrl
, but gives you the ability to specify a custom response type
and extra fields to be set on the query.
makeAuthorizationUrl : Authorization -> Url
makeAuthorizationUrl =
makeAuthorizationUrlWith Code Dict.empty
For example, to interact with a service implementing OpenID+Connect
you may require a different
token type and an extra query parameter as such:
makeAuthorizationUrlWith
(CustomResponse "code+id_token")
(Dict.fromList [ ( "resource", "001" ) ])
authorization
A parameterized AuthorizationResult
, see parseTokenWith
.
Empty
: means there were nothing (related to OAuth 2.0) to parseError
: a successfully parsed OAuth 2.0 errorSuccess
: a successfully parsed token and responsemakeTokenRequestWith : OAuth.GrantType -> Json.Decode.Decoder success -> Dict String String -> (Result Http.Error success -> msg) -> Authentication -> RequestParts msg
Like makeTokenRequest
, but gives you the ability to specify custom grant type and extra
fields to be set on the query.
makeTokenRequest : (Result Http.Error AuthenticationSuccess -> msg) -> Authentication -> RequestParts msg
makeTokenRequest =
makeTokenRequestWith
AuthorizationCode
defaultAuthenticationSuccessDecoder
Dict.empty
defaultExpiresInDecoder : Json.Decode.Decoder (Maybe Basics.Int)
Json decoder for the expiresIn
field.
defaultScopeDecoder : Json.Decode.Decoder (List String)
Json decoder for the scope
field (space-separated).
lenientScopeDecoder : Json.Decode.Decoder (List String)
Json decoder for the scope
(comma- or space-separated).
defaultTokenDecoder : Json.Decode.Decoder OAuth.Token
Json decoder for the access_token
field.
defaultRefreshTokenDecoder : Json.Decode.Decoder (Maybe OAuth.Token)
Json decoder for the refresh_token
field.
defaultErrorDecoder : Json.Decode.Decoder OAuth.ErrorCode
Json decoder for the error
field.
defaultErrorDescriptionDecoder : Json.Decode.Decoder (Maybe String)
Json decoder for the error_description
field.
defaultErrorUriDecoder : Json.Decode.Decoder (Maybe String)
Json decoder for the error_uri
field.
parseCodeWith : Parsers error success -> Url -> AuthorizationResultWith error success
See parseCode
, but gives you the ability to provide your own custom parsers.
{ codeParser : Url.Parser.Query.Parser (Maybe String)
, errorParser : Url.Parser.Query.Parser (Maybe OAuth.ErrorCode)
, authorizationSuccessParser : String -> Url.Parser.Query.Parser success
, authorizationErrorParser : OAuth.ErrorCode -> Url.Parser.Query.Parser error
}
Parsers used in the parseCode
function.
codeParser
: looks for a code
stringerrorParser
: looks for an error
to build a corresponding ErrorCode
authorizationSuccessParser
: selected when the tokenParser
succeeded to parse the remaining partsauthorizationErrorParser
: selected when the errorParser
succeeded to parse the remaining partsdefaultParsers : Parsers AuthorizationError AuthorizationSuccess
Default parsers according to RFC-6749.
defaultCodeParser : Url.Parser.Query.Parser (Maybe String)
Default code
parser according to RFC-6749.
defaultErrorParser : Url.Parser.Query.Parser (Maybe OAuth.ErrorCode)
Default error
parser according to RFC-6749.
defaultAuthorizationSuccessParser : String -> Url.Parser.Query.Parser AuthorizationSuccess
Default response success parser according to RFC-6749.
defaultAuthorizationErrorParser : OAuth.ErrorCode -> Url.Parser.Query.Parser AuthorizationError
Default response error parser according to RFC-6749.