Skip to content
Open
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
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,26 @@ See the [Installation Guide](https://www.datum.net/docs/quickstart/datumctl/) fo
```
Now you can use `kubectl` to interact with your Datum Cloud control plane.

4. **Manage contexts and clusters (optional):**
```bash
# Define a cluster (base API server URL)
datumctl config set-cluster prod --server https://api.datum.net

# Create a context for a project and namespace
datumctl config set-context prod-project \
--cluster prod \
--project <project-id> \
--namespace default

# Switch the current context
datumctl config use-context prod-project

# View or list contexts
datumctl config view
datumctl config get-contexts
```
Contexts are stored in `~/.datumctl/config`. Credentials remain in the system keychain.

### MCP Setup

MCP can target either an **organization** or **project** control plane. For maximum flexibility, we recommend starting with an organization context.
Expand Down Expand Up @@ -86,7 +106,7 @@ echo "Project ready: $PRJ_ID"

Start the Model Context Protocol (MCP) server targeting a specific Datum Cloud context:
```bash
# Exactly one of --organization or --project is required.
# Exactly one of --organization or --project is required (unless a current context provides one).
datumctl mcp --organization <org-id> --namespace <ns> [--port 8080]
# or
datumctl mcp --project <project-id> --namespace <ns> [--port 8080]
Expand Down
58 changes: 18 additions & 40 deletions docs/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@ API keys directly.
Authentication involves the following commands:

* `datumctl auth login`
* `datumctl auth list`
* `datumctl auth logout`
* `datumctl auth get-token`
* `datumctl auth update-kubeconfig`
* `datumctl auth switch`

Credentials and tokens are stored securely in your operating system's default
keyring.
Expand Down Expand Up @@ -47,6 +45,14 @@ Running this command will:
Your credentials (including refresh tokens) are stored securely in the system
keyring, associated with your user identifier (typically your email address).

On every successful login, `datumctl` also ensures a matching cluster/context
entry exists in `~/.datumctl/config` for the API host you authenticated against.
If a current context already exists, it remains unchanged.

`datumctl` stores a list of users in `~/.datumctl/config` and links each context
to a user key (in the `subject@auth-hostname` format). The actual tokens are
stored in your OS keyring under the `datumctl-auth` service.

## Updating kubeconfig

Once logged in, you typically need to configure `kubectl` to authenticate to
Expand All @@ -73,38 +79,8 @@ This command adds or updates the necessary cluster, user, and context entries
in your kubeconfig file. The user entry will be configured to use
`datumctl auth get-token --output=client.authentication.k8s.io/v1` as an `exec`
credential plugin. This means `kubectl` commands targeting this cluster will
automatically use your active `datumctl` login session for authentication.

## Listing logged-in users

To see which users you have authenticated locally, use the `list` command:

```
datumctl auth list
# Alias: datumctl auth ls
```

This will output a table showing the Name, Email, and Status (Active or blank)
for each set of stored credentials. The user marked `Active` is the one whose
credentials will be used by default for other `datumctl` commands and
`kubectl` (if configured via `update-kubeconfig`).

## Switching active user

If you have logged in with multiple user accounts (visible via
`datumctl auth list`), you can switch which account is active using the
`switch` command:

```
datumctl auth switch <user-email>
```

Replace `<user-email>` with the email address of the user you want to make
active. This user must already be logged in.

After switching, subsequent commands that require authentication (like
`datumctl organizations list` or `kubectl` operations configured via
`update-kubeconfig`) will use the credentials of the newly activated user.
automatically use the credentials associated with your current `datumctl`
context for authentication.

## Logging out

Expand All @@ -113,11 +89,11 @@ To remove stored credentials, use the `logout` command.
**Log out a specific user:**

```
datumctl auth logout <user-email>
datumctl auth logout <user-key>
```

Replace `<user-email>` with the email address shown in the
`datumctl auth list` command.
Replace `<user-key>` with the key shown in the `users` list in
`~/.datumctl/config`. Use `--all` to remove all credentials.

**Log out all users:**

Expand All @@ -129,12 +105,12 @@ This removes all Datum Cloud credentials stored by `datumctl` in your keyring.

## Getting tokens (advanced)

The `get-token` command retrieves the current access token for the *active*
authenticated user. This is primarily used internally by other tools (like
The `get-token` command retrieves the current access token for the credentials
associated with the current context. This is primarily used internally by other tools (like
`kubectl`) but can be used directly if needed.

```
datumctl auth get-token [-o <format>]
datumctl auth get-token [-o <format>] [--cluster <datumctl-cluster>]
```

* `-o, --output <format>`: (Optional) Specify the output format. Defaults to
Expand All @@ -143,6 +119,8 @@ datumctl auth get-token [-o <format>]
* `client.authentication.k8s.io/v1`: Prints a Kubernetes `ExecCredential`
JSON object containing the ID token, suitable for `kubectl`
authentication.
* `--cluster <datumctl-cluster>`: (Optional) Use credentials bound to the
specified datumctl cluster instead of the current context.

If the stored access token is expired, `get-token` will attempt to use the
refresh token to obtain a new one automatically.
16 changes: 10 additions & 6 deletions docs/developer/authentication_flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ sequenceDiagram
Datumctl->>DatumAuth: Verify ID Token signature & claims
Datumctl->>Datumctl: Extract user info (email, name) from ID Token
Datumctl->>OSKeyring: Store Credentials (Hostname, Tokens, User Info) keyed by user email
Datumctl->>OSKeyring: Update Active User pointer
Datumctl->>OSKeyring: Update Known Users list
Datumctl->>OSKeyring: Bind credentials to active_user.cluster.<cluster>
Datumctl->>Datumctl: Upsert cluster/context/user in ~/.datumctl/config (current-context unchanged if set)
Datumctl-->>-User: Login Successful
```

Expand Down Expand Up @@ -81,10 +82,13 @@ sequenceDiagram
endpoints, scopes, and user info, is marshalled to JSON and stored
securely in the OS keyring. The key for this entry is the user's email
address.
11. **Active User:** A pointer (`active_user`) is also stored in the keyring,
indicating which user's credentials should be used by default.
12. **Known Users:** The user's key (email) is added to a list (`known_users`)
in the keyring to facilitate listing and multi-user management.
11. **Cluster Mapping:** A cluster-specific pointer
(`active_user.cluster.<cluster>`) is stored in the keyring, indicating which
user's credentials should be used for that cluster.
12. **Config Users:** The config file stores a `users` list keyed by
`subject@hostname` and each context references a user.
13. **Known Users:** The user's key (subject@hostname) is added to a list (`known_users`)
in the keyring for compatibility and multi-user management.

## Token refresh & usage

Expand Down Expand Up @@ -112,7 +116,7 @@ When commands like `datumctl organizations list` or
* The `internal/keyring` package provides a wrapper around
`github.com/zalando/go-keyring`.
* The `internal/authutil` package defines constants for the service name
(`datumctl-auth`) and keys (`active_user`, `known_users`, `<user-email>`)
(`datumctl-auth`) and keys (`active_user.cluster.<cluster>`, `known_users`, `<subject@hostname>`)
used within the keyring.
* `authutil.StoredCredentials` is the structure marshalled into JSON for
storage.
Expand Down
39 changes: 39 additions & 0 deletions internal/authutil/cluster_credentials.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package authutil

import (
"errors"
"fmt"

"go.datum.net/datumctl/internal/keyring"
)

const clusterActiveUserKeyPrefix = "active_user.cluster."

func clusterActiveUserKey(clusterName string) string {
return clusterActiveUserKeyPrefix + clusterName
}

// GetActiveUserKeyForCluster returns the user key mapped to the cluster.
func GetActiveUserKeyForCluster(clusterName string) (string, error) {
if clusterName == "" {
return "", ErrNoCurrentContext
}

userKey, err := keyring.Get(ServiceName, clusterActiveUserKey(clusterName))
if err == nil && userKey != "" {
return userKey, nil
}
if err != nil && !errors.Is(err, keyring.ErrNotFound) {
return "", fmt.Errorf("failed to get active user for cluster %q: %w", clusterName, err)
}

return "", ErrNoActiveUserForCluster
}

// SetActiveUserKeyForCluster stores a cluster-specific mapping to a user key.
func SetActiveUserKeyForCluster(clusterName, userKey string) error {
if clusterName == "" || userKey == "" {
return fmt.Errorf("cluster name and user key are required")
}
return keyring.Set(ServiceName, clusterActiveUserKey(clusterName), userKey)
}
Loading