A Google service account, as a value: what the key file holds, and the signed
assertion that trades it for a Sheetshow.Token.
Google's flow is two steps. You sign a short-lived claim, I am this service
account and I would like these scopes, with the account's private key, and
the token endpoint gives back an access token. assertion/2 does the first
step; the second is one HTTP request, and token_request/2 is the data for
it, so everything here stays pure.
iex> pem = :public_key.pem_encode([
...> :public_key.pem_entry_encode(:RSAPrivateKey, :public_key.generate_key({:rsa, 2048, 65537}))
...> ])
iex> json = JSON.encode!(%{
...> "type" => "service_account",
...> "client_email" => "tests@example.iam.gserviceaccount.com",
...> "private_key" => pem
...> })
iex> account = Sheetshow.ServiceAccount.from_json!(json)
iex> account |> Sheetshow.ServiceAccount.assertion() |> String.split(".") |> length()
3Sheetshow never looks for a credential of its own accord: there is no well-known path, no environment variable, no cache. You read the file, you hold the token. Inspecting an account shows everything but the key.
Summary
Functions
The signed JWT that asks for an access token.
Reads a service-account key file. A convenience over from_json/1: the path
is yours to supply, and nothing about it is remembered.
Same as from_file/1, raising on failure.
Reads the JSON of a service-account key file. The private key is decoded here, so a credential that parses is one that can sign.
Same as from_json/1, raising on failure.
The token request as data, where to post and what to post, for whichever
HTTP client is doing the talking. Takes the same options as assertion/2.
Types
Functions
The signed JWT that asks for an access token.
Options: :scopes (default Sheetshow.Google.default_scopes/0), :lifetime in seconds
(default one hour, Google's maximum) and :now, which is there so a test can
say when it is.
account |> Sheetshow.ServiceAccount.assertion(scopes: ["https://www.googleapis.com/auth/spreadsheets.readonly"])
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwczovL29hdXRoMi5nb..."
@spec from_file(Path.t()) :: {:ok, t()} | {:error, Sheetshow.Error.t()}
Reads a service-account key file. A convenience over from_json/1: the path
is yours to supply, and nothing about it is remembered.
Same as from_file/1, raising on failure.
@spec from_json(String.t()) :: {:ok, t()} | {:error, Sheetshow.Error.t()}
Reads the JSON of a service-account key file. The private key is decoded here, so a credential that parses is one that can sign.
iex> {:error, %Sheetshow.Error{reason: :invalid_credentials}} =
...> Sheetshow.ServiceAccount.from_json(~s({"type": "authorized_user"}))
Same as from_json/1, raising on failure.
The token request as data, where to post and what to post, for whichever
HTTP client is doing the talking. Takes the same options as assertion/2.
Sheetshow.ServiceAccount.token_request(account)
%{
url: "https://oauth2.googleapis.com/token",
form: %{grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer", assertion: "eyJhbGci..."}
}