mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat!: add admin-controlled dynamic client registration toggle (#27316)
`POST /oauth2/register` (RFC 7591 Dynamic Client Registration) has exactly one gate today: `ExperimentOAuth2`, a static, process-lifetime flag that wraps the entire `/oauth2/*` route tree as an all-or-nothing switch. That flag is scheduled for removal at GA, which would leave DCR with zero admin control at all once it is gone. Add a persistent, DCR-specific `oauth2_dcr_enabled` deployment setting, independent of the experiment system, so admin control over DCR survives GA. `POST /oauth2/register` checks the flag and rejects new registrations with an RFC 7591-shaped `403` when disabled; discovery metadata (`GET /.well-known/oauth-authorization-server`) conditionally omits `registration_endpoint`. A new audited `GET`/`PUT /api/v2/oauth2-provider/settings` endpoint lets an owner toggle it live, no restart required. The setting defaults to disabled, matching the canonical design proposal; disabling only stops new self-registrations, clients that already registered continue to authorize and exchange tokens normally. Address issue described in [ENG-3056](https://linear.app/codercom/issue/ENG-3056/oauth2-dcr-admin-configurable-enabledisable). ## Where this sits in the request path ```mermaid sequenceDiagram autonumber participant A as Admin participant S as coderd participant DB as site_configs<br/>(oauth2_dcr_enabled) participant C as OAuth2/MCP Client Note over A,S: Admin toggles DCR (new) A->>S: PUT /api/v2/oauth2-provider/settings<br/>{dynamic_client_registration_enabled: false} S->>S: authorizeContext(ActionUpdate, ResourceDeploymentConfig) S->>DB: UPSERT oauth2_dcr_enabled = false S-->>A: 200 OK (audited) Note over C,S: Client discovery + registration afterward C->>S: GET /.well-known/oauth-authorization-server S->>DB: GetOAuth2DCREnabled (system ctx, every request, no cache) DB-->>S: false S-->>C: 200 metadata, registration_endpoint omitted C->>S: POST /oauth2/register S->>DB: GetOAuth2DCREnabled (system ctx, every request, no cache) DB-->>S: false S-->>C: 403 invalid_request,<br/>"Dynamic client registration is disabled" Note over C,S: A client that registered before the change is unaffected C->>S: GET /oauth2/authorize?client_id=... Note over S: no DCR-enabled check on this path S-->>C: 200 (proceeds normally) C->>S: PUT/DELETE /oauth2/clients/{client_id} (RFC 7592 self-management) Note over S: no DCR-enabled check on this path either S-->>C: 200 (proceeds normally) ``` ## Files changed: manual vs. generated Reviewers should focus on the **manual** files. The **generated** ones are `make gen` output that follows mechanically from the manual changes and don't need direct review. <details> <summary><b>Manual files (26)</b> — click to expand, grouped the same way as "Suggested review order" below</summary> **1. Database** | File | What changed | |---|---| | `coderd/database/queries/siteconfig.sql` | New `GetOAuth2DCREnabled`/`UpsertOAuth2DCREnabled` query pair on the existing generic `site_configs` table. No schema change. | | `coderd/database/dbauthz/dbauthz.go` | RBAC check (`rbac.ResourceDeploymentConfig`) on the two new query methods; extends the `subjectSystemOAuth2` system-actor role with read-only `ResourceDeploymentConfig` access, needed so the public discovery/registration endpoints can read the flag via `dbauthz.AsSystemOAuth2`. | | `coderd/database/dbauthz/dbauthz_test.go` | RBAC assertion coverage for `GetOAuth2DCREnabled`/`UpsertOAuth2DCREnabled` in the method-coverage test suite. | **2. Request gating (the actual feature)** | File | What changed | |---|---| | `coderd/oauth2provider/registration.go` | The actual gate: `CreateDynamicClientRegistration` reads the flag first and returns an RFC 7591-shaped `403` when disabled (defaults disabled if never configured). | | `coderd/oauth2provider/registration_test.go` | New unit test, `TestCreateDynamicClientRegistration_DCREnabled`: calls the handler directly (no HTTP server), covering enabled / explicitly disabled / never-configured. | | `coderd/oauth2provider/metadata.go` | `GetAuthorizationServerMetadata` conditionally omits `registration_endpoint` from discovery metadata when DCR is disabled. | | `coderd/oauth2provider/metadata_test.go` | New unit test, `TestGetAuthorizationServerMetadata_DCREnabled`: same three states, for the discovery handler. | **3. Admin settings endpoint** | File | What changed | |---|---| | `codersdk/oauth2.go` | New `OAuth2ProviderSettings` SDK type plus `Client.OAuth2ProviderSettings`/`PutOAuth2ProviderSettings` methods. | | `coderd/oauth2.go` | New `oauth2ProviderSettings`/`putOAuth2ProviderSettings` admin handlers (audited via `audit.InitRequest`); updates the `GetAuthorizationServerMetadata` call site to pass `api.Database`. | | `coderd/coderd.go` | Registers `GET`/`PUT /api/v2/oauth2-provider/settings`. | | `coderd/oauth2_provider_settings_test.go` | New test file: admin `GET`/`PUT` round-trip, default-disabled-before-any-`PUT`, and `403` for a non-owner on both `GET` and `PUT`. | **4. Audit wiring** | File | What changed | |---|---| | `coderd/database/types.go` | New `database.OAuth2ProviderSettings` audit-only struct (mirrors `NotificationsSettings`). | | `coderd/audit/diff.go` | Adds the new struct to the `Auditable` type union. | | `coderd/audit/request.go` | Adds the new struct to all four dispatch switches (`ResourceTarget`, `ResourceID`, `ResourceType`, `ResourceRequiresOrgID`). | | `codersdk/audit.go` | New API-facing `ResourceTypeOAuth2ProviderSettings` constant and its `FriendlyString` case. | | `enterprise/audit/table.go` | Field-level audit action map (`ActionTrack`/`ActionIgnore`) for the new struct. | | `coderd/database/migrations/000546_audit_oauth2_provider_settings.up.sql` | Adds `oauth2_provider_settings` to the `resource_type` Postgres enum, required for the audit wiring above (`resource_type` is a real enum, not a Go-only value). | | `coderd/database/migrations/000546_audit_oauth2_provider_settings.down.sql` | No-op (`ALTER TYPE ... ADD VALUE` can't be reverted). | **5. Test-suite ripple from the disabled-by-default flip** | File | What changed | |---|---| | `coderd/oauth2provider/oauth2providertest/helpers.go` | New shared test helper, `EnableDCR`, since DCR now defaults to disabled and many pre-existing tests need it turned on to register a client. | | `coderd/oauth2_test.go` | Adds `TestOAuth2DynamicClientRegistrationDisabled` (registers a client, disables DCR, verifies new registration is rejected while the existing client's self-management, authorize, and token exchange all keep working); calls `EnableDCR` in every pre-existing test that registers a client. | | `coderd/oauth2_error_compliance_test.go` | Calls `EnableDCR` in every test that registers a client, so RFC-error-format assertions aren't masked by the new disabled-by-default gate. | | `coderd/oauth2_metadata_validation_test.go` | Same: `EnableDCR` added to every registration-dependent test. | | `coderd/oauth2_security_test.go` | Same. | | `coderd/oauth2provider/validation_test.go` | Same (near-duplicate of `oauth2_metadata_validation_test.go` in a different package). | | `coderd/oauth2provider/provider_test.go` | Same. | | `coderd/mcp/mcp_e2e_test.go` | Same, for the MCP end-to-end dynamic-registration flow test. | </details> <details> <summary><b>Generated files (12)</b> — from <code>make gen</code>, no need to review directly</summary> `coderd/apidoc/docs.go`, `coderd/apidoc/swagger.json`, `coderd/database/dbmetrics/querymetrics.go`, `coderd/database/dbmock/dbmock.go`, `coderd/database/dump.sql`, `coderd/database/models.go`, `coderd/database/querier.go`, `coderd/database/queries.sql.go`, `docs/admin/security/audit-logs.md`, `docs/reference/api/enterprise.md`, `docs/reference/api/schemas.md`, `site/src/api/typesGenerated.ts`. </details> ## Suggested review order ### 1. Database Establishes the persisted setting and its RBAC rule; everything else builds on `GetOAuth2DCREnabled`/`UpsertOAuth2DCREnabled`. 1. `coderd/database/queries/siteconfig.sql` — the two new queries. Same boolean-encoding pattern as the existing `oauth2_github_default_eligible` key right above them in the same file. 2. `coderd/database/dbauthz/dbauthz.go` — the RBAC wrapper for those two queries, plus the `subjectSystemOAuth2` role extension (search this file for `ResourceDeploymentConfig`, it appears in both spots). 3. `coderd/database/dbauthz/dbauthz_test.go` — asserts the RBAC checks from (2) actually fire. ### 2. Request gating (the actual feature) Where `POST /oauth2/register` and discovery metadata change behavior. 1. `coderd/oauth2provider/registration.go` — the primary gate. Read this first; it's the feature. 2. `coderd/oauth2provider/registration_test.go` — its new unit test, exercising the gate's three states directly against the handler. 3. `coderd/oauth2provider/metadata.go` — the same gating pattern applied to the discovery `GET` endpoint. 4. `coderd/oauth2provider/metadata_test.go` — its new unit test. ### 3. Admin settings endpoint How an owner flips the setting live. 1. `codersdk/oauth2.go` — the `OAuth2ProviderSettings` SDK type and `Client` methods first; this is the public contract everything below implements against. 2. `coderd/oauth2.go` — the `GET`/`PUT` handlers themselves. 3. `coderd/coderd.go` — route registration, to see where those handlers get wired in. 4. `coderd/oauth2_provider_settings_test.go` — round-trip and permission tests. ### 4. Audit wiring Plumbing required so step 3's `PUT` is auditable; mechanical except for (3). 1. `coderd/database/types.go` — the audit-only struct; everything else in this layer exists to plumb it through. 2. `coderd/audit/diff.go` — adds it to the `Auditable` type union (the compiler enforces this one). 3. `coderd/audit/request.go` — the four dispatch switches; the one part of this layer worth reading closely. 4. `codersdk/audit.go` — the API-facing resource type constant. 5. `enterprise/audit/table.go` — the field-action map. 6. `coderd/database/migrations/000546_audit_oauth2_provider_settings.{up,down}.sql` — read last; a consequence of needing a new `resource_type` enum value for (1)-(5), not a design decision of its own. ### 5. Test-suite ripple from the disabled-by-default flip 1. `coderd/oauth2provider/oauth2providertest/helpers.go` — the new `EnableDCR` helper. Read first to understand the fix pattern before seeing it applied repeatedly. 2. `coderd/oauth2_test.go` — next, since it also contains the new `TestOAuth2DynamicClientRegistrationDisabled`, not just `EnableDCR` call sites. 3. The rest, in any order, they're mechanical repeats of the same one-line addition: `coderd/oauth2_error_compliance_test.go`, `coderd/oauth2_metadata_validation_test.go`, `coderd/oauth2_security_test.go`, `coderd/oauth2provider/validation_test.go`, `coderd/oauth2provider/provider_test.go`, `coderd/mcp/mcp_e2e_test.go`. ## Explicitly out of scope Per the design proposal: rate limiting on `POST /oauth2/register` (tracked separately), retroactively affecting already-registered clients when DCR is disabled (this only gates new self-registration), and an Initial Access Token requirement (a separate, follow-up ticket).
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/util/ptr"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/serpent"
|
||||
)
|
||||
|
||||
func (r *RootCmd) oauth2Provider() *serpent.Command {
|
||||
cmd := &serpent.Command{
|
||||
Use: "oauth2-provider",
|
||||
Short: "Manage Coder OAuth2 provider settings",
|
||||
Long: "Administrators can use these commands to change OAuth2 provider settings.\n" + FormatExamples(
|
||||
Example{
|
||||
Description: "Enable dynamic client registration (RFC 7591), allowing OAuth2/MCP clients to self-register without an admin creating an app first",
|
||||
Command: "coder oauth2-provider dcr enable",
|
||||
},
|
||||
Example{
|
||||
Description: "Disable dynamic client registration. Clients that already registered are unaffected; only new self-registration attempts are rejected",
|
||||
Command: "coder oauth2-provider dcr disable",
|
||||
},
|
||||
),
|
||||
Handler: func(inv *serpent.Invocation) error {
|
||||
return inv.Command.HelpHandler(inv)
|
||||
},
|
||||
Children: []*serpent.Command{
|
||||
r.oauth2ProviderDCR(),
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (r *RootCmd) oauth2ProviderDCR() *serpent.Command {
|
||||
cmd := &serpent.Command{
|
||||
Use: "dcr",
|
||||
Short: "Manage OAuth2 dynamic client registration (RFC 7591)",
|
||||
Handler: func(inv *serpent.Invocation) error {
|
||||
return inv.Command.HelpHandler(inv)
|
||||
},
|
||||
Children: []*serpent.Command{
|
||||
r.oauth2ProviderDCRToggle(dcrToggleEnable),
|
||||
r.oauth2ProviderDCRToggle(dcrToggleDisable),
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// dcrToggleAction distinguishes the "enable" and "disable" subcommands of
|
||||
// `coder oauth2-provider dcr`, which are otherwise identical.
|
||||
type dcrToggleAction int
|
||||
|
||||
const (
|
||||
dcrToggleDisable dcrToggleAction = iota
|
||||
dcrToggleEnable
|
||||
)
|
||||
|
||||
func (r *RootCmd) oauth2ProviderDCRToggle(action dcrToggleAction) *serpent.Command {
|
||||
enabled := action == dcrToggleEnable
|
||||
use, short, verb := "disable", "Disable OAuth2 dynamic client registration", "disable"
|
||||
if enabled {
|
||||
use, short, verb = "enable", "Enable OAuth2 dynamic client registration", "enable"
|
||||
}
|
||||
|
||||
cmd := &serpent.Command{
|
||||
Use: use,
|
||||
Short: short,
|
||||
Middleware: serpent.Chain(
|
||||
serpent.RequireNArgs(0),
|
||||
),
|
||||
Handler: func(inv *serpent.Invocation) error {
|
||||
client, err := r.InitClient(inv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = client.PutOAuth2ProviderSettings(inv.Context(), codersdk.OAuth2ProviderSettings{
|
||||
DynamicClientRegistrationEnabled: ptr.Ref(enabled),
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("unable to %s dynamic client registration: %w", verb, err)
|
||||
}
|
||||
|
||||
state := "disabled"
|
||||
if enabled {
|
||||
state = "enabled"
|
||||
}
|
||||
_, _ = fmt.Fprintf(inv.Stderr, "Dynamic client registration is now %s.\n", state)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/cli/clitest"
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
func TestOAuth2ProviderDCR(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
command string
|
||||
expectValue bool
|
||||
expectMsg string
|
||||
}{
|
||||
{
|
||||
name: "Enable",
|
||||
command: "enable",
|
||||
expectValue: true,
|
||||
expectMsg: "Dynamic client registration is now enabled.",
|
||||
},
|
||||
{
|
||||
name: "Disable",
|
||||
command: "disable",
|
||||
expectValue: false,
|
||||
expectMsg: "Dynamic client registration is now disabled.",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client := coderdtest.New(t, nil)
|
||||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
inv, root := clitest.New(t, "oauth2-provider", "dcr", tt.command)
|
||||
clitest.SetupConfig(t, client, root)
|
||||
|
||||
var buf bytes.Buffer
|
||||
inv.Stderr = &buf
|
||||
err := inv.Run()
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, buf.String(), tt.expectMsg)
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
settings, err := client.OAuth2ProviderSettings(ctx)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, settings.DynamicClientRegistrationEnabled, "GET must always return a concrete value")
|
||||
require.Equal(t, tt.expectValue, *settings.DynamicClientRegistrationEnabled)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOAuth2ProviderDCR_RegularUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client := coderdtest.New(t, nil)
|
||||
owner := coderdtest.CreateFirstUser(t, client)
|
||||
anotherClient, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
|
||||
|
||||
inv, root := clitest.New(t, "oauth2-provider", "dcr", "enable")
|
||||
clitest.SetupConfig(t, anotherClient, root)
|
||||
|
||||
var buf bytes.Buffer
|
||||
inv.Stderr = &buf
|
||||
err := inv.Run()
|
||||
var sdkError *codersdk.Error
|
||||
require.Error(t, err)
|
||||
require.ErrorAsf(t, err, &sdkError, "error should be of type *codersdk.Error")
|
||||
assert.Equal(t, http.StatusForbidden, sdkError.StatusCode())
|
||||
}
|
||||
@@ -109,6 +109,7 @@ func (r *RootCmd) CoreSubcommands() []*serpent.Command {
|
||||
r.logout(),
|
||||
r.netcheck(),
|
||||
r.notifications(),
|
||||
r.oauth2Provider(),
|
||||
r.organizations(),
|
||||
r.portForward(),
|
||||
r.publickey(),
|
||||
|
||||
Vendored
+52
-51
@@ -14,57 +14,58 @@ USAGE:
|
||||
$ coder templates init
|
||||
|
||||
SUBCOMMANDS:
|
||||
autoupdate Toggle auto-update policy for a workspace
|
||||
completion Install or update shell completion scripts for the
|
||||
detected or chosen shell.
|
||||
config-ssh Add an SSH Host entry for your workspaces "ssh
|
||||
workspace.coder"
|
||||
create Create a workspace
|
||||
delete Delete a workspace
|
||||
dotfiles Personalize your workspace by applying a canonical
|
||||
dotfiles repository
|
||||
external-auth Manage external authentication
|
||||
favorite Add a workspace to your favorites
|
||||
list List workspaces
|
||||
login Authenticate with Coder deployment
|
||||
logout Unauthenticate your local session
|
||||
logs View logs for a workspace
|
||||
netcheck Print network debug information for DERP and STUN
|
||||
notifications Manage Coder notifications
|
||||
open Open a workspace
|
||||
organizations Organization related commands
|
||||
ping Ping a workspace
|
||||
port-forward Forward ports from a workspace to the local machine. For
|
||||
reverse port forwarding, use "coder ssh -R".
|
||||
provisioner View and manage provisioner daemons and jobs
|
||||
publickey Output your Coder public key used for Git operations
|
||||
rename Rename a workspace
|
||||
reset-password Directly connect to the database to reset a user's
|
||||
password
|
||||
restart Restart a workspace
|
||||
schedule Schedule automated start and stop times for workspaces
|
||||
secret Manage secrets
|
||||
server Start a Coder server
|
||||
show Display details of a workspace's resources and agents
|
||||
speedtest Run upload and download tests from your machine to a
|
||||
workspace
|
||||
ssh Start a shell into a workspace or run a command
|
||||
start Start a workspace
|
||||
stat Show resource usage for the current workspace.
|
||||
state Manually manage Terraform state to fix broken workspaces
|
||||
stop Stop a workspace
|
||||
support Commands for troubleshooting issues with a Coder
|
||||
deployment.
|
||||
task Manage tasks
|
||||
templates Manage templates
|
||||
tokens Manage personal access tokens
|
||||
unfavorite Remove a workspace from your favorites
|
||||
update Will update and start a given workspace if it is out of
|
||||
date. If the workspace is already running, it will be
|
||||
stopped first.
|
||||
users Manage users
|
||||
version Show coder version
|
||||
whoami Fetch authenticated user info for Coder deployment
|
||||
autoupdate Toggle auto-update policy for a workspace
|
||||
completion Install or update shell completion scripts for the
|
||||
detected or chosen shell.
|
||||
config-ssh Add an SSH Host entry for your workspaces "ssh
|
||||
workspace.coder"
|
||||
create Create a workspace
|
||||
delete Delete a workspace
|
||||
dotfiles Personalize your workspace by applying a canonical
|
||||
dotfiles repository
|
||||
external-auth Manage external authentication
|
||||
favorite Add a workspace to your favorites
|
||||
list List workspaces
|
||||
login Authenticate with Coder deployment
|
||||
logout Unauthenticate your local session
|
||||
logs View logs for a workspace
|
||||
netcheck Print network debug information for DERP and STUN
|
||||
notifications Manage Coder notifications
|
||||
oauth2-provider Manage Coder OAuth2 provider settings
|
||||
open Open a workspace
|
||||
organizations Organization related commands
|
||||
ping Ping a workspace
|
||||
port-forward Forward ports from a workspace to the local machine. For
|
||||
reverse port forwarding, use "coder ssh -R".
|
||||
provisioner View and manage provisioner daemons and jobs
|
||||
publickey Output your Coder public key used for Git operations
|
||||
rename Rename a workspace
|
||||
reset-password Directly connect to the database to reset a user's
|
||||
password
|
||||
restart Restart a workspace
|
||||
schedule Schedule automated start and stop times for workspaces
|
||||
secret Manage secrets
|
||||
server Start a Coder server
|
||||
show Display details of a workspace's resources and agents
|
||||
speedtest Run upload and download tests from your machine to a
|
||||
workspace
|
||||
ssh Start a shell into a workspace or run a command
|
||||
start Start a workspace
|
||||
stat Show resource usage for the current workspace.
|
||||
state Manually manage Terraform state to fix broken workspaces
|
||||
stop Stop a workspace
|
||||
support Commands for troubleshooting issues with a Coder
|
||||
deployment.
|
||||
task Manage tasks
|
||||
templates Manage templates
|
||||
tokens Manage personal access tokens
|
||||
unfavorite Remove a workspace from your favorites
|
||||
update Will update and start a given workspace if it is out of
|
||||
date. If the workspace is already running, it will be
|
||||
stopped first.
|
||||
users Manage users
|
||||
version Show coder version
|
||||
whoami Fetch authenticated user info for Coder deployment
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
Global options are applied to all commands. They can be set using environment
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
coder v0.0.0-devel
|
||||
|
||||
USAGE:
|
||||
coder oauth2-provider
|
||||
|
||||
Manage Coder OAuth2 provider settings
|
||||
|
||||
Administrators can use these commands to change OAuth2 provider settings.
|
||||
- Enable dynamic client registration (RFC 7591), allowing OAuth2/MCP clients
|
||||
to
|
||||
self-register without an admin creating an app first:
|
||||
|
||||
$ coder oauth2-provider dcr enable
|
||||
|
||||
- Disable dynamic client registration. Clients that already registered are
|
||||
unaffected; only new self-registration attempts are rejected:
|
||||
|
||||
$ coder oauth2-provider dcr disable
|
||||
|
||||
SUBCOMMANDS:
|
||||
dcr Manage OAuth2 dynamic client registration (RFC 7591)
|
||||
|
||||
———
|
||||
Run `coder --help` for a list of global options.
|
||||
@@ -0,0 +1,13 @@
|
||||
coder v0.0.0-devel
|
||||
|
||||
USAGE:
|
||||
coder oauth2-provider dcr
|
||||
|
||||
Manage OAuth2 dynamic client registration (RFC 7591)
|
||||
|
||||
SUBCOMMANDS:
|
||||
disable Disable OAuth2 dynamic client registration
|
||||
enable Enable OAuth2 dynamic client registration
|
||||
|
||||
———
|
||||
Run `coder --help` for a list of global options.
|
||||
@@ -0,0 +1,9 @@
|
||||
coder v0.0.0-devel
|
||||
|
||||
USAGE:
|
||||
coder oauth2-provider dcr disable
|
||||
|
||||
Disable OAuth2 dynamic client registration
|
||||
|
||||
———
|
||||
Run `coder --help` for a list of global options.
|
||||
@@ -0,0 +1,9 @@
|
||||
coder v0.0.0-devel
|
||||
|
||||
USAGE:
|
||||
coder oauth2-provider dcr enable
|
||||
|
||||
Enable OAuth2 dynamic client registration
|
||||
|
||||
———
|
||||
Run `coder --help` for a list of global options.
|
||||
Reference in New Issue
Block a user