Skip to content
Merged
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
21 changes: 17 additions & 4 deletions cmd/auth-clearCachedCredentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/spf13/cobra"
)

var clearCachedCredsCmd = man.Docs.GetCommand("auth/clear-cached-credentials",
var auth_clearClientCredentialsCmd = man.Docs.GetCommand("auth/clear-client-credentials",
man.WithRun(auth_clearCreds),
man.WithHiddenFlags("with-client-creds", "with-client-creds-file"),
)
Expand All @@ -18,9 +18,22 @@ func auth_clearCreds(cmd *cobra.Command, args []string) {
flagHelper := cli.NewFlagHelper(cmd)
host := flagHelper.GetRequiredString("host")

if err := handlers.ClearCachedCredentials(host); err != nil {
cli.ExitWithError("Failed to clear cached client credentials and token", err)
p := cli.NewPrinter(true)

p.Printf("Clearing cached client credentials for %s... ", host)
if err := handlers.NewKeyring(host).DeleteClientCredentials(); err != nil {
fmt.Println("failed")
cli.ExitWithError("Failed to clear cached client credentials", err)
}
p.Println("ok")
}

func init() {
auth_clearClientCredentialsCmd.Flags().String(
auth_clearClientCredentialsCmd.GetDocFlag("all").Name,
auth_clearClientCredentialsCmd.GetDocFlag("all").Description,
auth_clearClientCredentialsCmd.GetDocFlag("all").Default,
)

fmt.Println(cli.SuccessMessage("Cached client credentials and token are clear."))
authCmd.AddCommand(&auth_clearClientCredentialsCmd.Command)
}
89 changes: 26 additions & 63 deletions cmd/auth-clientCredentials.go
Original file line number Diff line number Diff line change
@@ -1,94 +1,57 @@
package cmd

import (
"errors"
"fmt"
"log/slog"

"github.com/opentdf/otdfctl/pkg/cli"
"github.com/opentdf/otdfctl/pkg/handlers"
"github.com/opentdf/otdfctl/pkg/man"
"github.com/spf13/cobra"
)

var (
clientCredentialsCmd = man.Docs.GetCommand("auth/client-credentials",
man.WithRun(auth_clientCredentials),
)
noCacheCreds bool
var clientCredentialsCmd = man.Docs.GetCommand("auth/client-credentials",
man.WithRun(auth_clientCredentials),
man.WithHiddenFlags("with-client-creds", "with-client-creds-file"),
)

func auth_clientCredentials(cmd *cobra.Command, args []string) {
var err error
var c handlers.ClientCredentials

flagHelper := cli.NewFlagHelper(cmd)
host := flagHelper.GetRequiredString("host")
tlsNoVerify := flagHelper.GetOptionalBool("tls-no-verify")
clientID := flagHelper.GetOptionalString("client-id")
clientSecret := flagHelper.GetOptionalString("client-secret")

slog.Debug("Checking for client credentials file", slog.String("with-client-creds-file", clientCredsFile))
if clientCredsFile != "" {
creds, err := handlers.GetClientCredsFromFile(clientCredsFile)
if err != nil {
cli.ExitWithError("Failed to parse client credentials JSON", err)
}
clientID = creds.ClientID
clientSecret = creds.ClientSecret
}
p := cli.NewPrinter(true)

// if not provided by flag, check keyring cache for clientID
if clientID == "" {
slog.Debug("No client-id provided. Attempting to retrieve the default from keyring.")
clientID, err = handlers.GetClientIDFromCache(host)
if err != nil || clientID == "" {
cli.ExitWithError("Please provide required flag: (client-id)", errors.New("no client-id found"))
} else {
slog.Debug(cli.SuccessMessage("Retrieved stored client-id from keyring"))
}
if len(args) > 0 {
c.ClientId = args[0]
}
if len(args) > 1 {
c.ClientSecret = args[1]
}

// check if we have a clientSecret in the keyring, if a null value is passed in
if clientSecret == "" {
clientSecret, err = handlers.GetClientSecretFromCache(host, clientID)
if err == nil || clientSecret == "" {
cli.ExitWithError("Please provide required flag: (client-secret)", errors.New("no client-secret found"))
} else {
slog.Debug("Retrieved stored client-secret from keyring")
}
if c.ClientId == "" {
c.ClientId = cli.AskForInput("Enter client id: ")
}
if c.ClientSecret == "" {
c.ClientSecret = cli.AskForSecret("Enter client secret: ")
}

slog.Debug("Attempting to login with client credentials", slog.String("client-id", clientID))
if err := handlers.GetTokenWithClientCreds(cmd.Context(), host, clientID, clientSecret, tlsNoVerify); err != nil {
p.Printf("Logging in with client ID and secret for %s... ", host)
if _, err := handlers.GetTokenWithClientCreds(cmd.Context(), host, c, tlsNoVerify); err != nil {
fmt.Println("failed")
cli.ExitWithError("An error occurred during login. Please check your credentials and try again", err)
}
p.Println("ok")

fmt.Println(cli.SuccessMessage("Successfully logged in with client ID and secret"))
p.Print("Storing client ID and secret in keyring... ")
if err := handlers.NewKeyring(host).SetClientCredentials(c); err != nil {
fmt.Println("failed")
cli.ExitWithError("Failed to cache client credentials", err)
}
p.Println("ok")
}

func init() {
clientCredentialsCmd := man.Docs.GetCommand("auth/client-credentials",
man.WithRun(auth_clientCredentials),
// use the individual client-id and client-secret flags here instead of the global with-client-creds flag
man.WithHiddenFlags("with-client-creds", "with-client-creds-file"),
)
clientCredentialsCmd.Flags().StringP(
clientCredentialsCmd.GetDocFlag("client-id").Name,
clientCredentialsCmd.GetDocFlag("client-id").Shorthand,
clientCredentialsCmd.GetDocFlag("client-id").Default,
clientCredentialsCmd.GetDocFlag("client-id").Description,
)
clientCredentialsCmd.Flags().StringP(
clientCredentialsCmd.GetDocFlag("client-secret").Name,
clientCredentialsCmd.GetDocFlag("client-secret").Shorthand,
clientCredentialsCmd.GetDocFlag("client-secret").Default,
clientCredentialsCmd.GetDocFlag("client-secret").Description,
)
clientCredentialsCmd.Flags().BoolVarP(
&noCacheCreds,
clientCredentialsCmd.GetDocFlag("no-cache").Name,
clientCredentialsCmd.GetDocFlag("no-cache").Shorthand,
clientCredentialsCmd.GetDocFlag("no-cache").DefaultAsBool(),
clientCredentialsCmd.GetDocFlag("no-cache").Description,
)
authCmd.AddCommand(&clientCredentialsCmd.Command)
}
53 changes: 47 additions & 6 deletions cmd/auth-printAccessToken.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cmd

import (
"context"
"encoding/json"
"fmt"

"github.com/opentdf/otdfctl/pkg/cli"
Expand All @@ -9,17 +11,56 @@ import (
"github.com/spf13/cobra"
)

var printAccessToken = man.Docs.GetCommand("auth/print-access-token",
man.WithRun(auth_printAccessToken),
)
var auth_printAccessTokenCmd = man.Docs.GetCommand("auth/print-access-token",
man.WithRun(auth_printAccessToken))

func auth_printAccessToken(cmd *cobra.Command, args []string) {
flagHelper := cli.NewFlagHelper(cmd)
host := flagHelper.GetRequiredString("host")
jsonOut := flagHelper.GetOptionalBool("json")

printEnabled := !jsonOut
p := cli.NewPrinter(printEnabled)

p.Printf("Getting stored client credentials for %s... ", host)
clientCredentials, err := handlers.NewKeyring(host).GetClientCredentials()
if err != nil {
p.Println("failed")
cli.ExitWithError("Client credentials not found. Please use `auth client-credentials` to set them", err)
}
p.Println("ok")

tok, err := handlers.GetOIDCTokenFromCache(host)
p.Printf("Getting access token for %s... ", clientCredentials.ClientId)
tok, err := handlers.GetTokenWithClientCreds(
context.Background(),
host,
clientCredentials,
flagHelper.GetOptionalBool("tls-no-verify"),
)
if err != nil {
cli.ExitWithError("Failed to get OIDC token from cache", err)
p.Println("failed")
cli.ExitWithError("Failed to get token", err)
}
fmt.Print(tok)
p.Println("ok")
p.Printf("Access Token: %s\n", tok.AccessToken)

if jsonOut {
d, err := json.MarshalIndent(tok, "", " ")
if err != nil {
cli.ExitWithError("Failed to marshal token to json", err)
}

fmt.Println(string(d))
return
}
}

func init() {
auth_printAccessTokenCmd.Flags().Bool(
auth_printAccessTokenCmd.GetDocFlag("json").Name,
auth_printAccessTokenCmd.GetDocFlag("json").DefaultAsBool(),
auth_printAccessTokenCmd.GetDocFlag("json").Description,
)

authCmd.AddCommand(&auth_printAccessTokenCmd.Command)
}
15 changes: 7 additions & 8 deletions cmd/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import (
"github.com/spf13/cobra"
)

var authCmd = man.Docs.GetCommand("auth", man.WithHiddenFlags(
"with-client-creds",
"with-client-creds-file",
))

func init() {
cmd := man.Docs.GetCommand("auth",
man.WithSubcommands(clientCredentialsCmd),
man.WithSubcommands(printAccessToken),
man.WithSubcommands(clearCachedCredsCmd),
)
RootCmd.AddCommand(&authCmd.Command)

cmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
authCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
// not supported on linux
if runtime.GOOS == "linux" {
cli.ExitWithWarning(
Expand All @@ -24,6 +25,4 @@ func init() {
)
}
}

RootCmd.AddCommand(&cmd.Command)
}
2 changes: 1 addition & 1 deletion cmd/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ func NewHandler(cmd *cobra.Command) handlers.Handler {
cli.ExitWithError("Failed to get client credentials", err)
}

h, err := handlers.NewWithCredentials(host, creds.ClientID, creds.ClientSecret, tlsNoVerify)
h, err := handlers.NewWithCredentials(host, creds.ClientId, creds.ClientSecret, tlsNoVerify)
if err != nil {
if errors.Is(err, handlers.ErrUnauthenticated) {
cli.ExitWithError(fmt.Sprintf("Not logged in. Please authenticate via CLI auth flow(s) before using command (%s %s)", cmd.Parent().Use, cmd.Use), err)
Expand Down
8 changes: 0 additions & 8 deletions docs/man/auth/clear-cached-credentials.md

This file was deleted.

12 changes: 12 additions & 0 deletions docs/man/auth/clear-client-credentials.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: Clear the cached client credentials

command:
name: clear-client-credentials
flags:
- name: all
description: Clear all cached client credentials
default: false
---

Clear the cached client credentials from the OS keyring for the current platform endpoint.
18 changes: 6 additions & 12 deletions docs/man/auth/client-credentials.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,11 @@ title: Authenticate to the platform with the client-credentials flow

command:
name: client-credentials
flags:
- name: client-id
description: A clientId for use in client-credentials auth flow
shorthand: i
required: true
- name: client-secret
description: A clientSecret for use in client-credentials auth flow
shorthand: s
- name: no-cache
description: Do not cache credentials on the native OS and print access token to stdout instead
args:
- client-id
arbitrary_args:
- client-secret
---

Allows the user to login in via Client ID and Secret. The client credentials and OIDC Access Token will be stored
in the OS-specific keychain by default, otherwise printed to `stdout` if `--no-cache` is passed.
Allows the user to login in via Client Credentials flow. The client credentials will be stored safely
in the OS keyring for future use.
6 changes: 5 additions & 1 deletion docs/man/auth/print-access-token.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ title: Print the cached OIDC access token (if found)

command:
name: print-access-token
flags:
- name: json
description: Print the full token in JSON format
default: false
---

Retrieves the cached OIDC Access Token from the OS-specific keychain and prints to stdout if found.
Retrieves a new OIDC Access Token using the client credentials from the OS-specific keychain and prints to stdout if found.
13 changes: 13 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ require (
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0
github.com/zalando/go-keyring v0.2.5
golang.org/x/oauth2 v0.22.0
golang.org/x/term v0.22.0
google.golang.org/grpc v1.65.0
google.golang.org/protobuf v1.34.2
Expand All @@ -47,10 +48,14 @@ require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/gowebpki/jcs v1.0.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
Expand All @@ -75,6 +80,7 @@ require (
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/muhlemmer/gu v0.3.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/opentdf/platform/lib/ocrypto v0.1.5 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
Expand All @@ -85,6 +91,7 @@ require (
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
Expand All @@ -96,6 +103,12 @@ require (
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yuin/goldmark v1.5.4 // indirect
github.com/yuin/goldmark-emoji v1.0.2 // indirect
github.com/zitadel/logging v0.6.0 // indirect
github.com/zitadel/oidc/v3 v3.27.0 // indirect
github.com/zitadel/schema v1.3.0 // indirect
go.opentelemetry.io/otel v1.28.0 // indirect
go.opentelemetry.io/otel/metric v1.28.0 // indirect
go.opentelemetry.io/otel/trace v1.28.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
Expand Down
Loading