Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,52 @@ if err != nil {
fmt.Println(secrets[0].Value)
```

## How to fetch a Docker Hub access token

The client exposes a Docker Hub authentication accessor via `HubAuth()`. It
locates the right credential and decodes the JSON payload into a typed
`UserSession`, so you don't need to know the realm layout or the payload
format:

```go
hub := c.HubAuth()

// Fetch the session of the default signed-in account ...
session, err := hub.GetDefaultSession(context.Background())
if errors.Is(err, dockerhub.ErrNoSession) {
// Also matches dockerhub.ErrNoDefaultProfile, which wraps ErrNoSession.
log.Fatalf("not signed in to Docker Hub")
}
if err != nil {
log.Fatalf("failed fetching access token: %v", err)
}

// ... or fetch the session of a specific account.
session, err = hub.GetSession(context.Background(), "myuser")
if err != nil {
log.Fatalf("failed fetching access token: %v", err)
}

fmt.Println(session.AccessToken) // the raw JWT access token
fmt.Println(session.Claims.Username) // decoded token claims
fmt.Println(session.Claims.ExpiresAt)

// List the profiles of all signed-in accounts.
profiles, err := hub.ListProfiles(context.Background())
if err != nil {
log.Fatalf("failed listing profiles: %v", err)
}
for _, profile := range profiles {
fmt.Println(profile.Username, profile.UserID)
}
```

If you hold a bare `secrets.Resolver` instead of the full client, construct
the accessor directly with `dockerhub.New(engine)`.

Only tokens accessible to the calling application are returned; the engine's
access control decides what a client is allowed to read.

## How to create a plugin

### 1. Implement the plugin interface
Expand Down
10 changes: 10 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"connectrpc.com/connect"

"github.com/docker/secrets-engine/client/dockerhub"
"github.com/docker/secrets-engine/x/api"
healthv1 "github.com/docker/secrets-engine/x/api/health/v1"
"github.com/docker/secrets-engine/x/api/health/v1/healthv1connect"
Expand Down Expand Up @@ -138,6 +139,12 @@ func (c client) GetSecrets(ctx context.Context, pattern secrets.Pattern) ([]secr
return envelopes, nil
}

func (c client) HubAuth(opts ...dockerhub.Option) dockerhub.ClientAuth {
// New fails only on a nil engine; c never is.
hub, _ := dockerhub.New(c, opts...)
return hub
}

func (c client) Version(ctx context.Context) (DaemonVersion, error) {
resp, err := c.versionClient.GetVersion(ctx, connect.NewRequest(healthv1.GetVersionRequest_builder{}.Build()))
if isDialError(err) {
Expand All @@ -159,6 +166,9 @@ type Client interface {

// Version returns the name and version reported by the daemon.
Version(ctx context.Context) (DaemonVersion, error)

// HubAuth returns a Docker Hub authentication accessor backed by this client.
HubAuth(opts ...dockerhub.Option) dockerhub.ClientAuth
}

type PluginManagement interface {
Expand Down
8 changes: 8 additions & 0 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"

"github.com/docker/secrets-engine/client/dockerhub"
"github.com/docker/secrets-engine/x/api"
healthv1 "github.com/docker/secrets-engine/x/api/health/v1"
"github.com/docker/secrets-engine/x/api/health/v1/healthv1connect"
Expand Down Expand Up @@ -260,6 +261,13 @@ func TestSecretsEngineUnavailable(t *testing.T) {
require.ErrorIs(t, err, ErrSecretsEngineNotAvailable)
}

func TestHubAuth(t *testing.T) {
client, err := New(WithSocketPath(testhelper.RandomShortSocketName()))
require.NoError(t, err)
assert.NotNil(t, client.HubAuth())
assert.NotNil(t, client.HubAuth(dockerhub.Staging()))
}

func TestIsDialError(t *testing.T) {
require.True(t, isDialError(&net.OpError{
Op: "dial",
Expand Down
Loading