diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 750cc20998..bcd2b3b15c 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -9296,9 +9296,6 @@ const docTemplate = `{ "disable_path_apps": { "type": "boolean" }, - "disable_session_expiry_refresh": { - "type": "boolean" - }, "docs_url": { "$ref": "#/definitions/serpent.URL" }, @@ -9336,12 +9333,6 @@ const docTemplate = `{ "logging": { "$ref": "#/definitions/codersdk.LoggingConfig" }, - "max_session_expiry": { - "type": "integer" - }, - "max_token_lifetime": { - "type": "integer" - }, "metrics_cache_refresh_interval": { "type": "integer" }, @@ -9393,6 +9384,9 @@ const docTemplate = `{ "secure_auth_cookie": { "type": "boolean" }, + "session_lifetime": { + "$ref": "#/definitions/codersdk.SessionLifetime" + }, "ssh_keygen_algorithm": { "type": "string" }, @@ -11085,6 +11079,22 @@ const docTemplate = `{ } } }, + "codersdk.SessionLifetime": { + "type": "object", + "properties": { + "default_duration": { + "description": "DefaultDuration is for api keys, not tokens.", + "type": "integer" + }, + "disable_expiry_refresh": { + "description": "DisableExpiryRefresh will disable automatically refreshing api\nkeys when they are used from the api. This means the api key lifetime at\ncreation is the lifetime of the api key.", + "type": "boolean" + }, + "max_token_lifetime": { + "type": "integer" + } + } + }, "codersdk.SupportConfig": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 4643dc6fca..47bac4fc4e 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -8301,9 +8301,6 @@ "disable_path_apps": { "type": "boolean" }, - "disable_session_expiry_refresh": { - "type": "boolean" - }, "docs_url": { "$ref": "#/definitions/serpent.URL" }, @@ -8341,12 +8338,6 @@ "logging": { "$ref": "#/definitions/codersdk.LoggingConfig" }, - "max_session_expiry": { - "type": "integer" - }, - "max_token_lifetime": { - "type": "integer" - }, "metrics_cache_refresh_interval": { "type": "integer" }, @@ -8398,6 +8389,9 @@ "secure_auth_cookie": { "type": "boolean" }, + "session_lifetime": { + "$ref": "#/definitions/codersdk.SessionLifetime" + }, "ssh_keygen_algorithm": { "type": "string" }, @@ -9987,6 +9981,22 @@ } } }, + "codersdk.SessionLifetime": { + "type": "object", + "properties": { + "default_duration": { + "description": "DefaultDuration is for api keys, not tokens.", + "type": "integer" + }, + "disable_expiry_refresh": { + "description": "DisableExpiryRefresh will disable automatically refreshing api\nkeys when they are used from the api. This means the api key lifetime at\ncreation is the lifetime of the api key.", + "type": "boolean" + }, + "max_token_lifetime": { + "type": "integer" + } + } + }, "codersdk.SupportConfig": { "type": "object", "properties": { diff --git a/coderd/apikey.go b/coderd/apikey.go index b1d31ff613..10a83a05f4 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -84,7 +84,7 @@ func (api *API) postToken(rw http.ResponseWriter, r *http.Request) { cookie, key, err := api.createAPIKey(ctx, apikey.CreateParams{ UserID: user.ID, LoginType: database.LoginTypeToken, - DefaultLifetime: api.DeploymentValues.SessionDuration.Value(), + DefaultLifetime: api.DeploymentValues.Sessions.DefaultDuration.Value(), ExpiresAt: dbtime.Now().Add(lifeTime), Scope: scope, LifetimeSeconds: int64(lifeTime.Seconds()), @@ -128,7 +128,7 @@ func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) { lifeTime := time.Hour * 24 * 7 cookie, _, err := api.createAPIKey(ctx, apikey.CreateParams{ UserID: user.ID, - DefaultLifetime: api.DeploymentValues.SessionDuration.Value(), + DefaultLifetime: api.DeploymentValues.Sessions.DefaultDuration.Value(), LoginType: database.LoginTypePassword, RemoteAddr: r.RemoteAddr, // All api generated keys will last 1 week. Browser login tokens have @@ -354,7 +354,7 @@ func (api *API) tokenConfig(rw http.ResponseWriter, r *http.Request) { httpapi.Write( r.Context(), rw, http.StatusOK, codersdk.TokenConfig{ - MaxTokenLifetime: values.MaxTokenLifetime.Value(), + MaxTokenLifetime: values.Sessions.MaximumTokenDuration.Value(), }, ) } @@ -364,10 +364,10 @@ func (api *API) validateAPIKeyLifetime(lifetime time.Duration) error { return xerrors.New("lifetime must be positive number greater than 0") } - if lifetime > api.DeploymentValues.MaxTokenLifetime.Value() { + if lifetime > api.DeploymentValues.Sessions.MaximumTokenDuration.Value() { return xerrors.Errorf( "lifetime must be less than %v", - api.DeploymentValues.MaxTokenLifetime, + api.DeploymentValues.Sessions.MaximumTokenDuration, ) } diff --git a/coderd/apikey_test.go b/coderd/apikey_test.go index a20acf5ff3..29d0f01126 100644 --- a/coderd/apikey_test.go +++ b/coderd/apikey_test.go @@ -125,7 +125,7 @@ func TestTokenUserSetMaxLifetime(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancel() dc := coderdtest.DeploymentValues(t) - dc.MaxTokenLifetime = serpent.Duration(time.Hour * 24 * 7) + dc.Sessions.MaximumTokenDuration = serpent.Duration(time.Hour * 24 * 7) client := coderdtest.New(t, &coderdtest.Options{ DeploymentValues: dc, }) @@ -165,7 +165,7 @@ func TestSessionExpiry(t *testing.T) { // // We don't support updating the deployment config after startup, but for // this test it works because we don't copy the value (and we use pointers). - dc.SessionDuration = serpent.Duration(time.Second) + dc.Sessions.DefaultDuration = serpent.Duration(time.Second) userClient, _ := coderdtest.CreateAnotherUser(t, adminClient, adminUser.OrganizationID) @@ -174,8 +174,8 @@ func TestSessionExpiry(t *testing.T) { apiKey, err := db.GetAPIKeyByID(ctx, strings.Split(token, "-")[0]) require.NoError(t, err) - require.EqualValues(t, dc.SessionDuration.Value().Seconds(), apiKey.LifetimeSeconds) - require.WithinDuration(t, apiKey.CreatedAt.Add(dc.SessionDuration.Value()), apiKey.ExpiresAt, 2*time.Second) + require.EqualValues(t, dc.Sessions.DefaultDuration.Value().Seconds(), apiKey.LifetimeSeconds) + require.WithinDuration(t, apiKey.CreatedAt.Add(dc.Sessions.DefaultDuration.Value()), apiKey.ExpiresAt, 2*time.Second) // Update the session token to be expired so we can test that it is // rejected for extra points. diff --git a/coderd/coderd.go b/coderd/coderd.go index 0cc0962316..67b16e9032 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -566,7 +566,7 @@ func New(options *Options) *API { DB: options.Database, OAuth2Configs: oauthConfigs, RedirectToLogin: false, - DisableSessionExpiryRefresh: options.DeploymentValues.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableExpiryRefresh.Value(), Optional: false, SessionTokenFunc: nil, // Default behavior PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc, @@ -576,7 +576,7 @@ func New(options *Options) *API { DB: options.Database, OAuth2Configs: oauthConfigs, RedirectToLogin: true, - DisableSessionExpiryRefresh: options.DeploymentValues.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableExpiryRefresh.Value(), Optional: false, SessionTokenFunc: nil, // Default behavior PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc, @@ -586,7 +586,7 @@ func New(options *Options) *API { DB: options.Database, OAuth2Configs: oauthConfigs, RedirectToLogin: false, - DisableSessionExpiryRefresh: options.DeploymentValues.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableExpiryRefresh.Value(), Optional: true, SessionTokenFunc: nil, // Default behavior PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc, diff --git a/coderd/identityprovider/tokens.go b/coderd/identityprovider/tokens.go index 0673eb7d1a..e9c9e743e7 100644 --- a/coderd/identityprovider/tokens.go +++ b/coderd/identityprovider/tokens.go @@ -7,7 +7,6 @@ import ( "fmt" "net/http" "net/url" - "time" "github.com/google/uuid" "golang.org/x/oauth2" @@ -75,7 +74,11 @@ func extractTokenParams(r *http.Request, callbackURL *url.URL) (tokenParams, []c return params, nil, nil } -func Tokens(db database.Store, defaultLifetime time.Duration) http.HandlerFunc { +// Tokens +// TODO: the sessions lifetime config passed is for coder api tokens. +// Should there be a separate config for oauth2 tokens? They are related, +// but they are not the same. +func Tokens(db database.Store, lifetimes codersdk.SessionLifetime) http.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() app := httpmw.OAuth2ProviderApp(r) @@ -104,9 +107,9 @@ func Tokens(db database.Store, defaultLifetime time.Duration) http.HandlerFunc { switch params.grantType { // TODO: Client creds, device code. case codersdk.OAuth2ProviderGrantTypeRefreshToken: - token, err = refreshTokenGrant(ctx, db, app, defaultLifetime, params) + token, err = refreshTokenGrant(ctx, db, app, lifetimes, params) case codersdk.OAuth2ProviderGrantTypeAuthorizationCode: - token, err = authorizationCodeGrant(ctx, db, app, defaultLifetime, params) + token, err = authorizationCodeGrant(ctx, db, app, lifetimes, params) default: // Grant types are validated by the parser, so getting through here means // the developer added a type but forgot to add a case here. @@ -137,7 +140,7 @@ func Tokens(db database.Store, defaultLifetime time.Duration) http.HandlerFunc { } } -func authorizationCodeGrant(ctx context.Context, db database.Store, app database.OAuth2ProviderApp, defaultLifetime time.Duration, params tokenParams) (oauth2.Token, error) { +func authorizationCodeGrant(ctx context.Context, db database.Store, app database.OAuth2ProviderApp, lifetimes codersdk.SessionLifetime, params tokenParams) (oauth2.Token, error) { // Validate the client secret. secret, err := parseSecret(params.clientSecret) if err != nil { @@ -195,11 +198,9 @@ func authorizationCodeGrant(ctx context.Context, db database.Store, app database // TODO: We are ignoring scopes for now. tokenName := fmt.Sprintf("%s_%s_oauth_session_token", dbCode.UserID, app.ID) key, sessionToken, err := apikey.Generate(apikey.CreateParams{ - UserID: dbCode.UserID, - LoginType: database.LoginTypeOAuth2ProviderApp, - // TODO: This is just the lifetime for api keys, maybe have its own config - // settings. #11693 - DefaultLifetime: defaultLifetime, + UserID: dbCode.UserID, + LoginType: database.LoginTypeOAuth2ProviderApp, + DefaultLifetime: lifetimes.DefaultDuration.Value(), // For now, we allow only one token per app and user at a time. TokenName: tokenName, }) @@ -271,7 +272,7 @@ func authorizationCodeGrant(ctx context.Context, db database.Store, app database }, nil } -func refreshTokenGrant(ctx context.Context, db database.Store, app database.OAuth2ProviderApp, defaultLifetime time.Duration, params tokenParams) (oauth2.Token, error) { +func refreshTokenGrant(ctx context.Context, db database.Store, app database.OAuth2ProviderApp, lifetimes codersdk.SessionLifetime, params tokenParams) (oauth2.Token, error) { // Validate the token. token, err := parseSecret(params.refreshToken) if err != nil { @@ -326,11 +327,9 @@ func refreshTokenGrant(ctx context.Context, db database.Store, app database.OAut // TODO: We are ignoring scopes for now. tokenName := fmt.Sprintf("%s_%s_oauth_session_token", prevKey.UserID, app.ID) key, sessionToken, err := apikey.Generate(apikey.CreateParams{ - UserID: prevKey.UserID, - LoginType: database.LoginTypeOAuth2ProviderApp, - // TODO: This is just the lifetime for api keys, maybe have its own config - // settings. #11693 - DefaultLifetime: defaultLifetime, + UserID: prevKey.UserID, + LoginType: database.LoginTypeOAuth2ProviderApp, + DefaultLifetime: lifetimes.DefaultDuration.Value(), // For now, we allow only one token per app and user at a time. TokenName: tokenName, }) diff --git a/coderd/oauth2.go b/coderd/oauth2.go index 9e2df641bf..ef68e93a1f 100644 --- a/coderd/oauth2.go +++ b/coderd/oauth2.go @@ -354,7 +354,7 @@ func (api *API) getOAuth2ProviderAppAuthorize() http.HandlerFunc { // @Success 200 {object} oauth2.Token // @Router /oauth2/tokens [post] func (api *API) postOAuth2ProviderAppToken() http.HandlerFunc { - return identityprovider.Tokens(api.Database, api.DeploymentValues.SessionDuration.Value()) + return identityprovider.Tokens(api.Database, api.DeploymentValues.Sessions) } // @Summary Delete OAuth2 application tokens. diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 9665b43f31..ee1d455265 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -1737,9 +1737,9 @@ func (s *server) regenerateSessionToken(ctx context.Context, user database.User, newkey, sessionToken, err := apikey.Generate(apikey.CreateParams{ UserID: user.ID, LoginType: user.LoginType, - DefaultLifetime: s.DeploymentValues.SessionDuration.Value(), TokenName: workspaceSessionTokenName(workspace), - LifetimeSeconds: int64(s.DeploymentValues.MaxTokenLifetime.Value().Seconds()), + DefaultLifetime: s.DeploymentValues.Sessions.DefaultDuration.Value(), + LifetimeSeconds: int64(s.DeploymentValues.Sessions.MaximumTokenDuration.Value().Seconds()), }) if err != nil { return "", xerrors.Errorf("generate API key: %w", err) diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index 7e24372e66..6757bd2c63 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -166,7 +166,11 @@ func TestAcquireJob(t *testing.T) { // Set the max session token lifetime so we can assert we // create an API key with an expiration within the bounds of the // deployment config. - dv := &codersdk.DeploymentValues{MaxTokenLifetime: serpent.Duration(time.Hour)} + dv := &codersdk.DeploymentValues{ + Sessions: codersdk.SessionLifetime{ + MaximumTokenDuration: serpent.Duration(time.Hour), + }, + } gitAuthProvider := &sdkproto.ExternalAuthProviderResource{ Id: "github", } @@ -319,8 +323,8 @@ func TestAcquireJob(t *testing.T) { require.Len(t, toks, 2, "invalid api key") key, err := db.GetAPIKeyByID(ctx, toks[0]) require.NoError(t, err) - require.Equal(t, int64(dv.MaxTokenLifetime.Value().Seconds()), key.LifetimeSeconds) - require.WithinDuration(t, time.Now().Add(dv.MaxTokenLifetime.Value()), key.ExpiresAt, time.Minute) + require.Equal(t, int64(dv.Sessions.MaximumTokenDuration.Value().Seconds()), key.LifetimeSeconds) + require.WithinDuration(t, time.Now().Add(dv.Sessions.MaximumTokenDuration.Value()), key.ExpiresAt, time.Minute) want, err := json.Marshal(&proto.AcquiredJob_WorkspaceBuild_{ WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{ diff --git a/coderd/userauth.go b/coderd/userauth.go index 366f566c59..eda4dd60ab 100644 --- a/coderd/userauth.go +++ b/coderd/userauth.go @@ -252,7 +252,7 @@ func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) { UserID: user.ID, LoginType: database.LoginTypePassword, RemoteAddr: r.RemoteAddr, - DefaultLifetime: api.DeploymentValues.SessionDuration.Value(), + DefaultLifetime: api.DeploymentValues.Sessions.DefaultDuration.Value(), }) if err != nil { logger.Error(ctx, "unable to create API key", slog.Error(err)) @@ -1612,7 +1612,7 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C cookie, newKey, err := api.createAPIKey(dbauthz.AsSystemRestricted(ctx), apikey.CreateParams{ UserID: user.ID, LoginType: params.LoginType, - DefaultLifetime: api.DeploymentValues.SessionDuration.Value(), + DefaultLifetime: api.DeploymentValues.Sessions.DefaultDuration.Value(), RemoteAddr: r.RemoteAddr, }) if err != nil { diff --git a/coderd/workspaceapps.go b/coderd/workspaceapps.go index d4a31e1822..8c6ffdb62e 100644 --- a/coderd/workspaceapps.go +++ b/coderd/workspaceapps.go @@ -102,14 +102,14 @@ func (api *API) workspaceApplicationAuth(rw http.ResponseWriter, r *http.Request // the current session. exp := apiKey.ExpiresAt lifetimeSeconds := apiKey.LifetimeSeconds - if exp.IsZero() || time.Until(exp) > api.DeploymentValues.SessionDuration.Value() { - exp = dbtime.Now().Add(api.DeploymentValues.SessionDuration.Value()) - lifetimeSeconds = int64(api.DeploymentValues.SessionDuration.Value().Seconds()) + if exp.IsZero() || time.Until(exp) > api.DeploymentValues.Sessions.DefaultDuration.Value() { + exp = dbtime.Now().Add(api.DeploymentValues.Sessions.DefaultDuration.Value()) + lifetimeSeconds = int64(api.DeploymentValues.Sessions.DefaultDuration.Value().Seconds()) } cookie, _, err := api.createAPIKey(ctx, apikey.CreateParams{ UserID: apiKey.UserID, LoginType: database.LoginTypePassword, - DefaultLifetime: api.DeploymentValues.SessionDuration.Value(), + DefaultLifetime: api.DeploymentValues.Sessions.DefaultDuration.Value(), ExpiresAt: exp, LifetimeSeconds: lifetimeSeconds, Scope: database.APIKeyScopeApplicationConnect, diff --git a/coderd/workspaceapps/db.go b/coderd/workspaceapps/db.go index 32eaec1cf0..619bdd95ba 100644 --- a/coderd/workspaceapps/db.go +++ b/coderd/workspaceapps/db.go @@ -85,7 +85,7 @@ func (p *DBTokenProvider) Issue(ctx context.Context, rw http.ResponseWriter, r * DB: p.Database, OAuth2Configs: p.OAuth2Configs, RedirectToLogin: false, - DisableSessionExpiryRefresh: p.DeploymentValues.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: p.DeploymentValues.Sessions.DisableExpiryRefresh.Value(), // Optional is true to allow for public apps. If the authorization check // (later on) fails and the user is not authenticated, they will be // redirected to the login page or app auth endpoint using code below. diff --git a/codersdk/deployment.go b/codersdk/deployment.go index ee174075a7..34eaa4edd4 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -182,13 +182,11 @@ type DeploymentValues struct { RateLimit RateLimitConfig `json:"rate_limit,omitempty" typescript:",notnull"` Experiments serpent.StringArray `json:"experiments,omitempty" typescript:",notnull"` UpdateCheck serpent.Bool `json:"update_check,omitempty" typescript:",notnull"` - MaxTokenLifetime serpent.Duration `json:"max_token_lifetime,omitempty" typescript:",notnull"` Swagger SwaggerConfig `json:"swagger,omitempty" typescript:",notnull"` Logging LoggingConfig `json:"logging,omitempty" typescript:",notnull"` Dangerous DangerousConfig `json:"dangerous,omitempty" typescript:",notnull"` DisablePathApps serpent.Bool `json:"disable_path_apps,omitempty" typescript:",notnull"` - SessionDuration serpent.Duration `json:"max_session_expiry,omitempty" typescript:",notnull"` - DisableSessionExpiryRefresh serpent.Bool `json:"disable_session_expiry_refresh,omitempty" typescript:",notnull"` + Sessions SessionLifetime `json:"session_lifetime,omitempty" typescript:",notnull"` DisablePasswordAuth serpent.Bool `json:"disable_password_auth,omitempty" typescript:",notnull"` Support SupportConfig `json:"support,omitempty" typescript:",notnull"` ExternalAuthConfigs serpent.Struct[[]ExternalAuthConfig] `json:"external_auth,omitempty" typescript:",notnull"` @@ -244,6 +242,33 @@ func ParseSSHConfigOption(opt string) (key string, value string, err error) { return opt[:idx], opt[idx+1:], nil } +// SessionLifetime refers to "sessions" authenticating into Coderd. Coder has +// multiple different session types: api keys, tokens, workspace app tokens, +// agent tokens, etc. This configuration struct should be used to group all +// settings referring to any of these session lifetime controls. +// TODO: These config options were created back when coder only had api keys. +// Today, the config is ambigously used for all of them. For example: +// - cli based api keys ignore all settings +// - login uses the default lifetime, not the MaximumTokenDuration +// - Tokens use the Default & MaximumTokenDuration +// - ... etc ... +// The rational behind each decision is undocumented. The naming behind these +// config options is also confusing without any clear documentation. +// 'CreateAPIKey' is used to make all sessions, and it's parameters are just +// 'LifetimeSeconds' and 'DefaultLifetime'. Which does not directly correlate to +// the config options here. +type SessionLifetime struct { + // DisableExpiryRefresh will disable automatically refreshing api + // keys when they are used from the api. This means the api key lifetime at + // creation is the lifetime of the api key. + DisableExpiryRefresh serpent.Bool `json:"disable_expiry_refresh,omitempty" typescript:",notnull"` + + // DefaultDuration is for api keys, not tokens. + DefaultDuration serpent.Duration `json:"default_duration" typescript:",notnull"` + + MaximumTokenDuration serpent.Duration `json:"max_token_lifetime,omitempty" typescript:",notnull"` +} + type DERP struct { Server DERPServerConfig `json:"server" typescript:",notnull"` Config DERPConfig `json:"config" typescript:",notnull"` @@ -1579,7 +1604,7 @@ when required by your organization's security policy.`, // We have to add in the 25 leap days for the frontend to show the // "100 years" correctly. Default: ((100 * 365 * time.Hour * 24) + (25 * time.Hour * 24)).String(), - Value: &c.MaxTokenLifetime, + Value: &c.Sessions.MaximumTokenDuration, Group: &deploymentGroupNetworkingHTTP, YAML: "maxTokenLifetime", Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"), @@ -1773,7 +1798,7 @@ when required by your organization's security policy.`, Flag: "session-duration", Env: "CODER_SESSION_DURATION", Default: (24 * time.Hour).String(), - Value: &c.SessionDuration, + Value: &c.Sessions.DefaultDuration, Group: &deploymentGroupNetworkingHTTP, YAML: "sessionDuration", Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"), @@ -1784,7 +1809,7 @@ when required by your organization's security policy.`, Flag: "disable-session-expiry-refresh", Env: "CODER_DISABLE_SESSION_EXPIRY_REFRESH", - Value: &c.DisableSessionExpiryRefresh, + Value: &c.Sessions.DisableExpiryRefresh, Group: &deploymentGroupNetworkingHTTP, YAML: "disableSessionExpiryRefresh", }, diff --git a/docs/api/general.md b/docs/api/general.md index 69f57b9a99..330c41a335 100644 --- a/docs/api/general.md +++ b/docs/api/general.md @@ -200,7 +200,6 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \ "disable_owner_workspace_exec": true, "disable_password_auth": true, "disable_path_apps": true, - "disable_session_expiry_refresh": true, "docs_url": { "forceQuery": true, "fragment": "string", @@ -252,8 +251,6 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \ "log_filter": ["string"], "stackdriver": "string" }, - "max_session_expiry": 0, - "max_token_lifetime": 0, "metrics_cache_refresh_interval": 0, "oauth2": { "github": { @@ -341,6 +338,11 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \ "redirect_to_access_url": true, "scim_api_key": "string", "secure_auth_cookie": true, + "session_lifetime": { + "default_duration": 0, + "disable_expiry_refresh": true, + "max_token_lifetime": 0 + }, "ssh_keygen_algorithm": "string", "strict_transport_security": 0, "strict_transport_security_options": ["string"], diff --git a/docs/api/schemas.md b/docs/api/schemas.md index f0b5646fea..efc3a38f01 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -1925,7 +1925,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "disable_owner_workspace_exec": true, "disable_password_auth": true, "disable_path_apps": true, - "disable_session_expiry_refresh": true, "docs_url": { "forceQuery": true, "fragment": "string", @@ -1977,8 +1976,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "log_filter": ["string"], "stackdriver": "string" }, - "max_session_expiry": 0, - "max_token_lifetime": 0, "metrics_cache_refresh_interval": 0, "oauth2": { "github": { @@ -2066,6 +2063,11 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "redirect_to_access_url": true, "scim_api_key": "string", "secure_auth_cookie": true, + "session_lifetime": { + "default_duration": 0, + "disable_expiry_refresh": true, + "max_token_lifetime": 0 + }, "ssh_keygen_algorithm": "string", "strict_transport_security": 0, "strict_transport_security_options": ["string"], @@ -2295,7 +2297,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "disable_owner_workspace_exec": true, "disable_password_auth": true, "disable_path_apps": true, - "disable_session_expiry_refresh": true, "docs_url": { "forceQuery": true, "fragment": "string", @@ -2347,8 +2348,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "log_filter": ["string"], "stackdriver": "string" }, - "max_session_expiry": 0, - "max_token_lifetime": 0, "metrics_cache_refresh_interval": 0, "oauth2": { "github": { @@ -2436,6 +2435,11 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "redirect_to_access_url": true, "scim_api_key": "string", "secure_auth_cookie": true, + "session_lifetime": { + "default_duration": 0, + "disable_expiry_refresh": true, + "max_token_lifetime": 0 + }, "ssh_keygen_algorithm": "string", "strict_transport_security": 0, "strict_transport_security_options": ["string"], @@ -2526,7 +2530,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `disable_owner_workspace_exec` | boolean | false | | | | `disable_password_auth` | boolean | false | | | | `disable_path_apps` | boolean | false | | | -| `disable_session_expiry_refresh` | boolean | false | | | | `docs_url` | [serpent.URL](#serpenturl) | false | | | | `enable_terraform_debug_mode` | boolean | false | | | | `experiments` | array of string | false | | | @@ -2537,8 +2540,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `in_memory_database` | boolean | false | | | | `job_hang_detector_interval` | integer | false | | | | `logging` | [codersdk.LoggingConfig](#codersdkloggingconfig) | false | | | -| `max_session_expiry` | integer | false | | | -| `max_token_lifetime` | integer | false | | | | `metrics_cache_refresh_interval` | integer | false | | | | `oauth2` | [codersdk.OAuth2Config](#codersdkoauth2config) | false | | | | `oidc` | [codersdk.OIDCConfig](#codersdkoidcconfig) | false | | | @@ -2554,6 +2555,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `redirect_to_access_url` | boolean | false | | | | `scim_api_key` | string | false | | | | `secure_auth_cookie` | boolean | false | | | +| `session_lifetime` | [codersdk.SessionLifetime](#codersdksessionlifetime) | false | | | | `ssh_keygen_algorithm` | string | false | | | | `strict_transport_security` | integer | false | | | | `strict_transport_security_options` | array of string | false | | | @@ -4294,6 +4296,24 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `ssh` | integer | false | | | | `vscode` | integer | false | | | +## codersdk.SessionLifetime + +```json +{ + "default_duration": 0, + "disable_expiry_refresh": true, + "max_token_lifetime": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------------ | ------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `default_duration` | integer | false | | Default duration is for api keys, not tokens. | +| `disable_expiry_refresh` | boolean | false | | Disable expiry refresh will disable automatically refreshing api keys when they are used from the api. This means the api key lifetime at creation is the lifetime of the api key. | +| `max_token_lifetime` | integer | false | | | + ## codersdk.SupportConfig ```json diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go index c3b8cc0199..0ac2086f86 100644 --- a/enterprise/coderd/coderd.go +++ b/enterprise/coderd/coderd.go @@ -148,7 +148,7 @@ func New(ctx context.Context, options *Options) (_ *API, err error) { DB: options.Database, OAuth2Configs: oauthConfigs, RedirectToLogin: false, - DisableSessionExpiryRefresh: options.DeploymentValues.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableExpiryRefresh.Value(), Optional: false, SessionTokenFunc: nil, // Default behavior PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc, @@ -157,7 +157,7 @@ func New(ctx context.Context, options *Options) (_ *API, err error) { DB: options.Database, OAuth2Configs: oauthConfigs, RedirectToLogin: false, - DisableSessionExpiryRefresh: options.DeploymentValues.DisableSessionExpiryRefresh.Value(), + DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableExpiryRefresh.Value(), Optional: true, SessionTokenFunc: nil, // Default behavior PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc, diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index bdf744e104..be751559f2 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -427,13 +427,11 @@ export interface DeploymentValues { readonly rate_limit?: RateLimitConfig; readonly experiments?: string[]; readonly update_check?: boolean; - readonly max_token_lifetime?: number; readonly swagger?: SwaggerConfig; readonly logging?: LoggingConfig; readonly dangerous?: DangerousConfig; readonly disable_path_apps?: boolean; - readonly max_session_expiry?: number; - readonly disable_session_expiry_refresh?: boolean; + readonly session_lifetime?: SessionLifetime; readonly disable_password_auth?: boolean; readonly support?: SupportConfig; readonly external_auth?: ExternalAuthConfig[]; @@ -998,6 +996,13 @@ export interface SessionCountDeploymentStats { readonly reconnecting_pty: number; } +// From codersdk/deployment.go +export interface SessionLifetime { + readonly disable_expiry_refresh?: boolean; + readonly default_duration: number; + readonly max_token_lifetime?: number; +} + // From codersdk/deployment.go export interface SupportConfig { readonly links: LinkConfig[];