defmodule BearCli.Api do @moduledoc """ Bear 伺服器 HTTP 介面(一律使用 `Req`)。 目前僅實作 PAT 模式所需的 `GET /userinfo`。 """ @finch BearCli.Finch @doc """ 呼叫 `GET {issuer}/userinfo`(Bearer token)。 回傳: - `{:ok, claims}` — 200,claims 為 JSON map - `{:error, :unauthorized, description}` — 401 - `{:error, :server_error, status}` — 其他非 2xx - `{:error, :network, reason}` — 傳輸層錯誤 """ def userinfo(issuer, token) do url = String.trim_trailing(issuer, "/") <> "/userinfo" case Req.get(url, headers: [authorization: "Bearer " <> token, accept: "application/json"], retry: false, finch: [name: @finch] ) do {:ok, %Req.Response{status: 200, body: body}} -> {:ok, decode_body(body)} {:ok, %Req.Response{status: 401, body: body}} -> {:error, :unauthorized, error_description(body)} {:ok, %Req.Response{status: status}} -> {:error, :server_error, status} {:error, exception} -> {:error, :network, Exception.message(exception)} end end defp decode_body(%{} = body), do: body defp decode_body(body) when is_binary(body), do: Jason.decode!(body) defp decode_body(_), do: %{} defp error_description(body) when is_binary(body) do case Jason.decode(body) do {:ok, %{"error_description" => desc}} when is_binary(desc) -> desc {:ok, %{"error" => err}} when is_binary(err) -> err _ -> "invalid_token" end end defp error_description(_), do: "invalid_token" end