{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

-- | This module contains a new way of doing OAuth2 authorization and authentication
-- in order to obtain Access Token and maybe Refresh Token base on rfc6749.
--
-- This module will become default in future release. (TBD but likely 3.0).
--
-- The key concept/change is to introduce the 'GrantTypeFlow', which determines the entire work flow per spec.
-- Each work flow will have slight different request parameters, which often time you'll see
-- different configuration when creating OAuth2 application in the IdP developer application page.
--
-- Here are supported flows
--
-- 1. Authorization Code. This flow requires authorize call to obtain an authorize code,
-- then exchange the code for tokens.
--
-- 2. Resource Owner Password. This flow only requires to hit token endpoint with, of course,
-- username and password, to obtain tokens.
--
-- 3. Client Credentials. This flow also only requires to hit token endpoint but with different parameters.
-- Client credentials flow does not involve an end user hence you won't be able to hit userinfo endpoint
-- with access token obtained.
--
-- 5. PKCE (rfc7636). This is enhancement on top of authorization code flow.
--
-- Implicit flow is not supported because it is more for SPA (single page app)
-- and more or less obsolete by Authorization Code flow with PKCE.
--
-- Here is quick sample for how to use vocabularies from this new module.
--
-- Firstly, initialize your IdP (use google as example) and the application.
--
-- @
-- {\-# LANGUAGE DataKinds #-\}
--
-- data Google = Google deriving (Eq, Show)
-- googleIdp :: Idp Google
-- googleIdp =
--   Idp
--     { idpFetchUserInfo = authGetJSON @(IdpUserInfo Google),
--       idpAuthorizeEndpoint = [uri|https:\/\/accounts.google.com\/o\/oauth2\/v2\/auth|],
--       idpTokenEndpoint = [uri|https:\/\/oauth2.googleapis.com\/token|],
--       idpUserInfoEndpoint = [uri|https:\/\/www.googleapis.com\/oauth2\/v2\/userinfo|]
--     }
--
-- fooApp :: IdpApplication 'AuthorizationCode Google
-- fooApp =
--   AuthorizationCodeIdpApplication
--     { idpAppClientId = "xxxxx",
--       idpAppClientSecret = "xxxxx",
--       idpAppScope =
--         Set.fromList
--           [ \"https:\/\/www.googleapis.com\/auth\/userinfo.email\",
--             \"https:\/\/www.googleapis.com\/auth\/userinfo.profile\"
--           ],
--       idpAppAuthorizeState = \"CHANGE_ME\",
--       idpAppAuthorizeExtraParams = Map.empty,
--       idpAppRedirectUri = [uri|http:\/\/localhost\/oauth2\/callback|],
--       idpAppName = "default-google-App",
--       idpAppTokenRequestAuthenticationMethod = ClientSecretBasic,
--       idp = googleIdp
--     }
-- @
--
-- Secondly, construct the authorize URL.
--
-- @
-- authorizeUrl = mkAuthorizeRequest fooApp
-- @
--
-- Thirdly, after a successful redirect with authorize code,
-- you could exchange for access token
--
-- @
-- mgr <- liftIO $ newManager tlsManagerSettings
-- tokenResp <- conduitTokenRequest fooApp mgr authorizeCode
-- @
--
-- Lastly, you probably like to fetch user info
--
-- @
-- conduitUserInfoRequest fooApp mgr (accessToken tokenResp)
-- @
--
-- Also you could find example from @hoauth2-providers-tutorials@ module.
module Network.OAuth2.Experiment (
  module Network.OAuth2.Experiment.Types,
  module Network.OAuth2.Experiment.Pkce,
) where

import Network.OAuth2.Experiment.Pkce
import Network.OAuth2.Experiment.Types