GoogleCloudPlatform / elixir-samples

A collection of samples on using Elixir with Google Cloud Platform.
https://cloud.google.com/community
Apache License 2.0
291 stars 40 forks source link

Add example for using refresh token to the auth example #32

Open tensiondriven opened 2 years ago

tensiondriven commented 2 years ago

I have an offline task that needs to run periodically over a long time period, and my access token expires too quickly. I need to use a refresh token pattern to update the access token periodically, but documentation isn't clear.

Please add to the auth example the use of refresh token. Thanks!

nicnilov commented 1 year ago

Here is some explanation for future readers. Maybe someone could make a pull request from it, but there is really more conceptual info needed, because the code examples already exist elsewhere.

This elixir-samples repo is referenced from the elixir-google-api repo, which implements the support for Google Apis in an Elixir package. I'm not sure how relevant that package is in the context of GoogleCloudPlatform, but that's what I ended up using, so maybe the explanation is still meaningful.

Elixir is mostly used on the backend, and the refresh token is an OAuth concept. The OAuth flow contains a step where an end-user interaction is necessary through the UI in order for the end-user to give consent to the app to access the user's data. An Elixir server code running on the backend cannot present any UI to the user, especially because the user may not be there at the moment.

Because of that, all the server can do is refresh the access token using the refresh token to continue accessing the user's data, but it cannot obtain the refresh token on its own. Instead a separate flow needs to be implemented on the client, which obtains the refresh token and submits it to the server for future use.

In a scenario where a long-living server-side task runs periodically and accesses the Google API, one option is to obtain the refresh token manually beforehand. As the refresh token is valid until explicitly revoked, the task can use it to obtain the access token every time it runs, and continue with the API. Otherwise, if the user can be interacted with through some UI, the client OAuth flow can be implemented there, and then it's all straightforward.

Long story short, for the server side it all starts when the refresh token is already available. Then, as elixir-google-api's readme suggests, the goth package can be used to enable the token refresh. Expanding a bit on goth's own docs, here is how:

credentials =
  %{
    "client_id" => "...",
    "client_secret" => "...",
    "refresh_token" => "..."
  }

Goth.start_link(name: MyApp.Goth, source: {:refresh_token, credentials, []})

Goth.fetch!(MyApp.Goth)

In case of success, the response payload should contain a valid access token, which can be used to interact with the Google API. There are other ways to employ Goth as well, and it can also automatically refetch a new access token when the current one is nearing expiration.