mirror of
https://github.com/freckle/yesod-auth-oauth2.git
synced 2026-09-05 17:33:49 +02:00
Implement different exceptions for different cases
I had hoped to get away from this entirely, to an Either-based interface, but that seems to be stalling as an initiative. So in the meantime, let's at least make our exceptions more meaningful.
This commit is contained in:
parent
e3c61789ba
commit
1411bb5858
@ -5,15 +5,25 @@ module Yesod.Auth.OAuth2.Exception
|
|||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
import qualified Data.ByteString.Lazy as BL
|
import Data.ByteString.Lazy (ByteString)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
|
|
||||||
-- | Provider name and error
|
data YesodOAuth2Exception
|
||||||
--
|
= OAuth2Error Text ByteString
|
||||||
-- The error is a lazy bytestring because it's most often encoded JSON.
|
-- ^ HTTP error during OAuth2 handshake
|
||||||
--
|
--
|
||||||
-- Deprecated. Eventually, we'll return @Either@s all the way up.
|
-- Plugin name and JSON-encoded @OAuth2Error@ from @hoauth2@.
|
||||||
--
|
--
|
||||||
data YesodOAuth2Exception = InvalidProfileResponse Text BL.ByteString
|
| JSONDecodingError Text String
|
||||||
|
-- ^ User profile was not as expected
|
||||||
|
--
|
||||||
|
-- Plugin name and Aeson parse error message.
|
||||||
|
--
|
||||||
|
| GenericError Text String
|
||||||
|
-- ^ Other error conditions
|
||||||
|
--
|
||||||
|
-- Plugin name and error message.
|
||||||
|
--
|
||||||
deriving (Show, Typeable)
|
deriving (Show, Typeable)
|
||||||
|
|
||||||
instance Exception YesodOAuth2Exception
|
instance Exception YesodOAuth2Exception
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import Control.Monad (unless)
|
|||||||
import qualified Data.ByteString.Lazy.Char8 as BL8
|
import qualified Data.ByteString.Lazy.Char8 as BL8
|
||||||
import Network.HTTP.Client
|
import Network.HTTP.Client
|
||||||
import qualified Network.HTTP.Types as HT
|
import qualified Network.HTTP.Types as HT
|
||||||
|
import qualified Yesod.Auth.OAuth2.Exception as YesodOAuth2Exception
|
||||||
|
|
||||||
newtype User = User Text
|
newtype User = User Text
|
||||||
|
|
||||||
@ -34,31 +35,34 @@ oauth2Nylas clientId clientSecret =
|
|||||||
-- FIXME: was this working? I'm 95% sure that the client will throw its
|
-- FIXME: was this working? I'm 95% sure that the client will throw its
|
||||||
-- own exception on unsuccessful status codes.
|
-- own exception on unsuccessful status codes.
|
||||||
unless (HT.statusIsSuccessful $ responseStatus resp)
|
unless (HT.statusIsSuccessful $ responseStatus resp)
|
||||||
$ throwIO $ InvalidProfileResponse pluginName
|
$ throwIO
|
||||||
$ "Unsuccessful HTTP response: " <> userResponse
|
$ YesodOAuth2Exception.GenericError pluginName
|
||||||
|
$ "Unsuccessful HTTP response: "
|
||||||
|
<> BL8.unpack userResponse
|
||||||
|
|
||||||
either
|
either
|
||||||
(throwIO . InvalidProfileResponse pluginName . BL8.pack)
|
(throwIO . YesodOAuth2Exception.JSONDecodingError pluginName)
|
||||||
(\(User userId) -> pure Creds
|
(\(User userId) -> pure Creds
|
||||||
{ credsPlugin = pluginName
|
{ credsPlugin = pluginName
|
||||||
, credsIdent = userId
|
, credsIdent = userId
|
||||||
, credsExtra = setExtra token userResponse
|
, credsExtra = setExtra token userResponse
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
$ eitherDecode userResponse
|
$ eitherDecode userResponse
|
||||||
where
|
where
|
||||||
oauth = OAuth2
|
oauth = OAuth2
|
||||||
{ oauthClientId = clientId
|
{ oauthClientId = clientId
|
||||||
, oauthClientSecret = clientSecret
|
, oauthClientSecret = clientSecret
|
||||||
, oauthOAuthorizeEndpoint = "https://api.nylas.com/oauth/authorize" `withQuery`
|
, oauthOAuthorizeEndpoint = "https://api.nylas.com/oauth/authorize"
|
||||||
[ ("response_type", "code")
|
`withQuery` [ ("response_type", "code")
|
||||||
, ("client_id", encodeUtf8 clientId)
|
, ( "client_id"
|
||||||
|
, encodeUtf8 clientId
|
||||||
|
)
|
||||||
-- N.B. The scopes delimeter is unknown/untested. Verify that before
|
-- N.B. The scopes delimeter is unknown/untested. Verify that before
|
||||||
-- extracting this to an argument and offering a Scoped function. In
|
-- extracting this to an argument and offering a Scoped function. In
|
||||||
-- its current state, it doesn't matter because it's only one scope.
|
-- its current state, it doesn't matter because it's only one scope.
|
||||||
, scopeParam "," defaultScopes
|
, scopeParam "," defaultScopes
|
||||||
]
|
]
|
||||||
, oauthAccessTokenEndpoint = "https://api.nylas.com/oauth/token"
|
, oauthAccessTokenEndpoint = "https://api.nylas.com/oauth/token"
|
||||||
, oauthCallback = Nothing
|
, oauthCallback = Nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,7 +52,6 @@ module Yesod.Auth.OAuth2.Prelude
|
|||||||
, module URI.ByteString.Extension
|
, module URI.ByteString.Extension
|
||||||
|
|
||||||
-- * Temporary, until I finish re-structuring modules
|
-- * Temporary, until I finish re-structuring modules
|
||||||
, YesodOAuth2Exception(..)
|
|
||||||
, authOAuth2
|
, authOAuth2
|
||||||
, authOAuth2Widget
|
, authOAuth2Widget
|
||||||
) where
|
) where
|
||||||
@ -61,7 +60,6 @@ import Control.Exception.Safe
|
|||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
import Data.ByteString (ByteString)
|
import Data.ByteString (ByteString)
|
||||||
import qualified Data.ByteString.Lazy as BL
|
import qualified Data.ByteString.Lazy as BL
|
||||||
import qualified Data.ByteString.Lazy.Char8 as BL8
|
|
||||||
import Data.Semigroup ((<>))
|
import Data.Semigroup ((<>))
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
@ -72,7 +70,7 @@ import URI.ByteString
|
|||||||
import URI.ByteString.Extension
|
import URI.ByteString.Extension
|
||||||
import Yesod.Auth
|
import Yesod.Auth
|
||||||
import Yesod.Auth.OAuth2
|
import Yesod.Auth.OAuth2
|
||||||
import Yesod.Auth.OAuth2.Exception
|
import qualified Yesod.Auth.OAuth2.Exception as YesodOAuth2Exception
|
||||||
|
|
||||||
-- | Retrieve a user's profile as JSON
|
-- | Retrieve a user's profile as JSON
|
||||||
--
|
--
|
||||||
@ -92,18 +90,17 @@ authGetProfile name manager token url = do
|
|||||||
decoded <- fromAuthJSON name resp
|
decoded <- fromAuthJSON name resp
|
||||||
pure (decoded, resp)
|
pure (decoded, resp)
|
||||||
|
|
||||||
-- | Throws a @Left@ result as an @'InvalidProfileResponse'@
|
-- | Throws a @Left@ result as an @'YesodOAuth2Exception'@
|
||||||
fromAuthGet
|
fromAuthGet
|
||||||
:: Text -> Either (OAuth2Error Value) BL.ByteString -> IO BL.ByteString
|
:: Text -> Either (OAuth2Error Value) BL.ByteString -> IO BL.ByteString
|
||||||
fromAuthGet _ (Right bs) = pure bs -- nice
|
fromAuthGet _ (Right bs) = pure bs -- nice
|
||||||
fromAuthGet name (Left err) =
|
fromAuthGet name (Left err) =
|
||||||
throwIO $ InvalidProfileResponse name $ encode err
|
throwIO $ YesodOAuth2Exception.OAuth2Error name $ encode err
|
||||||
|
|
||||||
-- | Throws a decoding error as an @'InvalidProfileResponse'@
|
-- | Throws a decoding error as an @'YesodOAuth2Exception'@
|
||||||
fromAuthJSON :: FromJSON a => Text -> BL.ByteString -> IO a
|
fromAuthJSON :: FromJSON a => Text -> BL.ByteString -> IO a
|
||||||
fromAuthJSON name =
|
fromAuthJSON name =
|
||||||
-- FIXME: unique exception constructors
|
either (throwIO . YesodOAuth2Exception.JSONDecodingError name) pure
|
||||||
either (throwIO . InvalidProfileResponse name . BL8.pack) pure
|
|
||||||
. eitherDecode
|
. eitherDecode
|
||||||
|
|
||||||
-- | A tuple of @\"scope\"@ and the given scopes separated by a delimiter
|
-- | A tuple of @\"scope\"@ and the given scopes separated by a delimiter
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import Yesod.Auth.OAuth2.Prelude
|
|||||||
|
|
||||||
import Network.HTTP.Client
|
import Network.HTTP.Client
|
||||||
(httpLbs, parseUrlThrow, responseBody, setQueryString)
|
(httpLbs, parseUrlThrow, responseBody, setQueryString)
|
||||||
|
import Yesod.Auth.OAuth2.Exception as YesodOAuth2Exception
|
||||||
|
|
||||||
data SlackScope
|
data SlackScope
|
||||||
= SlackBasicScope
|
= SlackBasicScope
|
||||||
@ -53,21 +54,20 @@ oauth2SlackScoped scopes clientId clientSecret =
|
|||||||
userResponse <- responseBody <$> httpLbs req manager
|
userResponse <- responseBody <$> httpLbs req manager
|
||||||
|
|
||||||
either
|
either
|
||||||
(const $ throwIO $ InvalidProfileResponse pluginName userResponse)
|
(throwIO . YesodOAuth2Exception.JSONDecodingError pluginName)
|
||||||
(\(User userId) -> pure Creds
|
(\(User userId) -> pure Creds
|
||||||
{ credsPlugin = pluginName
|
{ credsPlugin = pluginName
|
||||||
, credsIdent = userId
|
, credsIdent = userId
|
||||||
, credsExtra = setExtra token userResponse
|
, credsExtra = setExtra token userResponse
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
$ eitherDecode userResponse
|
$ eitherDecode userResponse
|
||||||
where
|
where
|
||||||
oauth2 = OAuth2
|
oauth2 = OAuth2
|
||||||
{ oauthClientId = clientId
|
{ oauthClientId = clientId
|
||||||
, oauthClientSecret = clientSecret
|
, oauthClientSecret = clientSecret
|
||||||
, oauthOAuthorizeEndpoint = "https://slack.com/oauth/authorize" `withQuery`
|
, oauthOAuthorizeEndpoint = "https://slack.com/oauth/authorize"
|
||||||
[ scopeParam "," $ map scopeText scopes
|
`withQuery` [scopeParam "," $ map scopeText scopes]
|
||||||
]
|
|
||||||
, oauthAccessTokenEndpoint = "https://slack.com/api/oauth.access"
|
, oauthAccessTokenEndpoint = "https://slack.com/api/oauth.access"
|
||||||
, oauthCallback = Nothing
|
, oauthCallback = Nothing
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user