@@ -677,24 +677,6 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
677
677
}
678
678
}
679
679
680
- if vals .OAuth2 .Github .ClientSecret != "" || vals .OAuth2 .Github .DeviceFlow .Value () {
681
- options .GithubOAuth2Config , err = configureGithubOAuth2 (
682
- oauthInstrument ,
683
- vals .AccessURL .Value (),
684
- vals .OAuth2 .Github .ClientID .String (),
685
- vals .OAuth2 .Github .ClientSecret .String (),
686
- vals .OAuth2 .Github .DeviceFlow .Value (),
687
- vals .OAuth2 .Github .AllowSignups .Value (),
688
- vals .OAuth2 .Github .AllowEveryone .Value (),
689
- vals .OAuth2 .Github .AllowedOrgs ,
690
- vals .OAuth2 .Github .AllowedTeams ,
691
- vals .OAuth2 .Github .EnterpriseBaseURL .String (),
692
- )
693
- if err != nil {
694
- return xerrors .Errorf ("configure github oauth2: %w" , err )
695
- }
696
- }
697
-
698
680
// As OIDC clients can be confidential or public,
699
681
// we should only check for a client id being set.
700
682
// The underlying library handles the case of no
@@ -782,6 +764,20 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
782
764
return xerrors .Errorf ("set deployment id: %w" , err )
783
765
}
784
766
767
+ githubOAuth2ConfigParams , err := getGithubOAuth2ConfigParams (ctx , options .Database , vals )
768
+ if err != nil {
769
+ return xerrors .Errorf ("get github oauth2 config params: %w" , err )
770
+ }
771
+ if githubOAuth2ConfigParams != nil {
772
+ options .GithubOAuth2Config , err = configureGithubOAuth2 (
773
+ oauthInstrument ,
774
+ githubOAuth2ConfigParams ,
775
+ )
776
+ if err != nil {
777
+ return xerrors .Errorf ("configure github oauth2: %w" , err )
778
+ }
779
+ }
780
+
785
781
options .RuntimeConfig = runtimeconfig .NewManager ()
786
782
787
783
// This should be output before the logs start streaming.
@@ -1832,25 +1828,101 @@ func configureCAPool(tlsClientCAFile string, tlsConfig *tls.Config) error {
1832
1828
return nil
1833
1829
}
1834
1830
1835
- // TODO: convert the argument list to a struct, it's easy to mix up the order of the arguments
1836
- //
1831
+ const (
1832
+ // TODO: before merging, change this to use Coder's client ID instead of my dev one.
1833
+ GithubOAuth2DefaultProviderClientID = "Iv23liSBHklRMBNx5lk9"
1834
+ GithubOAuth2DefaultProviderAllowEveryone = true
1835
+ GithubOAuth2DefaultProviderDeviceFlow = true
1836
+ )
1837
+
1838
+ type githubOAuth2ConfigParams struct {
1839
+ accessURL * url.URL
1840
+ clientID string
1841
+ clientSecret string
1842
+ deviceFlow bool
1843
+ allowSignups bool
1844
+ allowEveryone bool
1845
+ allowOrgs []string
1846
+ rawTeams []string
1847
+ enterpriseBaseURL string
1848
+ }
1849
+
1850
+ func getGithubOAuth2ConfigParams (ctx context.Context , db database.Store , vals * codersdk.DeploymentValues ) (* githubOAuth2ConfigParams , error ) {
1851
+ params := githubOAuth2ConfigParams {
1852
+ accessURL : vals .AccessURL .Value (),
1853
+ clientID : vals .OAuth2 .Github .ClientID .String (),
1854
+ clientSecret : vals .OAuth2 .Github .ClientSecret .String (),
1855
+ deviceFlow : vals .OAuth2 .Github .DeviceFlow .Value (),
1856
+ allowSignups : vals .OAuth2 .Github .AllowSignups .Value (),
1857
+ allowEveryone : vals .OAuth2 .Github .AllowEveryone .Value (),
1858
+ allowOrgs : vals .OAuth2 .Github .AllowedOrgs .Value (),
1859
+ rawTeams : vals .OAuth2 .Github .AllowedTeams .Value (),
1860
+ enterpriseBaseURL : vals .OAuth2 .Github .EnterpriseBaseURL .String (),
1861
+ }
1862
+
1863
+ // If the user manually configured the GitHub OAuth2 provider,
1864
+ // we won't add the default configuration.
1865
+ if params .clientID != "" || params .clientSecret != "" || params .enterpriseBaseURL != "" {
1866
+ return & params , nil
1867
+ }
1868
+
1869
+ // Check if the user manually disabled the default GitHub OAuth2 provider.
1870
+ if ! vals .OAuth2 .Github .DefaultProviderEnable .Value () {
1871
+ return nil , nil //nolint:nilnil
1872
+ }
1873
+
1874
+ // Check if the deployment is eligible for the default GitHub OAuth2 provider.
1875
+ // We want to enable it only for new deployments, and avoid enabling it
1876
+ // if a deployment was upgraded from an older version.
1877
+ // nolint:gocritic // Requires system privileges
1878
+ defaultEligible , err := db .GetOAuth2GithubDefaultEligible (dbauthz .AsSystemRestricted (ctx ))
1879
+ if err != nil && ! errors .Is (err , sql .ErrNoRows ) {
1880
+ return nil , xerrors .Errorf ("get github default eligible: %w" , err )
1881
+ }
1882
+ defaultEligibleNotSet := errors .Is (err , sql .ErrNoRows )
1883
+
1884
+ if defaultEligibleNotSet {
1885
+ // nolint:gocritic // User count requires system privileges
1886
+ userCount , err := db .GetUserCount (dbauthz .AsSystemRestricted (ctx ))
1887
+ if err != nil {
1888
+ return nil , xerrors .Errorf ("get user count: %w" , err )
1889
+ }
1890
+ // We check if a deployment is new by checking if it has any users.
1891
+ defaultEligible = userCount == 0
1892
+ // nolint:gocritic // Requires system privileges
1893
+ if err := db .UpsertOAuth2GithubDefaultEligible (dbauthz .AsSystemRestricted (ctx ), defaultEligible ); err != nil {
1894
+ return nil , xerrors .Errorf ("upsert github default eligible: %w" , err )
1895
+ }
1896
+ }
1897
+
1898
+ if ! defaultEligible {
1899
+ return nil , nil //nolint:nilnil
1900
+ }
1901
+
1902
+ params .clientID = GithubOAuth2DefaultProviderClientID
1903
+ params .allowEveryone = GithubOAuth2DefaultProviderAllowEveryone
1904
+ params .deviceFlow = GithubOAuth2DefaultProviderDeviceFlow
1905
+
1906
+ return & params , nil
1907
+ }
1908
+
1837
1909
//nolint:revive // Ignore flag-parameter: parameter 'allowEveryone' seems to be a control flag, avoid control coupling (revive)
1838
- func configureGithubOAuth2 (instrument * promoauth.Factory , accessURL * url. URL , clientID , clientSecret string , deviceFlow , allowSignups , allowEveryone bool , allowOrgs [] string , rawTeams [] string , enterpriseBaseURL string ) (* coderd.GithubOAuth2Config , error ) {
1839
- redirectURL , err := accessURL .Parse ("/api/v2/users/oauth2/github/callback" )
1910
+ func configureGithubOAuth2 (instrument * promoauth.Factory , params * githubOAuth2ConfigParams ) (* coderd.GithubOAuth2Config , error ) {
1911
+ redirectURL , err := params . accessURL .Parse ("/api/v2/users/oauth2/github/callback" )
1840
1912
if err != nil {
1841
1913
return nil , xerrors .Errorf ("parse github oauth callback url: %w" , err )
1842
1914
}
1843
- if allowEveryone && len (allowOrgs ) > 0 {
1915
+ if params . allowEveryone && len (params . allowOrgs ) > 0 {
1844
1916
return nil , xerrors .New ("allow everyone and allowed orgs cannot be used together" )
1845
1917
}
1846
- if allowEveryone && len (rawTeams ) > 0 {
1918
+ if params . allowEveryone && len (params . rawTeams ) > 0 {
1847
1919
return nil , xerrors .New ("allow everyone and allowed teams cannot be used together" )
1848
1920
}
1849
- if ! allowEveryone && len (allowOrgs ) == 0 {
1921
+ if ! params . allowEveryone && len (params . allowOrgs ) == 0 {
1850
1922
return nil , xerrors .New ("allowed orgs is empty: must specify at least one org or allow everyone" )
1851
1923
}
1852
- allowTeams := make ([]coderd.GithubOAuth2Team , 0 , len (rawTeams ))
1853
- for _ , rawTeam := range rawTeams {
1924
+ allowTeams := make ([]coderd.GithubOAuth2Team , 0 , len (params . rawTeams ))
1925
+ for _ , rawTeam := range params . rawTeams {
1854
1926
parts := strings .SplitN (rawTeam , "/" , 2 )
1855
1927
if len (parts ) != 2 {
1856
1928
return nil , xerrors .Errorf ("github team allowlist is formatted incorrectly. got %s; wanted <organization>/<team>" , rawTeam )
@@ -1862,8 +1934,8 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1862
1934
}
1863
1935
1864
1936
endpoint := xgithub .Endpoint
1865
- if enterpriseBaseURL != "" {
1866
- enterpriseURL , err := url .Parse (enterpriseBaseURL )
1937
+ if params . enterpriseBaseURL != "" {
1938
+ enterpriseURL , err := url .Parse (params . enterpriseBaseURL )
1867
1939
if err != nil {
1868
1940
return nil , xerrors .Errorf ("parse enterprise base url: %w" , err )
1869
1941
}
@@ -1882,8 +1954,8 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1882
1954
}
1883
1955
1884
1956
instrumentedOauth := instrument .NewGithub ("github-login" , & oauth2.Config {
1885
- ClientID : clientID ,
1886
- ClientSecret : clientSecret ,
1957
+ ClientID : params . clientID ,
1958
+ ClientSecret : params . clientSecret ,
1887
1959
Endpoint : endpoint ,
1888
1960
RedirectURL : redirectURL .String (),
1889
1961
Scopes : []string {
@@ -1895,17 +1967,17 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1895
1967
1896
1968
createClient := func (client * http.Client , source promoauth.Oauth2Source ) (* github.Client , error ) {
1897
1969
client = instrumentedOauth .InstrumentHTTPClient (client , source )
1898
- if enterpriseBaseURL != "" {
1899
- return github .NewEnterpriseClient (enterpriseBaseURL , "" , client )
1970
+ if params . enterpriseBaseURL != "" {
1971
+ return github .NewEnterpriseClient (params . enterpriseBaseURL , "" , client )
1900
1972
}
1901
1973
return github .NewClient (client ), nil
1902
1974
}
1903
1975
1904
1976
var deviceAuth * externalauth.DeviceAuth
1905
- if deviceFlow {
1977
+ if params . deviceFlow {
1906
1978
deviceAuth = & externalauth.DeviceAuth {
1907
1979
Config : instrumentedOauth ,
1908
- ClientID : clientID ,
1980
+ ClientID : params . clientID ,
1909
1981
TokenURL : endpoint .TokenURL ,
1910
1982
Scopes : []string {"read:user" , "read:org" , "user:email" },
1911
1983
CodeURL : endpoint .DeviceAuthURL ,
@@ -1914,9 +1986,9 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1914
1986
1915
1987
return & coderd.GithubOAuth2Config {
1916
1988
OAuth2Config : instrumentedOauth ,
1917
- AllowSignups : allowSignups ,
1918
- AllowEveryone : allowEveryone ,
1919
- AllowOrganizations : allowOrgs ,
1989
+ AllowSignups : params . allowSignups ,
1990
+ AllowEveryone : params . allowEveryone ,
1991
+ AllowOrganizations : params . allowOrgs ,
1920
1992
AllowTeams : allowTeams ,
1921
1993
AuthenticatedUser : func (ctx context.Context , client * http.Client ) (* github.User , error ) {
1922
1994
api , err := createClient (client , promoauth .SourceGitAPIAuthUser )
@@ -1955,19 +2027,20 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1955
2027
team , _ , err := api .Teams .GetTeamMembershipBySlug (ctx , org , teamSlug , username )
1956
2028
return team , err
1957
2029
},
1958
- DeviceFlowEnabled : deviceFlow ,
2030
+ DeviceFlowEnabled : params . deviceFlow ,
1959
2031
ExchangeDeviceCode : func (ctx context.Context , deviceCode string ) (* oauth2.Token , error ) {
1960
- if ! deviceFlow {
2032
+ if ! params . deviceFlow {
1961
2033
return nil , xerrors .New ("device flow is not enabled" )
1962
2034
}
1963
2035
return deviceAuth .ExchangeDeviceCode (ctx , deviceCode )
1964
2036
},
1965
2037
AuthorizeDevice : func (ctx context.Context ) (* codersdk.ExternalAuthDevice , error ) {
1966
- if ! deviceFlow {
2038
+ if ! params . deviceFlow {
1967
2039
return nil , xerrors .New ("device flow is not enabled" )
1968
2040
}
1969
2041
return deviceAuth .AuthorizeDevice (ctx )
1970
2042
},
2043
+ DefaultProviderConfigured : params .clientID == GithubOAuth2DefaultProviderClientID ,
1971
2044
}, nil
1972
2045
}
1973
2046
0 commit comments