@@ -22,6 +22,8 @@ module Network.Google.Auth
2222 , getApplicationDefault
2323 , fromWellKnownPath
2424 , fromFilePath
25+ , saveAuthorizedUserToWellKnownPath
26+ , saveAuthorizedUser
2527
2628 -- ** Installed Application Credentials
2729 , installedApplication
@@ -33,8 +35,11 @@ module Network.Google.Auth
3335 -- ** Thread-safe Storage
3436 , Store
3537 , initStore
38+ , refreshStore
39+ , retrieveAuthFromStore
3640
3741 , Auth (..)
42+ , authToAuthorizedUser
3843 , exchange
3944 , refresh
4045
@@ -81,6 +86,19 @@ import Network.HTTP.Conduit (Manager)
8186import qualified Network.HTTP.Conduit as Client
8287import Network.HTTP.Types (hAuthorization)
8388
89+-- | 'authToAuthorizedUser' converts 'Auth' into an 'AuthorizedUser'
90+-- by returning 'Right' if there is a 'FromClient'-constructed
91+-- Credentials and a refreshed token; otherwise, returning
92+-- 'Left' with error message.
93+authToAuthorizedUser :: AllowScopes s => Auth s -> Either Text AuthorizedUser
94+authToAuthorizedUser a = AuthorizedUser
95+ <$> (_clientId <$> getClient)
96+ <*> maybe (Left "no refresh token") Right (_tokenRefresh (_token a))
97+ <*> (_clientSecret <$> getClient)
98+ where getClient = case _credentials a of
99+ FromClient c _ -> Right c
100+ _ -> Left "not FromClient"
101+
84102-- | An 'OAuthToken' that can potentially be expired, with the original
85103-- credentials that can be used to perform a refresh.
86104data Auth (s :: [Symbol]) = Auth
@@ -110,6 +128,33 @@ initStore :: (MonadIO m, MonadCatch m, AllowScopes s)
110128 -> m (Store s)
111129initStore c l m = exchange c l m >>= fmap Store . liftIO . newMVar
112130
131+-- | Refresh an 'OAuthToken' if it was expired.
132+-- Otherwise, keep original one.
133+refreshStore :: (MonadIO m, MonadCatch m, AllowScopes s)
134+ => Store s
135+ -> Logger
136+ -> Manager
137+ -> m (Store s)
138+refreshStore ss@(Store s) l m = do
139+ x <- retrieveAuthFromStore ss
140+ mx <- validate x
141+ if mx
142+ then pure ss
143+ else liftIO . modifyMVar s $ \y -> do
144+ my <- validate y
145+ if my
146+ then pure (y, ss)
147+ else do
148+ z <- refresh y l m
149+ tt <- fmap Store . liftIO . newMVar $ z
150+ pure (z, tt)
151+
152+-- | Retrieve auth from storage
153+retrieveAuthFromStore :: (MonadIO m, MonadCatch m, AllowScopes s)
154+ => Store s
155+ -> m (Auth s)
156+retrieveAuthFromStore (Store s) = liftIO (readMVar s)
157+
113158-- | Concurrently read the current token, and if expired, then
114159-- safely perform a single serial refresh.
115160getToken :: (MonadIO m, MonadCatch m, AllowScopes s)