@@ -688,24 +688,6 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
688
688
}
689
689
}
690
690
691
- if vals .OAuth2 .Github .ClientSecret != "" || vals .OAuth2 .Github .DeviceFlow .Value () {
692
- options .GithubOAuth2Config , err = configureGithubOAuth2 (
693
- oauthInstrument ,
694
- vals .AccessURL .Value (),
695
- vals .OAuth2 .Github .ClientID .String (),
696
- vals .OAuth2 .Github .ClientSecret .String (),
697
- vals .OAuth2 .Github .DeviceFlow .Value (),
698
- vals .OAuth2 .Github .AllowSignups .Value (),
699
- vals .OAuth2 .Github .AllowEveryone .Value (),
700
- vals .OAuth2 .Github .AllowedOrgs ,
701
- vals .OAuth2 .Github .AllowedTeams ,
702
- vals .OAuth2 .Github .EnterpriseBaseURL .String (),
703
- )
704
- if err != nil {
705
- return xerrors .Errorf ("configure github oauth2: %w" , err )
706
- }
707
- }
708
-
709
691
// As OIDC clients can be confidential or public,
710
692
// we should only check for a client id being set.
711
693
// The underlying library handles the case of no
@@ -793,6 +775,20 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
793
775
return xerrors .Errorf ("set deployment id: %w" , err )
794
776
}
795
777
778
+ githubOAuth2ConfigParams , err := getGithubOAuth2ConfigParams (ctx , options .Database , vals )
779
+ if err != nil {
780
+ return xerrors .Errorf ("get github oauth2 config params: %w" , err )
781
+ }
782
+ if githubOAuth2ConfigParams != nil {
783
+ options .GithubOAuth2Config , err = configureGithubOAuth2 (
784
+ oauthInstrument ,
785
+ githubOAuth2ConfigParams ,
786
+ )
787
+ if err != nil {
788
+ return xerrors .Errorf ("configure github oauth2: %w" , err )
789
+ }
790
+ }
791
+
796
792
options .RuntimeConfig = runtimeconfig .NewManager ()
797
793
798
794
// This should be output before the logs start streaming.
@@ -1843,25 +1839,101 @@ func configureCAPool(tlsClientCAFile string, tlsConfig *tls.Config) error {
1843
1839
return nil
1844
1840
}
1845
1841
1846
- // TODO: convert the argument list to a struct, it's easy to mix up the order of the arguments
1847
- //
1842
+ const (
1843
+ // Client ID for https://github.com/apps/coder
1844
+ GithubOAuth2DefaultProviderClientID = "Iv1.6a2b4b4aec4f4fe7"
1845
+ GithubOAuth2DefaultProviderAllowEveryone = true
1846
+ GithubOAuth2DefaultProviderDeviceFlow = true
1847
+ )
1848
+
1849
+ type githubOAuth2ConfigParams struct {
1850
+ accessURL * url.URL
1851
+ clientID string
1852
+ clientSecret string
1853
+ deviceFlow bool
1854
+ allowSignups bool
1855
+ allowEveryone bool
1856
+ allowOrgs []string
1857
+ rawTeams []string
1858
+ enterpriseBaseURL string
1859
+ }
1860
+
1861
+ func getGithubOAuth2ConfigParams (ctx context.Context , db database.Store , vals * codersdk.DeploymentValues ) (* githubOAuth2ConfigParams , error ) {
1862
+ params := githubOAuth2ConfigParams {
1863
+ accessURL : vals .AccessURL .Value (),
1864
+ clientID : vals .OAuth2 .Github .ClientID .String (),
1865
+ clientSecret : vals .OAuth2 .Github .ClientSecret .String (),
1866
+ deviceFlow : vals .OAuth2 .Github .DeviceFlow .Value (),
1867
+ allowSignups : vals .OAuth2 .Github .AllowSignups .Value (),
1868
+ allowEveryone : vals .OAuth2 .Github .AllowEveryone .Value (),
1869
+ allowOrgs : vals .OAuth2 .Github .AllowedOrgs .Value (),
1870
+ rawTeams : vals .OAuth2 .Github .AllowedTeams .Value (),
1871
+ enterpriseBaseURL : vals .OAuth2 .Github .EnterpriseBaseURL .String (),
1872
+ }
1873
+
1874
+ // If the user manually configured the GitHub OAuth2 provider,
1875
+ // we won't add the default configuration.
1876
+ if params .clientID != "" || params .clientSecret != "" || params .enterpriseBaseURL != "" {
1877
+ return & params , nil
1878
+ }
1879
+
1880
+ // Check if the user manually disabled the default GitHub OAuth2 provider.
1881
+ if ! vals .OAuth2 .Github .DefaultProviderEnable .Value () {
1882
+ return nil , nil //nolint:nilnil
1883
+ }
1884
+
1885
+ // Check if the deployment is eligible for the default GitHub OAuth2 provider.
1886
+ // We want to enable it only for new deployments, and avoid enabling it
1887
+ // if a deployment was upgraded from an older version.
1888
+ // nolint:gocritic // Requires system privileges
1889
+ defaultEligible , err := db .GetOAuth2GithubDefaultEligible (dbauthz .AsSystemRestricted (ctx ))
1890
+ if err != nil && ! errors .Is (err , sql .ErrNoRows ) {
1891
+ return nil , xerrors .Errorf ("get github default eligible: %w" , err )
1892
+ }
1893
+ defaultEligibleNotSet := errors .Is (err , sql .ErrNoRows )
1894
+
1895
+ if defaultEligibleNotSet {
1896
+ // nolint:gocritic // User count requires system privileges
1897
+ userCount , err := db .GetUserCount (dbauthz .AsSystemRestricted (ctx ))
1898
+ if err != nil {
1899
+ return nil , xerrors .Errorf ("get user count: %w" , err )
1900
+ }
1901
+ // We check if a deployment is new by checking if it has any users.
1902
+ defaultEligible = userCount == 0
1903
+ // nolint:gocritic // Requires system privileges
1904
+ if err := db .UpsertOAuth2GithubDefaultEligible (dbauthz .AsSystemRestricted (ctx ), defaultEligible ); err != nil {
1905
+ return nil , xerrors .Errorf ("upsert github default eligible: %w" , err )
1906
+ }
1907
+ }
1908
+
1909
+ if ! defaultEligible {
1910
+ return nil , nil //nolint:nilnil
1911
+ }
1912
+
1913
+ params .clientID = GithubOAuth2DefaultProviderClientID
1914
+ params .allowEveryone = GithubOAuth2DefaultProviderAllowEveryone
1915
+ params .deviceFlow = GithubOAuth2DefaultProviderDeviceFlow
1916
+
1917
+ return & params , nil
1918
+ }
1919
+
1848
1920
//nolint:revive // Ignore flag-parameter: parameter 'allowEveryone' seems to be a control flag, avoid control coupling (revive)
1849
- func configureGithubOAuth2 (instrument * promoauth.Factory , accessURL * url. URL , clientID , clientSecret string , deviceFlow , allowSignups , allowEveryone bool , allowOrgs [] string , rawTeams [] string , enterpriseBaseURL string ) (* coderd.GithubOAuth2Config , error ) {
1850
- redirectURL , err := accessURL .Parse ("/api/v2/users/oauth2/github/callback" )
1921
+ func configureGithubOAuth2 (instrument * promoauth.Factory , params * githubOAuth2ConfigParams ) (* coderd.GithubOAuth2Config , error ) {
1922
+ redirectURL , err := params . accessURL .Parse ("/api/v2/users/oauth2/github/callback" )
1851
1923
if err != nil {
1852
1924
return nil , xerrors .Errorf ("parse github oauth callback url: %w" , err )
1853
1925
}
1854
- if allowEveryone && len (allowOrgs ) > 0 {
1926
+ if params . allowEveryone && len (params . allowOrgs ) > 0 {
1855
1927
return nil , xerrors .New ("allow everyone and allowed orgs cannot be used together" )
1856
1928
}
1857
- if allowEveryone && len (rawTeams ) > 0 {
1929
+ if params . allowEveryone && len (params . rawTeams ) > 0 {
1858
1930
return nil , xerrors .New ("allow everyone and allowed teams cannot be used together" )
1859
1931
}
1860
- if ! allowEveryone && len (allowOrgs ) == 0 {
1932
+ if ! params . allowEveryone && len (params . allowOrgs ) == 0 {
1861
1933
return nil , xerrors .New ("allowed orgs is empty: must specify at least one org or allow everyone" )
1862
1934
}
1863
- allowTeams := make ([]coderd.GithubOAuth2Team , 0 , len (rawTeams ))
1864
- for _ , rawTeam := range rawTeams {
1935
+ allowTeams := make ([]coderd.GithubOAuth2Team , 0 , len (params . rawTeams ))
1936
+ for _ , rawTeam := range params . rawTeams {
1865
1937
parts := strings .SplitN (rawTeam , "/" , 2 )
1866
1938
if len (parts ) != 2 {
1867
1939
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
1873
1945
}
1874
1946
1875
1947
endpoint := xgithub .Endpoint
1876
- if enterpriseBaseURL != "" {
1877
- enterpriseURL , err := url .Parse (enterpriseBaseURL )
1948
+ if params . enterpriseBaseURL != "" {
1949
+ enterpriseURL , err := url .Parse (params . enterpriseBaseURL )
1878
1950
if err != nil {
1879
1951
return nil , xerrors .Errorf ("parse enterprise base url: %w" , err )
1880
1952
}
@@ -1893,8 +1965,8 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1893
1965
}
1894
1966
1895
1967
instrumentedOauth := instrument .NewGithub ("github-login" , & oauth2.Config {
1896
- ClientID : clientID ,
1897
- ClientSecret : clientSecret ,
1968
+ ClientID : params . clientID ,
1969
+ ClientSecret : params . clientSecret ,
1898
1970
Endpoint : endpoint ,
1899
1971
RedirectURL : redirectURL .String (),
1900
1972
Scopes : []string {
@@ -1906,17 +1978,17 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1906
1978
1907
1979
createClient := func (client * http.Client , source promoauth.Oauth2Source ) (* github.Client , error ) {
1908
1980
client = instrumentedOauth .InstrumentHTTPClient (client , source )
1909
- if enterpriseBaseURL != "" {
1910
- return github .NewEnterpriseClient (enterpriseBaseURL , "" , client )
1981
+ if params . enterpriseBaseURL != "" {
1982
+ return github .NewEnterpriseClient (params . enterpriseBaseURL , "" , client )
1911
1983
}
1912
1984
return github .NewClient (client ), nil
1913
1985
}
1914
1986
1915
1987
var deviceAuth * externalauth.DeviceAuth
1916
- if deviceFlow {
1988
+ if params . deviceFlow {
1917
1989
deviceAuth = & externalauth.DeviceAuth {
1918
1990
Config : instrumentedOauth ,
1919
- ClientID : clientID ,
1991
+ ClientID : params . clientID ,
1920
1992
TokenURL : endpoint .TokenURL ,
1921
1993
Scopes : []string {"read:user" , "read:org" , "user:email" },
1922
1994
CodeURL : endpoint .DeviceAuthURL ,
@@ -1925,9 +1997,9 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1925
1997
1926
1998
return & coderd.GithubOAuth2Config {
1927
1999
OAuth2Config : instrumentedOauth ,
1928
- AllowSignups : allowSignups ,
1929
- AllowEveryone : allowEveryone ,
1930
- AllowOrganizations : allowOrgs ,
2000
+ AllowSignups : params . allowSignups ,
2001
+ AllowEveryone : params . allowEveryone ,
2002
+ AllowOrganizations : params . allowOrgs ,
1931
2003
AllowTeams : allowTeams ,
1932
2004
AuthenticatedUser : func (ctx context.Context , client * http.Client ) (* github.User , error ) {
1933
2005
api , err := createClient (client , promoauth .SourceGitAPIAuthUser )
@@ -1966,19 +2038,20 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1966
2038
team , _ , err := api .Teams .GetTeamMembershipBySlug (ctx , org , teamSlug , username )
1967
2039
return team , err
1968
2040
},
1969
- DeviceFlowEnabled : deviceFlow ,
2041
+ DeviceFlowEnabled : params . deviceFlow ,
1970
2042
ExchangeDeviceCode : func (ctx context.Context , deviceCode string ) (* oauth2.Token , error ) {
1971
- if ! deviceFlow {
2043
+ if ! params . deviceFlow {
1972
2044
return nil , xerrors .New ("device flow is not enabled" )
1973
2045
}
1974
2046
return deviceAuth .ExchangeDeviceCode (ctx , deviceCode )
1975
2047
},
1976
2048
AuthorizeDevice : func (ctx context.Context ) (* codersdk.ExternalAuthDevice , error ) {
1977
- if ! deviceFlow {
2049
+ if ! params . deviceFlow {
1978
2050
return nil , xerrors .New ("device flow is not enabled" )
1979
2051
}
1980
2052
return deviceAuth .AuthorizeDevice (ctx )
1981
2053
},
2054
+ DefaultProviderConfigured : params .clientID == GithubOAuth2DefaultProviderClientID ,
1982
2055
}, nil
1983
2056
}
1984
2057
0 commit comments