diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 7bfd521b093ce..0881eac5b7c31 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -9295,9 +9295,6 @@ const docTemplate = `{ "disable_path_apps": { "type": "boolean" }, - "disable_session_expiry_refresh": { - "type": "boolean" - }, "docs_url": { "$ref": "#/definitions/serpent.URL" }, @@ -9335,12 +9332,6 @@ const docTemplate = `{ "logging": { "$ref": "#/definitions/codersdk.LoggingConfig" }, - "max_session_expiry": { - "type": "integer" - }, - "max_token_lifetime": { - "type": "integer" - }, "metrics_cache_refresh_interval": { "type": "integer" }, @@ -9392,6 +9383,9 @@ const docTemplate = `{ "secure_auth_cookie": { "type": "boolean" }, + "session_lifetime": { + "$ref": "#/definitions/codersdk.SessionLifetime" + }, "ssh_keygen_algorithm": { "type": "string" }, @@ -11084,6 +11078,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 c4dabcacaf6ba..88c025eaec71b 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -8300,9 +8300,6 @@ "disable_path_apps": { "type": "boolean" }, - "disable_session_expiry_refresh": { - "type": "boolean" - }, "docs_url": { "$ref": "#/definitions/serpent.URL" }, @@ -8340,12 +8337,6 @@ "logging": { "$ref": "#/definitions/codersdk.LoggingConfig" }, - "max_session_expiry": { - "type": "integer" - }, - "max_token_lifetime": { - "type": "integer" - }, "metrics_cache_refresh_interval": { "type": "integer" }, @@ -8397,6 +8388,9 @@ "secure_auth_cookie": { "type": "boolean" }, + "session_lifetime": { + "$ref": "#/definitions/codersdk.SessionLifetime" + }, "ssh_keygen_algorithm": { "type": "string" }, @@ -9986,6 +9980,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 b1d31ff613f65..10a83a05f4a24 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 a20acf5ff3fbd..29d0f01126b7a 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 0cc0962316571..67b16e9032bfe 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 0673eb7d1af7c..e9c9e743e7225 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 9e2df641bf7d3..ef68e93a1fc47 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 6183ffc02862a..965e7c6f78ed3 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -1725,9 +1725,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 05572d381ea00..3f5f344dea5ce 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", } @@ -310,8 +314,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 366f566c59349..eda4dd60abfa2 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 d4a31e18224d3..8c6ffdb62e34a 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 32eaec1cf0f57..619bdd95ba165 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 ee174075a72e4..34eaa4edd4c40 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 69f57b9a9975c..330c41a335b9b 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 f0b5646fea240..efc3a38f01219 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 c3b8cc019989e..0ac2086f8699e 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 bdf744e104b39..be751559f21d9 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[];