- OAuth2 Haskell binding
- Tutorial for OAuth2 binding
- OAuth2 Identity Providers
- Tutorial for OAuth2 Identity Providers
- Demo Application
make rb
- Feel free send pull request or submit issue ticket.
Haskell oauth2 binding
make rb
make stack
as default and simplify default cabal
builds.
(IDPData _ _ _ _ userApi _)
doesn't seems scale.
yesod-auth-oauth2 is currently being excluded from stackage because it depends on hoauth2, which isn't compatible with the latest aeson and http-types.
See: commercialhaskell/stackage#845 and commercialhaskell/stackage#880 for context.
See also: freckle/yesod-auth-oauth2#48
Per Google document, there are 4 attributes returned. hoauth2 only has the first two.
access_token The token that can be sent to a Google API.
refresh_token A token that may be used to obtain a new access token, included by default for installed applications. Refresh tokens are valid until the user revokes access.
expires_in The remaining lifetime of the access token.
token_type Identifies the type of token returned. Currently, this field always has the value Bearer.
After bump conduit depends (issue 2), it failed to work with snap thus broken snaplet-oauth.
issue that cannot connect with linkedin
Feature request of a haskell implementation of OAuth2 JWT
https://developers.google.com/accounts/docs/OAuth2ServiceAccount
This is my bash version
jwt1=`echo -n '{"alg":"RS256","typ":"JWT"}' | openssl base64 -e`
jwt2=`echo -n '{\
"iss":"522156758812-u8hj8dhnk5br3vnpqqvuscievhbnl0gg@developer.gserviceaccount.com",\
"scope":"https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/datastore",\
"aud":"https://accounts.google.com/o/oauth2/token",\
"exp":'$(($(date +%s)+3600))',\
"iat":'$(date +%s)'}' | openssl base64 -e`
jwt3=`echo -n "$jwt1.$jwt2" | tr -d '\n' | tr -d '=' | tr '/+' '_-'`
jwt4=`echo -n "$jwt3" | openssl sha -sha256 -sign google.p12 | openssl base64 -e`
jwt5=`echo -n "$jwt4" | tr -d '\n' | tr -d '=' | tr '/+' '_-'`
curl -H "Content-type: application/x-www-form-urlencoded" -X POST "https://accounts.google.com/o/oauth2/token" -d \
"grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=$jwt3.$jwt5"
Testing it on google datastore
curl -X GET "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=$1"
curl -X GET "https://www.googleapis.com/oauth2/v2/userinfo?access_token=$1"
curl -H "Content-type: application/json" -H "Authorization: Bearer $1" -X POST "https://www.googleapis.com/datastore/v1beta1/datasets/gcl-11/blindWrite" -d \
'{
"mutation": {
"upsert": [
{
"key": {
"path": [
{
"kind": "person",
"name": "gert"
}
]
}
}
]
}
}'
Add APIs to allow user update HEADER of request.
The request was originally from a guy working with stripe oauth api.
it requires access_token
in the URL and don't escape the value.
Hi, I am having an issue similar to #47, and I am hoping you can help. I started off with the yesod-auth-oauth2 plugin, but I have had to do some "hacking" and I am basically writing my own Yesod plugin at this point. I can confirm that the end point produces valid JSON:
{
"token_type": "Bearer",
"scope": "User.Read",
"expires_in": "3599",
"ext_expires_in": "0",
"expires_on": "1511997544",
"not_before": "1511993644",
"resource": "223079d7-da8a-49da-b8c5-2394bb50c326",
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ing0Nzh4eU9wbHNNMUg3TlhrN1N4MTd4MXVwYyIsImtpZCI6Ing0Nzh4eU9wbHNNMUg3TlhrN1N4MTd4MXVwYyJ9.eyJhdWQiOiIyMjMwNzlkNy1kYThhLTQ5ZGEtYjhjNS0yMzk0YmI1MGMzMjYiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC82ZmM1NGIwZS1mOTIwLTRiYmMtYWZmMC01NDliN2IzMjIxMjYvIiwiaWF0IjoxNTExOTkzNjQ0LCJuYmYiOjE1MTE5OTM2NDQsImV4cCI6MTUxMTk5NzU0NCwiYWNyIjoiMSIsImFpbyI6IlkyTmdZSWo2VSs3RFdMMDRvaUNPNGVDZHRBdVpYZkd1Wi90OFREVm1zbnl6RTRob1d3SUEiLCJhbXIiOlsicHdkIl0sImFwcGlkIjoiMjIzMDc5ZDctZGE4YS00OWRhLWI4YzUtMjM5NGJiNTBjMzI2IiwiYXBwaWRhY3IiOiIxIiwiZmFtaWx5X25hbWUiOiJTb2xsYSIsImdpdmVuX25hbWUiOiJBbGV4YW5kZXIiLCJpcGFkZHIiOiIxNzMuMTY0LjgwLjIwMSIsIm5hbWUiOiJBbGV4YW5kZXIgU29sbGEiLCJvaWQiOiI3MzM5OWE3ZC03NzVhLTQ1NjEtYTFiYi0yZGUzN2IzZDk0ZmQiLCJzY3AiOiJVc2VyLlJlYWQiLCJzdWIiOiJudElGT1BBM0VUZUtkQjlneVVCYlZZZ2JtU19FOUliUlhwQzNFZ3NyZXhnIiwidGlkIjoiNmZjNTRiMGUtZjkyMC00YmJjLWFmZjAtNTQ5YjdiMzIyMTI2IiwidW5pcXVlX25hbWUiOiJvcHNAb2xkbWlsbGNlbnRlci5vcmciLCJ1cG4iOiJvcHNAb2xkbWlsbGNlbnRlci5vcmciLCJ1dGkiOiJyX2Y3TTRIQU5rZXhUdzdJb3NVUEFBIiwidmVyIjoiMS4wIn0.WYWegS4TXPacvpw0KYOcAsb2bk630ZToih1Lc-HYaIufx1ckx5eFnXsmvnJ1xJdLyMZhKWqbXnXbsZX448bzaoiDH8FHjLi7gBefd5dIT82kcITm30zqP6vh_a4nB_HJlNIbWKVHPR6a_2w8RPfvSq5C4gKqKiVgaIkCFGlgxsEfDqRgxpdu1AP556o_M-MqOQQdVxdr8yXNqqNTtHnx2MXPRR0i4edwacBeHkQyriwdkIjrpPgDSRNQ7TH3kOGhx21pyn8m9X46W8FLVReY7kxa5XLZ7XAW03dnS05lXww6ugkzv0dCJIghnZfLNgs5kEHykSNaYTOdQMcYf0vzAw",
"refresh_token": "AQABAAAAAABHh4kmS_aKT5XrjzxRAtHzXJ3BbzWYPG4vu9K044Ix9IHWke23NBsBd5RGdJ9p-htS0NdL6AUYxuHoWg-BFQvzntMF0gW_hzPOzR2dvsGbaK9AnkWHYjC0noT3-jNBtxsU6FA2OdsYRlAwCp0uTyULKZhVySllqSuG4eOWaKS23t55ZZzl3oZFYnafhPA-J13zG4ZKXCgKR99EMMnJ2N6d1YhMP7jqw7WT-W7oZH4fj1Czt1XIND130JZvD0fiEp8sK--my_5NuFlVFX0Mr6tk0PEJtWzpwP9eHfhZqje1Uh1sdm4QBIgSvyHuP7-usYdH9dTFXTeJ9nxcL6bUuDLIloG2j-mS1RlYKZjzZZmBcVHEfJ6b5KgJ1thr2rp8NTe_VwCFgdnFhoMrIsh4rKrL7KGP7-W51lwBnLjsotz-G8NLzzH7FEfpqN9MUDsaAZhyUUjyKykRE7FLLn5_GLLjws8yHyRNmZV630dPWetz3tSQqw3zivfzt6ams2VrLqmCpETUWNMoc4fz4xU0-q_QsrLVpNcDL4Z3Her_HMaOE6_PFLjh7a2S2GN_d8h_9IpKm9FQRez1nuwnP0CBN_J6FNKl9SNVaLRXkZD7rS7wANPhTYRpH3Z0fIrwzeMA7YdLoqml3_u_-LxaCxdWeFN06akRMS7gfwZIAkp5OTv_KavAjQDiHkAfoFPA3smWVVxJhe5GzdF3N7RMAF0JzEFgaVK9HaW1Lw3BDZkZ3uM5fKtwsMzajyDX39tgwg2BF5LbCLZ6aFNft_7KTJun2YjqMWkgREiwHLe8api3Uy5ftCAA",
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyMjMwNzlkNy1kYThhLTQ5ZGEtYjhjNS0yMzk0YmI1MGMzMjYiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC82ZmM1NGIwZS1mOTIwLTRiYmMtYWZmMC01NDliN2IzMjIxMjYvIiwiaWF0IjoxNTExOTkzNjQ0LCJuYmYiOjE1MTE5OTM2NDQsImV4cCI6MTUxMTk5NzU0NCwiYW1yIjpbInB3ZCJdLCJmYW1pbHlfbmFtZSI6IlNvbGxhIiwiZ2l2ZW5fbmFtZSI6IkFsZXhhbmRlciIsImlwYWRkciI6IjE3My4xNjQuODAuMjAxIiwibmFtZSI6IkFsZXhhbmRlciBTb2xsYSIsIm9pZCI6IjczMzk5YTdkLTc3NWEtNDU2MS1hMWJiLTJkZTM3YjNkOTRmZCIsInN1YiI6Im50SUZPUEEzRVRlS2RCOWd5VUJiVllnYm1TX0U5SWJSWHBDM0Vnc3JleGciLCJ0aWQiOiI2ZmM1NGIwZS1mOTIwLTRiYmMtYWZmMC01NDliN2IzMjIxMjYiLCJ1bmlxdWVfbmFtZSI6Im9wc0BvbGRtaWxsY2VudGVyLm9yZyIsInVwbiI6Im9wc0BvbGRtaWxsY2VudGVyLm9yZyIsInZlciI6IjEuMCJ9."
}
However, for some reason, the code I have written (following hoauth2 and yesod-auth-oauth2):
getAccessToken :: Manager -> OAuth2 -> ExchangeToken -> IO (OAuth2Result Errors OAuth2Token)
getAccessToken manager oa code = do
let (uri, defaultBody) = accessTokenUrl oa code
let body = defaultBody <> [ ("client_id", TE.encodeUtf8 . oauthClientId $ oa )
, ("client_secret", TE.encodeUtf8 . oauthClientSecret $ oa)
, ("resource", TE.encodeUtf8 . oauthClientId $ oa)
]
(response :: OAuth2Result Errors OAuth2Token) <- (parseResponseJSON . handleResponse)
<$> performOAuth2PostRequest manager oa uri body
putStrLn "Performing OAuth2 Post Request..."
case response of
Left e -> putStrLn . Text.pack . show $ e
Right token -> putStrLn . Text.pack . show $ token
return $ error "Need to finish processing the AccessToken"
performOAuth2PostRequest :: Manager -> OAuth2 -> URI -> PostBody -> IO (Response Lazy.ByteString)
performOAuth2PostRequest manager oa uri body = do
defaultReq <- uriToRequest uri
let addBasicAuth = applyBasicAuth (TE.encodeUtf8 . oauthClientId $ oa)
(TE.encodeUtf8 . oauthClientSecret $ oa)
let req = (addBasicAuth . updateRequestHeaders Nothing) defaultReq
HTTP.httpLbs (urlEncodedBody body req) manager
produces an error I don't understand. (Notice that the error is coming from the Left e
case):
OAuth2Error {error = Left "Decode error", errorDescription = Just "Error: Error in $: key \"error\" not present\n Original Response:\n\"{\\\"token_type\\\":\\\"Bearer\\\",\\\"scope\\\":\\\"User.Read\\\",\\\"expires_in\\\":\\\"3599\\\",\\\"ext_expires_in\\\":\\\"0\\\",\\\"expires_on\\\":\\\"1512000084\\\",\\\"not_before\\\":\\\"1511996184\\\",\\\"resource\\\":\\\"223079d7-da8a-49da-b8c5-2394bb50c326\\\",\\\"access_token\\\":\\\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ing0Nzh4eU9wbHNNMUg3TlhrN1N4MTd4MXVwYyIsImtpZCI6Ing0Nzh4eU9wbHNNMUg3TlhrN1N4MTd4MXVwYyJ9.eyJhdWQiOiIyMjMwNzlkNy1kYThhLTQ5ZGEtYjhjNS0yMzk0YmI1MGMzMjYiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC82ZmM1NGIwZS1mOTIwLTRiYmMtYWZmMC01NDliN2IzMjIxMjYvIiwiaWF0IjoxNTExOTk2MTg0LCJuYmYiOjE1MTE5OTYxODQsImV4cCI6MTUxMjAwMDA4NCwiYWNyIjoiMSIsImFpbyI6IlkyTUFndlgxVzV5dHprbmR6dktOVzNEbXpPNmNyZHFpYWU2Q3Izc2taRDFhZGFhdUJRQT0iLCJhbXIiOlsicHdkIl0sImFwcGlkIjoiMjIzMDc5ZDctZGE4YS00OWRhLWI4YzUtMjM5NGJiNTBjMzI2IiwiYXBwaWRhY3IiOiIxIiwiZmFtaWx5X25hbWUiOiJTb2xsYSIsImdpdmVuX25hbWUiOiJBbGV4YW5kZXIiLCJpcGFkZHIiOiIxNzMuMTY0LjgwLjIwMSIsIm5hbWUiOiJBbGV4YW5kZXIgU29sbGEiLCJvaWQiOiI3MzM5OWE3ZC03NzVhLTQ1NjEtYTFiYi0yZGUzN2IzZDk0ZmQiLCJzY3AiOiJVc2VyLlJlYWQiLCJzdWIiOiJudElGT1BBM0VUZUtkQjlneVVCYlZZZ2JtU19FOUliUlhwQzNFZ3NyZXhnIiwidGlkIjoiNmZjNTRiMGUtZjkyMC00YmJjLWFmZjAtNTQ5YjdiMzIyMTI2IiwidW5pcXVlX25hbWUiOiJvcHNAb2xkbWlsbGNlbnRlci5vcmciLCJ1cG4iOiJvcHNAb2xkbWlsbGNlbnRlci5vcmciLCJ1dGkiOiJPMUhRSmJXUWRFQ0VDOEVYVEZnUUFBIiwidmVyIjoiMS4wIn0.gU5dWrDvxkDdWcFURZHB3KJrJH0pWvulX-F1tGACiDU3MhWfcgMbomTM_TK3308cvBfBmmZD0lcgVX1BzErG5mtkcQ8edlSzidzwfLdRm-kLzA-obqOB1pFPgE2uPEsUCj6btcp3KdvNsqKxK5Ci2jY44RZOscn6RpwP18lC-qQokVDMvmAAg8tKCbSy2Km046UQSZOA1kShlnFKox0Pum12Ef-6KiCLjQ88E0xUWPKNyhbVUrl3MX_4OzIBjbd25rakzFejPLxdUnJbh0AbZD5xMNBSsPPxLsg97nvDGI6EPbChFpSvGGH-gS133s0666L4c7W55BZOHhlffh9WdQ\\\",\\\"refresh_token\\\":\\\"AQABAAAAAABHh4kmS_aKT5XrjzxRAtHzRbW1mWZjN0JXaJb5-m0xsf_PdQJrcS4NbMbypLzRbCeMpzjEc0YovwWzLLPNAqsdsRzAqKhHZX-jAz9hZeuRWMPvpakYqVgcjzBrm42_qPbcm1f_nauxLUnj13CH4pVUb4qQXBQ0C9_iXqgKkqsPecMCQZfkQth5OTZk2_A39L1yM4_6Hrgjdub-kxhDdqx5vPXE36cwdKFWt1qMjUfDI4vFBPt3C-wQYYT-AFcBFtZQbzxiCowYHEcds28550PrDdX_mDvUOuTkUpiT_J0Ec4dBxFHE4gbMNqG0v1F4oiNoOAcnwDFhXoqAOR1wfpwLhnAgRWHSWl-UqKe72wUAJdRCc0o0V6OD0-lr7CPNqcFp_uJHeRd3SzEKt3HRuPZPTi4duSBF1PoNwCT_qdOqBQd0oMP8j2kqYKhAhqEJo7QFID6ohrXI-m6kv-_n_dzC7zWFVCj7N6pwAplVzudjjYTYvldo-lMFYwA26v3axRua3WlfIPQ9Fw883q7tKDfrK0hJ4zIRtqf2CSZULiL6MbF-m_f_iu2LPtzPc1xuZtcYCvCK64EGYrlAP0g1PZ1TpHSvQOnUXNotsxtN9p8bkfVIfduOU7X1AierVgzQMT0h9uHsuYoHFxcZLe5zbZbQrVsYrJoqCCGnv8S6PWSawD5_M8crNyuqrDtTMxADK-kkcu1_Egjzb4IQuOaqIjizFE-oZZjq0S_lGNEOKleqynaVD3vmhxOmGkwl8B8LkJ55K-bjIioOnFBESA7u7P9zz3X_jCIG0khqkQSr2mxlhCAA\\\",\\\"id_token\\\":\\\"eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyMjMwNzlkNy1kYThhLTQ5ZGEtYjhjNS0yMzk0YmI1MGMzMjYiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC82ZmM1NGIwZS1mOTIwLTRiYmMtYWZmMC01NDliN2IzMjIxMjYvIiwiaWF0IjoxNTExOTk2MTg0LCJuYmYiOjE1MTE5OTYxODQsImV4cCI6MTUxMjAwMDA4NCwiYW1yIjpbInB3ZCJdLCJmYW1pbHlfbmFtZSI6IlNvbGxhIiwiZ2l2ZW5fbmFtZSI6IkFsZXhhbmRlciIsImlwYWRkciI6IjE3My4xNjQuODAuMjAxIiwibmFtZSI6IkFsZXhhbmRlciBTb2xsYSIsIm9pZCI6IjczMzk5YTdkLTc3NWEtNDU2MS1hMWJiLTJkZTM3YjNkOTRmZCIsInN1YiI6Im50SUZPUEEzRVRlS2RCOWd5VUJiVllnYm1TX0U5SWJSWHBDM0Vnc3JleGciLCJ0aWQiOiI2ZmM1NGIwZS1mOTIwLTRiYmMtYWZmMC01NDliN2IzMjIxMjYiLCJ1bmlxdWVfbmFtZSI6Im9wc0BvbGRtaWxsY2VudGVyLm9yZyIsInVwbiI6Im9wc0BvbGRtaWxsY2VudGVyLm9yZyIsInZlciI6IjEuMCJ9.\\\"}\"", errorUri = Nothing}
From the error, it appears that Data.Aeson's decode
function can't decode the JSON, but I am not sure why. Thinking that it might have to do with the JSON's extra fields, I made my own data AzureOAuth2Token
type like the JSON with FromJSON
instances, but the decode
fails for that type too. Am I doing something wrong?
In the examples for this library I see the following value oauthCallback
for Fitbit IDP:
oauthCallback = Just [uri|http://localhost:9988/oauth2/callback|]
If I understood OAuth2 correctly then this should be URL that is called by Fitbit server in form:
http://localhost:9988/oauth2/callback?code=Foo&state=Bar
But how could it call my localhost? Does it mean that I can't test OAuth2 locally and I need some deployed globally accessible server for this, that implements /oauth2/callback?code=Foo&state=Bar
endpoint?
I'm trying to use hoauth2 in a project with aeson 0.7, it'd be great if 0.3.6 were on Hackage.
look at stack docker manual.
I'm playing with the GMAIL API using hoauth2 and basically it doesn't work when the auth in included both in the URL and the header (as it is the case in your code). The implementation below works however (where only the header is used). Are you sure oauth2 allows to pass the auth in both in the header and url?
authGetBS' :: Manager -- ^ HTTP connection manager.
-> AccessToken
-> URI -- ^ URL
-> IO (OAuth2Result BSL.ByteString) -- ^ Response as ByteString
authGetBS' manager token url = liftM handleResponse go
where go = do
-- was: req <- parseUrl $ BS.unpack $ url `appendAccessToken` token
req <- parseUrl $ BS.unpack $ url
authenticatedRequest manager token HT.GET req
use case:
For checking organization membership github return a response status = 204
https://developer.github.com/v3/orgs/members/#check-membership
and verify IDP keys existence at start up.
add base uri
HttpClient.hs
x.y.z.Extra.hs
This works
validateToken :: BS.ByteString -> IO BL.ByteString
validateToken accessToken = doSimplePostRequest ("https://www.googleapis.com/oauth2/v1/tokeninfo",(accessTokenToParam accessToken))
But this does not?
userinfo :: BS.ByteString -> IO BL.ByteString
userinfo accessToken = doSimplePostRequest ("https://www.googleapis.com/oauth2/v2/userinfo",(accessTokenToParam accessToken))
*** Exception: StatusCodeException (Status {statusCode = 404, statusMessage = "Not Found"}) [("Cache-Control","no-cache, no-store, max-age=0, must-revalidate"),("Pragma","no-cache"),("Expires","Fri, 01 Jan 1990 00:00:00 GMT"),("Date","Sun, 09 Dec 2012 03:32:03 GMT"),("Content-Type","text/html; charset=UTF-8"),("X-Content-Type-Options","nosniff"),("X-Frame-Options","SAMEORIGIN"),("X-XSS-Protection","1; mode=block"),("Server","GSE"),("Transfer-Encoding","chunked"),("X-Response-Body-Start","Not Found")]
replace
-- | Parses a @OAuth2Result BSL.ByteString@ that contains not JSON but a Query String
parseResponseString :: FromJSON a
=> OAuth2Result BSL.ByteString
-> OAuth2Result a
parseResponseString (Left b) = Left b
parseResponseString (Right b) = case parseQuery $ BSL.toStrict b of
[] -> Left errorMessage
a -> case fromJSON $ queryToValue a of
Error _ -> Left errorMessage
Success x -> Right x
where
queryToValue = Object . HM.fromList . map paramToPair
paramToPair (k, mv) = (T.decodeUtf8 k, maybe Null (String . T.decodeUtf8) mv)
errorMessage = "hoauth2.HttpClient.parseResponseString/Could not decode URL: " `BSL.append` b
Hey,
Just wondering if you have any idea why i'm receiving this error when trying to authenticate with Github. I'm using yesod-auth-oauth2, which uses hoauth2. This worked several months ago. I've just recently updated all of my deps to the latest. I will try to figure it out myself but, just wondering if you've ever seen this.
InvalidProfileResponse "github" "hoauth2.HttpClient.parseResponseJSON/Could not decode JSON:
edit: Seems more like a yesod-auth-oauth2 issue.
Hello.
I tried to authenticate with StackExchange.
Then, I have encountered problem similar to #32.
It responses url encoded access_token
.
I'm getting error Could not decode JSONaccess_token=xxxxx&expires=86400
.
And, I can't find endpoint that returns json.
Is there any fix about this issue ?
Should I change OAuth2.hs as @katyo did, and send PR ?
I just tried an example of OAuth with Google and after login I get an error:
$ ./test-google
https://accounts.google.com/o/oauth2/auth?client_id=726618178530.apps.googleusercontent.com&response_type=code&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile
visit the url and paste code here:
< I paste code here >
AccessToken: AccessToken {accessToken = "__REMOVED__", refreshToken = Just "__REMOVED__"}
Left (Chunk "Could not decode JSON" (Chunk "{\n \"issued_to\": \"726618178530.apps.googleusercontent.com\",\n \"audience\": \"726618178530.apps.googleusercontent.com\",\n \"user_id\": \"__REMOVED__\",\n \"scope\": \"https://www.googleapis.com/auth/userinfo.profile\",\n \"expires_in\": 3600,\n \"access_type\": \"offline\"\n}\n" Empty))
Left (Chunk "Could not decode JSON" (Chunk "{\n \"issued_to\": \"726618178530.apps.googleusercontent.com\",\n \"audience\": \"726618178530.apps.googleusercontent.com\",\n \"user_id\": \"__REMOVED__\",\n \"scope\": \"https://www.googleapis.com/auth/userinfo.profile\",\n \"expires_in\": 3599,\n \"access_type\": \"offline\"\n}\n" Empty))
Left (Chunk "Could not decode JSON" (Chunk "{\n \"id\": \"__REMOVED__\",\n \"locale\": \"pl\"\n}\n" Empty))
Left (Chunk "Could not decode JSON" (Chunk "{\n \"id\": \"102710679971281134014\",\n \"locale\": \"pl\"\n}\n" Empty))
My application and the test included in the repo now returns the following error when authenticating with Google: "OAuth 2 parameters can only have a single value: client_id"
I've just tried the test included in this repo and it seems to fail as well.
It is likely that hoauth2 isn't fully compliant with the standard. This issue pops up now because Google changed something recently -- see http://stackoverflow.com/questions/29694840/googles-openid-connect-says-oauth-2-parameters-can-only-have-a-single-value-c
I'm not sure it is a bug in your code or rather in my facebook app configuration. When I run it I get an error:
➜ hoauth2 git:(master) ✗ ./example/Facebook/test
"https://www.facebook.com/dialog/oauth?client_id=317076475103268&response_type=code&redirect_uri=http%3A%2F%2Flocalhost&scope=user_about_me%2Cemail"
visit the url and paste code here:
__REMOVED__
test: StatusCodeException (Status {statusCode = 400, statusMessage = "Bad Request"}) [("Access-Control-Allow-Origin","*"),("Cache-Control","no-store"),("Content-Type","application/json"),("Expires","Sat, 01 Jan 2000 00:00:00 GMT"),("Pragma","no-cache"),("WWW-Authenticate","OAuth \"Facebook Platform\" \"invalid_code\" \"Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request\""),("X-FB-Rev","924754"),("X-FB-Debug","__REMOVED__"),("Date","Tue, 03 Sep 2013 08:37:37 GMT"),("Connection","keep-alive"),("Content-Length","190"),("X-Response-Body-Start","{\"error\":{\"message\":\"Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request\",\"type\":\"OAuthException\",\"code\":100}}")] (CJ {expose = []})
I have listed http://localhost in "Valid OAuth redirect URIs" in facebook app configuration
I tried to set up a working Google integration today and ran into this error when trying to refresh an access token (I could not get the examples to build and refresh properly, either):
Left "hoauth2.HttpClient.parseResponseString/Could not decode URL: {\n \"access_token\" : \"lt_...EFpxs_m\",\n \"expires_in\" : 3600,\n \"id_token\" : \"eyJhbGc...6VO_CRWw\",\n \"token_type\" : \"Bearer\"\n}"
I was puzzled, as fetching an access token works perfectly well. I found that this function relies on doFlexiblePostRequest
, which tries first to parse the JSON and then to parse the URL. So this error is misleading. By forcing it to try the JSON, I saw this:
Left "hoauth2.HttpClient.parseResponseJSON/Could not decode JSON: {\n \"access_token\": \"y...cyuCn9YY6SpPd0P4GHlWpcBgg9_o8uVX2RiBHYY0As2Y8wmmHUmO\",\n \"token_type\": \"Bearer\",\n \"expires_in\": 3600\n}\n, with error:
Error in $: expected Text, encountered Object"
This was confusing, until I realized the instance of FromJSON
for AccessToken
, the return type for this function, is just this:
newtype AccessToken = AccessToken { atoken :: Text } deriving (Show, FromJSON, ToJSON)
But what Google sends over is this:
{
\n \"access_token\": \"y...G\",
\n \"token_type\": \"Bearer\",
\n \"expires_in\": 3600\n}
}
To remedy the problem, I made a new data type that I could derive instances from just as is done with the overall OAuth2Token
. The code written below works successfully to refresh tokens.
data OAuthRefreshToken = OAuthRefreshToken {
accessToken :: AccessToken
, tokenType :: Maybe Text
, expiresIn :: Maybe Int
} deriving (Show, Generic)
instance FromJSON OAuthRefreshToken where
parseJSON = genericParseJSON defaultOptions { fieldLabelModifier = camelTo2 '_' }
instance ToJSON OAuthRefreshToken where
toEncoding = genericToEncoding defaultOptions { fieldLabelModifier = camelTo2 '_' }
fetchRefreshToken ::
Manager
-> OAuth2
-> RefreshToken
-> IO (OAuth2Result OAuthRefreshToken)
fetchRefreshToken manager oa token = doJSONPostRequest manager oa uri body
where (uri, body) = refreshAccessTokenUrl oa token
I don't know what you'd prefer to do as the answer to this problem so I haven't submitted a PR. Instead, I'm sharing my solution here.
Cheers!
Thomas
Keys.hs
in order to make the demo server workmake open data type (IDP
now is so called close data type, cause whenever needs a new IDP, the data structure needs to be update)
Hi,
can you update the dependencies for this cabal package?
I tested it with:
http-conduit-1.6.0
conduit-0.5.2.3
http-types-0.7.2
and found no problems.
Certain responses (like 401) from the OAuth provider can cause exceptions to be thrown when calling authGetJSON
. This is problematic because Yesod displays exceptions to the end user automatically, which can contain the URL including GET variables with secret access tokens.
As a temporary fix, I've wrapped authGetJSON
with catchAny
from Control.Exception.Enclosed
.
authGetJSON' manager token url = catchAny (authGetJSON manager token url) handler
where
handler e = return $ Left $ BSL8.pack $ show e
I'm glad to use your library in my project but I need also integration with Twitter. Is there any chance for the Twitter implementation any time soon?
IDPData
to have functions for fetch tokenDamian Soriano (Gmail) to me Aug 8
Hi!I am creating a Haskell application that connects go bigquery. I wanted to use your OAuth2 lib to do that: https://github.com/freizl/hoauth2
I search a little bit in the doc and example and I didn't found anything about service account authentication (https://developers.google.com/identity/protocols/OAuth2ServiceAccount). I was wondering if that feature is already implemented in the lib or not, since I found no example about that.
Many thanks
Regards
After upgrading hoauth2 to anything above 0.5.5 I receive the following error from Facebook when making a POST request to retrieve an access token:
{\"error\":{\"message\":\"Missing client_id parameter.\",\"type\":\"OAuthException\",\"code\":101,\"fbtrace_id\":\"AKOkEeCq8LL\"}}
The code that invokes this didn't change since the upgrade, and is as follows:
get "/oauth/facebook" $ do
r <- request
code <- param "code"
mgr <- liftIO $ newManager tlsManagerSettings
let (url, body) = accessTokenUrl oauthConf code
resp <- liftIO $ doJSONPostRequest mgr oauthConf url (body ++ [("state", "test")])
case (resp :: OAuth2Result AccessToken) of
Right token -> do
user <- liftIO $ userinfo' mgr token
Was the client_id
removed from being set in the query parameters in the doJSONPostRequest
? I took a look at the source code and it doesn't look like that method or the helper methods it invokes changes.
I was confused, but Facebook's response body contains URL-encoded string, not JSON-encoded.
I got an error:
Could not decode JSONaccess_token=…&expires=…
Per platform, including
I've recently starting getting this error trying to authenticate against github:
InternalIOException data: end of file
I suspect it is related to: https://github.com/snoyberg/http-conduit/issues/136
It doesn't need monad-control
in https://github.com/freizl/hoauth2/blob/master/hoauth2.cabal, it's not imported anywhere. You should also check the other dependencies.
I'm trying to use authPostBS
to send emails through Gmail API. I'm running into two issues:
authPostBS
adds the token to the body and the headers, which I believe deviates from the OAuth 2 specs.authPost*
family of functions, there is no way to set the content type header as far as I see.To make my code work I had to implement something like below (see authPostBS99
). I also had to change the updateRequestHeaders
function to add the missing application/json
. This is not ideal as it breaks backward compatibility.
Is there something I'm missing here? Has this code been tested with Google API?
-- | Conduct POST request.
authPostBS99 :: FromJSON err => Manager -- ^ HTTP connection manager.
-> AccessToken
-> URI
-> BSL.ByteString
-> IO (OAuth2Result err BSL.ByteString) -- ^ Response as ByteString
authPostBS99 manager token url body = do
req <- uriToRequest url
authRequest req upReq manager
where upBody req = req { requestBody = RequestBodyLBS body }
upHeaders = updateRequestHeaders (Just token) . setMethod HT.POST
upReq = upHeaders . upBody
Build test cases when CI to remind people fix test cases.
tips: copy the Keys.hs
while it doesn't exists
When I'm adding hoauth2
library to the dependencies of my project, I see the following warning when I build project with stack-1.7.1
:
Warning: Unable to find a known candidate for the Cabal entry Keys, but did find:
* Keys.hs.sample If you are using a custom preprocessor for this module with its own file extension, consider adding the file(s) to your .cabal under extra-source-files.
Version of hoauth2
library: 1.7.2
OAuth2Error error
instead of OAuth2Error
?
data TokenError = A | B | C | UnknownTokenErrorCode Text
instance FromJSON TokenError where
parse to token error otherwise 'UnknownTokenErrorCode Text'
data OAuth2Error e =
{ error :: e
, description :: Maybe Text
, error_uri :: Maybe URI
}
but the problem is e
has to be able to parsed from JSON. Given e
can be any type, doing error :: Either Text e
feels safer. Unless
data OAuth2ErrorCode = AuthCode.Error | TokenCode.Error
AuthCode.Error = A | B | C | UnknownCode Text
TokenCode.Error = A | B | D | E | UnknownCode Text
data OAuth2Error (e :: OAuth2ErrorCode) =
{ error :: e
, description :: Maybe Text
, error_uri :: Maybe URI
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.