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
65 changes: 63 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,45 @@ Currently `Basic Authentication` via `Active Directory` is the *ONLY* supported
| KEYFACTOR_AUTH_CLIENT_ID | Keyfactor Auth Client ID | |
| KEYFACTOR_AUTH_CLIENT_SECRET | Keyfactor Auth Client Secret | |
| KEYFACTOR_AUTH_TOKEN_URL | URL to request an access token from Keyfactor Auth | |
| KEYFACTOR_AUTH_SCOPES | Scopes to request when authenticating to Keyfactor Command API. Each scope MUST be separated by `,` | `openid` |
| KEYFACTOR_AUTH_AUDIENCE | Audience to request when authenticating to Keyfactor Command API | |
| KEYFACTOR_AUTH_SCOPES | Scopes to request when authenticating to Keyfactor Command API. Multiple scopes MUST be separated by `,` (comma) | _(none)_ |
| KEYFACTOR_AUTH_AUDIENCE | Audience to request when authenticating to Keyfactor Command API. Sent as the `audience` form parameter. Leave unset for Microsoft Entra ID (see below) | |
| KEYFACTOR_AUTH_ACCESS_TOKEN | Access token to use to authenticate to Keyfactor Command API. This can be supplied directly or generated via client credentials | |
| KEYFACTOR_AUTH_CA_CERT | Either a file path or PEM encoded string to a CA certificate to use when connecting to Keyfactor Auth | |

`kfutil` supports two oAuth2 grant types:

- **Client Credentials** — `kfutil` exchanges a `client_id` and `client_secret` at the `token_url` for an
access token. This is the most common configuration.
- **Static Access Token** — you supply a previously obtained access token directly via `KEYFACTOR_AUTH_ACCESS_TOKEN`.

The interactive authorization-code / PKCE grant is **not** supported.

#### Third-party IdP (Microsoft Entra ID / Azure AD)

`kfutil` is not limited to the Keyfactor-hosted Keycloak IdP. Because the token endpoint (`token_url`) and
`scopes` are fully configurable, you can authenticate against any OAuth2 IdP that Keyfactor Command trusts,
including **Microsoft Entra ID** (formerly Azure AD).

The Entra-side setup (registering the Command API application, exposing an API / Application ID URI, and
granting the calling application permission to it) is documented in the Keyfactor Command / Entra SSO
documentation and is a prerequisite for the steps below. Once that is in place, configure `kfutil` as follows:

- **`token_url`** must point at the Entra v2.0 token endpoint for your tenant:
`https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token`
- **`client_id`** / **`client_secret`** are the Entra app registration's *Application (client) ID* and a
*client secret* generated for it.
- **`scopes`** is **mandatory** for Entra and must be `api://<command-app-id-uri>/.default`. `kfutil` does not
send any scope by default, so a request with no scope configured will be rejected by Entra.
When using the `KEYFACTOR_AUTH_SCOPES` environment variable, supply exactly **one** scope with **no commas**
(the value is split on `,`).
- **`audience`** must be **left unset** for Entra. Entra does not accept an `audience` form parameter; the
target resource is encoded inside the `.default` scope instead.

> **Scope vs. audience — the key distinction:** Some IdPs (such as Keycloak) identify the target resource via
> an `audience` parameter. Entra ID does **not**; it derives the resource from the requested scope
> (`api://<command-app-id-uri>/.default`). If you set `audience` when authenticating against Entra, the token
> request will fail. Use `scopes` for Entra and leave `audience` empty.

### kfutil specific

All the variables listed below need to be set in your environment. The `kfutil` command will look for these variables
Expand Down Expand Up @@ -141,6 +175,19 @@ export KEYFACTOR_HOSTNAME="<mykeyfactorhost.mydomain.com>"
export KEYFACTOR_AUTH_ACCESS_TOKEN="<my-access-token>"
```

#### oAuth Client Credentials with Microsoft Entra ID (Azure AD)

This authenticates to Keyfactor Command using an Entra ID app registration. Note the Entra v2.0 `token_url`,
the mandatory `.default` scope, and that `KEYFACTOR_AUTH_AUDIENCE` is intentionally **not** set.
```bash
export KEYFACTOR_HOSTNAME="<mykeyfactorhost.mydomain.com>"
export KEYFACTOR_AUTH_CLIENT_ID="<entra-app-client-id>"
export KEYFACTOR_AUTH_CLIENT_SECRET="<entra-client-secret>"
export KEYFACTOR_AUTH_TOKEN_URL="https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token"
export KEYFACTOR_AUTH_SCOPES="api://<command-app-id-uri>/.default"
# Do NOT set KEYFACTOR_AUTH_AUDIENCE for Entra ID
```

#### Additional variables

```bash
Expand Down Expand Up @@ -187,6 +234,20 @@ $env:KEYFACTOR_HOSTNAME = "<mykeyfactorhost.mydomain.com>"
$env:KEYFACTOR_AUTH_ACCESS_TOKEN = "<my-access-token>"
```

#### oAuth Client Credentials with Microsoft Entra ID (Azure AD)

This authenticates to Keyfactor Command using an Entra ID app registration. Note the Entra v2.0 token URL,
the mandatory `.default` scope, and that `KEYFACTOR_AUTH_AUDIENCE` is intentionally **not** set.

```powershell
$env:KEYFACTOR_HOSTNAME = "<mykeyfactorhost.mydomain.com>"
$env:KEYFACTOR_AUTH_CLIENT_ID = "<entra-app-client-id>"
$env:KEYFACTOR_AUTH_CLIENT_SECRET = "<entra-client-secret>"
$env:KEYFACTOR_AUTH_TOKEN_URL = "https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token"
$env:KEYFACTOR_AUTH_SCOPES = "api://<command-app-id-uri>/.default"
# Do NOT set KEYFACTOR_AUTH_AUDIENCE for Entra ID
```

#### Additional variables:

```bash
Expand Down
58 changes: 57 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ var (
kfcClientSecret string
kfcTokenUrl string
kfcAPIPath string
kfcScopes string
kfcAudience string
logInsecure bool
outputFormat string
offline bool
Expand Down Expand Up @@ -149,6 +151,46 @@ func getServerConfigFromEnv() (*auth_providers.Server, error) {
skipVerify, svOk := os.LookupEnv(auth_providers.EnvKeyfactorSkipVerify)
var skipVerifyBool bool

// CLI persistent flags, when explicitly provided, take precedence over environment
// variables. Changed() is used (rather than a non-empty check) so that flags with a
// non-empty default (e.g. --api-path) do not silently override an environment value
// the user did set.
authFlags := RootCmd.PersistentFlags()
if authFlags.Changed("username") {
username, uOk = kfcUsername, true
}
if authFlags.Changed("password") {
password, pOk = kfcPassword, true
}
if authFlags.Changed("domain") {
domain, dOk = kfcDomain, true
}
if authFlags.Changed("hostname") {
hostname, hOk = kfcHostName, true
}
if authFlags.Changed("api-path") {
apiPath, aOk = kfcAPIPath, true
}
if authFlags.Changed("client-id") {
clientId, cOk = kfcClientId, true
}
if authFlags.Changed("client-secret") {
clientSecret, csOk = kfcClientSecret, true
}
if authFlags.Changed("token-url") {
tokenUrl, tOk = kfcTokenUrl, true
}
if authFlags.Changed("audience") {
audience = kfcAudience
}
if authFlags.Changed("scopes") {
if kfcScopes != "" {
scopes = strings.Split(kfcScopes, ",")
} else {
scopes = nil
}
}

isBasicAuth := uOk && pOk
isOAuth := cOk && csOk && tOk

Expand Down Expand Up @@ -1023,7 +1065,7 @@ func init() {
)

RootCmd.PersistentFlags().StringVarP(
&kfcClientId,
&kfcTokenUrl,
"token-url",
"",
"",
Expand Down Expand Up @@ -1058,6 +1100,20 @@ func init() {
"KeyfactorAPI",
"API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI)",
)
RootCmd.PersistentFlags().StringVarP(
&kfcScopes,
"scopes",
"",
"",
"Comma-separated list of OAuth2 scopes to request when authenticating via a third-party IdP (e.g. Entra ID: api://<app-id-uri>/.default).",
)
RootCmd.PersistentFlags().StringVarP(
&kfcAudience,
"audience",
"",
"",
"OAuth2 audience to request when authenticating to Keyfactor Command. Not used by all IdPs (e.g. leave unset for Entra ID).",
)

// Cobra also supports local flags, which will only run
// when this action is called directly.
Expand Down
47 changes: 47 additions & 0 deletions docs/auth_providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,53 @@ interactive auth flow. Here's an example of what that would look like:
}
```

#### oAuth Client Credentials with a Third-party IdP (Microsoft Entra ID / Azure AD)

`kfutil` can authenticate against any OAuth2 IdP that Keyfactor Command trusts, not only the Keyfactor-hosted
Keycloak. The example below configures **Microsoft Entra ID** (formerly Azure AD) using the client-credentials
grant. The Entra-side setup — registering the Command API application, exposing its API / Application ID URI,
and granting the calling app permission — lives in the Keyfactor Command / Entra SSO documentation and is a
prerequisite.

```json
{
"servers": {
"default": {
"host": "keyfactor.example.com",
"auth_type": "oauth",
"client_id": "<entra-app-client-id>",
"client_secret": "<entra-client-secret>",
"token_url": "https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token",
"scopes": ["api://<command-app-id-uri>/.default"],
"api_path": "KeyfactorAPI"
}
}
}
```

Key points when targeting Entra ID:

- **`token_url`** is the Entra v2.0 token endpoint for your tenant:
`https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token`.
- **`client_id`** / **`client_secret`** are the Entra app registration's *Application (client) ID* and a
generated *client secret*.
- **`scopes`** is **mandatory** and must be `api://<command-app-id-uri>/.default`. `kfutil` sends no scope by
default, so an Entra request with no scope configured is rejected.
- **`audience`** must be **omitted**. Entra ID does not accept an `audience` form parameter.

##### Scope vs. audience

This is the most common point of confusion when moving from Keycloak to Entra ID:

- **Keycloak** (the Keyfactor-hosted IdP) identifies the target resource with the `audience` field, and scopes
are typically optional.
- **Entra ID** identifies the target resource through the requested **scope** (`api://<command-app-id-uri>/.default`)
and does **not** support an `audience` parameter.

Therefore, for Entra ID set `scopes` and leave `audience` unset. Setting `audience` against Entra will cause the
token request to fail. Note also that the `KEYFACTOR_AUTH_SCOPES` environment variable is split on `,` (comma),
so when using env vars supply exactly one Entra scope with no commas.

#### Usage

##### Default
Expand Down
2 changes: 2 additions & 0 deletions docs/kfutil.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ A CLI wrapper around the Keyfactor Platform API.

```
--api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI")
--audience string OAuth2 audience to request when authenticating to Keyfactor Command. Not used by all IdPs (e.g. leave unset for Entra ID).
--auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default")
--auth-provider-type string Provider type choices: (azid)
--client-id string OAuth2 client-id to use for authenticating to Keyfactor Command.
Expand All @@ -25,6 +26,7 @@ A CLI wrapper around the Keyfactor Platform API.
--offline Will not attempt to connect to GitHub for latest release information and resources.
--password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text.
--profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists.
--scopes string Comma-separated list of OAuth2 scopes to request when authenticating via a third-party IdP (e.g. Entra ID: api://<app-id-uri>/.default).
--skip-tls-verify Disable TLS verification for API requests to Keyfactor Command.
--token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command.
--username string Username to use for authenticating to Keyfactor Command.
Expand Down
2 changes: 2 additions & 0 deletions docs/kfutil_completion.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ See each sub-command's help for details on how to use the generated script.

```
--api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI")
--audience string OAuth2 audience to request when authenticating to Keyfactor Command. Not used by all IdPs (e.g. leave unset for Entra ID).
--auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default")
--auth-provider-type string Provider type choices: (azid)
--client-id string OAuth2 client-id to use for authenticating to Keyfactor Command.
Expand All @@ -32,6 +33,7 @@ See each sub-command's help for details on how to use the generated script.
--offline Will not attempt to connect to GitHub for latest release information and resources.
--password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text.
--profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists.
--scopes string Comma-separated list of OAuth2 scopes to request when authenticating via a third-party IdP (e.g. Entra ID: api://<app-id-uri>/.default).
--skip-tls-verify Disable TLS verification for API requests to Keyfactor Command.
--token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command.
--username string Username to use for authenticating to Keyfactor Command.
Expand Down
2 changes: 2 additions & 0 deletions docs/kfutil_completion_bash.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ kfutil completion bash

```
--api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI")
--audience string OAuth2 audience to request when authenticating to Keyfactor Command. Not used by all IdPs (e.g. leave unset for Entra ID).
--auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default")
--auth-provider-type string Provider type choices: (azid)
--client-id string OAuth2 client-id to use for authenticating to Keyfactor Command.
Expand All @@ -55,6 +56,7 @@ kfutil completion bash
--offline Will not attempt to connect to GitHub for latest release information and resources.
--password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text.
--profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists.
--scopes string Comma-separated list of OAuth2 scopes to request when authenticating via a third-party IdP (e.g. Entra ID: api://<app-id-uri>/.default).
--skip-tls-verify Disable TLS verification for API requests to Keyfactor Command.
--token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command.
--username string Username to use for authenticating to Keyfactor Command.
Expand Down
2 changes: 2 additions & 0 deletions docs/kfutil_completion_fish.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ kfutil completion fish [flags]

```
--api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI")
--audience string OAuth2 audience to request when authenticating to Keyfactor Command. Not used by all IdPs (e.g. leave unset for Entra ID).
--auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default")
--auth-provider-type string Provider type choices: (azid)
--client-id string OAuth2 client-id to use for authenticating to Keyfactor Command.
Expand All @@ -46,6 +47,7 @@ kfutil completion fish [flags]
--offline Will not attempt to connect to GitHub for latest release information and resources.
--password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text.
--profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists.
--scopes string Comma-separated list of OAuth2 scopes to request when authenticating via a third-party IdP (e.g. Entra ID: api://<app-id-uri>/.default).
--skip-tls-verify Disable TLS verification for API requests to Keyfactor Command.
--token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command.
--username string Username to use for authenticating to Keyfactor Command.
Expand Down
2 changes: 2 additions & 0 deletions docs/kfutil_completion_powershell.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ kfutil completion powershell [flags]

```
--api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI")
--audience string OAuth2 audience to request when authenticating to Keyfactor Command. Not used by all IdPs (e.g. leave unset for Entra ID).
--auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default")
--auth-provider-type string Provider type choices: (azid)
--client-id string OAuth2 client-id to use for authenticating to Keyfactor Command.
Expand All @@ -43,6 +44,7 @@ kfutil completion powershell [flags]
--offline Will not attempt to connect to GitHub for latest release information and resources.
--password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text.
--profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists.
--scopes string Comma-separated list of OAuth2 scopes to request when authenticating via a third-party IdP (e.g. Entra ID: api://<app-id-uri>/.default).
--skip-tls-verify Disable TLS verification for API requests to Keyfactor Command.
--token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command.
--username string Username to use for authenticating to Keyfactor Command.
Expand Down
Loading
Loading