gogol icon indicating copy to clipboard operation
gogol copied to clipboard

When to use Network.Google.Auth.authorize & why doesn't Credentials.FromClient have AccessToken & RefreshToken?

Open saurabhnanda opened this issue 6 years ago • 2 comments

I've been trying to wrap my head around this one:

  • After the oauth flow of an "installed application", the user is given a one-time code, which he/she is supposed to share with the application
  • The application then exchanges the one-time code for an oauth access-token & refresh-token:
oauthReturn :: Maybe Text -> AppM Text
oauthReturn Nothing = throwIO $ OAuthError "Did not receive `code` as a URL queryParam at the end of oauth flow"
oauthReturn (Just code_) = do
  env <- ask
  let oauthCreds = Google.allow (plusLoginScope Google.! userInfoEmailScope) $ Google.FromClient (env ^. oAuthClient) (OAuthCode code_)
  auth <- Google.exchange oauthCreds (env ^. googleLogger) (env ^. googleManager)
  pure ([qc|{code_} has been exchanged to {_token auth}|])
  • After that, if one wants to use Network.Google.send along with Network.Google.runGoogle, one needs to construct a value of type Network.Google.Env. Here's the type signature of newEnvWith:
newEnvWith :: (MonadIO m, MonadCatch m, AllowScopes s) => Credentials s -> Logger -> Manager -> m (Env s)
  • The only type related to oauth authentication is Credentials s, and none of the constructors of Credentials s bother with taking the oauth access-token & refresh-token obtained from the step above!

What am I missing here?

saurabhnanda avatar Jun 11 '18 02:06 saurabhnanda

Okay, managed to figure out how to create an Env at the end of the oauth flow, which calls exchange internally to get the access/refresh tokens:

oauthReturn :: Maybe Text -> AppM Text
oauthReturn Nothing = throwIO $ OAuthError "Did not receive `code` as a URL queryParam at the end of oauth flow"
oauthReturn (Just code_) = do
  env <- ask
  let oauthCreds = Google.allow (plusLoginScope Google.! userInfoEmailScope) $ Google.FromClient (env ^. oAuthClient) (OAuthCode code_)
  googleEnv <- Google.newEnvWith oauthCreds (env ^. googleLogger) (env ^. googleManager)
  auth <- retrieveAuthFromStore $ googleEnv ^. Google.envStore
  pure ([qc|{code_} has been exchanged to {_token auth}|])

saurabhnanda avatar Jun 11 '18 07:06 saurabhnanda

I experienced a similar problem with this as well. Creating Credentials from an auth code is fine, but in my case I wanted to create them directly with the refresh token which wasn't possible.

What I ended up doing is to generate the tokens with FromClient, exchange the result to get an Auth record, convert to AuthorizedUser with authToAuthorizedUser and then saveAuthorizedUser in a file. After that you can use the file to generate the Credentals.

The file itself is stored as a json with the client_id, client_secret and refresh_token so it's perfectly possible to ignore the above steps and just use that, but it would be nice if we could get a way of skipping the file and just getting a Credentials record from a client and a refresh token.

Stealthmate avatar Jul 24 '18 01:07 Stealthmate