Skip to content

Commit 5c039c3

Browse files
authored
Replace password based auth with oauth device flow (#1085)
1 parent 5f83052 commit 5c039c3

File tree

91 files changed

+2418
-1673
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+2418
-1673
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ jobs:
6262
env:
6363
HEXPM_OTP: OTP-28.1
6464
HEXPM_ELIXIR: v1.18.4
65+
HEXPM_BRANCH: main
6566
HEXPM_PATH: hexpm
6667
HEXPM_ELIXIR_PATH: hexpm_elixir
6768
HEXPM_OTP_PATH: hexpm_otp
@@ -92,7 +93,7 @@ jobs:
9293
9394
- name: Set up hexpm
9495
run: |
95-
git clone https://github.com/hexpm/hexpm.git hexpm
96+
git clone -b ${HEXPM_BRANCH} https://github.com/hexpm/hexpm.git hexpm
9697
cd hexpm; PATH=$(pwd)/../${HEXPM_ELIXIR_PATH}/bin:$(pwd)/../${HEXPM_OTP_PATH}/bin:${PATH} MIX_HOME=$(pwd)/../${HEXPM_MIX_HOME} MIX_ARCHIVES=$(pwd)/../${HEXPM_MIX_HOME} MIX_ENV=hex ../${HEXPM_ELIXIR_PATH}/bin/mix deps.get; cd ..
9798
cd hexpm; PATH=$(pwd)/../${HEXPM_ELIXIR_PATH}/bin:$(pwd)/../${HEXPM_OTP_PATH}/bin:${PATH} MIX_HOME=$(pwd)/../${HEXPM_MIX_HOME} MIX_ARCHIVES=$(pwd)/../${HEXPM_MIX_HOME} MIX_ENV=hex ../${HEXPM_ELIXIR_PATH}/bin/mix compile; cd ..
9899

config/config.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Config
22

33
if config_env() == :test do
4-
config :logger, level: :warn
4+
config :logger, level: :warning
55
config :logger, :console, format: "$date $time [$level] $message\n"
66
end

lib/hex/api/client.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ defmodule Hex.API.Client do
1818
defp maybe_put_api_key(config, opts) do
1919
cond do
2020
opts[:key] ->
21-
Map.put(config, :api_key, opts[:key])
21+
# Add Bearer prefix only for OAuth tokens
22+
token = if opts[:oauth], do: "Bearer #{opts[:key]}", else: opts[:key]
23+
Map.put(config, :api_key, token)
2224

2325
opts[:user] && opts[:pass] ->
2426
# For basic auth, add it as an HTTP header

lib/hex/api/oauth.ex

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
defmodule Hex.API.OAuth do
2+
@moduledoc false
3+
4+
alias Hex.API.Client
5+
6+
@client_id "78ea6566-89fd-481e-a1d6-7d9d78eacca8"
7+
8+
@doc """
9+
Initiates the OAuth device authorization flow.
10+
11+
Returns device code, user code, and verification URIs for user authentication.
12+
Optionally accepts a name parameter to identify the token.
13+
14+
## Examples
15+
16+
iex> Hex.API.OAuth.device_authorization("api")
17+
{:ok, {200, _headers, %{
18+
"device_code" => "...",
19+
"user_code" => "ABCD-1234",
20+
"verification_uri" => "https://hex.pm/oauth/device",
21+
"verification_uri_complete" => "https://hex.pm/oauth/device?user_code=ABCD-1234",
22+
"expires_in" => 600,
23+
"interval" => 5
24+
}}}
25+
"""
26+
def device_authorization(scopes, name \\ nil) do
27+
config = Client.config()
28+
opts = if name, do: [name: name], else: []
29+
:mix_hex_api_oauth.device_authorization(config, @client_id, scopes, opts)
30+
end
31+
32+
@doc """
33+
Polls the OAuth token endpoint for device authorization completion.
34+
35+
## Examples
36+
37+
iex> Hex.API.OAuth.poll_device_token(device_code)
38+
{:ok, {200, _headers, %{
39+
"access_token" => "...",
40+
"refresh_token" => "...",
41+
"token_type" => "Bearer",
42+
"expires_in" => 3600
43+
}}}
44+
"""
45+
def poll_device_token(device_code) do
46+
config = Client.config()
47+
:mix_hex_api_oauth.poll_device_token(config, @client_id, device_code)
48+
end
49+
50+
@doc """
51+
Exchanges a token for a new token with different scopes using RFC 8693 token exchange.
52+
53+
## Examples
54+
55+
iex> Hex.API.OAuth.exchange_token(subject_token, "api:write")
56+
{:ok, {200, _headers, %{
57+
"access_token" => "...",
58+
"refresh_token" => "...",
59+
"token_type" => "Bearer",
60+
"expires_in" => 3600
61+
}}}
62+
"""
63+
def exchange_token(subject_token, scope) do
64+
config = Client.config()
65+
:mix_hex_api_oauth.exchange_token(config, @client_id, subject_token, scope)
66+
end
67+
68+
@doc """
69+
Refreshes an access token using a refresh token.
70+
71+
## Examples
72+
73+
iex> Hex.API.OAuth.refresh_token(refresh_token)
74+
{:ok, {200, _headers, %{
75+
"access_token" => "...",
76+
"refresh_token" => "...",
77+
"token_type" => "Bearer",
78+
"expires_in" => 3600
79+
}}}
80+
"""
81+
def refresh_token(refresh_token) do
82+
config = Client.config()
83+
:mix_hex_api_oauth.refresh_token(config, @client_id, refresh_token)
84+
end
85+
86+
@doc """
87+
Revokes an OAuth token (access or refresh token).
88+
89+
## Examples
90+
91+
iex> Hex.API.OAuth.revoke_token(token)
92+
{:ok, {200, _headers, nil}}
93+
"""
94+
def revoke_token(token) do
95+
config = Client.config()
96+
:mix_hex_api_oauth.revoke_token(config, @client_id, token)
97+
end
98+
end

lib/hex/crypto.ex

Lines changed: 0 additions & 91 deletions
This file was deleted.

0 commit comments

Comments
 (0)