-
-
Notifications
You must be signed in to change notification settings - Fork 53
/
StackExchange.hs
93 lines (81 loc) · 3.05 KB
/
StackExchange.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
{-# LANGUAGE QuasiQuotes #-}
-- | [StackExchange authentication guide](https://api.stackexchange.com/docs/authentication)
--
-- * [StackExchange Apps page](https://stackapps.com/apps/oauth)
module Network.OAuth2.Provider.StackExchange where
import Control.Monad.IO.Class (MonadIO (..))
import Control.Monad.Trans.Except (ExceptT (..))
import Data.Aeson
import Data.ByteString (ByteString)
import Data.ByteString.Lazy.Char8 qualified as BSL
import Data.Map.Strict qualified as Map
import Data.Set qualified as Set
import Data.Text.Lazy (Text)
import GHC.Generics
import Network.HTTP.Conduit (Manager)
import Network.OAuth.OAuth2
import Network.OAuth2.Experiment
import Network.OAuth2.Provider
import URI.ByteString (URI)
import URI.ByteString.QQ (uri)
-- fix key from your application edit page
-- https://stackapps.com/apps/oauth
stackexchangeAppKey :: ByteString
stackexchangeAppKey = ""
userInfoEndpoint :: URI
userInfoEndpoint =
appendQueryParams
[ ("key", stackexchangeAppKey)
, ("site", "stackoverflow")
]
[uri|https://api.stackexchange.com/2.2/me|]
sampleStackExchangeAuthorizationCodeApp :: AuthorizationCodeApplication
sampleStackExchangeAuthorizationCodeApp =
AuthorizationCodeApplication
{ acClientId = ""
, acClientSecret = ""
, acScope = Set.empty
, acAuthorizeState = "CHANGE_ME"
, acAuthorizeRequestExtraParams = Map.empty
, acRedirectUri = [uri|http://localhost|]
, acName = "sample-stackexchange-authorization-code-app"
, acTokenRequestAuthenticationMethod = ClientSecretPost
}
fetchUserInfo ::
(MonadIO m, HasUserInfoRequest a, FromJSON b) =>
IdpApplication i a ->
Manager ->
AccessToken ->
ExceptT BSL.ByteString m b
fetchUserInfo = conduitUserInfoRequestWithCustomMethod (authGetJSONWithAuthMethod AuthInRequestQuery)
defaultStackExchangeIdp :: Idp StackExchange
defaultStackExchangeIdp =
Idp
{ -- Only StackExchange has such specical app key which has to be append in userinfo uri.
-- I feel it's not worth to invent a way to read from config
-- file which would break the generic of Idp data type.
-- Until discover a easier way, hard code for now.
idpUserInfoEndpoint = userInfoEndpoint
, idpAuthorizeEndpoint = [uri|https://stackexchange.com/oauth|]
, idpTokenEndpoint = [uri|https://stackexchange.com/oauth/access_token|]
, idpDeviceAuthorizationEndpoint = Nothing
}
data StackExchangeResp = StackExchangeResp
{ hasMore :: Bool
, quotaMax :: Integer
, quotaRemaining :: Integer
, items :: [StackExchangeUser]
}
deriving (Show, Generic)
data StackExchangeUser = StackExchangeUser
{ userId :: Integer
, displayName :: Text
, profileImage :: Text
}
deriving (Show, Generic)
instance FromJSON StackExchangeResp where
parseJSON = genericParseJSON defaultOptions {fieldLabelModifier = camelTo2 '_'}
instance FromJSON StackExchangeUser where
parseJSON = genericParseJSON defaultOptions {fieldLabelModifier = camelTo2 '_'}
appendStackExchangeAppKey :: URI -> ByteString -> URI
appendStackExchangeAppKey useruri k = appendQueryParams [("key", k)] useruri