Implement token refresh utility

This commit is contained in:
Rene Vergara 2022-10-26 15:34:29 -05:00
parent 5806473e8e
commit 0eae258dee
Signed by: pitmutt
GPG key ID: 65122AD495A7F5B2
6 changed files with 138 additions and 31 deletions

View file

@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Added
- New utility to refresh Xero tokens periodically.
### Changed
- Refactored code for requesting Xero tokens to make it reusable.
## [1.1.1] - 2022-10-08 ## [1.1.1] - 2022-10-08
### Changed ### Changed

View file

@ -1,6 +1,6 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
module Main where module Server where
import Config import Config
import Control.Concurrent (forkIO) import Control.Concurrent (forkIO)

35
app/TokenRefresh.hs Normal file
View file

@ -0,0 +1,35 @@
{-# LANGUAGE OverloadedStrings #-}
module TokenRefresh where
import Config
import Data.Time.Clock
import Database.MongoDB
import Xero
main :: IO ()
main = do
putStrLn "Reading config..."
now <- getCurrentTime
loadedConfig <- loadZGoConfig "zgo.cfg"
pipe <- connect $ host (c_dbHost loadedConfig)
let db = c_dbName loadedConfig
j <-
access
pipe
master
db
(auth (c_dbUser loadedConfig) (c_dbPassword loadedConfig))
if j
then putStrLn "Connected to MongoDB!"
else fail "MongoDB connection failed!"
credsData <- access pipe master db findXero
let creds = cast' . Doc =<< credsData
tokens <- access pipe master db (findExpiringTokens now)
if not (null tokens)
then do
let t = map (cast' . Doc) tokens
case creds of
Just c -> mapM_ (refreshToken pipe db c "") t
Nothing -> fail "No credentials"
else putStrLn "No tokens to refresh1"

View file

@ -56,9 +56,10 @@ library:
executables: executables:
zgo-backend-exe: zgo-backend-exe:
main: Main.hs main: Server.hs
source-dirs: app source-dirs: app
ghc-options: ghc-options:
- -main-is Server
- -threaded - -threaded
- -rtsopts - -rtsopts
- -with-rtsopts=-N - -with-rtsopts=-N
@ -79,6 +80,33 @@ executables:
- configurator - configurator
- warp-tls - warp-tls
- warp - warp
zgo-token-refresh:
main: TokenRefresh.hs
source-dirs: app
ghc-options:
- -main-is TokenRefresh
- -threaded
- -rtsopts
- -with-rtsopts=-N
- -Wall
dependencies:
- base
- zgo-backend
- base
- scotty
- wai-extra
- securemem
- text
- aeson
- mongoDB
- http-types
- http-conduit
- time
- bytestring
- configurator
- warp-tls
- warp
tests: tests:
zgo-backend-test: zgo-backend-test:

View file

@ -280,16 +280,23 @@ upsertToken t = do
findToken :: T.Text -> Action IO (Maybe Document) findToken :: T.Text -> Action IO (Maybe Document)
findToken a = findOne (select ["address" =: a] "xerotokens") findToken a = findOne (select ["address" =: a] "xerotokens")
findExpiringTokens :: UTCTime -> Action IO [Document]
findExpiringTokens now =
rest =<<
find
(select ["refExpires" =: ["$lte" =: addUTCTime 1728000 now]] "xerotokens")
-- | Function to request accesstoken -- | Function to request accesstoken
requestXeroToken :: Pipe -> T.Text -> Xero -> T.Text -> T.Text -> IO Bool requestXeroToken :: Pipe -> T.Text -> Xero -> T.Text -> T.Text -> IO Bool
requestXeroToken pipe dbName cred code address = do requestXeroToken pipe dbName cred code address = do
token <- access pipe master dbName $ findToken address token <- access pipe master dbName $ findToken address
let oToken = token >>= cast' . Doc
refreshToken pipe dbName cred code oToken
refreshToken :: Pipe -> T.Text -> Xero -> T.Text -> Maybe XeroToken -> IO Bool
refreshToken pipe dbName cred code token = do
let pars = let pars =
case token of case token of
Just xT -> do
let xToken = cast' (Doc xT) :: Maybe XeroToken
case xToken of
Nothing -> error "Failed to parse XeroToken BSON"
Just x -> "grant_type=refresh_token&refresh_token=" <> t_refresh x Just x -> "grant_type=refresh_token&refresh_token=" <> t_refresh x
Nothing -> Nothing ->
"grant_type=authorization_code&code=" <> "grant_type=authorization_code&code=" <>
@ -309,8 +316,10 @@ requestXeroToken pipe dbName cred code address = do
case rCode of case rCode of
200 -> do 200 -> do
let newToken = getResponseBody (res :: Response XeroToken) let newToken = getResponseBody (res :: Response XeroToken)
let accCode = t_code <$> (token >>= cast' . Doc) let accCode = t_code <$> token
pToken <- processToken newToken address (fromMaybe "" accCode) let address = t_address <$> token
pToken <-
processToken newToken (fromMaybe "" address) (fromMaybe "" accCode)
--print pToken --print pToken
_ <- access pipe master dbName $ upsertToken pToken _ <- access pipe master dbName $ upsertToken pToken
_ <- getTenantId pipe dbName pToken _ <- getTenantId pipe dbName pToken

View file

@ -5,7 +5,7 @@ cabal-version: 1.12
-- see: https://github.com/sol/hpack -- see: https://github.com/sol/hpack
name: zgo-backend name: zgo-backend
version: 1.1.0 version: 1.1.1
synopsis: Haskell Back-end for the ZGo point-of-sale application synopsis: Haskell Back-end for the ZGo point-of-sale application
description: Please see the README on GitLab at <https://gitlab.com/pitmutt/zgo-backend#readme> description: Please see the README on GitLab at <https://gitlab.com/pitmutt/zgo-backend#readme>
category: Web category: Web
@ -70,12 +70,39 @@ library
default-language: Haskell2010 default-language: Haskell2010
executable zgo-backend-exe executable zgo-backend-exe
main-is: Main.hs main-is: Server.hs
other-modules: other-modules:
TokenRefresh
Paths_zgo_backend Paths_zgo_backend
hs-source-dirs: hs-source-dirs:
app app
ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall ghc-options: -main-is Server -threaded -rtsopts -with-rtsopts=-N -Wall
build-depends:
aeson
, base
, bytestring
, configurator
, http-conduit
, http-types
, mongoDB
, scotty
, securemem
, text
, time
, wai-extra
, warp
, warp-tls
, zgo-backend
default-language: Haskell2010
executable zgo-token-refresh
main-is: TokenRefresh.hs
other-modules:
Server
Paths_zgo_backend
hs-source-dirs:
app
ghc-options: -main-is TokenRefresh -threaded -rtsopts -with-rtsopts=-N -Wall
build-depends: build-depends:
aeson aeson
, base , base