hoauth2-providers 0.8.0 → 0.9.0
raw patch · 20 files changed
+232/−78 lines, 20 filesdep ~hoauth2dep ~uri-bytestring
Dependency ranges changed: hoauth2, uri-bytestring
Files
- CHANGELOG.md +45/−0
- README.md +1/−0
- hoauth2-providers.cabal +39/−33
- src/Network/OAuth2/Provider.hs +1/−0
- src/Network/OAuth2/Provider/Auth0.hs +2/−2
- src/Network/OAuth2/Provider/AzureAD.hs +2/−2
- src/Network/OAuth2/Provider/DropBox.hs +2/−2
- src/Network/OAuth2/Provider/Facebook.hs +4/−4
- src/Network/OAuth2/Provider/Fitbit.hs +2/−2
- src/Network/OAuth2/Provider/GitHub.hs +2/−2
- src/Network/OAuth2/Provider/Google.hs +2/−2
- src/Network/OAuth2/Provider/Linear.hs +101/−0
- src/Network/OAuth2/Provider/LinkedIn.hs +6/−6
- src/Network/OAuth2/Provider/Okta.hs +7/−3
- src/Network/OAuth2/Provider/Slack.hs +2/−2
- src/Network/OAuth2/Provider/StackExchange.hs +8/−11
- src/Network/OAuth2/Provider/Twitter.hs +2/−2
- src/Network/OAuth2/Provider/Weibo.hs +2/−2
- src/Network/OAuth2/Provider/ZOHO.hs +2/−2
- src/Network/OIDC/WellKnown.hs +0/−1
+ CHANGELOG.md view
@@ -0,0 +1,45 @@+# hoauth2-provider Changelog++## 0.9.0 (2025-10-05)++- Add Linear provider+- Fix `OktaUser` default values; parameterize StackExchange key+- Update Facebook and LinkedIn configurations+- Allow `uri-bytestring`-0.4 and `microlens`-0.5+- Internal refactors and cleanups++## 0.8.0 (2024-11-19)++* Updated crypton dependency to version 1.0++## 0.7.0 (2024-03-07)++* Replaced cryptonite dependency with crypton++## 0.6.0 (2024-01-19)++* Updated base64 dependency to version 1.0++## 0.5.0 (2023-12-30)++* Updated aeson dependency to version 2.2+* Updated binary dependency to version 0.10+* Updated bytestring dependency to version 0.12+* Updated container dependency to version 0.7++## 0.4.0 (2023-11-17)++* Updated text dependency to version 2.2++## 0.3.0 (2023-10-26)++* Added mkAzureADIdp helper function+* Renamed Experiment.GrantType to Experiment.Grants+* Added capability to fetch deviceAuthorizationEndpoint from OIDC configuration+* Added generator for all IdP names+* Added IdP-initiated login flow demo for Okta++## 0.2.0 (2022-11-17)++* Updated endpoint domain for Azure IdP+* Renamed `DropboxName` to `DropboxUserName`
+ README.md view
@@ -0,0 +1,1 @@+See [file:./src/Network/OAuth2/Provider](./src/Network/OAuth2/Provider) the list of supported Identity Providers.
hoauth2-providers.cabal view
@@ -1,19 +1,24 @@-cabal-version: 2.4-name: hoauth2-providers-version: 0.8.0-synopsis: OAuth2 Identity Providers-description: A few well known Identity Providers-homepage: https://github.com/freizl/hoauth2-license: MIT-license-file: LICENSE-author: Haisheng Wu-maintainer: Haisheng Wu <freizl@gmail.com>-copyright: Haisheng Wu-category: Network-build-type: Simple-stability: Beta-tested-with: GHC <=9.6.1+cabal-version: 2.4+name: hoauth2-providers+version: 0.9.0+synopsis: OAuth2 Identity Providers+description: A few well known Identity Providers+homepage: https://github.com/freizl/hoauth2+license: MIT+license-file: LICENSE+author: Haisheng Wu+maintainer: Haisheng Wu <freizl@gmail.com>+copyright: Haisheng Wu+category: Network+build-type: Simple+stability: Beta+tested-with:+ GHC ==8.10.7 || ==9.0.2 || ==9.2.7 || ==9.4.4 || ==9.6.1 || ==9.8.2 +extra-source-files:+ CHANGELOG.md+ README.md+ source-repository head type: git location: git://github.com/freizl/hoauth2.git@@ -43,6 +48,7 @@ Network.OAuth2.Provider.Fitbit Network.OAuth2.Provider.GitHub Network.OAuth2.Provider.Google+ Network.OAuth2.Provider.Linear Network.OAuth2.Provider.LinkedIn Network.OAuth2.Provider.Okta Network.OAuth2.Provider.Slack@@ -53,21 +59,21 @@ Network.OIDC.WellKnown build-depends:- , aeson >=2.0 && <2.3- , base >=4.11 && <5- , bytestring >=0.9 && <0.13+ , aeson >=2.0 && <2.3+ , base >=4.11 && <5+ , bytestring >=0.9 && <0.13 , containers ^>=0.6- , crypton >=0.32 && <1.1- , hoauth2 >=2.9 && <2.15- , HsOpenSSL >=0.11 && <0.12- , http-conduit >=2.1 && <2.4- , http-types >=0.11 && <0.13- , jose-jwt >=0.10 && <0.11- , mtl >=2.0 && <2.4- , text >=2.0 && <2.3- , time >=1.12 && <1.13- , transformers >=0.4 && <0.7- , uri-bytestring >=0.2.3 && <0.4+ , crypton >=0.32 && <1.1+ , hoauth2 >=2.9 && <2.16+ , HsOpenSSL >=0.11 && <0.12+ , http-conduit >=2.1 && <2.4+ , http-types >=0.11 && <0.13+ , jose-jwt >=0.10 && <0.11+ , mtl >=2.0 && <2.4+ , text >=2.0 && <2.3+ , time >=1.12 && <1.13+ , transformers >=0.4 && <0.7+ , uri-bytestring >=0.4 && <0.5 , uri-bytestring-aeson ^>=0.1 ghc-options:@@ -84,11 +90,11 @@ hs-source-dirs: test ghc-options: -Wall build-depends:- , aeson >=2.0 && <2.3- , base >=4.11 && <5+ , aeson >=2.0 && <2.3+ , base >=4.11 && <5 , hoauth2-providers- , hspec >=2 && <3- , uri-bytestring >=0.2.3 && <0.4+ , hspec >=2 && <3+ , uri-bytestring >=0.4 && <0.5 other-modules: Network.OIDC.WellKnownSpec default-language: Haskell2010
src/Network/OAuth2/Provider.hs view
@@ -10,6 +10,7 @@ | Fitbit | GitHub | Google+ | Linear | LinkedIn | Okta | Slack
src/Network/OAuth2/Provider/Auth0.hs view
@@ -16,7 +16,7 @@ import Data.Text.Lazy (Text) import GHC.Generics import Network.HTTP.Conduit-import Network.OAuth.OAuth2+import Network.OAuth2 import Network.OAuth2.Experiment import Network.OAuth2.Provider import Network.OIDC.WellKnown@@ -32,7 +32,7 @@ , acRedirectUri = [uri|http://localhost|] , acName = "sample-auth0-authorization-code-app" , acAuthorizeRequestExtraParams = Map.empty- , acTokenRequestAuthenticationMethod = ClientSecretBasic+ , acClientAuthenticationMethod = ClientSecretBasic } fetchUserInfo ::
src/Network/OAuth2/Provider/AzureAD.hs view
@@ -12,7 +12,7 @@ import Data.Text.Lazy (Text) import GHC.Generics import Network.HTTP.Conduit (Manager)-import Network.OAuth.OAuth2+import Network.OAuth2 import Network.OAuth2.Experiment import Network.OAuth2.Provider import Network.OIDC.WellKnown@@ -32,7 +32,7 @@ , acAuthorizeRequestExtraParams = Map.empty , acRedirectUri = [uri|http://localhost|] , acName = "sample-azure-authorization-code-app"- , acTokenRequestAuthenticationMethod = ClientSecretBasic+ , acClientAuthenticationMethod = ClientSecretBasic } fetchUserInfo ::
src/Network/OAuth2/Provider/DropBox.hs view
@@ -12,7 +12,7 @@ import Data.Text.Lazy (Text) import GHC.Generics import Network.HTTP.Conduit (Manager)-import Network.OAuth.OAuth2+import Network.OAuth2 import Network.OAuth2.Experiment import Network.OAuth2.Provider import URI.ByteString.QQ@@ -27,7 +27,7 @@ , acAuthorizeRequestExtraParams = Map.empty , acRedirectUri = [uri|http://localhost|] , acName = "sample-dropbox-authorization-code-app"- , acTokenRequestAuthenticationMethod = ClientSecretBasic+ , acClientAuthenticationMethod = ClientSecretBasic } fetchUserInfo ::
src/Network/OAuth2/Provider/Facebook.hs view
@@ -12,7 +12,7 @@ import Data.Text.Lazy (Text) import GHC.Generics import Network.HTTP.Conduit (Manager)-import Network.OAuth.OAuth2+import Network.OAuth2 import Network.OAuth2.Experiment import Network.OAuth2.Provider import URI.ByteString.QQ@@ -22,12 +22,12 @@ AuthorizationCodeApplication { acClientId = "" , acClientSecret = ""- , acScope = Set.fromList ["user_about_me", "email"]+ , acScope = Set.fromList ["public_profile", "email"] , acAuthorizeRequestExtraParams = Map.empty , acAuthorizeState = "CHANGE_ME" , acRedirectUri = [uri|http://localhost|] , acName = "sample-facebook-authorization-code-app"- , acTokenRequestAuthenticationMethod = ClientSecretPost+ , acClientAuthenticationMethod = ClientSecretPost } fetchUserInfo ::@@ -43,7 +43,7 @@ Idp { idpUserInfoEndpoint = [uri|https://graph.facebook.com/me?fields=id,name,email|] , idpAuthorizeEndpoint = [uri|https://www.facebook.com/dialog/oauth|]- , idpTokenEndpoint = [uri|https://graph.facebook.com/v2.3/oauth/access_token|]+ , idpTokenEndpoint = [uri|https://graph.facebook.com/v22.0/oauth/access_token|] , idpDeviceAuthorizationEndpoint = Nothing }
src/Network/OAuth2/Provider/Fitbit.hs view
@@ -12,7 +12,7 @@ import Data.Set qualified as Set import Data.Text.Lazy (Text) import Network.HTTP.Conduit (Manager)-import Network.OAuth.OAuth2+import Network.OAuth2 import Network.OAuth2.Experiment import Network.OAuth2.Provider import URI.ByteString.QQ@@ -27,7 +27,7 @@ , acAuthorizeState = "CHANGE_ME" , acRedirectUri = [uri|http://localhost|] , acName = "sample-fitbit-authorization-code-app"- , acTokenRequestAuthenticationMethod = ClientSecretBasic+ , acClientAuthenticationMethod = ClientSecretBasic } fetchUserInfo ::
src/Network/OAuth2/Provider/GitHub.hs view
@@ -12,7 +12,7 @@ import Data.Text.Lazy (Text) import GHC.Generics import Network.HTTP.Conduit (Manager)-import Network.OAuth.OAuth2+import Network.OAuth2 import Network.OAuth2.Experiment import Network.OAuth2.Provider import URI.ByteString.QQ@@ -27,7 +27,7 @@ , acAuthorizeRequestExtraParams = Map.empty , acRedirectUri = [uri|http://localhost|] , acName = "sample-github-authorization-code-app"- , acTokenRequestAuthenticationMethod = ClientSecretBasic+ , acClientAuthenticationMethod = ClientSecretBasic } fetchUserInfo ::
src/Network/OAuth2/Provider/Google.hs view
@@ -23,7 +23,7 @@ import Jose.Jws import Jose.Jwt import Network.HTTP.Conduit (Manager)-import Network.OAuth.OAuth2+import Network.OAuth2 import Network.OAuth2.Experiment import Network.OAuth2.Provider import OpenSSL.EVP.PKey (toKeyPair)@@ -50,7 +50,7 @@ , acAuthorizeState = "CHANGE_ME" , acRedirectUri = [uri|http://localhost|] , acAuthorizeRequestExtraParams = Map.empty- , acTokenRequestAuthenticationMethod = ClientSecretBasic+ , acClientAuthenticationMethod = ClientSecretBasic } -- * Service Account
+ src/Network/OAuth2/Provider/Linear.hs view
@@ -0,0 +1,101 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}++-- | [OAuth 2.0 Authentication](https://developers.linear.app/docs/oauth/authentication)+module Network.OAuth2.Provider.Linear where++import Control.Monad.IO.Class (MonadIO (..))+import Control.Monad.Trans.Except (ExceptT (..))+import Data.Aeson+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.OAuth2+import Network.OAuth2.Experiment+import Network.OAuth2.Provider+import URI.ByteString.QQ++sampleLinearAuthorizationCodeApp :: AuthorizationCodeApplication+sampleLinearAuthorizationCodeApp =+ AuthorizationCodeApplication+ { acClientId = ""+ , acClientSecret = ""+ , acScope = Set.empty+ , acAuthorizeState = "CHANGE_ME"+ , acAuthorizeRequestExtraParams = Map.empty+ , acRedirectUri = [uri|http://localhost|]+ , acName = "sample-linear-authorization-code-app"+ , acClientAuthenticationMethod = ClientSecretPost+ }++fetchUserInfo ::+ (MonadIO m, HasUserInfoRequest a, FromJSON b) =>+ IdpApplication i a ->+ Manager ->+ AccessToken ->+ ExceptT BSL.ByteString m b+fetchUserInfo =+ conduitUserInfoRequestWithCustomMethod+ (\mgr at url -> authPostJSON mgr at url [("query", "{ viewer { id name email } }")])++defaultLinearIdp :: Idp Linear+defaultLinearIdp =+ Idp+ { idpAuthorizeEndpoint = [uri|https://linear.app/oauth/authorize|]+ , idpTokenEndpoint = [uri|https://api.linear.app/oauth/token|]+ , idpUserInfoEndpoint = [uri|https://api.linear.app/graphql|]+ , idpDeviceAuthorizationEndpoint = Nothing+ }++-- sample response+-- {+-- "data": {+-- "viewer": {+-- "id": "***",+-- "name": "Hai W.",+-- "email": "freizl.em@gmail.com"+-- }+-- }+-- }++newtype LinearResponse a = LinearResponse+ { _data :: LinearData a+ }+ deriving (Show, Generic)++-- Define a data type for the "data" part of the JSON, which contains the viewer+newtype LinearData a = LinearData+ { _viewer :: a+ }+ deriving (Show, Generic)++data LinearUser = LinearUser+ { id :: Text+ , name :: Text+ , email :: Text+ }+ deriving (Show, Generic)++instance FromJSON a => FromJSON (LinearData a) where+ parseJSON = withObject "Data" $ \v ->+ LinearData+ <$> v .: "viewer"++-- Define a custom FromJSON instance for Response to parse the outer structure+instance FromJSON a => FromJSON (LinearResponse a) where+ parseJSON = withObject "Response" $ \v ->+ LinearResponse+ <$> v .: "data"++instance FromJSON LinearUser where+ parseJSON = withObject "LinearUser" $ \v ->+ LinearUser+ <$> v .: "id"+ <*> v .: "name"+ <*> v .: "email"++getUser :: LinearResponse LinearUser -> LinearUser+getUser (LinearResponse (LinearData a)) = a
src/Network/OAuth2/Provider/LinkedIn.hs view
@@ -12,7 +12,7 @@ import Data.Text.Lazy (Text) import GHC.Generics import Network.HTTP.Conduit (Manager)-import Network.OAuth.OAuth2+import Network.OAuth2 import Network.OAuth2.Experiment import Network.OAuth2.Provider import URI.ByteString.QQ (uri)@@ -22,12 +22,12 @@ AuthorizationCodeApplication { acClientId = "" , acClientSecret = ""- , acScope = Set.fromList ["r_liteprofile"]+ , acScope = Set.fromList ["openid", "profile"] , acAuthorizeState = "CHANGE_ME" , acAuthorizeRequestExtraParams = Map.empty , acRedirectUri = [uri|http://localhost|] , acName = "sample-linkedin-authorization-code-app"- , acTokenRequestAuthenticationMethod = ClientSecretPost+ , acClientAuthenticationMethod = ClientSecretPost } fetchUserInfo ::@@ -41,15 +41,15 @@ defaultLinkedInIdp :: Idp LinkedIn defaultLinkedInIdp = Idp- { idpUserInfoEndpoint = [uri|https://api.linkedin.com/v2/me|]+ { idpUserInfoEndpoint = [uri|https://api.linkedin.com/v2/userinfo|] , idpAuthorizeEndpoint = [uri|https://www.linkedin.com/oauth/v2/authorization|] , idpTokenEndpoint = [uri|https://www.linkedin.com/oauth/v2/accessToken|] , idpDeviceAuthorizationEndpoint = Nothing } data LinkedInUser = LinkedInUser- { localizedFirstName :: Text- , localizedLastName :: Text+ { name :: Text+ , email :: Text } deriving (Show, Generic, Eq)
src/Network/OAuth2/Provider/Okta.hs view
@@ -24,7 +24,7 @@ import Jose.Jws import Jose.Jwt import Network.HTTP.Conduit (Manager)-import Network.OAuth.OAuth2+import Network.OAuth2 import Network.OAuth2.Experiment import Network.OAuth2.Provider import Network.OIDC.WellKnown@@ -40,7 +40,7 @@ , acAuthorizeRequestExtraParams = Map.empty , acRedirectUri = [uri|http://localhost|] , acName = "sample-okta-authorization-code-app"- , acTokenRequestAuthenticationMethod = ClientSecretBasic+ , acClientAuthenticationMethod = ClientSecretBasic } fetchUserInfo ::@@ -89,8 +89,12 @@ tToSeconds = formatTime defaultTimeLocale "%s" data OktaUser = OktaUser- { name :: Text+ { sub :: Text+ , email :: Text , preferredUsername :: Text+ , givenName :: Text+ , familyName :: Text+ , emailVerified :: Bool } deriving (Show, Generic)
src/Network/OAuth2/Provider/Slack.hs view
@@ -14,7 +14,7 @@ import Data.Text.Lazy (Text) import GHC.Generics import Network.HTTP.Conduit (Manager)-import Network.OAuth.OAuth2+import Network.OAuth2 import Network.OAuth2.Experiment import Network.OAuth2.Provider import URI.ByteString.QQ (uri)@@ -28,7 +28,7 @@ , acAuthorizeState = "CHANGE_ME" , acAuthorizeRequestExtraParams = Map.empty , acRedirectUri = [uri|http://localhost|]- , acTokenRequestAuthenticationMethod = ClientSecretBasic+ , acClientAuthenticationMethod = ClientSecretBasic , acName = "sample-slack-authorization-code-app" }
src/Network/OAuth2/Provider/StackExchange.hs view
@@ -15,19 +15,16 @@ import Data.Text.Lazy (Text) import GHC.Generics import Network.HTTP.Conduit (Manager)-import Network.OAuth.OAuth2+import Network.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+-- Found the key from your application edit page -- https://stackapps.com/apps/oauth-stackexchangeAppKey :: ByteString-stackexchangeAppKey = ""--userInfoEndpoint :: URI-userInfoEndpoint =+userInfoEndpoint :: ByteString -> URI+userInfoEndpoint stackexchangeAppKey = appendQueryParams [ ("key", stackexchangeAppKey) , ("site", "stackoverflow")@@ -44,7 +41,7 @@ , acAuthorizeRequestExtraParams = Map.empty , acRedirectUri = [uri|http://localhost|] , acName = "sample-stackexchange-authorization-code-app"- , acTokenRequestAuthenticationMethod = ClientSecretPost+ , acClientAuthenticationMethod = ClientSecretPost } fetchUserInfo ::@@ -55,14 +52,14 @@ ExceptT BSL.ByteString m b fetchUserInfo = conduitUserInfoRequestWithCustomMethod (authGetJSONWithAuthMethod AuthInRequestQuery) -defaultStackExchangeIdp :: Idp StackExchange-defaultStackExchangeIdp =+defaultStackExchangeIdp :: ByteString -> Idp StackExchange+defaultStackExchangeIdp stackexchangeAppKey = 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+ idpUserInfoEndpoint = userInfoEndpoint stackexchangeAppKey , idpAuthorizeEndpoint = [uri|https://stackexchange.com/oauth|] , idpTokenEndpoint = [uri|https://stackexchange.com/oauth/access_token|] , idpDeviceAuthorizationEndpoint = Nothing
src/Network/OAuth2/Provider/Twitter.hs view
@@ -13,7 +13,7 @@ import Data.Text.Lazy (Text) import GHC.Generics import Network.HTTP.Conduit (Manager)-import Network.OAuth.OAuth2+import Network.OAuth2 import Network.OAuth2.Experiment import Network.OAuth2.Provider import URI.ByteString.QQ@@ -27,7 +27,7 @@ , acAuthorizeState = "CHANGE_ME" , acRedirectUri = [uri|http://localhost|] , acName = "sample-twitter-authorization-code-app"- , acTokenRequestAuthenticationMethod = ClientSecretBasic+ , acClientAuthenticationMethod = ClientSecretBasic , acAuthorizeRequestExtraParams = Map.empty }
src/Network/OAuth2/Provider/Weibo.hs view
@@ -12,7 +12,7 @@ import Data.Text.Lazy (Text) import GHC.Generics import Network.HTTP.Conduit (Manager)-import Network.OAuth.OAuth2+import Network.OAuth2 import Network.OAuth2.Experiment import Network.OAuth2.Provider import URI.ByteString.QQ@@ -27,7 +27,7 @@ , acAuthorizeState = "CHANGE_ME" , acAuthorizeRequestExtraParams = Map.empty , acRedirectUri = [uri|http://localhost|]- , acTokenRequestAuthenticationMethod = ClientSecretBasic+ , acClientAuthenticationMethod = ClientSecretBasic } fetchUserInfo ::
src/Network/OAuth2/Provider/ZOHO.hs view
@@ -12,7 +12,7 @@ import Data.Text.Lazy (Text) import GHC.Generics import Network.HTTP.Conduit (Manager)-import Network.OAuth.OAuth2+import Network.OAuth2 import Network.OAuth2.Experiment import Network.OAuth2.Provider import URI.ByteString.QQ@@ -27,7 +27,7 @@ , acAuthorizeState = "CHANGE_ME" , acRedirectUri = [uri|http://localhost/oauth2/callback|] , acName = "sample-zoho-authorization-code-app"- , acTokenRequestAuthenticationMethod = ClientSecretBasic+ , acClientAuthenticationMethod = ClientSecretBasic } fetchUserInfo ::
src/Network/OIDC/WellKnown.hs view
@@ -19,7 +19,6 @@ import URI.ByteString.Aeson () -- | Slim OpenID Configuration--- TODO: could add more fields to be complete. -- -- See spec <https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata> data OpenIDConfiguration = OpenIDConfiguration