{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} -- | -- -- OAuth2 plugin for http://spotify.com -- module Yesod.Auth.OAuth2.Spotify ( oauth2Spotify , module Yesod.Auth.OAuth2 ) where #if __GLASGOW_HASKELL__ < 710 import Control.Applicative ((<$>), (<*>), pure) #endif import Control.Exception.Lifted import Control.Monad (mzero) import Data.Aeson import Data.ByteString (ByteString) import Data.Maybe import Data.Text (Text) import Data.Text.Encoding (encodeUtf8) import Network.HTTP.Conduit(Manager) import Yesod.Auth import Yesod.Auth.OAuth2 import qualified Data.ByteString as B import qualified Data.Text as T data SpotifyUserImage = SpotifyUserImage { spotifyUserImageHeight :: Maybe Int , spotifyUserImageWidth :: Maybe Int , spotifyUserImageUrl :: Text } instance FromJSON SpotifyUserImage where parseJSON (Object v) = SpotifyUserImage <$> v .: "height" <*> v .: "width" <*> v .: "url" parseJSON _ = mzero data SpotifyUser = SpotifyUser { spotifyUserId :: Text , spotifyUserHref :: Text , spotifyUserUri :: Text , spotifyUserDisplayName :: Maybe Text , spotifyUserProduct :: Maybe Text , spotifyUserCountry :: Maybe Text , spotifyUserEmail :: Maybe Text , spotifyUserImages :: Maybe [SpotifyUserImage] } instance FromJSON SpotifyUser where parseJSON (Object v) = SpotifyUser <$> v .: "id" <*> v .: "href" <*> v .: "uri" <*> v .:? "display_name" <*> v .:? "product" <*> v .:? "country" <*> v .:? "email" <*> v .:? "images" parseJSON _ = mzero oauth2Spotify :: YesodAuth m => Text -- ^ Client ID -> Text -- ^ Client Secret -> [ByteString] -- ^ Scopes -> AuthPlugin m oauth2Spotify clientId clientSecret scope = authOAuth2 "spotify" OAuth2 { oauthClientId = encodeUtf8 clientId , oauthClientSecret = encodeUtf8 clientSecret , oauthOAuthorizeEndpoint = B.append "https://accounts.spotify.com/authorize?scope=" (B.intercalate "%20" scope) , oauthAccessTokenEndpoint = "https://accounts.spotify.com/api/token" , oauthCallback = Nothing } fetchSpotifyProfile fetchSpotifyProfile :: Manager -> AccessToken -> IO (Creds m) fetchSpotifyProfile manager token = do result <- authGetJSON manager token "https://api.spotify.com/v1/me" case result of Right user -> return $ toCreds user Left err -> throwIO $ InvalidProfileResponse "spotify" err toCreds :: SpotifyUser -> Creds m toCreds user = Creds { credsPlugin = "spotify" , credsIdent = spotifyUserId user , credsExtra = mapMaybe getExtra extrasTemplate } where userImage :: Maybe SpotifyUserImage userImage = spotifyUserImages user >>= listToMaybe userImagePart :: (SpotifyUserImage -> Maybe a) -> Maybe a userImagePart getter = userImage >>= getter extrasTemplate = [ ("href", Just $ spotifyUserHref user) , ("uri", Just $ spotifyUserUri user) , ("display_name", spotifyUserDisplayName user) , ("product", spotifyUserProduct user) , ("country", spotifyUserCountry user) , ("email", spotifyUserEmail user) , ("image_url", spotifyUserImageUrl <$> userImage) , ("image_height", T.pack . show <$> userImagePart spotifyUserImageHeight) , ("image_width", T.pack . show <$> userImagePart spotifyUserImageWidth) ] getExtra :: (Text, Maybe Text) -> Maybe (Text, Text) getExtra (key, val) = fmap ((,) key) val