feat: 實作 bear CLI PAT 模式 MVP(login/whoami/token/logout/status)
- 新增 Elixir + Mix escript 專案(Req 0.5+、Jason) - login --token <PAT>:以 PAT 驗證 /userinfo 後寫入憑證檔(0600) - whoami:GET /userinfo 顯示身分 claims(支援 --json) - token:印出 access token 供 pipe(--refresh 在 PAT 模式忽略) - logout:清除本機憑證;status:純本機判定 - 憑證檔採原子寫入(rename)、0600、O_CREAT|O_EXCL 防 symlink - 全域選項:--issuer/--config/--json/-v/-h/--version - 29 個單元測試;mix precommit(compile --warnings-as-errors + format + test) - 更新 README.md 與 docs/commands.md 反映 PAT 模式 MVP Device Flow 登入待伺服器端 P1(bear 倉庫)完成後再接。issue #2
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
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
|
||||
Reference in New Issue
Block a user