-- | 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.
--
-- The key concept/change is to introduce the Grant flow, 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)
-- given it is deprecated 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.
--
-- @
--
-- import Network.OAuth2.Experiment
-- import URI.ByteString.QQ
--
-- data Google = Google deriving (Eq, Show)
--
-- googleIdp :: Idp Google
-- googleIdp =
--   Idp
--     { 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|]
--     , idpDeviceAuthorizationEndpoint = Just [uri|https:\/\/oauth2.googleapis.com\/device\/code|]
--     }
--
-- fooApp :: AuthorizationCodeApplication
-- fooApp =
--   AuthorizationCodeApplication
--     { acClientId = "xxxxx",
--       acClientSecret = "xxxxx",
--       acScope =
--         Set.fromList
--           [ \"https:\/\/www.googleapis.com\/auth\/userinfo.email\",
--             \"https:\/\/www.googleapis.com\/auth\/userinfo.profile\"
--           ],
--       acAuthorizeState = \"CHANGE_ME\",
--       acAuthorizeRequestExtraParams = Map.empty,
--       acRedirectUri = [uri|http:\/\/localhost\/oauth2\/callback|],
--       acName = "sample-google-authorization-code-app",
--       acTokenRequestAuthenticationMethod = ClientSecretBasic,
--     }
--
-- fooIdpApplication :: IdpApplication AuthorizationCodeApplication Google
-- fooIdpApplication = IdpApplication fooApp googleIdp
-- @
--
-- Secondly, construct the authorize URL.
--
-- @
-- authorizeUrl = mkAuthorizationRequest fooIdpApplication
-- @
--
-- Thirdly, after a successful redirect with authorize code,
-- you could exchange for access token
--
-- @
-- mgr <- liftIO $ newManager tlsManagerSettings
-- tokenResp <- conduitTokenRequest fooIdpApplication mgr authorizeCode
-- @
--
-- If you'd like to fetch user info, uses this method
--
-- @
-- conduitUserInfoRequest fooIdpApplication mgr (accessToken tokenResp)
-- @
--
-- You could also find example from @hoauth2-providers-tutorials@ module.
module Network.OAuth2.Experiment (
  -- * Application per Grant type
  module Network.OAuth2.Experiment.Grants,

  -- * Authorization Code
  module Network.OAuth2.Experiment.Flows.AuthorizationRequest,

  -- * Device Authorization
  module Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest,

  -- * Token Request
  module Network.OAuth2.Experiment.Flows.TokenRequest,

  -- * Refresh Token Request
  module Network.OAuth2.Experiment.Flows.RefreshTokenRequest,

  -- * UserInfo Request
  module Network.OAuth2.Experiment.Flows.UserInfoRequest,

  -- * Types
  module Network.OAuth2.Experiment.Types,
  module Network.OAuth2.Experiment.Pkce,
  module Network.OAuth.OAuth2,

  -- * Utils
  module Network.OAuth2.Experiment.Utils,
) where

import Network.OAuth.OAuth2 (ClientAuthenticationMethod (..))
import Network.OAuth2.Experiment.Flows.AuthorizationRequest (
  HasAuthorizeRequest,
  mkAuthorizationRequest,
  mkPkceAuthorizeRequest,
 )
import Network.OAuth2.Experiment.Flows.DeviceAuthorizationRequest (
  DeviceAuthorizationResponse (..),
  HasDeviceAuthorizationRequest,
  conduitDeviceAuthorizationRequest,
 )
import Network.OAuth2.Experiment.Flows.RefreshTokenRequest (
  HasRefreshTokenRequest,
  conduitRefreshTokenRequest,
 )
import Network.OAuth2.Experiment.Flows.TokenRequest (
  ExchangeTokenInfo,
  HasTokenRequest,
  NoNeedExchangeToken (..),
  TokenRequest,
  conduitPkceTokenRequest,
  conduitTokenRequest,
 )
import Network.OAuth2.Experiment.Flows.UserInfoRequest (
  HasUserInfoRequest,
  conduitUserInfoRequest,
  conduitUserInfoRequestWithCustomMethod,
 )
import Network.OAuth2.Experiment.Grants
import Network.OAuth2.Experiment.Pkce (
  CodeVerifier (..),
 )
import Network.OAuth2.Experiment.Types (
  AuthorizeState (..),
  ClientId (..),
  ClientSecret (..),
  HasOAuth2Key,
  Idp (..),
  IdpApplication (..),
  Password (..),
  RedirectUri (..),
  Scope (..),
  Username (..),
 )
import Network.OAuth2.Experiment.Utils (uriToText)
