google-oauth2-easy (empty) → 0.0.0
raw patch · 10 files changed
+432/−0 lines, 10 filesdep +aesondep +basedep +criterionsetup-changed
Dependencies added: aeson, base, criterion, google-oauth2-easy, http-api-data, http-client, mtl, servant, servant-client, tasty, tasty-hspec, text, text-conversions, unordered-containers
Files
- CHANGELOG.md +7/−0
- LICENSE.md +30/−0
- README.md +1/−0
- Setup.hs +7/−0
- benchmark/Main.hs +6/−0
- google-oauth2-easy.cabal +80/−0
- library/Google/Authentication/Easy.hs +158/−0
- package.yaml +60/−0
- stack.yaml +66/−0
- test-suite/Main.hs +17/−0
+ CHANGELOG.md view
@@ -0,0 +1,7 @@+# Change log++google-oauth2-easy uses [Semantic Versioning][].+The change log is available through the [releases on GitHub][].++[Semantic Versioning]: http://semver.org/spec/v2.0.0.html+[releases on GitHub]: https://github.com/githubuser/google-oauth2-easy/releases
+ LICENSE.md view
@@ -0,0 +1,30 @@+Copyright (c) 2018, Joe Vargas++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Joe Vargas nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.A
+ README.md view
@@ -0,0 +1,1 @@+# google-oauth2-easy
+ Setup.hs view
@@ -0,0 +1,7 @@+-- This script is used to build and install your package. Typically you don't+-- need to change it. The Cabal documentation has more information about this+-- file: <https://www.haskell.org/cabal/users-guide/installing-packages.html>.+import qualified Distribution.Simple++main :: IO ()+main = Distribution.Simple.defaultMain
+ benchmark/Main.hs view
@@ -0,0 +1,6 @@+-- You can benchmark your code quickly and effectively with Criterion. See its+-- website for help: <http://www.serpentine.com/criterion/>.+import Criterion.Main++main :: IO ()+main = defaultMain [bench "const" (whnf const ())]
+ google-oauth2-easy.cabal view
@@ -0,0 +1,80 @@+-- This file has been generated from package.yaml by hpack version 0.20.0.+--+-- see: https://github.com/sol/hpack+--+-- hash: 15faf95dc4b17f2c955e679d24fce822a2ce9e5a3ebdc5fe17b1f1205b8cd508++name: google-oauth2-easy+version: 0.0.0+synopsis: Opininated use of Google Authentication for ease+description: Easy Google Authentication integration using the Authorization Code Grant and Refresh Token+category: Network+homepage: https://github.com/jxv/google-oauth2-easy#readme+bug-reports: https://github.com/jxv/google-oauth2-easy/issues+author: Joe Vargas+maintainer: Joe Vargas+license: BSD3+license-file: LICENSE.md+build-type: Simple+cabal-version: >= 1.10++extra-source-files:+ CHANGELOG.md+ LICENSE.md+ package.yaml+ README.md+ stack.yaml++source-repository head+ type: git+ location: https://github.com/jxv/google-oauth2-easy++library+ hs-source-dirs:+ library+ ghc-options: -Wall+ build-depends:+ aeson+ , base >=4.7 && <5+ , http-api-data+ , http-client+ , mtl+ , servant+ , servant-client+ , text+ , text-conversions+ , unordered-containers+ exposed-modules:+ Google.Authentication.Easy+ other-modules:+ Paths_google_oauth2_easy+ default-language: Haskell2010++test-suite google-oauth2-easy-test-suite+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs:+ test-suite+ ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N+ build-depends:+ base+ , google-oauth2-easy+ , tasty+ , tasty-hspec+ other-modules:+ Paths_google_oauth2_easy+ default-language: Haskell2010++benchmark google-oauth2-easy-benchmarks+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs:+ benchmark+ ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N+ build-depends:+ base+ , criterion+ , google-oauth2-easy+ other-modules:+ Paths_google_oauth2_easy+ default-language: Haskell2010
+ library/Google/Authentication/Easy.hs view
@@ -0,0 +1,158 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}+module Google.Authentication.Easy+ ( Service(..)+ , callPostTokenInfo'+ , callPostToken'+ , AccessTokenForm(..)+ , PostTokenBody(..)+ , PostTokenInfoResponse(..)+ , RefreshTokenResp(..)+ , RefreshTokenBody(..)+ , AuthorizationCodeResp(..)+ , AuthorizationCodeBody(..)+ , ServantError+ , Manager+ ) where++import qualified Data.HashMap.Lazy as HML (singleton, fromList)+import Control.Applicative ((<|>))+import Control.Monad.IO.Class (MonadIO(..))+import Control.Monad.Reader (MonadReader, asks)+import Data.Aeson+import Data.Text+import Data.Text.Conversions+import Data.Proxy+import GHC.Generics (Generic)+import Network.HTTP.Client (Manager)+import Servant.API+import Servant.Client+import Web.FormUrlEncoded (Form(..), ToForm(..))++apiBaseUrl :: BaseUrl+apiBaseUrl = BaseUrl Https "www.googleapis.com" 443 ""++type Api = "oauth2" :> (("v3" :> PostTokenInfo) :<|> ("v4" :> PostToken))++type PostTokenInfo = "tokeninfo" :> ReqBody '[FormUrlEncoded] AccessTokenForm :> Post '[JSON] PostTokenInfoResponse+type PostToken = "token" :> ReqBody '[FormUrlEncoded] PostTokenBody :> Post '[JSON] PostTokenResponse++data AccessTokenForm = AccessTokenForm+ { access_token :: Text+ } deriving (Show, Eq)++instance ToForm AccessTokenForm where+ toForm form = Form $ HML.singleton "access_token" [toText $ (access_token :: AccessTokenForm -> Text) form]++data PostTokenInfoResponse = PostTokenInfoResponse+ { azp :: Text+ , aud :: Text+ , sub :: Text+ , scope :: Text+ , exp :: Text+ , expires_in :: Text+ , email :: Text+ , email_verified :: Text+ , access_type :: Text+ } deriving (Show, Eq, Generic)++instance FromJSON PostTokenInfoResponse++data PostTokenBody+ = PostTokenBody'AuthorizationCodeBody AuthorizationCodeBody+ | PostTokenBody'RefreshTokenBody RefreshTokenBody+ deriving (Show, Eq)++instance ToForm PostTokenBody where+ toForm (PostTokenBody'AuthorizationCodeBody body) = toForm body+ toForm (PostTokenBody'RefreshTokenBody body) = toForm body++data AuthorizationCodeBody = AuthorizationCodeBody+ { code :: Text+ , client_id :: Text+ , client_secret :: Text+ , redirect_uri :: Text -- "https://www.camp47.com"+ , grant_type :: Text -- "authorization_code"+ } deriving (Show, Eq, Generic)++instance ToJSON AuthorizationCodeBody++instance ToForm AuthorizationCodeBody where+ toForm AuthorizationCodeBody{code,client_id,client_secret,redirect_uri,grant_type} = Form $ HML.fromList+ [ ("code", [toText code])+ , ("client_id", [toText client_id])+ , ("client_secret", [toText client_secret])+ , ("redirect_uri", [toText redirect_uri])+ , ("grant_type", [toText grant_type])+ ]++data RefreshTokenBody = RefreshTokenBody+ { refresh_token :: Text+ , client_id :: Text+ , client_secret :: Text+ , grant_type :: Text -- "refresh_token"+ } deriving (Show, Eq, Generic)++instance ToForm RefreshTokenBody where+ toForm RefreshTokenBody{refresh_token,client_id,client_secret,grant_type} = Form $ HML.fromList+ [ ("refresh_token", [toText refresh_token])+ , ("client_id", [toText client_id])+ , ("client_secret", [toText client_secret])+ , ("grant_type", [toText grant_type])+ ]++data PostTokenResponse+ = PostTokenResponse'AuthorizationCodeResp AuthorizationCodeResp+ | PostTokenResponse'RefreshTokenResp RefreshTokenResp+ deriving (Show, Eq)++instance FromJSON PostTokenResponse where+ parseJSON v =+ (PostTokenResponse'AuthorizationCodeResp <$> parseJSON v) <|>+ (PostTokenResponse'RefreshTokenResp <$> parseJSON v)++data AuthorizationCodeResp = AuthorizationCodeResp+ { access_token :: Text+ , expires_in :: Int+ , token_type :: Text+ , refresh_token :: Text+ , id_token :: Text+ } deriving (Show, Eq, Generic)++instance FromJSON AuthorizationCodeResp++data RefreshTokenResp = RefreshTokenResp+ { access_token :: Text+ , expires_in :: Int+ , token_type :: Text+ } deriving (Show, Eq, Generic)++instance FromJSON RefreshTokenResp++api :: Proxy Api+api = Proxy++postTokenInfo :: AccessTokenForm -> ClientM PostTokenInfoResponse+postToken :: PostTokenBody -> ClientM PostTokenResponse+postTokenInfo :<|> postToken = client api++class Monad m => Service m where+ callPostTokenInfo :: AccessTokenForm -> m (Either ServantError PostTokenInfoResponse)+ callPostToken :: PostTokenBody -> m (Either ServantError PostTokenResponse)+++callPostTokenInfo' :: (MonadIO m, MonadReader r m) => (r -> Manager) -> AccessTokenForm -> m (Either ServantError PostTokenInfoResponse)+callPostTokenInfo' getManager accessTokenForm = do+ manager <- asks getManager+ liftIO $ runClientM (postTokenInfo accessTokenForm) (ClientEnv manager apiBaseUrl)++callPostToken' :: (MonadIO m, MonadReader r m) => (r -> Manager) -> PostTokenBody -> m (Either ServantError PostTokenResponse)+callPostToken' getManager authorizationCodeForm = do+ manager <- asks getManager+ liftIO $ runClientM (postToken authorizationCodeForm) (ClientEnv manager apiBaseUrl)++
+ package.yaml view
@@ -0,0 +1,60 @@+name: google-oauth2-easy+version: '0.0.0'+github: jxv/google-oauth2-easy+license: BSD3+license-file: LICENSE.md+author: Joe Vargas+maintainer: Joe Vargas+synopsis: Opininated use of Google Authentication for ease+description: Easy Google Authentication integration using the Authorization Code Grant and Refresh Token+category: Network++extra-source-files:+- CHANGELOG.md+- LICENSE.md+- package.yaml+- README.md+- stack.yaml++ghc-options: -Wall++library:+ dependencies:+ - base >=4.7 && <5+ - aeson+ - unordered-containers+ - http-api-data+ - http-client+ - mtl+ - servant+ - servant-client+ - text+ - text-conversions+ source-dirs: library++benchmarks:+ google-oauth2-easy-benchmarks:+ source-dirs: benchmark+ main: Main.hs+ dependencies:+ - base+ - google-oauth2-easy+ - criterion+ ghc-options:+ - -rtsopts+ - -threaded+ - -with-rtsopts=-N++tests:+ google-oauth2-easy-test-suite:+ source-dirs: test-suite+ main: Main.hs+ dependencies:+ - base+ - google-oauth2-easy+ - tasty+ - tasty-hspec+ ghc-options:+ - -rtsopts+ - -threaded+ - -with-rtsopts=-N
+ stack.yaml view
@@ -0,0 +1,66 @@+# This file was automatically generated by 'stack init'+#+# Some commonly used options have been documented as comments in this file.+# For advanced use and comprehensive documentation of the format, please see:+# https://docs.haskellstack.org/en/stable/yaml_configuration/++# Resolver to choose a 'specific' stackage snapshot or a compiler version.+# A snapshot resolver dictates the compiler version and the set of packages+# to be used for project dependencies. For example:+#+# resolver: lts-3.5+# resolver: nightly-2015-09-21+# resolver: ghc-7.10.2+# resolver: ghcjs-0.1.0_ghc-7.10.2+# resolver:+# name: custom-snapshot+# location: "./custom-snapshot.yaml"+resolver: lts-10.8++# User packages to be built.+# Various formats can be used as shown in the example below.+#+# packages:+# - some-directory+# - https://example.com/foo/bar/baz-0.0.2.tar.gz+# - location:+# git: https://github.com/commercialhaskell/stack.git+# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a+# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a+# extra-dep: true+# subdirs:+# - auto-update+# - wai+#+# A package marked 'extra-dep: true' will only be built if demanded by a+# non-dependency (i.e. a user package), and its test suites and benchmarks+# will not be run. This is useful for tweaking upstream packages.+packages:+- .+# Dependency packages to be pulled from upstream that are not in the resolver+# (e.g., acme-missiles-0.3)+# extra-deps: []++# Override default flag values for local packages and extra-deps+# flags: {}++# Extra package databases containing global packages+# extra-package-dbs: []++# Control whether we use the GHC we find on the path+# system-ghc: true+#+# Require a specific version of stack, using version ranges+# require-stack-version: -any # Default+# require-stack-version: ">=1.6"+#+# Override the architecture used by stack, especially useful on Windows+# arch: i386+# arch: x86_64+#+# Extra directories used by stack for building+# extra-include-dirs: [/path/to/dir]+# extra-lib-dirs: [/path/to/dir]+#+# Allow a newer minor version of GHC than the snapshot specifies+# compiler-check: newer-minor
+ test-suite/Main.hs view
@@ -0,0 +1,17 @@+-- Tasty makes it easy to test your code. It is a test framework that can+-- combine many different types of tests into one suite. See its website for+-- help: <http://documentup.com/feuerbach/tasty>.+import qualified Test.Tasty+-- Hspec is one of the providers for Tasty. It provides a nice syntax for+-- writing tests. Its website has more info: <https://hspec.github.io>.+import Test.Tasty.Hspec++main :: IO ()+main = do+ test <- testSpec "google-oauth2-easy" spec+ Test.Tasty.defaultMain test++spec :: Spec+spec = parallel $ do+ it "is trivially true" $ do+ True `shouldBe` True