@@ -172,6 +172,17 @@ func createOIDCConfig(ctx context.Context, logger slog.Logger, vals *codersdk.De
172
172
groupAllowList [group ] = true
173
173
}
174
174
175
+ secondaryClaimsSrc := coderd .MergedClaimsSourceUserInfo
176
+ if ! vals .OIDC .IgnoreUserInfo && vals .OIDC .UserInfoFromAccessToken {
177
+ return nil , xerrors .Errorf ("to use 'oidc-access-token-claims', 'oidc-ignore-userinfo' must be set to 'false'" )
178
+ }
179
+ if vals .OIDC .IgnoreUserInfo {
180
+ secondaryClaimsSrc = coderd .MergedClaimsSourceNone
181
+ }
182
+ if vals .OIDC .UserInfoFromAccessToken {
183
+ secondaryClaimsSrc = coderd .MergedClaimsSourceAccessToken
184
+ }
185
+
175
186
return & coderd.OIDCConfig {
176
187
OAuth2Config : useCfg ,
177
188
Provider : oidcProvider ,
@@ -187,7 +198,7 @@ func createOIDCConfig(ctx context.Context, logger slog.Logger, vals *codersdk.De
187
198
NameField : vals .OIDC .NameField .String (),
188
199
EmailField : vals .OIDC .EmailField .String (),
189
200
AuthURLParams : vals .OIDC .AuthURLParams .Value ,
190
- IgnoreUserInfo : vals . OIDC . IgnoreUserInfo . Value () ,
201
+ SecondaryClaims : secondaryClaimsSrc ,
191
202
SignInText : vals .OIDC .SignInText .String (),
192
203
SignupsDisabledText : vals .OIDC .SignupsDisabledText .String (),
193
204
IconURL : vals .OIDC .IconURL .String (),
@@ -677,24 +688,6 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
677
688
}
678
689
}
679
690
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
691
// As OIDC clients can be confidential or public,
699
692
// we should only check for a client id being set.
700
693
// The underlying library handles the case of no
@@ -782,6 +775,20 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
782
775
return xerrors .Errorf ("set deployment id: %w" , err )
783
776
}
784
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
+
785
792
options .RuntimeConfig = runtimeconfig .NewManager ()
786
793
787
794
// This should be output before the logs start streaming.
@@ -1832,25 +1839,101 @@ func configureCAPool(tlsClientCAFile string, tlsConfig *tls.Config) error {
1832
1839
return nil
1833
1840
}
1834
1841
1835
- // TODO: convert the argument list to a struct, it's easy to mix up the order of the arguments
1836
- //
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
+
1837
1920
//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" )
1921
+ func configureGithubOAuth2 (instrument * promoauth.Factory , params * githubOAuth2ConfigParams ) (* coderd.GithubOAuth2Config , error ) {
1922
+ redirectURL , err := params . accessURL .Parse ("/api/v2/users/oauth2/github/callback" )
1840
1923
if err != nil {
1841
1924
return nil , xerrors .Errorf ("parse github oauth callback url: %w" , err )
1842
1925
}
1843
- if allowEveryone && len (allowOrgs ) > 0 {
1926
+ if params . allowEveryone && len (params . allowOrgs ) > 0 {
1844
1927
return nil , xerrors .New ("allow everyone and allowed orgs cannot be used together" )
1845
1928
}
1846
- if allowEveryone && len (rawTeams ) > 0 {
1929
+ if params . allowEveryone && len (params . rawTeams ) > 0 {
1847
1930
return nil , xerrors .New ("allow everyone and allowed teams cannot be used together" )
1848
1931
}
1849
- if ! allowEveryone && len (allowOrgs ) == 0 {
1932
+ if ! params . allowEveryone && len (params . allowOrgs ) == 0 {
1850
1933
return nil , xerrors .New ("allowed orgs is empty: must specify at least one org or allow everyone" )
1851
1934
}
1852
- allowTeams := make ([]coderd.GithubOAuth2Team , 0 , len (rawTeams ))
1853
- for _ , rawTeam := range rawTeams {
1935
+ allowTeams := make ([]coderd.GithubOAuth2Team , 0 , len (params . rawTeams ))
1936
+ for _ , rawTeam := range params . rawTeams {
1854
1937
parts := strings .SplitN (rawTeam , "/" , 2 )
1855
1938
if len (parts ) != 2 {
1856
1939
return nil , xerrors .Errorf ("github team allowlist is formatted incorrectly. got %s; wanted <organization>/<team>" , rawTeam )
@@ -1862,8 +1945,8 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1862
1945
}
1863
1946
1864
1947
endpoint := xgithub .Endpoint
1865
- if enterpriseBaseURL != "" {
1866
- enterpriseURL , err := url .Parse (enterpriseBaseURL )
1948
+ if params . enterpriseBaseURL != "" {
1949
+ enterpriseURL , err := url .Parse (params . enterpriseBaseURL )
1867
1950
if err != nil {
1868
1951
return nil , xerrors .Errorf ("parse enterprise base url: %w" , err )
1869
1952
}
@@ -1882,8 +1965,8 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1882
1965
}
1883
1966
1884
1967
instrumentedOauth := instrument .NewGithub ("github-login" , & oauth2.Config {
1885
- ClientID : clientID ,
1886
- ClientSecret : clientSecret ,
1968
+ ClientID : params . clientID ,
1969
+ ClientSecret : params . clientSecret ,
1887
1970
Endpoint : endpoint ,
1888
1971
RedirectURL : redirectURL .String (),
1889
1972
Scopes : []string {
@@ -1895,17 +1978,17 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1895
1978
1896
1979
createClient := func (client * http.Client , source promoauth.Oauth2Source ) (* github.Client , error ) {
1897
1980
client = instrumentedOauth .InstrumentHTTPClient (client , source )
1898
- if enterpriseBaseURL != "" {
1899
- return github .NewEnterpriseClient (enterpriseBaseURL , "" , client )
1981
+ if params . enterpriseBaseURL != "" {
1982
+ return github .NewEnterpriseClient (params . enterpriseBaseURL , "" , client )
1900
1983
}
1901
1984
return github .NewClient (client ), nil
1902
1985
}
1903
1986
1904
1987
var deviceAuth * externalauth.DeviceAuth
1905
- if deviceFlow {
1988
+ if params . deviceFlow {
1906
1989
deviceAuth = & externalauth.DeviceAuth {
1907
1990
Config : instrumentedOauth ,
1908
- ClientID : clientID ,
1991
+ ClientID : params . clientID ,
1909
1992
TokenURL : endpoint .TokenURL ,
1910
1993
Scopes : []string {"read:user" , "read:org" , "user:email" },
1911
1994
CodeURL : endpoint .DeviceAuthURL ,
@@ -1914,9 +1997,9 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1914
1997
1915
1998
return & coderd.GithubOAuth2Config {
1916
1999
OAuth2Config : instrumentedOauth ,
1917
- AllowSignups : allowSignups ,
1918
- AllowEveryone : allowEveryone ,
1919
- AllowOrganizations : allowOrgs ,
2000
+ AllowSignups : params . allowSignups ,
2001
+ AllowEveryone : params . allowEveryone ,
2002
+ AllowOrganizations : params . allowOrgs ,
1920
2003
AllowTeams : allowTeams ,
1921
2004
AuthenticatedUser : func (ctx context.Context , client * http.Client ) (* github.User , error ) {
1922
2005
api , err := createClient (client , promoauth .SourceGitAPIAuthUser )
@@ -1955,19 +2038,20 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1955
2038
team , _ , err := api .Teams .GetTeamMembershipBySlug (ctx , org , teamSlug , username )
1956
2039
return team , err
1957
2040
},
1958
- DeviceFlowEnabled : deviceFlow ,
2041
+ DeviceFlowEnabled : params . deviceFlow ,
1959
2042
ExchangeDeviceCode : func (ctx context.Context , deviceCode string ) (* oauth2.Token , error ) {
1960
- if ! deviceFlow {
2043
+ if ! params . deviceFlow {
1961
2044
return nil , xerrors .New ("device flow is not enabled" )
1962
2045
}
1963
2046
return deviceAuth .ExchangeDeviceCode (ctx , deviceCode )
1964
2047
},
1965
2048
AuthorizeDevice : func (ctx context.Context ) (* codersdk.ExternalAuthDevice , error ) {
1966
- if ! deviceFlow {
2049
+ if ! params . deviceFlow {
1967
2050
return nil , xerrors .New ("device flow is not enabled" )
1968
2051
}
1969
2052
return deviceAuth .AuthorizeDevice (ctx )
1970
2053
},
2054
+ DefaultProviderConfigured : params .clientID == GithubOAuth2DefaultProviderClientID ,
1971
2055
}, nil
1972
2056
}
1973
2057
0 commit comments