Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: regclient/regclient
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.8.1
Choose a base ref
...
head repository: regclient/regclient
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v0.8.2
Choose a head ref
  • 4 commits
  • 3 files changed
  • 1 contributor

Commits on Feb 12, 2025

  1. Fix: Allow authentication with a token

    This fixes a regression caused by PR #893.
    
    Signed-off-by: Brandon Mitchell <git@bmitch.net>
    sudo-bmitch committed Feb 12, 2025
    Copy the full SHA
    0cd8674 View commit details
  2. Merge pull request #908 from sudo-bmitch/pr-auth-by-token

    Fix: Allow authentication with a token
    sudo-bmitch authored Feb 12, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    796a9bd View commit details

Commits on Feb 14, 2025

  1. Merge for release v0.8.2

    sudo-bmitch committed Feb 14, 2025
    Copy the full SHA
    6f5f56a View commit details
  2. Release v0.8.2

    Signed-off-by: Brandon Mitchell <git@bmitch.net>
    sudo-bmitch committed Feb 14, 2025
    Copy the full SHA
    e7e5436 View commit details
Showing with 84 additions and 34 deletions.
  1. +7 −8 internal/auth/auth.go
  2. +73 −0 internal/auth/auth_test.go
  3. +4 −26 release.md
15 changes: 7 additions & 8 deletions internal/auth/auth.go
Original file line number Diff line number Diff line change
@@ -624,17 +624,18 @@ func (b *bearerHandler) GenerateAuth() (string, error) {
return fmt.Sprintf("Bearer %s", b.token.Token), nil
}

// attempt to post if a refresh token is available
if b.token.RefreshToken != "" {
if err := b.tryPost(); err == nil {
// attempt to post if a refresh token is available or token auth is being used
cred := b.credsFn(b.host)
if b.token.RefreshToken != "" || cred.Token != "" {
if err := b.tryPost(cred); err == nil {
return fmt.Sprintf("Bearer %s", b.token.Token), nil
} else if err != ErrUnauthorized {
return "", fmt.Errorf("failed to request auth token (post): %w%.0w", err, errs.ErrHTTPUnauthorized)
}
}

// attempt a get (with basic auth if user/pass available)
if err := b.tryGet(); err == nil {
if err := b.tryGet(cred); err == nil {
return fmt.Sprintf("Bearer %s", b.token.Token), nil
} else if err != ErrUnauthorized {
return "", fmt.Errorf("failed to request auth token (get): %w%.0w", err, errs.ErrHTTPUnauthorized)
@@ -655,8 +656,7 @@ func (b *bearerHandler) isExpired() bool {
}

// tryGet requests a new token with a GET request
func (b *bearerHandler) tryGet() error {
cred := b.credsFn(b.host)
func (b *bearerHandler) tryGet(cred Cred) error {
req, err := http.NewRequest("GET", b.realm, nil)
if err != nil {
return err
@@ -691,8 +691,7 @@ func (b *bearerHandler) tryGet() error {
}

// tryPost requests a new token via a POST request
func (b *bearerHandler) tryPost() error {
cred := b.credsFn(b.host)
func (b *bearerHandler) tryPost(cred Cred) error {
form := url.Values{}
if len(b.scopes) > 0 {
form.Set("scope", strings.Join(b.scopes, " "))
73 changes: 73 additions & 0 deletions internal/auth/auth_test.go
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
"net/http"
@@ -653,3 +654,75 @@ func TestBearer(t *testing.T) {
t.Errorf("token5 (rerun) is already expired")
}
}

// TestBearerToken verifies a login with a token in the credential with a bearer request goes to POST.
func TestBearerToken(t *testing.T) {
t.Parallel()
useragent := "regclient/test"
user := "user"
token := "testtoken"
tokenResp, _ := json.Marshal(bearerToken{
Token: token,
ExpiresIn: 900,
IssuedAt: time.Now(),
Scope: "repository:reponame:pull",
RefreshToken: token,
})
tokenForm := url.Values{}
tokenForm.Set("scope", "repository:reponame:pull")
tokenForm.Set("service", "test")
tokenForm.Set("client_id", useragent)
tokenForm.Set("grant_type", "refresh_token")
tokenForm.Set("refresh_token", token)
tokenBody := tokenForm.Encode()
rrs := []reqresp.ReqResp{
{
ReqEntry: reqresp.ReqEntry{
Name: "req token",
Method: "POST",
Path: "/tokens",
Body: []byte(tokenBody),
},
RespEntry: reqresp.RespEntry{
Status: 200,
Body: tokenResp,
},
},
}
ts := httptest.NewServer(reqresp.NewHandler(t, rrs))
defer ts.Close()
tsURL, _ := url.Parse(ts.URL)
tsHost := tsURL.Host
bearer := NewBearerHandler(&http.Client{}, useragent, tsHost,
func(h string) Cred { return Cred{User: user, Token: token} },
slog.New(slog.NewTextHandler(io.Discard, &slog.HandlerOptions{})),
).(*bearerHandler)

// handle token1, verify expired token gets current time and isn't expired
err := bearer.AddScope("repository:reponame:pull")
if err != nil {
t.Errorf("failed adding scope: %v", err)
}
c, err := parseAuthHeader(
`Bearer realm="` + tsURL.String() +
`/tokens",service="test"` +
`,scope="repository:reponame:pull"`)
if err != nil {
t.Errorf("failed on parse challenge: %v", err)
}
err = bearer.ProcessChallenge(c[0])
if err != nil {
t.Errorf("failed on response to token: %v", err)
}
resp, err := bearer.GenerateAuth()
if err != nil {
t.Errorf("failed to generate auth response: %v", err)
}
bearerResp := fmt.Sprintf("Bearer %s", token)
if resp != bearerResp {
t.Errorf("token1 is invalid, expected %s, received %s", "Bearer token1", resp)
}
if bearer.isExpired() {
t.Errorf("token1 is already expired")
}
}
30 changes: 4 additions & 26 deletions release.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,13 @@
# Release v0.8.1
# Release v0.8.2

Security:

- Go v1.23.6 fixes CVE-2025-22866. ([PR 906][pr-906])

Features:

- Improve regctl arg completion. ([PR 895][pr-895])
- Add cobra command for documentation. ([PR 900][pr-900])
This fixes a regression in v0.8.1 for users authenticating using a refresh token.

Fixes:

- Do not request offline refresh token. ([PR 893][pr-893])
- Ignore unsupported entries in docker config. ([PR 894][pr-894])
- Align log levels with slog. ([PR 901][pr-901])
- Interval overrides a default schedule in regsync and regbot. ([PR 904][pr-904])

Miscellaneous:

- Adding a logo. ([PR 889][pr-889])
- Allow authentication with a token. ([PR 908][pr-908])

Contributors:

- @obaibula
- @sudo-bmitch

[pr-889]: https://github.com/regclient/regclient/pull/889
[pr-893]: https://github.com/regclient/regclient/pull/893
[pr-894]: https://github.com/regclient/regclient/pull/894
[pr-895]: https://github.com/regclient/regclient/pull/895
[pr-900]: https://github.com/regclient/regclient/pull/900
[pr-901]: https://github.com/regclient/regclient/pull/901
[pr-904]: https://github.com/regclient/regclient/pull/904
[pr-906]: https://github.com/regclient/regclient/pull/906
[pr-908]: https://github.com/regclient/regclient/pull/908