mirror of
https://github.com/coder/coder.git
synced 2026-09-21 20:51:01 +08:00
feat: enable GitHub OAuth2 login by default on new deployments (#16662)
Third and final PR to address https://github.com/coder/coder/issues/16230. This PR enables GitHub OAuth2 login by default on new deployments. Combined with https://github.com/coder/coder/pull/16629, this will allow the first admin user to sign up with GitHub rather than email and password. We take care not to enable the default on deployments that would upgrade to a Coder version with this change. To disable the default provider an admin can set the `CODER_OAUTH2_GITHUB_DEFAULT_PROVIDER` env variable to false.
This commit is contained in:
+114
-41
@@ -688,24 +688,6 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
|
||||
}
|
||||
}
|
||||
|
||||
if vals.OAuth2.Github.ClientSecret != "" || vals.OAuth2.Github.DeviceFlow.Value() {
|
||||
options.GithubOAuth2Config, err = configureGithubOAuth2(
|
||||
oauthInstrument,
|
||||
vals.AccessURL.Value(),
|
||||
vals.OAuth2.Github.ClientID.String(),
|
||||
vals.OAuth2.Github.ClientSecret.String(),
|
||||
vals.OAuth2.Github.DeviceFlow.Value(),
|
||||
vals.OAuth2.Github.AllowSignups.Value(),
|
||||
vals.OAuth2.Github.AllowEveryone.Value(),
|
||||
vals.OAuth2.Github.AllowedOrgs,
|
||||
vals.OAuth2.Github.AllowedTeams,
|
||||
vals.OAuth2.Github.EnterpriseBaseURL.String(),
|
||||
)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("configure github oauth2: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// As OIDC clients can be confidential or public,
|
||||
// we should only check for a client id being set.
|
||||
// The underlying library handles the case of no
|
||||
@@ -793,6 +775,20 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
|
||||
return xerrors.Errorf("set deployment id: %w", err)
|
||||
}
|
||||
|
||||
githubOAuth2ConfigParams, err := getGithubOAuth2ConfigParams(ctx, options.Database, vals)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get github oauth2 config params: %w", err)
|
||||
}
|
||||
if githubOAuth2ConfigParams != nil {
|
||||
options.GithubOAuth2Config, err = configureGithubOAuth2(
|
||||
oauthInstrument,
|
||||
githubOAuth2ConfigParams,
|
||||
)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("configure github oauth2: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
options.RuntimeConfig = runtimeconfig.NewManager()
|
||||
|
||||
// This should be output before the logs start streaming.
|
||||
@@ -1843,25 +1839,101 @@ func configureCAPool(tlsClientCAFile string, tlsConfig *tls.Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: convert the argument list to a struct, it's easy to mix up the order of the arguments
|
||||
//
|
||||
const (
|
||||
// Client ID for https://github.com/apps/coder
|
||||
GithubOAuth2DefaultProviderClientID = "Iv1.6a2b4b4aec4f4fe7"
|
||||
GithubOAuth2DefaultProviderAllowEveryone = true
|
||||
GithubOAuth2DefaultProviderDeviceFlow = true
|
||||
)
|
||||
|
||||
type githubOAuth2ConfigParams struct {
|
||||
accessURL *url.URL
|
||||
clientID string
|
||||
clientSecret string
|
||||
deviceFlow bool
|
||||
allowSignups bool
|
||||
allowEveryone bool
|
||||
allowOrgs []string
|
||||
rawTeams []string
|
||||
enterpriseBaseURL string
|
||||
}
|
||||
|
||||
func getGithubOAuth2ConfigParams(ctx context.Context, db database.Store, vals *codersdk.DeploymentValues) (*githubOAuth2ConfigParams, error) {
|
||||
params := githubOAuth2ConfigParams{
|
||||
accessURL: vals.AccessURL.Value(),
|
||||
clientID: vals.OAuth2.Github.ClientID.String(),
|
||||
clientSecret: vals.OAuth2.Github.ClientSecret.String(),
|
||||
deviceFlow: vals.OAuth2.Github.DeviceFlow.Value(),
|
||||
allowSignups: vals.OAuth2.Github.AllowSignups.Value(),
|
||||
allowEveryone: vals.OAuth2.Github.AllowEveryone.Value(),
|
||||
allowOrgs: vals.OAuth2.Github.AllowedOrgs.Value(),
|
||||
rawTeams: vals.OAuth2.Github.AllowedTeams.Value(),
|
||||
enterpriseBaseURL: vals.OAuth2.Github.EnterpriseBaseURL.String(),
|
||||
}
|
||||
|
||||
// If the user manually configured the GitHub OAuth2 provider,
|
||||
// we won't add the default configuration.
|
||||
if params.clientID != "" || params.clientSecret != "" || params.enterpriseBaseURL != "" {
|
||||
return ¶ms, nil
|
||||
}
|
||||
|
||||
// Check if the user manually disabled the default GitHub OAuth2 provider.
|
||||
if !vals.OAuth2.Github.DefaultProviderEnable.Value() {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
// Check if the deployment is eligible for the default GitHub OAuth2 provider.
|
||||
// We want to enable it only for new deployments, and avoid enabling it
|
||||
// if a deployment was upgraded from an older version.
|
||||
// nolint:gocritic // Requires system privileges
|
||||
defaultEligible, err := db.GetOAuth2GithubDefaultEligible(dbauthz.AsSystemRestricted(ctx))
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, xerrors.Errorf("get github default eligible: %w", err)
|
||||
}
|
||||
defaultEligibleNotSet := errors.Is(err, sql.ErrNoRows)
|
||||
|
||||
if defaultEligibleNotSet {
|
||||
// nolint:gocritic // User count requires system privileges
|
||||
userCount, err := db.GetUserCount(dbauthz.AsSystemRestricted(ctx))
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("get user count: %w", err)
|
||||
}
|
||||
// We check if a deployment is new by checking if it has any users.
|
||||
defaultEligible = userCount == 0
|
||||
// nolint:gocritic // Requires system privileges
|
||||
if err := db.UpsertOAuth2GithubDefaultEligible(dbauthz.AsSystemRestricted(ctx), defaultEligible); err != nil {
|
||||
return nil, xerrors.Errorf("upsert github default eligible: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if !defaultEligible {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
params.clientID = GithubOAuth2DefaultProviderClientID
|
||||
params.allowEveryone = GithubOAuth2DefaultProviderAllowEveryone
|
||||
params.deviceFlow = GithubOAuth2DefaultProviderDeviceFlow
|
||||
|
||||
return ¶ms, nil
|
||||
}
|
||||
|
||||
//nolint:revive // Ignore flag-parameter: parameter 'allowEveryone' seems to be a control flag, avoid control coupling (revive)
|
||||
func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, clientID, clientSecret string, deviceFlow, allowSignups, allowEveryone bool, allowOrgs []string, rawTeams []string, enterpriseBaseURL string) (*coderd.GithubOAuth2Config, error) {
|
||||
redirectURL, err := accessURL.Parse("/api/v2/users/oauth2/github/callback")
|
||||
func configureGithubOAuth2(instrument *promoauth.Factory, params *githubOAuth2ConfigParams) (*coderd.GithubOAuth2Config, error) {
|
||||
redirectURL, err := params.accessURL.Parse("/api/v2/users/oauth2/github/callback")
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("parse github oauth callback url: %w", err)
|
||||
}
|
||||
if allowEveryone && len(allowOrgs) > 0 {
|
||||
if params.allowEveryone && len(params.allowOrgs) > 0 {
|
||||
return nil, xerrors.New("allow everyone and allowed orgs cannot be used together")
|
||||
}
|
||||
if allowEveryone && len(rawTeams) > 0 {
|
||||
if params.allowEveryone && len(params.rawTeams) > 0 {
|
||||
return nil, xerrors.New("allow everyone and allowed teams cannot be used together")
|
||||
}
|
||||
if !allowEveryone && len(allowOrgs) == 0 {
|
||||
if !params.allowEveryone && len(params.allowOrgs) == 0 {
|
||||
return nil, xerrors.New("allowed orgs is empty: must specify at least one org or allow everyone")
|
||||
}
|
||||
allowTeams := make([]coderd.GithubOAuth2Team, 0, len(rawTeams))
|
||||
for _, rawTeam := range rawTeams {
|
||||
allowTeams := make([]coderd.GithubOAuth2Team, 0, len(params.rawTeams))
|
||||
for _, rawTeam := range params.rawTeams {
|
||||
parts := strings.SplitN(rawTeam, "/", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, xerrors.Errorf("github team allowlist is formatted incorrectly. got %s; wanted <organization>/<team>", rawTeam)
|
||||
@@ -1873,8 +1945,8 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
|
||||
}
|
||||
|
||||
endpoint := xgithub.Endpoint
|
||||
if enterpriseBaseURL != "" {
|
||||
enterpriseURL, err := url.Parse(enterpriseBaseURL)
|
||||
if params.enterpriseBaseURL != "" {
|
||||
enterpriseURL, err := url.Parse(params.enterpriseBaseURL)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("parse enterprise base url: %w", err)
|
||||
}
|
||||
@@ -1893,8 +1965,8 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
|
||||
}
|
||||
|
||||
instrumentedOauth := instrument.NewGithub("github-login", &oauth2.Config{
|
||||
ClientID: clientID,
|
||||
ClientSecret: clientSecret,
|
||||
ClientID: params.clientID,
|
||||
ClientSecret: params.clientSecret,
|
||||
Endpoint: endpoint,
|
||||
RedirectURL: redirectURL.String(),
|
||||
Scopes: []string{
|
||||
@@ -1906,17 +1978,17 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
|
||||
|
||||
createClient := func(client *http.Client, source promoauth.Oauth2Source) (*github.Client, error) {
|
||||
client = instrumentedOauth.InstrumentHTTPClient(client, source)
|
||||
if enterpriseBaseURL != "" {
|
||||
return github.NewEnterpriseClient(enterpriseBaseURL, "", client)
|
||||
if params.enterpriseBaseURL != "" {
|
||||
return github.NewEnterpriseClient(params.enterpriseBaseURL, "", client)
|
||||
}
|
||||
return github.NewClient(client), nil
|
||||
}
|
||||
|
||||
var deviceAuth *externalauth.DeviceAuth
|
||||
if deviceFlow {
|
||||
if params.deviceFlow {
|
||||
deviceAuth = &externalauth.DeviceAuth{
|
||||
Config: instrumentedOauth,
|
||||
ClientID: clientID,
|
||||
ClientID: params.clientID,
|
||||
TokenURL: endpoint.TokenURL,
|
||||
Scopes: []string{"read:user", "read:org", "user:email"},
|
||||
CodeURL: endpoint.DeviceAuthURL,
|
||||
@@ -1925,9 +1997,9 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
|
||||
|
||||
return &coderd.GithubOAuth2Config{
|
||||
OAuth2Config: instrumentedOauth,
|
||||
AllowSignups: allowSignups,
|
||||
AllowEveryone: allowEveryone,
|
||||
AllowOrganizations: allowOrgs,
|
||||
AllowSignups: params.allowSignups,
|
||||
AllowEveryone: params.allowEveryone,
|
||||
AllowOrganizations: params.allowOrgs,
|
||||
AllowTeams: allowTeams,
|
||||
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
|
||||
api, err := createClient(client, promoauth.SourceGitAPIAuthUser)
|
||||
@@ -1966,19 +2038,20 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
|
||||
team, _, err := api.Teams.GetTeamMembershipBySlug(ctx, org, teamSlug, username)
|
||||
return team, err
|
||||
},
|
||||
DeviceFlowEnabled: deviceFlow,
|
||||
DeviceFlowEnabled: params.deviceFlow,
|
||||
ExchangeDeviceCode: func(ctx context.Context, deviceCode string) (*oauth2.Token, error) {
|
||||
if !deviceFlow {
|
||||
if !params.deviceFlow {
|
||||
return nil, xerrors.New("device flow is not enabled")
|
||||
}
|
||||
return deviceAuth.ExchangeDeviceCode(ctx, deviceCode)
|
||||
},
|
||||
AuthorizeDevice: func(ctx context.Context) (*codersdk.ExternalAuthDevice, error) {
|
||||
if !deviceFlow {
|
||||
if !params.deviceFlow {
|
||||
return nil, xerrors.New("device flow is not enabled")
|
||||
}
|
||||
return deviceAuth.AuthorizeDevice(ctx)
|
||||
},
|
||||
DefaultProviderConfigured: params.clientID == GithubOAuth2DefaultProviderClientID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@ import (
|
||||
"github.com/coder/coder/v2/cli/clitest"
|
||||
"github.com/coder/coder/v2/cli/config"
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbgen"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtestutil"
|
||||
"github.com/coder/coder/v2/coderd/database/migrations"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
@@ -306,6 +308,145 @@ func TestServer(t *testing.T) {
|
||||
require.Less(t, numLines, 20)
|
||||
})
|
||||
|
||||
t.Run("OAuth2GitHubDefaultProvider", func(t *testing.T) {
|
||||
type testCase struct {
|
||||
name string
|
||||
githubDefaultProviderEnabled string
|
||||
githubClientID string
|
||||
githubClientSecret string
|
||||
expectGithubEnabled bool
|
||||
expectGithubDefaultProviderConfigured bool
|
||||
createUserPreStart bool
|
||||
createUserPostRestart bool
|
||||
}
|
||||
|
||||
runGitHubProviderTest := func(t *testing.T, tc testCase) {
|
||||
t.Parallel()
|
||||
if !dbtestutil.WillUsePostgres() {
|
||||
t.Skip("test requires postgres")
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(testutil.Context(t, testutil.WaitLong))
|
||||
defer cancelFunc()
|
||||
|
||||
dbURL, err := dbtestutil.Open(t)
|
||||
require.NoError(t, err)
|
||||
db, _ := dbtestutil.NewDB(t, dbtestutil.WithURL(dbURL))
|
||||
|
||||
if tc.createUserPreStart {
|
||||
_ = dbgen.User(t, db, database.User{})
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"server",
|
||||
"--postgres-url", dbURL,
|
||||
"--http-address", ":0",
|
||||
"--access-url", "https://example.com",
|
||||
}
|
||||
if tc.githubClientID != "" {
|
||||
args = append(args, fmt.Sprintf("--oauth2-github-client-id=%s", tc.githubClientID))
|
||||
}
|
||||
if tc.githubClientSecret != "" {
|
||||
args = append(args, fmt.Sprintf("--oauth2-github-client-secret=%s", tc.githubClientSecret))
|
||||
}
|
||||
if tc.githubClientID != "" || tc.githubClientSecret != "" {
|
||||
args = append(args, "--oauth2-github-allow-everyone")
|
||||
}
|
||||
if tc.githubDefaultProviderEnabled != "" {
|
||||
args = append(args, fmt.Sprintf("--oauth2-github-default-provider-enable=%s", tc.githubDefaultProviderEnabled))
|
||||
}
|
||||
|
||||
inv, cfg := clitest.New(t, args...)
|
||||
errChan := make(chan error, 1)
|
||||
go func() {
|
||||
errChan <- inv.WithContext(ctx).Run()
|
||||
}()
|
||||
accessURLChan := make(chan *url.URL, 1)
|
||||
go func() {
|
||||
accessURLChan <- waitAccessURL(t, cfg)
|
||||
}()
|
||||
|
||||
var accessURL *url.URL
|
||||
select {
|
||||
case err := <-errChan:
|
||||
require.NoError(t, err)
|
||||
case accessURL = <-accessURLChan:
|
||||
require.NotNil(t, accessURL)
|
||||
}
|
||||
|
||||
client := codersdk.New(accessURL)
|
||||
|
||||
authMethods, err := client.AuthMethods(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expectGithubEnabled, authMethods.Github.Enabled)
|
||||
require.Equal(t, tc.expectGithubDefaultProviderConfigured, authMethods.Github.DefaultProviderConfigured)
|
||||
|
||||
cancelFunc()
|
||||
select {
|
||||
case err := <-errChan:
|
||||
require.NoError(t, err)
|
||||
case <-time.After(testutil.WaitLong):
|
||||
t.Fatal("server did not exit")
|
||||
}
|
||||
|
||||
if tc.createUserPostRestart {
|
||||
_ = dbgen.User(t, db, database.User{})
|
||||
}
|
||||
|
||||
// Ensure that it stays at that setting after the server restarts.
|
||||
inv, cfg = clitest.New(t, args...)
|
||||
clitest.Start(t, inv)
|
||||
accessURL = waitAccessURL(t, cfg)
|
||||
client = codersdk.New(accessURL)
|
||||
|
||||
ctx = testutil.Context(t, testutil.WaitLong)
|
||||
authMethods, err = client.AuthMethods(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expectGithubEnabled, authMethods.Github.Enabled)
|
||||
require.Equal(t, tc.expectGithubDefaultProviderConfigured, authMethods.Github.DefaultProviderConfigured)
|
||||
}
|
||||
|
||||
for _, tc := range []testCase{
|
||||
{
|
||||
name: "NewDeployment",
|
||||
expectGithubEnabled: true,
|
||||
expectGithubDefaultProviderConfigured: true,
|
||||
createUserPreStart: false,
|
||||
createUserPostRestart: true,
|
||||
},
|
||||
{
|
||||
name: "ExistingDeployment",
|
||||
expectGithubEnabled: false,
|
||||
expectGithubDefaultProviderConfigured: false,
|
||||
createUserPreStart: true,
|
||||
createUserPostRestart: false,
|
||||
},
|
||||
{
|
||||
name: "ManuallyDisabled",
|
||||
githubDefaultProviderEnabled: "false",
|
||||
expectGithubEnabled: false,
|
||||
expectGithubDefaultProviderConfigured: false,
|
||||
},
|
||||
{
|
||||
name: "ConfiguredClientID",
|
||||
githubClientID: "123",
|
||||
expectGithubEnabled: true,
|
||||
expectGithubDefaultProviderConfigured: false,
|
||||
},
|
||||
{
|
||||
name: "ConfiguredClientSecret",
|
||||
githubClientSecret: "456",
|
||||
expectGithubEnabled: true,
|
||||
expectGithubDefaultProviderConfigured: false,
|
||||
},
|
||||
} {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
runGitHubProviderTest(t, tc)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Validate that a warning is printed that it may not be externally
|
||||
// reachable.
|
||||
t.Run("LocalAccessURL", func(t *testing.T) {
|
||||
|
||||
+3
@@ -498,6 +498,9 @@ OAUTH2 / GITHUB OPTIONS:
|
||||
--oauth2-github-client-secret string, $CODER_OAUTH2_GITHUB_CLIENT_SECRET
|
||||
Client secret for Login with GitHub.
|
||||
|
||||
--oauth2-github-default-provider-enable bool, $CODER_OAUTH2_GITHUB_DEFAULT_PROVIDER_ENABLE (default: true)
|
||||
Enable the default GitHub OAuth2 provider managed by Coder.
|
||||
|
||||
--oauth2-github-device-flow bool, $CODER_OAUTH2_GITHUB_DEVICE_FLOW (default: false)
|
||||
Enable device flow for Login with GitHub.
|
||||
|
||||
|
||||
+3
@@ -265,6 +265,9 @@ oauth2:
|
||||
# Enable device flow for Login with GitHub.
|
||||
# (default: false, type: bool)
|
||||
deviceFlow: false
|
||||
# Enable the default GitHub OAuth2 provider managed by Coder.
|
||||
# (default: true, type: bool)
|
||||
defaultProviderEnable: true
|
||||
# Organizations the user must be a member of to Login with GitHub.
|
||||
# (default: <unset>, type: string-array)
|
||||
allowedOrgs: []
|
||||
|
||||
Generated
+15
-1
@@ -10331,7 +10331,7 @@ const docTemplate = `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"github": {
|
||||
"$ref": "#/definitions/codersdk.AuthMethod"
|
||||
"$ref": "#/definitions/codersdk.GithubAuthMethod"
|
||||
},
|
||||
"oidc": {
|
||||
"$ref": "#/definitions/codersdk.OIDCAuthMethod"
|
||||
@@ -11857,6 +11857,17 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.GithubAuthMethod": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default_provider_configured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.Group": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -12519,6 +12530,9 @@ const docTemplate = `{
|
||||
"client_secret": {
|
||||
"type": "string"
|
||||
},
|
||||
"default_provider_enable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"device_flow": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
||||
Generated
+15
-1
@@ -9189,7 +9189,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"github": {
|
||||
"$ref": "#/definitions/codersdk.AuthMethod"
|
||||
"$ref": "#/definitions/codersdk.GithubAuthMethod"
|
||||
},
|
||||
"oidc": {
|
||||
"$ref": "#/definitions/codersdk.OIDCAuthMethod"
|
||||
@@ -10642,6 +10642,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.GithubAuthMethod": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default_provider_configured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.Group": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -11255,6 +11266,9 @@
|
||||
"client_secret": {
|
||||
"type": "string"
|
||||
},
|
||||
"default_provider_enable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"device_flow": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
||||
@@ -1845,6 +1845,13 @@ func (q *querier) GetNotificationsSettings(ctx context.Context) (string, error)
|
||||
return q.db.GetNotificationsSettings(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) GetOAuth2GithubDefaultEligible(ctx context.Context) (bool, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceDeploymentConfig); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return q.db.GetOAuth2GithubDefaultEligible(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderApp, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceOauth2App); err != nil {
|
||||
return database.OAuth2ProviderApp{}, err
|
||||
@@ -4435,6 +4442,13 @@ func (q *querier) UpsertNotificationsSettings(ctx context.Context, value string)
|
||||
return q.db.UpsertNotificationsSettings(ctx, value)
|
||||
}
|
||||
|
||||
func (q *querier) UpsertOAuth2GithubDefaultEligible(ctx context.Context, eligible bool) error {
|
||||
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceDeploymentConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
return q.db.UpsertOAuth2GithubDefaultEligible(ctx, eligible)
|
||||
}
|
||||
|
||||
func (q *querier) UpsertOAuthSigningKey(ctx context.Context, value string) error {
|
||||
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceSystem); err != nil {
|
||||
return err
|
||||
|
||||
@@ -4405,6 +4405,12 @@ func (s *MethodTestSuite) TestSystemFunctions() {
|
||||
Value: "value",
|
||||
}).Asserts(rbac.ResourceSystem, policy.ActionUpdate)
|
||||
}))
|
||||
s.Run("GetOAuth2GithubDefaultEligible", s.Subtest(func(db database.Store, check *expects) {
|
||||
check.Args().Asserts(rbac.ResourceDeploymentConfig, policy.ActionRead).Errors(sql.ErrNoRows)
|
||||
}))
|
||||
s.Run("UpsertOAuth2GithubDefaultEligible", s.Subtest(func(db database.Store, check *expects) {
|
||||
check.Args(true).Asserts(rbac.ResourceDeploymentConfig, policy.ActionUpdate)
|
||||
}))
|
||||
}
|
||||
|
||||
func (s *MethodTestSuite) TestNotifications() {
|
||||
|
||||
@@ -254,6 +254,7 @@ type data struct {
|
||||
announcementBanners []byte
|
||||
healthSettings []byte
|
||||
notificationsSettings []byte
|
||||
oauth2GithubDefaultEligible *bool
|
||||
applicationName string
|
||||
logoURL string
|
||||
appSecurityKey string
|
||||
@@ -3515,6 +3516,16 @@ func (q *FakeQuerier) GetNotificationsSettings(_ context.Context) (string, error
|
||||
return string(q.notificationsSettings), nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetOAuth2GithubDefaultEligible(_ context.Context) (bool, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
if q.oauth2GithubDefaultEligible == nil {
|
||||
return false, sql.ErrNoRows
|
||||
}
|
||||
return *q.oauth2GithubDefaultEligible, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetOAuth2ProviderAppByID(_ context.Context, id uuid.UUID) (database.OAuth2ProviderApp, error) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
@@ -11154,6 +11165,14 @@ func (q *FakeQuerier) UpsertNotificationsSettings(_ context.Context, data string
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpsertOAuth2GithubDefaultEligible(_ context.Context, eligible bool) error {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
q.oauth2GithubDefaultEligible = &eligible
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpsertOAuthSigningKey(_ context.Context, value string) error {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
@@ -871,6 +871,13 @@ func (m queryMetricsStore) GetNotificationsSettings(ctx context.Context) (string
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetOAuth2GithubDefaultEligible(ctx context.Context) (bool, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetOAuth2GithubDefaultEligible(ctx)
|
||||
m.queryLatencies.WithLabelValues("GetOAuth2GithubDefaultEligible").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderApp, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetOAuth2ProviderAppByID(ctx, id)
|
||||
@@ -2817,6 +2824,13 @@ func (m queryMetricsStore) UpsertNotificationsSettings(ctx context.Context, valu
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) UpsertOAuth2GithubDefaultEligible(ctx context.Context, eligible bool) error {
|
||||
start := time.Now()
|
||||
r0 := m.s.UpsertOAuth2GithubDefaultEligible(ctx, eligible)
|
||||
m.queryLatencies.WithLabelValues("UpsertOAuth2GithubDefaultEligible").Observe(time.Since(start).Seconds())
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) UpsertOAuthSigningKey(ctx context.Context, value string) error {
|
||||
start := time.Now()
|
||||
r0 := m.s.UpsertOAuthSigningKey(ctx, value)
|
||||
|
||||
@@ -1762,6 +1762,21 @@ func (mr *MockStoreMockRecorder) GetNotificationsSettings(ctx any) *gomock.Call
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNotificationsSettings", reflect.TypeOf((*MockStore)(nil).GetNotificationsSettings), ctx)
|
||||
}
|
||||
|
||||
// GetOAuth2GithubDefaultEligible mocks base method.
|
||||
func (m *MockStore) GetOAuth2GithubDefaultEligible(ctx context.Context) (bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetOAuth2GithubDefaultEligible", ctx)
|
||||
ret0, _ := ret[0].(bool)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetOAuth2GithubDefaultEligible indicates an expected call of GetOAuth2GithubDefaultEligible.
|
||||
func (mr *MockStoreMockRecorder) GetOAuth2GithubDefaultEligible(ctx any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOAuth2GithubDefaultEligible", reflect.TypeOf((*MockStore)(nil).GetOAuth2GithubDefaultEligible), ctx)
|
||||
}
|
||||
|
||||
// GetOAuth2ProviderAppByID mocks base method.
|
||||
func (m *MockStore) GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderApp, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -5936,6 +5951,20 @@ func (mr *MockStoreMockRecorder) UpsertNotificationsSettings(ctx, value any) *go
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertNotificationsSettings", reflect.TypeOf((*MockStore)(nil).UpsertNotificationsSettings), ctx, value)
|
||||
}
|
||||
|
||||
// UpsertOAuth2GithubDefaultEligible mocks base method.
|
||||
func (m *MockStore) UpsertOAuth2GithubDefaultEligible(ctx context.Context, eligible bool) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpsertOAuth2GithubDefaultEligible", ctx, eligible)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UpsertOAuth2GithubDefaultEligible indicates an expected call of UpsertOAuth2GithubDefaultEligible.
|
||||
func (mr *MockStoreMockRecorder) UpsertOAuth2GithubDefaultEligible(ctx, eligible any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertOAuth2GithubDefaultEligible", reflect.TypeOf((*MockStore)(nil).UpsertOAuth2GithubDefaultEligible), ctx, eligible)
|
||||
}
|
||||
|
||||
// UpsertOAuthSigningKey mocks base method.
|
||||
func (m *MockStore) UpsertOAuthSigningKey(ctx context.Context, value string) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
@@ -185,6 +185,7 @@ type sqlcQuerier interface {
|
||||
GetNotificationTemplateByID(ctx context.Context, id uuid.UUID) (NotificationTemplate, error)
|
||||
GetNotificationTemplatesByKind(ctx context.Context, kind NotificationTemplateKind) ([]NotificationTemplate, error)
|
||||
GetNotificationsSettings(ctx context.Context) (string, error)
|
||||
GetOAuth2GithubDefaultEligible(ctx context.Context) (bool, error)
|
||||
GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderApp, error)
|
||||
GetOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderAppCode, error)
|
||||
GetOAuth2ProviderAppCodeByPrefix(ctx context.Context, secretPrefix []byte) (OAuth2ProviderAppCode, error)
|
||||
@@ -553,6 +554,7 @@ type sqlcQuerier interface {
|
||||
// Insert or update notification report generator logs with recent activity.
|
||||
UpsertNotificationReportGeneratorLog(ctx context.Context, arg UpsertNotificationReportGeneratorLogParams) error
|
||||
UpsertNotificationsSettings(ctx context.Context, value string) error
|
||||
UpsertOAuth2GithubDefaultEligible(ctx context.Context, eligible bool) error
|
||||
UpsertOAuthSigningKey(ctx context.Context, value string) error
|
||||
UpsertProvisionerDaemon(ctx context.Context, arg UpsertProvisionerDaemonParams) (ProvisionerDaemon, error)
|
||||
UpsertRuntimeConfig(ctx context.Context, arg UpsertRuntimeConfigParams) error
|
||||
|
||||
@@ -8100,6 +8100,23 @@ func (q *sqlQuerier) GetNotificationsSettings(ctx context.Context) (string, erro
|
||||
return notifications_settings, err
|
||||
}
|
||||
|
||||
const getOAuth2GithubDefaultEligible = `-- name: GetOAuth2GithubDefaultEligible :one
|
||||
SELECT
|
||||
CASE
|
||||
WHEN value = 'true' THEN TRUE
|
||||
ELSE FALSE
|
||||
END
|
||||
FROM site_configs
|
||||
WHERE key = 'oauth2_github_default_eligible'
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetOAuth2GithubDefaultEligible(ctx context.Context) (bool, error) {
|
||||
row := q.db.QueryRowContext(ctx, getOAuth2GithubDefaultEligible)
|
||||
var column_1 bool
|
||||
err := row.Scan(&column_1)
|
||||
return column_1, err
|
||||
}
|
||||
|
||||
const getOAuthSigningKey = `-- name: GetOAuthSigningKey :one
|
||||
SELECT value FROM site_configs WHERE key = 'oauth_signing_key'
|
||||
`
|
||||
@@ -8243,6 +8260,28 @@ func (q *sqlQuerier) UpsertNotificationsSettings(ctx context.Context, value stri
|
||||
return err
|
||||
}
|
||||
|
||||
const upsertOAuth2GithubDefaultEligible = `-- name: UpsertOAuth2GithubDefaultEligible :exec
|
||||
INSERT INTO site_configs (key, value)
|
||||
VALUES (
|
||||
'oauth2_github_default_eligible',
|
||||
CASE
|
||||
WHEN $1::bool THEN 'true'
|
||||
ELSE 'false'
|
||||
END
|
||||
)
|
||||
ON CONFLICT (key) DO UPDATE
|
||||
SET value = CASE
|
||||
WHEN $1::bool THEN 'true'
|
||||
ELSE 'false'
|
||||
END
|
||||
WHERE site_configs.key = 'oauth2_github_default_eligible'
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) UpsertOAuth2GithubDefaultEligible(ctx context.Context, eligible bool) error {
|
||||
_, err := q.db.ExecContext(ctx, upsertOAuth2GithubDefaultEligible, eligible)
|
||||
return err
|
||||
}
|
||||
|
||||
const upsertOAuthSigningKey = `-- name: UpsertOAuthSigningKey :exec
|
||||
INSERT INTO site_configs (key, value) VALUES ('oauth_signing_key', $1)
|
||||
ON CONFLICT (key) DO UPDATE set value = $1 WHERE site_configs.key = 'oauth_signing_key'
|
||||
|
||||
@@ -107,3 +107,27 @@ ON CONFLICT (key) DO UPDATE SET value = $2 WHERE site_configs.key = $1;
|
||||
DELETE FROM site_configs
|
||||
WHERE site_configs.key = $1;
|
||||
|
||||
-- name: GetOAuth2GithubDefaultEligible :one
|
||||
SELECT
|
||||
CASE
|
||||
WHEN value = 'true' THEN TRUE
|
||||
ELSE FALSE
|
||||
END
|
||||
FROM site_configs
|
||||
WHERE key = 'oauth2_github_default_eligible';
|
||||
|
||||
-- name: UpsertOAuth2GithubDefaultEligible :exec
|
||||
INSERT INTO site_configs (key, value)
|
||||
VALUES (
|
||||
'oauth2_github_default_eligible',
|
||||
CASE
|
||||
WHEN sqlc.arg(eligible)::bool THEN 'true'
|
||||
ELSE 'false'
|
||||
END
|
||||
)
|
||||
ON CONFLICT (key) DO UPDATE
|
||||
SET value = CASE
|
||||
WHEN sqlc.arg(eligible)::bool THEN 'true'
|
||||
ELSE 'false'
|
||||
END
|
||||
WHERE site_configs.key = 'oauth2_github_default_eligible';
|
||||
|
||||
+6
-1
@@ -765,6 +765,8 @@ type GithubOAuth2Config struct {
|
||||
AllowEveryone bool
|
||||
AllowOrganizations []string
|
||||
AllowTeams []GithubOAuth2Team
|
||||
|
||||
DefaultProviderConfigured bool
|
||||
}
|
||||
|
||||
func (c *GithubOAuth2Config) Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {
|
||||
@@ -806,7 +808,10 @@ func (api *API) userAuthMethods(rw http.ResponseWriter, r *http.Request) {
|
||||
Password: codersdk.AuthMethod{
|
||||
Enabled: !api.DeploymentValues.DisablePasswordAuth.Value(),
|
||||
},
|
||||
Github: codersdk.AuthMethod{Enabled: api.GithubOAuth2Config != nil},
|
||||
Github: codersdk.GithubAuthMethod{
|
||||
Enabled: api.GithubOAuth2Config != nil,
|
||||
DefaultProviderConfigured: api.GithubOAuth2Config != nil && api.GithubOAuth2Config.DefaultProviderConfigured,
|
||||
},
|
||||
OIDC: codersdk.OIDCAuthMethod{
|
||||
AuthMethod: codersdk.AuthMethod{Enabled: api.OIDCConfig != nil},
|
||||
SignInText: signInText,
|
||||
|
||||
+19
-8
@@ -503,14 +503,15 @@ type OAuth2Config struct {
|
||||
}
|
||||
|
||||
type OAuth2GithubConfig struct {
|
||||
ClientID serpent.String `json:"client_id" typescript:",notnull"`
|
||||
ClientSecret serpent.String `json:"client_secret" typescript:",notnull"`
|
||||
DeviceFlow serpent.Bool `json:"device_flow" typescript:",notnull"`
|
||||
AllowedOrgs serpent.StringArray `json:"allowed_orgs" typescript:",notnull"`
|
||||
AllowedTeams serpent.StringArray `json:"allowed_teams" typescript:",notnull"`
|
||||
AllowSignups serpent.Bool `json:"allow_signups" typescript:",notnull"`
|
||||
AllowEveryone serpent.Bool `json:"allow_everyone" typescript:",notnull"`
|
||||
EnterpriseBaseURL serpent.String `json:"enterprise_base_url" typescript:",notnull"`
|
||||
ClientID serpent.String `json:"client_id" typescript:",notnull"`
|
||||
ClientSecret serpent.String `json:"client_secret" typescript:",notnull"`
|
||||
DeviceFlow serpent.Bool `json:"device_flow" typescript:",notnull"`
|
||||
DefaultProviderEnable serpent.Bool `json:"default_provider_enable" typescript:",notnull"`
|
||||
AllowedOrgs serpent.StringArray `json:"allowed_orgs" typescript:",notnull"`
|
||||
AllowedTeams serpent.StringArray `json:"allowed_teams" typescript:",notnull"`
|
||||
AllowSignups serpent.Bool `json:"allow_signups" typescript:",notnull"`
|
||||
AllowEveryone serpent.Bool `json:"allow_everyone" typescript:",notnull"`
|
||||
EnterpriseBaseURL serpent.String `json:"enterprise_base_url" typescript:",notnull"`
|
||||
}
|
||||
|
||||
type OIDCConfig struct {
|
||||
@@ -1593,6 +1594,16 @@ func (c *DeploymentValues) Options() serpent.OptionSet {
|
||||
YAML: "deviceFlow",
|
||||
Default: "false",
|
||||
},
|
||||
{
|
||||
Name: "OAuth2 GitHub Default Provider Enable",
|
||||
Description: "Enable the default GitHub OAuth2 provider managed by Coder.",
|
||||
Flag: "oauth2-github-default-provider-enable",
|
||||
Env: "CODER_OAUTH2_GITHUB_DEFAULT_PROVIDER_ENABLE",
|
||||
Value: &c.OAuth2.Github.DefaultProviderEnable,
|
||||
Group: &deploymentGroupOAuth2GitHub,
|
||||
YAML: "defaultProviderEnable",
|
||||
Default: "true",
|
||||
},
|
||||
{
|
||||
Name: "OAuth2 GitHub Allowed Orgs",
|
||||
Description: "Organizations the user must be a member of to Login with GitHub.",
|
||||
|
||||
+9
-4
@@ -275,10 +275,10 @@ type OAuthConversionResponse struct {
|
||||
|
||||
// AuthMethods contains authentication method information like whether they are enabled or not or custom text, etc.
|
||||
type AuthMethods struct {
|
||||
TermsOfServiceURL string `json:"terms_of_service_url,omitempty"`
|
||||
Password AuthMethod `json:"password"`
|
||||
Github AuthMethod `json:"github"`
|
||||
OIDC OIDCAuthMethod `json:"oidc"`
|
||||
TermsOfServiceURL string `json:"terms_of_service_url,omitempty"`
|
||||
Password AuthMethod `json:"password"`
|
||||
Github GithubAuthMethod `json:"github"`
|
||||
OIDC OIDCAuthMethod `json:"oidc"`
|
||||
}
|
||||
|
||||
type AuthMethod struct {
|
||||
@@ -289,6 +289,11 @@ type UserLoginType struct {
|
||||
LoginType LoginType `json:"login_type"`
|
||||
}
|
||||
|
||||
type GithubAuthMethod struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
DefaultProviderConfigured bool `json:"default_provider_configured"`
|
||||
}
|
||||
|
||||
type OIDCAuthMethod struct {
|
||||
AuthMethod
|
||||
SignInText string `json:"signInText"`
|
||||
|
||||
Generated
+1
@@ -328,6 +328,7 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \
|
||||
],
|
||||
"client_id": "string",
|
||||
"client_secret": "string",
|
||||
"default_provider_enable": true,
|
||||
"device_flow": true,
|
||||
"enterprise_base_url": "string"
|
||||
}
|
||||
|
||||
Generated
+38
-16
@@ -787,6 +787,7 @@
|
||||
```json
|
||||
{
|
||||
"github": {
|
||||
"default_provider_configured": true,
|
||||
"enabled": true
|
||||
},
|
||||
"oidc": {
|
||||
@@ -803,12 +804,12 @@
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|------------------------|----------------------------------------------------|----------|--------------|-------------|
|
||||
| `github` | [codersdk.AuthMethod](#codersdkauthmethod) | false | | |
|
||||
| `oidc` | [codersdk.OIDCAuthMethod](#codersdkoidcauthmethod) | false | | |
|
||||
| `password` | [codersdk.AuthMethod](#codersdkauthmethod) | false | | |
|
||||
| `terms_of_service_url` | string | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|------------------------|--------------------------------------------------------|----------|--------------|-------------|
|
||||
| `github` | [codersdk.GithubAuthMethod](#codersdkgithubauthmethod) | false | | |
|
||||
| `oidc` | [codersdk.OIDCAuthMethod](#codersdkoidcauthmethod) | false | | |
|
||||
| `password` | [codersdk.AuthMethod](#codersdkauthmethod) | false | | |
|
||||
| `terms_of_service_url` | string | false | | |
|
||||
|
||||
## codersdk.AuthorizationCheck
|
||||
|
||||
@@ -1977,6 +1978,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
|
||||
],
|
||||
"client_id": "string",
|
||||
"client_secret": "string",
|
||||
"default_provider_enable": true,
|
||||
"device_flow": true,
|
||||
"enterprise_base_url": "string"
|
||||
}
|
||||
@@ -2449,6 +2451,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
|
||||
],
|
||||
"client_id": "string",
|
||||
"client_secret": "string",
|
||||
"default_provider_enable": true,
|
||||
"device_flow": true,
|
||||
"enterprise_base_url": "string"
|
||||
}
|
||||
@@ -3101,6 +3104,22 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
|
||||
| `updated_at` | string | false | | |
|
||||
| `user_id` | string | false | | |
|
||||
|
||||
## codersdk.GithubAuthMethod
|
||||
|
||||
```json
|
||||
{
|
||||
"default_provider_configured": true,
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-------------------------------|---------|----------|--------------|-------------|
|
||||
| `default_provider_configured` | boolean | false | | |
|
||||
| `enabled` | boolean | false | | |
|
||||
|
||||
## codersdk.Group
|
||||
|
||||
```json
|
||||
@@ -3807,6 +3826,7 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
|
||||
],
|
||||
"client_id": "string",
|
||||
"client_secret": "string",
|
||||
"default_provider_enable": true,
|
||||
"device_flow": true,
|
||||
"enterprise_base_url": "string"
|
||||
}
|
||||
@@ -3833,6 +3853,7 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
|
||||
],
|
||||
"client_id": "string",
|
||||
"client_secret": "string",
|
||||
"default_provider_enable": true,
|
||||
"device_flow": true,
|
||||
"enterprise_base_url": "string"
|
||||
}
|
||||
@@ -3840,16 +3861,17 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-----------------------|-----------------|----------|--------------|-------------|
|
||||
| `allow_everyone` | boolean | false | | |
|
||||
| `allow_signups` | boolean | false | | |
|
||||
| `allowed_orgs` | array of string | false | | |
|
||||
| `allowed_teams` | array of string | false | | |
|
||||
| `client_id` | string | false | | |
|
||||
| `client_secret` | string | false | | |
|
||||
| `device_flow` | boolean | false | | |
|
||||
| `enterprise_base_url` | string | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|---------------------------|-----------------|----------|--------------|-------------|
|
||||
| `allow_everyone` | boolean | false | | |
|
||||
| `allow_signups` | boolean | false | | |
|
||||
| `allowed_orgs` | array of string | false | | |
|
||||
| `allowed_teams` | array of string | false | | |
|
||||
| `client_id` | string | false | | |
|
||||
| `client_secret` | string | false | | |
|
||||
| `default_provider_enable` | boolean | false | | |
|
||||
| `device_flow` | boolean | false | | |
|
||||
| `enterprise_base_url` | string | false | | |
|
||||
|
||||
## codersdk.OAuth2ProviderApp
|
||||
|
||||
|
||||
Generated
+1
@@ -159,6 +159,7 @@ curl -X GET http://coder-server:8080/api/v2/users/authmethods \
|
||||
```json
|
||||
{
|
||||
"github": {
|
||||
"default_provider_configured": true,
|
||||
"enabled": true
|
||||
},
|
||||
"oidc": {
|
||||
|
||||
Generated
+11
@@ -373,6 +373,17 @@ Client secret for Login with GitHub.
|
||||
|
||||
Enable device flow for Login with GitHub.
|
||||
|
||||
### --oauth2-github-default-provider-enable
|
||||
|
||||
| | |
|
||||
|-------------|-----------------------------------------------------------|
|
||||
| Type | <code>bool</code> |
|
||||
| Environment | <code>$CODER_OAUTH2_GITHUB_DEFAULT_PROVIDER_ENABLE</code> |
|
||||
| YAML | <code>oauth2.github.defaultProviderEnable</code> |
|
||||
| Default | <code>true</code> |
|
||||
|
||||
Enable the default GitHub OAuth2 provider managed by Coder.
|
||||
|
||||
### --oauth2-github-allowed-orgs
|
||||
|
||||
| | |
|
||||
|
||||
@@ -499,6 +499,9 @@ OAUTH2 / GITHUB OPTIONS:
|
||||
--oauth2-github-client-secret string, $CODER_OAUTH2_GITHUB_CLIENT_SECRET
|
||||
Client secret for Login with GitHub.
|
||||
|
||||
--oauth2-github-default-provider-enable bool, $CODER_OAUTH2_GITHUB_DEFAULT_PROVIDER_ENABLE (default: true)
|
||||
Enable the default GitHub OAuth2 provider managed by Coder.
|
||||
|
||||
--oauth2-github-device-flow bool, $CODER_OAUTH2_GITHUB_DEVICE_FLOW (default: false)
|
||||
Enable device flow for Login with GitHub.
|
||||
|
||||
|
||||
Generated
+8
-1
@@ -200,7 +200,7 @@ export interface AuthMethod {
|
||||
export interface AuthMethods {
|
||||
readonly terms_of_service_url?: string;
|
||||
readonly password: AuthMethod;
|
||||
readonly github: AuthMethod;
|
||||
readonly github: GithubAuthMethod;
|
||||
readonly oidc: OIDCAuthMethod;
|
||||
}
|
||||
|
||||
@@ -916,6 +916,12 @@ export interface GitSSHKey {
|
||||
readonly public_key: string;
|
||||
}
|
||||
|
||||
// From codersdk/users.go
|
||||
export interface GithubAuthMethod {
|
||||
readonly enabled: boolean;
|
||||
readonly default_provider_configured: boolean;
|
||||
}
|
||||
|
||||
// From codersdk/groups.go
|
||||
export interface Group {
|
||||
readonly id: string;
|
||||
@@ -1326,6 +1332,7 @@ export interface OAuth2GithubConfig {
|
||||
readonly client_id: string;
|
||||
readonly client_secret: string;
|
||||
readonly device_flow: boolean;
|
||||
readonly default_provider_enable: boolean;
|
||||
readonly allowed_orgs: string;
|
||||
readonly allowed_teams: string;
|
||||
readonly allow_signups: boolean;
|
||||
|
||||
@@ -20,7 +20,7 @@ export const SigningIn: Story = {
|
||||
isSigningIn: true,
|
||||
authMethods: {
|
||||
password: { enabled: true },
|
||||
github: { enabled: true },
|
||||
github: { enabled: true, default_provider_configured: false },
|
||||
oidc: { enabled: false, signInText: "", iconUrl: "" },
|
||||
},
|
||||
},
|
||||
@@ -44,7 +44,7 @@ export const WithGithub: Story = {
|
||||
args: {
|
||||
authMethods: {
|
||||
password: { enabled: true },
|
||||
github: { enabled: true },
|
||||
github: { enabled: true, default_provider_configured: false },
|
||||
oidc: { enabled: false, signInText: "", iconUrl: "" },
|
||||
},
|
||||
},
|
||||
@@ -54,7 +54,7 @@ export const WithOIDC: Story = {
|
||||
args: {
|
||||
authMethods: {
|
||||
password: { enabled: true },
|
||||
github: { enabled: false },
|
||||
github: { enabled: false, default_provider_configured: false },
|
||||
oidc: { enabled: true, signInText: "", iconUrl: "" },
|
||||
},
|
||||
},
|
||||
@@ -64,7 +64,7 @@ export const WithOIDCWithoutPassword: Story = {
|
||||
args: {
|
||||
authMethods: {
|
||||
password: { enabled: false },
|
||||
github: { enabled: false },
|
||||
github: { enabled: false, default_provider_configured: false },
|
||||
oidc: { enabled: true, signInText: "", iconUrl: "" },
|
||||
},
|
||||
},
|
||||
@@ -74,7 +74,7 @@ export const WithoutAny: Story = {
|
||||
args: {
|
||||
authMethods: {
|
||||
password: { enabled: false },
|
||||
github: { enabled: false },
|
||||
github: { enabled: false, default_provider_configured: false },
|
||||
oidc: { enabled: false, signInText: "", iconUrl: "" },
|
||||
},
|
||||
},
|
||||
@@ -84,7 +84,7 @@ export const WithGithubAndOIDC: Story = {
|
||||
args: {
|
||||
authMethods: {
|
||||
password: { enabled: true },
|
||||
github: { enabled: true },
|
||||
github: { enabled: true, default_provider_configured: false },
|
||||
oidc: { enabled: true, signInText: "", iconUrl: "" },
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1684,20 +1684,20 @@ export const MockUserAgent = {
|
||||
|
||||
export const MockAuthMethodsPasswordOnly: TypesGen.AuthMethods = {
|
||||
password: { enabled: true },
|
||||
github: { enabled: false },
|
||||
github: { enabled: false, default_provider_configured: true },
|
||||
oidc: { enabled: false, signInText: "", iconUrl: "" },
|
||||
};
|
||||
|
||||
export const MockAuthMethodsPasswordTermsOfService: TypesGen.AuthMethods = {
|
||||
terms_of_service_url: "https://www.youtube.com/watch?v=C2f37Vb2NAE",
|
||||
password: { enabled: true },
|
||||
github: { enabled: false },
|
||||
github: { enabled: false, default_provider_configured: true },
|
||||
oidc: { enabled: false, signInText: "", iconUrl: "" },
|
||||
};
|
||||
|
||||
export const MockAuthMethodsExternal: TypesGen.AuthMethods = {
|
||||
password: { enabled: false },
|
||||
github: { enabled: true },
|
||||
github: { enabled: true, default_provider_configured: true },
|
||||
oidc: {
|
||||
enabled: true,
|
||||
signInText: "Google",
|
||||
@@ -1707,7 +1707,7 @@ export const MockAuthMethodsExternal: TypesGen.AuthMethods = {
|
||||
|
||||
export const MockAuthMethodsAll: TypesGen.AuthMethods = {
|
||||
password: { enabled: true },
|
||||
github: { enabled: true },
|
||||
github: { enabled: true, default_provider_configured: true },
|
||||
oidc: {
|
||||
enabled: true,
|
||||
signInText: "Google",
|
||||
|
||||
Reference in New Issue
Block a user