Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576defmodule GitSync.Github.OAuth do @moduledoc """ GitHub's OAuth web flow, hand-rolled.
GitHub publishes no OIDC discovery document, and nothing here establishes an identity: the flow exists only to obtain an API token for the operator who is already signed in. """
alias GitSync.Connection alias GitSync.Forge.Token
@scopes ["repo", "admin:repo_hook"]
def scopes, do: @scopes
@doc """ A CSRF token to carry through the round trip to GitHub. """ def state, do: 24 |> :crypto.strong_rand_bytes() |> Base.url_encode64(padding: false)
def authorize_url(%Connection{base_url: base_url, client_id: client_id}, redirect_uri, state) do query = URI.encode_query( client_id: client_id, redirect_uri: redirect_uri, scope: Enum.join(@scopes, " "), state: state )
web_url(base_url, "/login/oauth/authorize") <> "?" <> query end
def exchange_code(%Connection{} = connection, code, redirect_uri) do token(connection, code: code, redirect_uri: redirect_uri ) end
@doc """ Trades a refresh token for a fresh access token and its successor. """ def refresh(%Connection{refresh_token: refresh_token} = connection) do token(connection, grant_type: "refresh_token", refresh_token: refresh_token ) end
defp token(%Connection{} = connection, grant) do request = GitSync.Http.request( url: web_url(connection.base_url, "/login/oauth/access_token"), headers: [accept: "application/json"], form: [client_id: connection.client_id, client_secret: connection.client_secret] ++ grant )
case Req.post(request) do {:ok, %Req.Response{status: 200, body: %{"access_token" => access} = body}} -> {:ok, Token.new(access, body["refresh_token"], body["expires_in"])}
{:ok, %Req.Response{status: 200, body: %{"error_description" => description}}} -> {:error, description}
{:ok, %Req.Response{status: status}} -> {:error, "GitHub returned HTTP #{status}"}
{:error, exception} -> {:error, Exception.message(exception)} end end
defp web_url(base_url, path), do: String.trim_trailing(base_url, "/") <> pathend