[GH-ISSUE #5045] [Feature Request] Auth method: CLI #3969

Closed
opened 2026-05-05 14:31:27 -06:00 by gitea-mirror · 12 comments
Owner

Originally created by @Saancreed on GitHub (Nov 3, 2025).
Original GitHub issue: https://github.com/fatedier/frp/issues/5045

Describe the feature request

On the client side, we have a need to be able to fetch PrivilegeKey from another (and often simple) program running on the same machine as the client. The idea would be to add a new, client-only auth method, let's call it cli, that runs an arbitrary program with given arguments, captures its standard output (stripping it of any trailing newlines), and uses it as the key. The credential retrieved this way could be used with either token or oidc auth method on the server side, depending on whether this program prints a pre-shared key or issues an access token.

For example:

# frps.toml
[auth]
method = "token"
token = "{{ .Envs.FRP_TOKEN }}"

# frpc.toml, retrieve credential from 1Password
[auth]
method = "cli"
cli.command = ["op", "item", "get", "frp", "--fields", "credential", "--reveal"]

or

# frps.toml
[auth]
method = "oidc"
oidc.issuer = "https://login.microsoftonline.com/{{ .Envs.FRP_AZURE_TENANT_ID }}/v2.0"
oidc.audience = "{{ .Envs.FRP_AZURE_APPLICATION_ID }}"

# frpc.toml, ask Azure CLI to issue access token
[auth]
method = "cli"
cli.command = ["az", "account", "get-access-token", "--tenant", "{{ .Envs.FRP_AZURE_TENANT_ID }}", "--scope", "{{ .Envs.FRP_AZURE_APPLICATION_ID }}/.default", "--query", "accessToken", "--output", "tsv"]

Optionally, we could consider supporting running the subprocess with additional environment variables but I wouldn't consider that a priority; on UNIX-like systems it's already easy enough to prepend something like "env", "AZURE_CONFIG_DIR=/etc/azure" to command.

For simplicity, we could assume that any non-zero exit code from the launched subprocess is an error, and that the output will already be properly (UTF-8?) encoded.

Describe alternatives you've considered

  • Explicit support for desired credential providers directly in frp

    • Rejected, frp shouldn't contain vendor-specific code in its core codebase.
  • Some sort of plugin system, inspired by RPC-based server plugin system?

    • Complicated, hosting a full-blown HTTP server is something we'd like to avoid if simple CLI invocation does the job.

Affected area

  • Docs
  • Installation
  • Performance and Scalability
  • Security
  • User Experience
  • Test and Release
  • Developer Infrastructure
  • Client Plugin
  • Server Plugin
  • Extensions
  • Others
Originally created by @Saancreed on GitHub (Nov 3, 2025). Original GitHub issue: https://github.com/fatedier/frp/issues/5045 ### Describe the feature request On the client side, we have a need to be able to fetch PrivilegeKey from another (and often simple) program running on the same machine as the client. The idea would be to add a new, client-only auth method, let's call it `cli`, that runs an arbitrary program with given arguments, captures its standard output (stripping it of any trailing newlines), and uses it as the key. The credential retrieved this way could be used with either `token` or `oidc` auth method on the server side, depending on whether this program prints a pre-shared key or issues an access token. For example: ```toml # frps.toml [auth] method = "token" token = "{{ .Envs.FRP_TOKEN }}" # frpc.toml, retrieve credential from 1Password [auth] method = "cli" cli.command = ["op", "item", "get", "frp", "--fields", "credential", "--reveal"] ``` or ```toml # frps.toml [auth] method = "oidc" oidc.issuer = "https://login.microsoftonline.com/{{ .Envs.FRP_AZURE_TENANT_ID }}/v2.0" oidc.audience = "{{ .Envs.FRP_AZURE_APPLICATION_ID }}" # frpc.toml, ask Azure CLI to issue access token [auth] method = "cli" cli.command = ["az", "account", "get-access-token", "--tenant", "{{ .Envs.FRP_AZURE_TENANT_ID }}", "--scope", "{{ .Envs.FRP_AZURE_APPLICATION_ID }}/.default", "--query", "accessToken", "--output", "tsv"] ``` Optionally, we could consider supporting running the subprocess with additional environment variables but I wouldn't consider that a priority; on UNIX-like systems it's already easy enough to prepend something like `"env", "AZURE_CONFIG_DIR=/etc/azure"` to `command`. For simplicity, we could assume that any non-zero exit code from the launched subprocess is an error, and that the output will already be properly (UTF-8?) encoded. ### Describe alternatives you've considered * Explicit support for desired credential providers directly in frp * Rejected, frp shouldn't contain vendor-specific code in its core codebase. * Some sort of plugin system, inspired by RPC-based server plugin system? * Complicated, hosting a full-blown HTTP server is something we'd like to avoid if simple CLI invocation does the job. ### Affected area - [ ] Docs - [ ] Installation - [ ] Performance and Scalability - [x] Security - [ ] User Experience - [ ] Test and Release - [ ] Developer Infrastructure - [ ] Client Plugin - [ ] Server Plugin - [ ] Extensions - [ ] Others
gitea-mirror 2026-05-05 14:31:27 -06:00
  • closed this issue
  • added the
    todo
    label
Author
Owner

@fatedier commented on GitHub (Nov 4, 2025):

I’m not inclined to use auth.method = "cli". This configuration is unclear and doesn’t explicitly specify which authentication method is actually being used.

https://github.com/fatedier/frp/blob/dev/pkg/config/v1/value_source.go

We already have the definition of ValueSource, which currently only supports reading from a file. It might be possible to extend it to support fetching values via exec.

However, I’m a bit concerned that providing the exec option could introduce security risks. Some users’ configuration files might be loaded from remote sources. Would it be safer to limit support to reading only from files?

<!-- gh-comment-id:3483689951 --> @fatedier commented on GitHub (Nov 4, 2025): I’m not inclined to use auth.method = "cli". This configuration is unclear and doesn’t explicitly specify which authentication method is actually being used. https://github.com/fatedier/frp/blob/dev/pkg/config/v1/value_source.go We already have the definition of `ValueSource`, which currently only supports reading from a file. It might be possible to extend it to support fetching values via `exec`. However, I’m a bit concerned that providing the `exec` option could introduce security risks. Some users’ configuration files might be loaded from remote sources. Would it be safer to limit support to reading only from files?
Author
Owner

@Saancreed commented on GitHub (Nov 4, 2025):

I’m not inclined to use auth.method = "cli". This configuration is unclear and doesn’t explicitly specify which authentication method is actually being used.

Well, it's just an initial proposal of how I thought this feature could be shaped. I actually considered this to be an upside, sort of. When using such mechanism, it becomes more or less irrelevant how the acquired PrivilegeKey will be interpreted by the server; in a way, method on the client side becomes more of a "method of credential acquisition" than "method of authentication".

But if you disagree, this "custom credential source" option can be moved into token or oidc methods themselves. I don't see much value in it, as (if I correctly understand how frp works), with such credential source it doesn't matter on the client side what is configured as top level auth.method. It does make the intent clear, but slightly obfuscates what is relevant and what isn't.

We already have the definition of ValueSource, which currently only supports reading from a file. It might be possible to extend it to support fetching values via exec.

However, I’m a bit concerned that providing the exec option could introduce security risks. […] Would it be safer to limit support to reading only from files?

Hmm, that would be… limiting, but perhaps sufficient? It would work if all we wanted was reading the value of pre-shared token, but it gets trickier if it's expiring access token that has to be periodically regenerated. In order to keep this "on demand", we'd probably have to read from a named pipe that's only written to when frpc accesses it… which I'm not sure how possible or portable it would be.

On the other hand, if we drop the "on demand" part, another process could periodically write the access token to a plain file and replace it before it expires, but that mostly makes things like audit logs whenever a token is issued have little to no value.

Some users’ configuration files might be loaded from remote sources.

I'd consider that to already be concering. As far as I can tell, if one launches frpc with a config from untrusted source, it can already extract any secret stored in an environment variable and submit it to an external, attacker-controlled server. If we allowed reading from files, we'd be adding the ability to also send contents of, let's say, ~/.ssh/*.

I'm not claiming that ability to launch any process can't be more harmful, because of course it can be, but even reading from files should be something reserved for trusted configurations. At which point, if this mechanism becomes explicitly opt-in, via explicit --allow-file-auth-method flag perhaps, why not go all the way in and make it into --allow-cli-auth-method?

<!-- gh-comment-id:3486410478 --> @Saancreed commented on GitHub (Nov 4, 2025): > I’m not inclined to use auth.method = "cli". This configuration is unclear and doesn’t explicitly specify which authentication method is actually being used. Well, it's just an initial proposal of how I thought this feature could be shaped. I actually considered this to be an upside, sort of. When using such mechanism, it becomes more or less irrelevant how the acquired `PrivilegeKey` will be interpreted by the server; in a way, `method` on the client side becomes more of a "method of credential acquisition" than "method of authentication". But if you disagree, this "custom credential source" option can be moved into `token` or `oidc` methods themselves. I don't see much value in it, as (if I correctly understand how frp works), with such credential source it doesn't matter on the client side what is configured as top level `auth.method`. It does make the intent clear, but slightly obfuscates what is relevant and what isn't. > We already have the definition of `ValueSource`, which currently only supports reading from a file. It might be possible to extend it to support fetching values via `exec`. > > However, I’m a bit concerned that providing the `exec` option could introduce security risks. […] Would it be safer to limit support to reading only from files? Hmm, that would be… limiting, but perhaps sufficient? It would work if all we wanted was reading the value of pre-shared `token`, but it gets trickier if it's expiring access token that has to be periodically regenerated. In order to keep this "on demand", we'd probably have to read from a named pipe that's only written to when frpc accesses it… which I'm not sure how possible or portable it would be. On the other hand, if we drop the "on demand" part, another process could periodically write the access token to a plain file and replace it before it expires, but that mostly makes things like audit logs whenever a token is issued have little to no value. > Some users’ configuration files might be loaded from remote sources. I'd consider _that_ to already be concering. As far as I can tell, if one launches frpc with a config from untrusted source, it can already extract any secret stored in an environment variable and submit it to an external, attacker-controlled server. If we allowed reading from files, we'd be adding the ability to also send contents of, let's say, `~/.ssh/*`. I'm not claiming that ability to launch any process can't be more harmful, because of course it can be, but even reading from files should be something reserved for trusted configurations. At which point, if this mechanism becomes explicitly opt-in, via explicit `--allow-file-auth-method` flag perhaps, why not go all the way in and make it into `--allow-cli-auth-method`?
Author
Owner

@fatedier commented on GitHub (Nov 5, 2025):

Overall, I’m inclined to support this feature. Using a flag to control whether external commands are allowed to execute is a good idea — it must be explicitly enabled by the user and cannot be disabled through configuration files.

The specific implementation plan still needs further discussion.

<!-- gh-comment-id:3489255038 --> @fatedier commented on GitHub (Nov 5, 2025): Overall, I’m inclined to support this feature. Using a flag to control whether external commands are allowed to execute is a good idea — it must be explicitly enabled by the user and cannot be disabled through configuration files. The specific implementation plan still needs further discussion.
Author
Owner

@Saancreed commented on GitHub (Nov 5, 2025):

Wonderful, thank you. Having some cli / exec option together with a flag required to enable it sounds good for us.

The naming of the config option and the flag itself isn't too important, we'll just adapt to your preferences. Once they are confirmed, we can contribute initial implementation (with documentation updates and tests) if it would be helpful. Let me know how you'd like to see this done and I'll be able to start working on some pull requests.

<!-- gh-comment-id:3492027247 --> @Saancreed commented on GitHub (Nov 5, 2025): Wonderful, thank you. Having some `cli` / `exec` option together with a flag required to enable it sounds good for us. The naming of the config option and the flag itself isn't too important, we'll just adapt to your preferences. Once they are confirmed, we can contribute initial implementation (with documentation updates and tests) if it would be helpful. Let me know how you'd like to see this done and I'll be able to start working on some pull requests.
Author
Owner

@fatedier commented on GitHub (Nov 6, 2025):

Are there any other widely used projects that provide similar functionality? How are they designed?

<!-- gh-comment-id:3494578367 --> @fatedier commented on GitHub (Nov 6, 2025): Are there any other widely used projects that provide similar functionality? How are they designed?
Author
Owner

@Saancreed commented on GitHub (Nov 6, 2025):

There are a few I'm aware of, and likely many more that I'm not aware of. Perhaps the best example would be kubectl, which allows executing any program to provide client credentials used to authenticate to Kubernetes clusters: https://kubernetes.io/docs/reference/config-api/kubeconfig.v1/#ExecConfig

This is used by Azure CLI, which when asked to connect to Azure Kubernetes Service with az aks get-credentials, creates a kubeconfig entry for cluster credentials that looks like this:

exec:
  apiVersion: client.authentication.k8s.io/v1beta1
  args:
  - get-token
  - --environment
  - AzurePublicCloud
  - --server-id
  - …
  - --client-id
  - …
  - --tenant-id
  - …
  command: kubelogin
  env: null
  interactiveMode: IfAvailable
  provideClusterInfo: false

Other examples (not necessarily used for producing credentials) would be:

Finally, some programs like Git and Docker have similar integrations via Credential Helpers but they aren't as flexible and impose somewhat greater restrictions on what invoked program has to do:

Honorary mention goes to openssh and sudo that can run an askpass program to retrieve the user's passphrase instead of asking the user directly.

Based on all this, and considering kubectl to be the closest equivalent to what we want to achieve, I'd suggest that:

  • exec form should be preferred over shell form
  • although kubectl splits command and args into separate entries, it seems to be an unnecessary complication; a single array that contains both of them concatenated would be simpler
  • being able to provide additional environment variables for invoked subprocess is nice to have but I wouldn't consider it important enough to include this in the initial implementation
  • similarly, we could support subcommands that require user interaction but I wouldn't consider it to be a priority

As a side note, and as I mentioned in the initial proposal, there's probably no need to require any particular output format; just taking the entire stdout and stripping trailing newlines, if any, should be good enough.

<!-- gh-comment-id:3497596765 --> @Saancreed commented on GitHub (Nov 6, 2025): There are a few I'm aware of, and likely many more that I'm not aware of. Perhaps the best example would be `kubectl`, which allows executing any program to provide client credentials used to authenticate to Kubernetes clusters: https://kubernetes.io/docs/reference/config-api/kubeconfig.v1/#ExecConfig This is used by Azure CLI, which when asked to connect to Azure Kubernetes Service with `az aks get-credentials`, creates a kubeconfig entry for cluster credentials that looks like this: ``` exec: apiVersion: client.authentication.k8s.io/v1beta1 args: - get-token - --environment - AzurePublicCloud - --server-id - … - --client-id - … - --tenant-id - … command: kubelogin env: null interactiveMode: IfAvailable provideClusterInfo: false ``` Other examples (not necessarily used for producing credentials) would be: * AWS CLI with its `credential_process` setting: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sourcing-external.html * Terraform with its `external` data source: https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external * Tilt with `local` function: https://docs.tilt.dev/api.html#api.local Finally, some programs like Git and Docker have similar integrations via Credential Helpers but they aren't as flexible and impose somewhat greater restrictions on what invoked program has to do: * https://git-scm.com/docs/gitcredentials#_custom_helpers * https://docs.docker.com/reference/cli/docker/login/#credential-helpers Honorary mention goes to `openssh` and `sudo` that can run an _askpass_ program to retrieve the user's passphrase instead of asking the user directly. Based on all this, and considering `kubectl` to be the closest equivalent to what we want to achieve, I'd suggest that: * exec form should be preferred over shell form * although `kubectl` splits `command` and `args` into separate entries, it seems to be an unnecessary complication; a single array that contains both of them concatenated would be simpler * being able to provide additional environment variables for invoked subprocess is nice to have but I wouldn't consider it important enough to include this in the initial implementation * similarly, we could support subcommands that require user interaction but I wouldn't consider it to be a priority As a side note, and as I mentioned in the initial proposal, there's probably no need to require any particular output format; just taking the entire stdout and stripping trailing newlines, if any, should be good enough.
Author
Owner

@fatedier commented on GitHub (Nov 7, 2025):

Because of historical iterations in the project, the OIDC access token is also carried via the PrivilegeKey field in the Login message. However, how PrivilegeKey is set and parsed depends on the selected auth.method. When method = "token", auth.token is MD5-hashed before being sent; when method = "oidc", the value is used as-is.

That means the example you provided would fail:

# frps.toml
[auth]
method = "token"
token = "{{ .Envs.FRP_TOKEN }}"

# frpc.toml, retrieve credential from 1Password
[auth]
method = "cli"
cli.command = ["op", "item", "get", "frp", "--fields", "credential", "--reveal"]

The reason is that frpc isn’t using auth.method = "token", so it doesn’t know it must apply the token flow (MD5) before placing the value into PrivilegeKey.

Short term, as mentioned earlier, we could extend ValueSource to support exec (with dynamic refresh), and make auth.oidc accept the same extension fields.

Longer term, we probably need to revisit this part of the handshake/protocol. Instead of overloading PrivilegeKey, we should send a more structured credential payload. That would also make it possible for frps to support multiple authentication mechanisms concurrently in the future.

<!-- gh-comment-id:3500547157 --> @fatedier commented on GitHub (Nov 7, 2025): Because of historical iterations in the project, the OIDC access token is also carried via the `PrivilegeKey` field in the Login message. However, how `PrivilegeKey` is set and parsed depends on the selected `auth.method`. When `method = "token"`, `auth.token` is **MD5-hashed** before being sent; when `method = "oidc"`, the value is used **as-is**. That means the example you provided would fail: ```toml # frps.toml [auth] method = "token" token = "{{ .Envs.FRP_TOKEN }}" # frpc.toml, retrieve credential from 1Password [auth] method = "cli" cli.command = ["op", "item", "get", "frp", "--fields", "credential", "--reveal"] ``` The reason is that `frpc` isn’t using `auth.method = "token"`, so it doesn’t know it must apply the token flow (MD5) before placing the value into `PrivilegeKey`. **Short term**, as mentioned earlier, we could extend `ValueSource` to support `exec` (with dynamic refresh), and make `auth.oidc` accept the same extension fields. **Longer term**, we probably need to revisit this part of the handshake/protocol. Instead of overloading `PrivilegeKey`, we should send a more structured credential payload. That would also make it possible for `frps` to support multiple authentication mechanisms concurrently in the future.
Author
Owner

@Saancreed commented on GitHub (Nov 7, 2025):

Ah, I see, the distinction is more important than I thought. In that case we could go with something like

# retrieve token credential from 1Password
[auth]
method = "token"
tokenSource.type = "exec"
tokenSource.exec.command = ["op", "item", "get", "frp", "--fields", "credential", "--reveal"]

# retrieve oidc credential from Azure CLI
[auth]
method = "oidc"
oidcSource.type = "exec" # or "file"
oidcSource.exec.command = ["az", "account", "get-access-token", "--tenant", "{{ .Envs.FRP_AZURE_TENANT_ID }}", "--scope", "{{ .Envs.FRP_AZURE_APPLICATION_ID }}/.default", "--query", "accessToken", "--output", "tsv"]

Not sure if the command should be in exec section or somewhere else though. I suppose there's little reason to support multiple authentication methods on the client side concurrently? Then we wouldn't have to worry about conflicts. Nevermind, I looked more closely at how ValueSource is designed and I just mirrored tokenSource but for oidc method. But maybe we should reuse tokenSource even if method = "oidc"?

Short term, as mentioned earlier, we could extend ValueSource to support exec (with dynamic refresh), and make auth.oidc accept the same extension fields.

By dynamic refresh, do you mean ValueSource (or something else) checking if the token expired (or is about to), and only then run the command again? It could be a nice improvement for heartbeats, but maybe we could initially make it the command's responsibility to handle caches, if any, on its side.

Anyway, I think this is enough information for me to start prototyping something on top of ValueSource

<!-- gh-comment-id:3502554516 --> @Saancreed commented on GitHub (Nov 7, 2025): Ah, I see, the distinction is more important than I thought. In that case we could go with something like ```toml # retrieve token credential from 1Password [auth] method = "token" tokenSource.type = "exec" tokenSource.exec.command = ["op", "item", "get", "frp", "--fields", "credential", "--reveal"] # retrieve oidc credential from Azure CLI [auth] method = "oidc" oidcSource.type = "exec" # or "file" oidcSource.exec.command = ["az", "account", "get-access-token", "--tenant", "{{ .Envs.FRP_AZURE_TENANT_ID }}", "--scope", "{{ .Envs.FRP_AZURE_APPLICATION_ID }}/.default", "--query", "accessToken", "--output", "tsv"] ``` ~~Not sure if the command should be in `exec` section or somewhere else though. I suppose there's little reason to support multiple authentication methods on the client side concurrently? Then we wouldn't have to worry about conflicts.~~ Nevermind, I looked more closely at how `ValueSource` is designed and I just mirrored `tokenSource` but for `oidc` method. But maybe we should reuse `tokenSource` even if `method = "oidc"`? > **Short term**, as mentioned earlier, we could extend `ValueSource` to support `exec` (with dynamic refresh), and make `auth.oidc` accept the same extension fields. By dynamic refresh, do you mean `ValueSource` (or something else) checking if the token expired (or is about to), and only then run the command again? It could be a nice improvement for heartbeats, but maybe we could initially make it the command's responsibility to handle caches, if any, on its side. Anyway, I think this is enough information for me to start prototyping something on top of `ValueSource`…
Author
Owner

@fatedier commented on GitHub (Nov 10, 2025):

But maybe we should reuse tokenSource even if method = "oidc"?

I prefer to use the name tokenSource for oidc method.

In addition, we should refer to the design in kubeconfig and keep the command, args, and env fields, so that it aligns better with common user practices.

exec:
  apiVersion: client.authentication.k8s.io/v1beta1
  args:
  - get-token
  - --environment
  - AzurePublicCloud
  - --server-id
  - …
  - --client-id
  - …
  - --tenant-id
  - …
  command: kubelogin
  env: null
  interactiveMode: IfAvailable
  provideClusterInfo: false
<!-- gh-comment-id:3509273560 --> @fatedier commented on GitHub (Nov 10, 2025): > But maybe we should reuse tokenSource even if method = "oidc"? I prefer to use the name `tokenSource` for `oidc` method. In addition, we should refer to the design in kubeconfig and keep the `command`, `args`, and `env` fields, so that it aligns better with common user practices. ``` exec: apiVersion: client.authentication.k8s.io/v1beta1 args: - get-token - --environment - AzurePublicCloud - --server-id - … - --client-id - … - --tenant-id - … command: kubelogin env: null interactiveMode: IfAvailable provideClusterInfo: false ```
Author
Owner

@Saancreed commented on GitHub (Nov 10, 2025):

I prefer to use the name tokenSource for oidc method.

Sure, I went with that. Minor concern about this choice though: what if some users already have configs that look like

[auth]
method = "oidc"

[auth.oidc]
clientID = "…"
clientSecret = "…"
audience = "…"
scope = "…"
tokenEndpointURL = "…"

[auth.tokenSource]
type = "file"
file.path = "/var/lib/frpc/token"

and only use auth.method to toggle between the two? Probably not likely to happen in practice as the server currently supports only one authentication method, so perhaps it would be a good idea to make tokenSource also mutually exclusive with oidc section, the same way it currently is mutually exclusive with token to avoid ambiguities.

In addition, we should refer to the design in kubeconfig and keep the command, args, and env fields, so that it aligns better with common user practices.

Okay, not a problem. I'm not sure if env should be also copied as-is from kubeconfig (where it's an array of structs with name and value members), what I currently have is a simple map and that appears to be working fine. With this, the current design looks like so:

[auth]
method = "oidc"

[auth.tokenSource]
type = "exec"
exec.command = "az"
exec.args = [
    "account", "get-access-token",
    "--tenant", "{{ .Envs.FRP_AZURE_TENANT_ID }}",
    "--scope", "{{ .Envs.FRP_AZURE_APPLICATION_ID }}/.default",
    "--query", "accessToken",
    "--output", "tsv",
]

[auth.tokenSource.exec.env]
AZURE_CONFIG_DIR = "/etc/azure"
<!-- gh-comment-id:3512434989 --> @Saancreed commented on GitHub (Nov 10, 2025): > I prefer to use the name `tokenSource` for `oidc` method. Sure, I went with that. Minor concern about this choice though: what if some users already have configs that look like ```toml [auth] method = "oidc" [auth.oidc] clientID = "…" clientSecret = "…" audience = "…" scope = "…" tokenEndpointURL = "…" [auth.tokenSource] type = "file" file.path = "/var/lib/frpc/token" ``` and only use `auth.method` to toggle between the two? Probably not likely to happen in practice as the server currently supports only one authentication method, so perhaps it would be a good idea to make `tokenSource` also mutually exclusive with `oidc` section, the same way it currently is mutually exclusive with `token` to avoid ambiguities. > In addition, we should refer to the design in kubeconfig and keep the `command`, `args`, and `env` fields, so that it aligns better with common user practices. Okay, not a problem. I'm not sure if `env` should be also copied as-is from `kubeconfig` (where it's an array of structs with `name` and `value` members), what I currently have is a simple map and that appears to be working fine. With this, the current design looks like so: ```toml [auth] method = "oidc" [auth.tokenSource] type = "exec" exec.command = "az" exec.args = [ "account", "get-access-token", "--tenant", "{{ .Envs.FRP_AZURE_TENANT_ID }}", "--scope", "{{ .Envs.FRP_AZURE_APPLICATION_ID }}/.default", "--query", "accessToken", "--output", "tsv", ] [auth.tokenSource.exec.env] AZURE_CONFIG_DIR = "/etc/azure" ```
Author
Owner

@Saancreed commented on GitHub (Nov 24, 2025):

@fatedier Now that related PR with the feature itself is merged, would you like us to also contribute some tests and documentation for this feature?

<!-- gh-comment-id:3571529271 --> @Saancreed commented on GitHub (Nov 24, 2025): @fatedier Now that related PR with the feature itself is merged, would you like us to also contribute some tests and documentation for this feature?
Author
Owner

@fatedier commented on GitHub (Nov 25, 2025):

@Saancreed There are still some issues that need to be adjusted and fixed. I’ll make some modifications and improvements myself.

<!-- gh-comment-id:3573497149 --> @fatedier commented on GitHub (Nov 25, 2025): @Saancreed There are still some issues that need to be adjusted and fixed. I’ll make some modifications and improvements myself.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: github-starred/frp#3969
No description provided.