Resolve OAuth2 client/rs confusion (#2719)

* Resolve OAuth2 client/rs confusion

* feedback
This commit is contained in:
Firstyear 2024-04-24 15:34:50 +10:00 committed by GitHub
parent 3707124218
commit acc800f00e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 154 additions and 137 deletions

View file

@ -54,7 +54,6 @@ All interactions with the project are covered by our [code of conduct].
When we develop features, we follow our project's guidelines on [rights and ethics].
[code of conduct]: https://github.com/kanidm/kanidm/blob/master/CODE_OF_CONDUCT.md
[rights and ethics]: https://github.com/kanidm/kanidm/blob/master/book/src/developers/developer_ethics.md
## Getting in Contact / Questions
@ -63,7 +62,6 @@ We have a Matrix-powered [gitter community channel] where project members are al
and answer questions. Alternately you can open a new [GitHub discussion].
[gitter community channel]: https://app.gitter.im/#/room/#kanidm_community:gitter.im
[github discussion]: https://github.com/kanidm/kanidm/discussions
## What does Kanidm mean?

View file

@ -27,9 +27,9 @@ text=Kanidm's definition of Passkeys may differ from that of other systems. This
### Attested Passkeys
These are the same as Passkeys, except that the device must present a cryptographic certificate or
origin during registration. This allows [account policy](account_policy.md) to be defined to only allow the
use of certain models of authenticator. In general only FIDO2 keys or TPM's are capable of meeting
attestation requirements.
origin during registration. This allows [account policy](account_policy.md) to be defined to only
allow the use of certain models of authenticator. In general only FIDO2 keys or TPM's are capable of
meeting attestation requirements.
### Password + TOTP

View file

@ -12,47 +12,57 @@ connect.
## How Does OAuth2 Work?
A user wishes to access a service (resource, resource server). The resource server does not have an
active session for the client, so it redirects to the authorisation server (Kanidm) to determine if
the client should be allowed to proceed, and has the appropriate permissions (scopes) for the
requested resources.
OAuth2 uses a number of terms in surprising ways that makes it often unclear and difficult to
understand.
A user wishes to access a service (resource, resource server) through an OAuth2 client. The client
does not have an active session for the user so it redirects to the authorisation server (Kanidm) to
determine if the user should be allowed to proceed, and has the appropriate permissions (scopes) for
the requested resources.
The authorisation server checks the current session of the user and may present a login flow if
required. Given the identity of the user known to the authorisation sever, and the requested scopes,
the authorisation server makes a decision if it allows the authorisation to proceed. The user is
then prompted to consent to the authorisation from the authorisation server to the resource server
as some identity information may be revealed by granting this consent.
then prompted to consent to the authorisation from the authorisation server to the client as some
identity information may be revealed by granting this consent.
If successful and consent given, the user is redirected back to the resource server with an
authorisation code. The resource server then contacts the authorisation server directly with this
code and exchanges it for a valid token that may be provided to the user's browser.
If successful and consent is given, the user is redirected back to the client with an authorisation
code. The client then contacts the authorisation server directly with this code and exchanges it for
a valid OAuth token.
The resource server may then optionally contact the token introspection endpoint of the
authorisation server about the provided OAuth token, which yields extra metadata about the identity
that holds the token from the authorisation. This metadata may include identity information, but
also may include extended metadata, sometimes referred to as "claims". Claims are information bound
to a token based on properties of the session that may allow the resource server to make extended
authorisation decisions without the need to contact the authorisation server to arbitrate.
The client may then optionally contact the token introspection endpoint of the authorisation server
about the provided OAuth token, which yields extra metadata about the identity that holds the token
from the authorisation. This metadata may include identity information, but also may include
extended metadata, sometimes referred to as "claims". Claims are information bound to a token based
on properties of the session that may allow the client to make extended authorisation decisions
without the need to contact the authorisation server to arbitrate.
In many cases the client and the resource server are the same entity. When the client and resource
server are _separate_ services, the client can then forward the access token to the resource server
for authorisation of the user's request.
It's important to note that OAuth2 at its core is an authorisation system which has layered
identity-providing elements on top.
### Resource Server
### Resource Server and Clients
This is the server that a user wants to access. Common examples could be Nextcloud, a wiki, or
something else. This is the system that "needs protecting" and wants to delegate authorisation
decisions to Kanidm.
This is the resource that a user wants to access. Common examples could be Nextcloud, a Wiki, or a
chat service. In these cases the service is both the _client_ and the _resource server_ within the
OAuth2 workflow. We will refer to the combination of both client and resource server as a service.
It's important for you to know _how_ your resource server supports OAuth2. For example, does it
> NOTE: previous versions of this document incorrectly named clients as resource servers due to
> clarity issues in the OAuth2 RFC.
It's important for you to know _how_ your service will interact with OAuth2. For example, does it
support RFC 7662 token introspection or does it rely on OpenID connect for identity information?
In general Kanidm requires that your resource server supports:
In general Kanidm requires that your service supports:
- HTTP basic authentication to the authorisation server
- PKCE S256 code verification
- OIDC only - JWT ES256 for token signatures
Kanidm issues tokens that are rfc9068 JWT's allowing client introspection.
Kanidm issues tokens that are RFC 9068 JWT's allowing service introspection.
Kanidm will expose its OAuth2 APIs at the following URLs:
@ -81,48 +91,46 @@ For manual OpenID configuration:
### Scope Relationships
For an authorisation to proceed, the resource server will request a list of scopes, which are unique
to that resource server. For example, when a user wishes to login to the admin panel of the resource
server, it may request the "admin" scope from Kanidm for authorisation. But when a user wants to
login, it may only request "access" as a scope from Kanidm.
For an authorisation to proceed, the client will request a list of scopes, which are unique to that
service. For example, when a user wishes to login to the admin panel of the resource server, it may
request the "admin" scope from Kanidm for authorisation. But when a user wants to login, it may only
request "access" as a scope from Kanidm.
As each resource server may have its own scopes and understanding of these, Kanidm isolates scopes
to each resource server connected to Kanidm. Kanidm has two methods of granting scopes to accounts
(users).
As each service may have its own scopes and understanding of these, Kanidm isolates scopes to each
service connected to Kanidm. Kanidm has two methods of granting scopes to accounts (users).
The first is scope mappings. These provide a set of scopes if a user is a member of a specific group
within Kanidm. This allows you to create a relationship between the scopes of a resource server, and
the groups/roles in Kanidm which can be specific to that resource server.
within Kanidm. This allows you to create a relationship between the scopes of a service, and the
groups/roles in Kanidm which can be specific to that service.
For an authorisation to proceed, all scopes requested by the resource server must be available in
the final scope set that is granted to the account.
For an authorisation to proceed, all scopes requested by the client must be available in the final
scope set that is granted to the account.
The second is supplemental scope mappings. These function the same as scope maps where membership of
a group provides a set of scopes to the account. However these scopes are NOT consulted during
authorisation decisions made by Kanidm. These scopes exists to allow optional properties to be
provided (such as personal information about a subset of accounts to be revealed) or so that the
resource server may make it's own authorisation decisions based on the provided scopes.
service may make its own authorisation decisions based on the provided scopes.
This use of scopes is the primary means to control who can access what resources. These access
decisions can take place either on Kanidm or the resource server.
decisions can take place either on Kanidm or the service.
For example, if you have a resource server that always requests a scope of "read", then users with
scope maps that supply the read scope will be allowed by Kanidm to proceed to the resource server.
Kanidm can then provide the supplementary scopes into provided tokens, so that the resource server
can use these to choose if it wishes to display UI elements. If a user has a supplemental "admin"
scope, then that user may be able to access an administration panel of the resource server. In this
way Kanidm is still providing the authorisation information, but the control is then exercised by
the resource server.
For example, if you have a client that always requests a scope of "read", then users with scope maps
that supply the read scope will be allowed by Kanidm to proceed to the service. Kanidm can then
provide the supplementary scopes into provided tokens, so that the service can use these to choose
if it wishes to display UI elements. If a user has a supplemental "admin" scope, then that user may
be able to access an administration panel of the service. In this way Kanidm is still providing the
authorisation information, but the control is then exercised by the service.
## Configuration
### Create the Kanidm Configuration
After you have understood your resource server requirements you first need to configure Kanidm. By
default members of `system_admins` or `idm_hp_oauth2_manage_priv` are able to create or manage
OAuth2 resource server integrations.
After you have understood your service requirements you first need to configure Kanidm. By default
members of `system_admins` or `idm_hp_oauth2_manage_priv` are able to create or manage OAuth2 client
integrations.
You can create a new resource server with:
You can create a new client with:
```bash
kanidm system oauth2 create <name> <displayname> <origin>
@ -141,7 +149,7 @@ kanidm system oauth2 update-scope-map nextcloud nextcloud_admins admin
{{#template ../templates/kani-warning.md
imagepath=../images
title=WARNING
text=If you are creating an OpenID Connect (OIDC) resource server you <b>MUST</b> provide a scope map named <code>openid</code>. Without this, OpenID Connect clients <b>WILL NOT WORK</b>!
text=If you are creating an OpenID Connect (OIDC) client you <b>MUST</b> provide a scope map named <code>openid</code>. Without this, OpenID Connect clients <b>WILL NOT WORK</b>!
}}
<!-- deno-fmt-ignore-end -->
@ -164,7 +172,7 @@ kanidm system oauth2 update-sup-scope-map <name> <kanidm_group_name> [scopes]...
kanidm system oauth2 update-sup-scope-map nextcloud nextcloud_admins admin
```
Once created you can view the details of the resource server.
Once created you can view the details of the client.
```bash
kanidm system oauth2 get nextcloud
@ -189,28 +197,26 @@ kanidm system oauth2 show-basic-secret nextcloud
### Configure the Resource Server
On your resource server, you should configure the client ID as the `oauth2_rs_name` from Kanidm, and
the password to be the value shown in `oauth2_rs_basic_secret`. Ensure that the code
On your client, you should configure the client ID as the `oauth2_rs_name` from Kanidm, and the
password to be the value shown in `oauth2_rs_basic_secret`. Ensure that the code
challenge/verification method is set to S256.
You should now be able to test authorisation.
## Resetting Resource Server Security Material
In the case of disclosure of the basic secret, or some other security event where you may wish to
invalidate a resource servers active sessions/tokens, you can reset the secret material of the
server with:
In the case of disclosure of the basic secret or some other security event where you may wish to
invalidate a services active sessions/tokens. You can reset the secret material of the server with:
```bash
kanidm system oauth2 reset-secrets
```
Each resource server has unique signing keys and access secrets, so this is limited to each resource
server.
Each client has unique signing keys and access secrets, so this is limited to each service.
## Custom Claim Maps
Some OIDC clients may consume custom claims from an id token for access control or other policy
Some OAuth services may consume custom claims from an id token for access control or other policy
decisions. Each custom claim is a key:values set, where there can be many values associated to a
claim name. Different applications may expect these values to be formatted (joined) in different
ways.
@ -255,9 +261,9 @@ kanidm system oauth2 delete-claim-map nextcloud account_role nextcloud_admins
## Public Client Configuration
Some applications are unable to provide client authentication. A common example is single page web
applications that act as the OAuth2 client and its corresponding webserver that is the resource
server. In this case the SPA is unable to act as a confidential client since the basic secret would
need to be embedded in every client.
applications that act as the OAuth2 client and its corresponding webserver is the resource server.
In this case the SPA is unable to act as a confidential client since the basic secret would need to
be embedded in every client.
Another common example is native applications that use a redirect to localhost. These can't have a
client secret embedded, so must act as public clients.
@ -265,7 +271,7 @@ client secret embedded, so must act as public clients.
Public clients for this reason require PKCE to bind a specific browser session to its OAuth2
exchange. PKCE can not be disabled for public clients for this reason.
To create an OAuth2 public resource server:
To create an OAuth2 public client:
```bash
kanidm system oauth2 create-public <name> <displayname> <origin>
@ -282,31 +288,30 @@ kanidm system oauth2 enable-localhost-redirects mywebapp
## Extended Options for Legacy Clients
Not all resource servers support modern standards like PKCE or ECDSA. In these situations it may be
necessary to disable these on a per-resource server basis. Disabling these on one resource server
will not affect others. These settings are explained in detail in
[our FAQ](../frequently_asked_questions.html#oauth2)
Not all clients support modern standards like PKCE or ECDSA. In these situations it may be necessary
to disable these on a per-client basis. Disabling these on one client will not affect others. These
settings are explained in detail in [our FAQ](../frequently_asked_questions.html#oauth2)
<!-- deno-fmt-ignore-start -->
{{#template ../templates/kani-warning.md
imagepath=../images
title=WARNING
text=Changing these settings MAY have serious consequences on the security of your resource server. You should avoid changing these if at all possible!
text=Changing these settings MAY have serious consequences on the security of your services. You should avoid changing these if at all possible!
}}
<!-- deno-fmt-ignore-end -->
To disable PKCE for a confidential resource server:
To disable PKCE for a confidential client:
```bash
kanidm system oauth2 warning-insecure-client-disable-pkce <resource server name>
kanidm system oauth2 warning-insecure-client-disable-pkce <client name>
```
To enable legacy cryptograhy (RSA PKCS1-5 SHA256):
```bash
kanidm system oauth2 warning-enable-legacy-crypto <resource server name>
kanidm system oauth2 warning-enable-legacy-crypto <client name>
```
## Example Integrations
@ -320,11 +325,11 @@ with an appropriate include.
# NB: may be just path, reduces copy-paste
OIDCRedirectURI /oauth2/callback
OIDCCryptoPassphrase <random password here>
OIDCProviderMetadataURL https://kanidm.example.com/oauth2/openid/<resource server name>/.well-known/openid-configuration
OIDCProviderMetadataURL https://kanidm.example.com/oauth2/openid/<client name>/.well-known/openid-configuration
OIDCScope "openid"
OIDCUserInfoTokenMethod authz_header
OIDCClientID <resource server name>
OIDCClientSecret <resource server password>
OIDCClientID <client name>
OIDCClientSecret <client password>
OIDCPKCEMethod S256
OIDCCookieSameSite On
# Set the `REMOTE_USER` field to the `preferred_username` instead of the UUID.
@ -425,14 +430,14 @@ GUI:
authenticator:
type: OIDC
oidc_issuer: https://idm.example.com/oauth2/openid/:client\_id:/
oauth_client_id: <resource server name/>
oauth_client_secret: <resource server secret>
oauth_client_id: <client name/>
oauth_client_secret: <client secret>
```
Velociraptor does not support PKCE. You will need to run the following:
```bash
kanidm system oauth2 warning-insecure-client-disable-pkce <resource server name>
kanidm system oauth2 warning-insecure-client-disable-pkce <client name>
```
Initial users are mapped via their email in the Velociraptor server.config.yaml config:
@ -449,46 +454,13 @@ these to a group with a scope map due to Velociraptors high impact.
```bash
# kanidm group create velociraptor_users
# kanidm group add_members velociraptor_users ...
kanidm system oauth2 create_scope_map <resource server name> velociraptor_users openid email
```
### Vouch Proxy
> **WARNING** Vouch proxy requires a unique identifier but does not use the proper scope, "sub". It
> uses the fields "username" or "email" as primary identifiers instead. As a result, this can cause
> user or deployment issues, at worst security bypasses. You should avoid Vouch Proxy if possible
> due to these issues.
>
> - <https://github.com/vouch/vouch-proxy/issues/309>
> - <https://github.com/vouch/vouch-proxy/issues/310>
Note: **You need to run at least the version 0.37.0**
Vouch Proxy supports multiple OAuth and OIDC login providers. To configure it you need to pass:
```yaml
oauth:
auth_url: https://idm.wherekanidmruns.com/ui/oauth2
callback_url: https://login.wherevouchproxyruns.com/auth
client_id: <oauth2_rs_name> # Found in kanidm system oauth2 get XXXX (should be the same as XXXX)
client_secret: <oauth2_rs_basic_secret> # Found in kanidm system oauth2 get XXXX
code_challenge_method: S256
provider: oidc
scopes:
- email # Required due to vouch proxy reliance on mail as a primary identifier
token_url: https://idm.wherekanidmruns.com/oauth2/token
user_info_url: https://idm.wherekanidmruns.com/oauth2/openid/<oauth2_rs_name>/userinfo
```
The `email` scope needs to be passed and thus the mail attribute needs to exist on the account:
```bash
kanidm person update <ID> --mail "YYYY@somedomain.com" --name idm_admin
kanidm system oauth2 create_scope_map <client name> velociraptor_users openid email
```
### Grafana
Grafana is a open source analytics and interactive visualization web application. It provides charts, graphs, and alerts when connected to supported data source.
Grafana is a open source analytics and interactive visualization web application. It provides
charts, graphs, and alerts when connected to supported data source.
Prepare the environment:
@ -547,3 +519,36 @@ role_attribute_path = contains(grafana_role[*], 'GrafanaAdmin') && 'GrafanaAdmin
allow_assign_grafana_admin = true
```
### Vouch Proxy
> **WARNING** Vouch proxy requires a unique identifier but does not use the proper scope, "sub". It
> uses the fields "username" or "email" as primary identifiers instead. As a result, this can cause
> user or deployment issues, at worst security bypasses. You should avoid Vouch Proxy if possible
> due to these issues.
>
> - <https://github.com/vouch/vouch-proxy/issues/309>
> - <https://github.com/vouch/vouch-proxy/issues/310>
Note: **You need to run at least the version 0.37.0**
Vouch Proxy supports multiple OAuth and OIDC login providers. To configure it you need to pass:
```yaml
oauth:
auth_url: https://idm.wherekanidmruns.com/ui/oauth2
callback_url: https://login.wherevouchproxyruns.com/auth
client_id: <oauth2_rs_name> # Found in kanidm system oauth2 get XXXX (should be the same as XXXX)
client_secret: <oauth2_rs_basic_secret> # Found in kanidm system oauth2 get XXXX
code_challenge_method: S256
provider: oidc
scopes:
- email # Required due to vouch proxy reliance on mail as a primary identifier
token_url: https://idm.wherekanidmruns.com/oauth2/token
user_info_url: https://idm.wherekanidmruns.com/oauth2/openid/<oauth2_rs_name>/userinfo
```
The `email` scope needs to be passed and thus the mail attribute needs to exist on the account:
```bash
kanidm person update <ID> --mail "YYYY@somedomain.com" --name idm_admin
```

View file

@ -1,7 +1,7 @@
# Community Packages
There are several community maintained packages that you may use in your system. However, they are not officially supported and may not function identically.
There are several community maintained packages that you may use in your system. However, they are
not officially supported and may not function identically.
- [Arch Linux](https://aur.archlinux.org/packages?O=0&K=kanidm)
- [OpenSUSE](https://software.opensuse.org/search?baseproject=ALL&q=kanidm)

View file

@ -1,32 +1,45 @@
# PPA Packages
This pulls the packages from the Kanidm [debs releases](https://github.com/kanidm/kanidm/releases/tag/debs) and makes a package archive for “nightly” packages. Packages are distributed for the latest LTS versions, Ubuntu 22.04 & Debian 12.
This pulls the packages from the Kanidm
[debs releases](https://github.com/kanidm/kanidm/releases/tag/debs) and makes a package archive for
“nightly” packages. Packages are distributed for the latest LTS versions, Ubuntu 22.04 & Debian 12.
Please note that while the commands below should also work on other Ubuntu-based distributions, we cannot ensure their compatibility with PPA. Pop OS for example, would require an altered setup in line with their [instructions](https://support.system76.com/articles/ppa-third-party/).
Please note that while the commands below should also work on other Ubuntu-based distributions, we
cannot ensure their compatibility with PPA. Pop OS for example, would require an altered setup in
line with their [instructions](https://support.system76.com/articles/ppa-third-party/).
## Adding it to your system
Set pipefail so that failures are caught.
```bash
set -o pipefail
```
Make sure you have a “trusted GPG” directory.
```bash
sudo mkdir -p /etc/apt/trusted.gpg.d/
```
Download the Kanidm PPA GPG public key.
```bash
curl -s --compressed "https://kanidm.github.io/kanidm_ppa/KEY.gpg" \
| gpg --dearmor \
| sudo tee /etc/apt/trusted.gpg.d/kanidm_ppa.gpg >/dev/null
```
Add the Kanidm PPA to your local APT configuration, with autodetection of Ubuntu vs. Debian.
```bash
sudo curl -s --compressed "https://kanidm.github.io/kanidm_ppa/kanidm_ppa.list" \
| grep $( ( . /etc/os-release && echo $ID) ) \
| sudo tee /etc/apt/sources.list.d/kanidm_ppa.list
```
Update your local package cache.
```bash
sudo apt update
```

View file

@ -44,6 +44,7 @@ This is a list of supported features and standards within Kanidm.
- RBAC scope mapping
- [RFC6819 OAauth 2.0 Threat Model and Security Considerations](https://www.rfc-editor.org/rfc/rfc6819)
- [RFC7009 Token Revocation](https://datatracker.ietf.org/doc/html/rfc7009)
- [RFC7662 OAuth 2.0 Token Introspection](https://www.rfc-editor.org/rfc/rfc7662)
- [RFC7636 Proof Key for Code Exchange (SHA256 Only)](https://www.rfc-editor.org/rfc/rfc7636)
- [RFC8414 OAuth 2.0 Authorisation Server Metadata](https://www.rfc-editor.org/rfc/rfc8414)
- [RFC9068 OAuth 2.0 JWT Access Tokens](https://www.rfc-editor.org/rfc/rfc9068)

View file

@ -913,16 +913,16 @@ impl ValueEnum for Oauth2ClaimMapJoin {
#[derive(Debug, Subcommand)]
pub enum Oauth2Opt {
#[clap(name = "list")]
/// List all configured oauth2 resource servers
/// List all configured oauth2 clients
List(CommonOpt),
#[clap(name = "get")]
/// Display a selected oauth2 resource server
/// Display a selected oauth2 client
Get(Named),
// #[clap(name = "set")]
// /// Set options for a selected oauth2 resource server
// /// Set options for a selected oauth2 client
// Set(),
#[clap(name = "create")]
/// Create a new oauth2 confidential resource server that is protected by basic auth.
/// Create a new oauth2 confidential client that is protected by basic auth.
CreateBasic {
#[clap(name = "name")]
name: String,
@ -934,8 +934,8 @@ pub enum Oauth2Opt {
copt: CommonOpt,
},
#[clap(name = "create-public")]
/// Create a new OAuth2 public resource server that requires PKCE. You should prefer
/// using confidential resource server types if possible over public ones.
/// Create a new OAuth2 public client that requires PKCE. You should prefer
/// using confidential client types if possible over public ones.
///
/// Public clients have many limitations and can not access all API's of OAuth2. For
/// example rfc7662 token introspection requires client authentication.
@ -994,18 +994,18 @@ pub enum Oauth2Opt {
},
#[clap(name = "reset-secrets")]
/// Reset the secrets associated to this resource server
/// Reset the secrets associated to this client
ResetSecrets(Named),
#[clap(name = "show-basic-secret")]
/// Show the associated basic secret for this resource server
/// Show the associated basic secret for this client
ShowBasicSecret(Named),
#[clap(name = "delete")]
/// Delete a oauth2 resource server
/// Delete a oauth2 client
Delete(Named),
/// Set a new displayname for a resource server
/// Set a new displayname for a client
#[clap(name = "set-displayname")]
SetDisplayname(Oauth2SetDisplayname),
/// Set a new name for this resource server. You may need to update
/// Set a new name for this client. You may need to update
/// your integrated applications after this so that they continue to
/// function correctly.
#[clap(name = "set-name")]
@ -1025,18 +1025,18 @@ pub enum Oauth2Opt {
url: String,
},
#[clap(name = "enable-pkce")]
/// Enable PKCE on this oauth2 resource server. This defaults to being enabled.
/// Enable PKCE on this oauth2 client. This defaults to being enabled.
EnablePkce(Named),
/// Disable PKCE on this oauth2 resource server to work around insecure clients that
/// Disable PKCE on this oauth2 client to work around insecure clients that
/// may not support it. You should request the client to enable PKCE!
#[clap(name = "warning-insecure-client-disable-pkce")]
DisablePkce(Named),
#[clap(name = "warning-enable-legacy-crypto")]
/// Enable legacy signing crypto on this oauth2 resource server. This defaults to being disabled.
/// Enable legacy signing crypto on this oauth2 client. This defaults to being disabled.
/// You only need to enable this for openid clients that do not support modern crytopgraphic
/// operations.
EnableLegacyCrypto(Named),
/// Disable legacy signing crypto on this oauth2 resource server. This is the default.
/// Disable legacy signing crypto on this oauth2 client. This is the default.
#[clap(name = "disable-legacy-crypto")]
DisableLegacyCrypto(Named),
#[clap(name = "prefer-short-username")]
@ -1329,7 +1329,7 @@ pub enum SystemOpt {
commands: DeniedNamesOpt,
},
#[clap(name = "oauth2")]
/// Configure and display oauth2/oidc resource server configuration
/// Configure and display oauth2/oidc client configuration
Oauth2 {
#[clap(subcommand)]
commands: Oauth2Opt,