{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}

module Network.OAuth2.Provider.Auth0 where

import Control.Monad.IO.Class
import Control.Monad.Trans.Except
import Data.Aeson
import Data.Map.Strict qualified as Map
import Data.Set qualified as Set
import Data.Text.Lazy (Text)
import GHC.Generics
import Network.OAuth.OAuth2
import Network.OAuth2.Experiment
import Network.OIDC.WellKnown
import URI.ByteString.QQ

data Auth0 = Auth0
  deriving (Int -> Auth0 -> ShowS
[Auth0] -> ShowS
Auth0 -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Auth0] -> ShowS
$cshowList :: [Auth0] -> ShowS
show :: Auth0 -> String
$cshow :: Auth0 -> String
showsPrec :: Int -> Auth0 -> ShowS
$cshowsPrec :: Int -> Auth0 -> ShowS
Show, Auth0 -> Auth0 -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Auth0 -> Auth0 -> Bool
$c/= :: Auth0 -> Auth0 -> Bool
== :: Auth0 -> Auth0 -> Bool
$c== :: Auth0 -> Auth0 -> Bool
Eq)

type instance IdpUserInfo Auth0 = Auth0User

defaultAuth0App :: Idp Auth0 -> IdpApplication 'AuthorizationCode Auth0
defaultAuth0App :: Idp Auth0 -> IdpApplication 'AuthorizationCode Auth0
defaultAuth0App Idp Auth0
i =
  AuthorizationCodeIdpApplication
    { $sel:idpAppClientId:AuthorizationCodeIdpApplication :: ClientId
idpAppClientId = ClientId
"",
      $sel:idpAppClientSecret:AuthorizationCodeIdpApplication :: ClientSecret
idpAppClientSecret = ClientSecret
"",
      $sel:idpAppScope:AuthorizationCodeIdpApplication :: Set Scope
idpAppScope = forall a. Ord a => [a] -> Set a
Set.fromList [Scope
"openid", Scope
"profile", Scope
"email", Scope
"offline_access"],
      $sel:idpAppAuthorizeState:AuthorizationCodeIdpApplication :: AuthorizeState
idpAppAuthorizeState = AuthorizeState
"CHANGE_ME",
      $sel:idpAppAuthorizeExtraParams:AuthorizationCodeIdpApplication :: Map Text Text
idpAppAuthorizeExtraParams = forall k a. Map k a
Map.empty,
      $sel:idpAppRedirectUri:AuthorizationCodeIdpApplication :: URI
idpAppRedirectUri = [uri|http://localhost|],
      $sel:idpAppName:AuthorizationCodeIdpApplication :: Text
idpAppName = Text
"default-auth0-App",
      $sel:idpAppTokenRequestAuthenticationMethod:AuthorizationCodeIdpApplication :: ClientAuthenticationMethod
idpAppTokenRequestAuthenticationMethod = ClientAuthenticationMethod
ClientSecretBasic,
      $sel:idp:AuthorizationCodeIdpApplication :: Idp Auth0
idp = Idp Auth0
i
    }

defaultAuth0Idp :: Idp Auth0
defaultAuth0Idp :: Idp Auth0
defaultAuth0Idp =
  Idp
    { $sel:idpFetchUserInfo:Idp :: forall (m :: * -> *).
(FromJSON (IdpUserInfo Auth0), MonadIO m) =>
Manager
-> AccessToken -> URI -> ExceptT ByteString m (IdpUserInfo Auth0)
idpFetchUserInfo = forall a (m :: * -> *).
(FromJSON a, MonadIO m) =>
Manager -> AccessToken -> URI -> ExceptT ByteString m a
authGetJSON @(IdpUserInfo Auth0),
      --  https://auth0.com/docs/api/authentication#user-profile
      $sel:idpUserInfoEndpoint:Idp :: URI
idpUserInfoEndpoint = [uri|https://foo.auth0.com/userinfo|],
      -- https://auth0.com/docs/api/authentication#authorization-code-flow
      $sel:idpAuthorizeEndpoint:Idp :: URI
idpAuthorizeEndpoint = [uri|https://foo.auth0.com/authorize|],
      -- https://auth0.com/docs/api/authentication#authorization-code-flow44
      $sel:idpTokenEndpoint:Idp :: URI
idpTokenEndpoint = [uri|https://foo.auth0.com/oauth/token|]
    }

mkAuth0Idp ::
  MonadIO m =>
  -- | Full domain with no http protocol. e.g. @foo.auth0.com@
  Text ->
  ExceptT Text m (Idp Auth0)
mkAuth0Idp :: forall (m :: * -> *).
MonadIO m =>
Text -> ExceptT Text m (Idp Auth0)
mkAuth0Idp Text
domain = do
  OpenIDConfigurationUris {URI
$sel:jwksUri:OpenIDConfigurationUris :: OpenIDConfigurationUris -> URI
$sel:userinfoUri:OpenIDConfigurationUris :: OpenIDConfigurationUris -> URI
$sel:tokenUri:OpenIDConfigurationUris :: OpenIDConfigurationUris -> URI
$sel:authorizationUri:OpenIDConfigurationUris :: OpenIDConfigurationUris -> URI
jwksUri :: URI
userinfoUri :: URI
tokenUri :: URI
authorizationUri :: URI
..} <- forall (m :: * -> *).
MonadIO m =>
Text -> ExceptT Text m OpenIDConfigurationUris
fetchWellKnownUris Text
domain
  forall (f :: * -> *) a. Applicative f => a -> f a
pure
    ( Idp Auth0
defaultAuth0Idp
        { $sel:idpUserInfoEndpoint:Idp :: URI
idpUserInfoEndpoint = URI
userinfoUri,
          $sel:idpAuthorizeEndpoint:Idp :: URI
idpAuthorizeEndpoint = URI
authorizationUri,
          $sel:idpTokenEndpoint:Idp :: URI
idpTokenEndpoint = URI
tokenUri
        }
    )

-- | scopes: ["openid", "profile", "email"]
data Auth0User = Auth0User
  { Auth0User -> Text
name :: Text,
    Auth0User -> Text
email :: Text,
    Auth0User -> Text
sub :: Text
  }
  deriving (Int -> Auth0User -> ShowS
[Auth0User] -> ShowS
Auth0User -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Auth0User] -> ShowS
$cshowList :: [Auth0User] -> ShowS
show :: Auth0User -> String
$cshow :: Auth0User -> String
showsPrec :: Int -> Auth0User -> ShowS
$cshowsPrec :: Int -> Auth0User -> ShowS
Show, forall x. Rep Auth0User x -> Auth0User
forall x. Auth0User -> Rep Auth0User x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep Auth0User x -> Auth0User
$cfrom :: forall x. Auth0User -> Rep Auth0User x
Generic)

instance FromJSON Auth0User