Commit 1fffc41f authored by johnnyjoy's avatar johnnyjoy

feat: sliding expiration for user sessions

parent e93d695a
...@@ -38,8 +38,9 @@ message GetCurrentSessionRequest {} ...@@ -38,8 +38,9 @@ message GetCurrentSessionRequest {}
message GetCurrentSessionResponse { message GetCurrentSessionResponse {
User user = 1; User user = 1;
// Current session expiration time (if available). // Last time the session was accessed.
google.protobuf.Timestamp expires_at = 2; // Used for sliding expiration calculation (last_accessed_time + 2 weeks).
google.protobuf.Timestamp last_accessed_at = 2;
} }
message CreateSessionRequest { message CreateSessionRequest {
...@@ -78,18 +79,15 @@ message CreateSessionRequest { ...@@ -78,18 +79,15 @@ message CreateSessionRequest {
// SSO provider authentication method. // SSO provider authentication method.
SSOCredentials sso_credentials = 2; SSOCredentials sso_credentials = 2;
} }
// Whether the session should never expire.
// Optional field that defaults to false for security.
bool never_expire = 3 [(google.api.field_behavior) = OPTIONAL];
} }
message CreateSessionResponse { message CreateSessionResponse {
// The authenticated user information. // The authenticated user information.
User user = 1; User user = 1;
// Token expiration time. // Last time the session was accessed.
google.protobuf.Timestamp expires_at = 2; // Used for sliding expiration calculation (last_accessed_time + 2 weeks).
google.protobuf.Timestamp last_accessed_at = 2;
} }
message DeleteSessionRequest {} message DeleteSessionRequest {}
...@@ -481,14 +481,12 @@ message UserSession { ...@@ -481,14 +481,12 @@ message UserSession {
// The timestamp when the session was created. // The timestamp when the session was created.
google.protobuf.Timestamp create_time = 3 [(google.api.field_behavior) = OUTPUT_ONLY]; google.protobuf.Timestamp create_time = 3 [(google.api.field_behavior) = OUTPUT_ONLY];
// The timestamp when the session expires.
google.protobuf.Timestamp expire_time = 4 [(google.api.field_behavior) = OUTPUT_ONLY];
// The timestamp when the session was last accessed. // The timestamp when the session was last accessed.
google.protobuf.Timestamp last_accessed_time = 5 [(google.api.field_behavior) = OUTPUT_ONLY]; // Used for sliding expiration calculation (last_accessed_time + 2 weeks).
google.protobuf.Timestamp last_accessed_time = 4 [(google.api.field_behavior) = OUTPUT_ONLY];
// Client information associated with this session. // Client information associated with this session.
ClientInfo client_info = 6 [(google.api.field_behavior) = OUTPUT_ONLY]; ClientInfo client_info = 5 [(google.api.field_behavior) = OUTPUT_ONLY];
message ClientInfo { message ClientInfo {
// User agent string of the client. // User agent string of the client.
......
...@@ -63,10 +63,11 @@ func (*GetCurrentSessionRequest) Descriptor() ([]byte, []int) { ...@@ -63,10 +63,11 @@ func (*GetCurrentSessionRequest) Descriptor() ([]byte, []int) {
type GetCurrentSessionResponse struct { type GetCurrentSessionResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
// Current session expiration time (if available). // Last time the session was accessed.
ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"` // Used for sliding expiration calculation (last_accessed_time + 2 weeks).
unknownFields protoimpl.UnknownFields LastAccessedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=last_accessed_at,json=lastAccessedAt,proto3" json:"last_accessed_at,omitempty"`
sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *GetCurrentSessionResponse) Reset() { func (x *GetCurrentSessionResponse) Reset() {
...@@ -106,9 +107,9 @@ func (x *GetCurrentSessionResponse) GetUser() *User { ...@@ -106,9 +107,9 @@ func (x *GetCurrentSessionResponse) GetUser() *User {
return nil return nil
} }
func (x *GetCurrentSessionResponse) GetExpiresAt() *timestamppb.Timestamp { func (x *GetCurrentSessionResponse) GetLastAccessedAt() *timestamppb.Timestamp {
if x != nil { if x != nil {
return x.ExpiresAt return x.LastAccessedAt
} }
return nil return nil
} }
...@@ -122,10 +123,7 @@ type CreateSessionRequest struct { ...@@ -122,10 +123,7 @@ type CreateSessionRequest struct {
// //
// *CreateSessionRequest_PasswordCredentials_ // *CreateSessionRequest_PasswordCredentials_
// *CreateSessionRequest_SsoCredentials // *CreateSessionRequest_SsoCredentials
Credentials isCreateSessionRequest_Credentials `protobuf_oneof:"credentials"` Credentials isCreateSessionRequest_Credentials `protobuf_oneof:"credentials"`
// Whether the session should never expire.
// Optional field that defaults to false for security.
NeverExpire bool `protobuf:"varint,3,opt,name=never_expire,json=neverExpire,proto3" json:"never_expire,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
...@@ -185,13 +183,6 @@ func (x *CreateSessionRequest) GetSsoCredentials() *CreateSessionRequest_SSOCred ...@@ -185,13 +183,6 @@ func (x *CreateSessionRequest) GetSsoCredentials() *CreateSessionRequest_SSOCred
return nil return nil
} }
func (x *CreateSessionRequest) GetNeverExpire() bool {
if x != nil {
return x.NeverExpire
}
return false
}
type isCreateSessionRequest_Credentials interface { type isCreateSessionRequest_Credentials interface {
isCreateSessionRequest_Credentials() isCreateSessionRequest_Credentials()
} }
...@@ -214,10 +205,11 @@ type CreateSessionResponse struct { ...@@ -214,10 +205,11 @@ type CreateSessionResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
// The authenticated user information. // The authenticated user information.
User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
// Token expiration time. // Last time the session was accessed.
ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"` // Used for sliding expiration calculation (last_accessed_time + 2 weeks).
unknownFields protoimpl.UnknownFields LastAccessedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=last_accessed_at,json=lastAccessedAt,proto3" json:"last_accessed_at,omitempty"`
sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *CreateSessionResponse) Reset() { func (x *CreateSessionResponse) Reset() {
...@@ -257,9 +249,9 @@ func (x *CreateSessionResponse) GetUser() *User { ...@@ -257,9 +249,9 @@ func (x *CreateSessionResponse) GetUser() *User {
return nil return nil
} }
func (x *CreateSessionResponse) GetExpiresAt() *timestamppb.Timestamp { func (x *CreateSessionResponse) GetLastAccessedAt() *timestamppb.Timestamp {
if x != nil { if x != nil {
return x.ExpiresAt return x.LastAccessedAt
} }
return nil return nil
} }
...@@ -429,15 +421,13 @@ var File_api_v1_auth_service_proto protoreflect.FileDescriptor ...@@ -429,15 +421,13 @@ var File_api_v1_auth_service_proto protoreflect.FileDescriptor
const file_api_v1_auth_service_proto_rawDesc = "" + const file_api_v1_auth_service_proto_rawDesc = "" +
"\n" + "\n" +
"\x19api/v1/auth_service.proto\x12\fmemos.api.v1\x1a\x19api/v1/user_service.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x1a\n" + "\x19api/v1/auth_service.proto\x12\fmemos.api.v1\x1a\x19api/v1/user_service.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x1a\n" +
"\x18GetCurrentSessionRequest\"~\n" + "\x18GetCurrentSessionRequest\"\x89\x01\n" +
"\x19GetCurrentSessionResponse\x12&\n" + "\x19GetCurrentSessionResponse\x12&\n" +
"\x04user\x18\x01 \x01(\v2\x12.memos.api.v1.UserR\x04user\x129\n" + "\x04user\x18\x01 \x01(\v2\x12.memos.api.v1.UserR\x04user\x12D\n" +
"\n" + "\x10last_accessed_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x0elastAccessedAt\"\xb8\x03\n" +
"expires_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\"\xe0\x03\n" +
"\x14CreateSessionRequest\x12k\n" + "\x14CreateSessionRequest\x12k\n" +
"\x14password_credentials\x18\x01 \x01(\v26.memos.api.v1.CreateSessionRequest.PasswordCredentialsH\x00R\x13passwordCredentials\x12\\\n" + "\x14password_credentials\x18\x01 \x01(\v26.memos.api.v1.CreateSessionRequest.PasswordCredentialsH\x00R\x13passwordCredentials\x12\\\n" +
"\x0fsso_credentials\x18\x02 \x01(\v21.memos.api.v1.CreateSessionRequest.SSOCredentialsH\x00R\x0essoCredentials\x12&\n" + "\x0fsso_credentials\x18\x02 \x01(\v21.memos.api.v1.CreateSessionRequest.SSOCredentialsH\x00R\x0essoCredentials\x1aW\n" +
"\fnever_expire\x18\x03 \x01(\bB\x03\xe0A\x01R\vneverExpire\x1aW\n" +
"\x13PasswordCredentials\x12\x1f\n" + "\x13PasswordCredentials\x12\x1f\n" +
"\busername\x18\x01 \x01(\tB\x03\xe0A\x02R\busername\x12\x1f\n" + "\busername\x18\x01 \x01(\tB\x03\xe0A\x02R\busername\x12\x1f\n" +
"\bpassword\x18\x02 \x01(\tB\x03\xe0A\x02R\bpassword\x1am\n" + "\bpassword\x18\x02 \x01(\tB\x03\xe0A\x02R\bpassword\x1am\n" +
...@@ -445,11 +435,10 @@ const file_api_v1_auth_service_proto_rawDesc = "" + ...@@ -445,11 +435,10 @@ const file_api_v1_auth_service_proto_rawDesc = "" +
"\x06idp_id\x18\x01 \x01(\x05B\x03\xe0A\x02R\x05idpId\x12\x17\n" + "\x06idp_id\x18\x01 \x01(\x05B\x03\xe0A\x02R\x05idpId\x12\x17\n" +
"\x04code\x18\x02 \x01(\tB\x03\xe0A\x02R\x04code\x12&\n" + "\x04code\x18\x02 \x01(\tB\x03\xe0A\x02R\x04code\x12&\n" +
"\fredirect_uri\x18\x03 \x01(\tB\x03\xe0A\x02R\vredirectUriB\r\n" + "\fredirect_uri\x18\x03 \x01(\tB\x03\xe0A\x02R\vredirectUriB\r\n" +
"\vcredentials\"z\n" + "\vcredentials\"\x85\x01\n" +
"\x15CreateSessionResponse\x12&\n" + "\x15CreateSessionResponse\x12&\n" +
"\x04user\x18\x01 \x01(\v2\x12.memos.api.v1.UserR\x04user\x129\n" + "\x04user\x18\x01 \x01(\v2\x12.memos.api.v1.UserR\x04user\x12D\n" +
"\n" + "\x10last_accessed_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x0elastAccessedAt\"\x16\n" +
"expires_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\"\x16\n" +
"\x14DeleteSessionRequest2\x8b\x03\n" + "\x14DeleteSessionRequest2\x8b\x03\n" +
"\vAuthService\x12\x8b\x01\n" + "\vAuthService\x12\x8b\x01\n" +
"\x11GetCurrentSession\x12&.memos.api.v1.GetCurrentSessionRequest\x1a'.memos.api.v1.GetCurrentSessionResponse\"%\x82\xd3\xe4\x93\x02\x1f\x12\x1d/api/v1/auth/sessions/current\x12z\n" + "\x11GetCurrentSession\x12&.memos.api.v1.GetCurrentSessionRequest\x1a'.memos.api.v1.GetCurrentSessionResponse\"%\x82\xd3\xe4\x93\x02\x1f\x12\x1d/api/v1/auth/sessions/current\x12z\n" +
...@@ -484,11 +473,11 @@ var file_api_v1_auth_service_proto_goTypes = []any{ ...@@ -484,11 +473,11 @@ var file_api_v1_auth_service_proto_goTypes = []any{
} }
var file_api_v1_auth_service_proto_depIdxs = []int32{ var file_api_v1_auth_service_proto_depIdxs = []int32{
7, // 0: memos.api.v1.GetCurrentSessionResponse.user:type_name -> memos.api.v1.User 7, // 0: memos.api.v1.GetCurrentSessionResponse.user:type_name -> memos.api.v1.User
8, // 1: memos.api.v1.GetCurrentSessionResponse.expires_at:type_name -> google.protobuf.Timestamp 8, // 1: memos.api.v1.GetCurrentSessionResponse.last_accessed_at:type_name -> google.protobuf.Timestamp
5, // 2: memos.api.v1.CreateSessionRequest.password_credentials:type_name -> memos.api.v1.CreateSessionRequest.PasswordCredentials 5, // 2: memos.api.v1.CreateSessionRequest.password_credentials:type_name -> memos.api.v1.CreateSessionRequest.PasswordCredentials
6, // 3: memos.api.v1.CreateSessionRequest.sso_credentials:type_name -> memos.api.v1.CreateSessionRequest.SSOCredentials 6, // 3: memos.api.v1.CreateSessionRequest.sso_credentials:type_name -> memos.api.v1.CreateSessionRequest.SSOCredentials
7, // 4: memos.api.v1.CreateSessionResponse.user:type_name -> memos.api.v1.User 7, // 4: memos.api.v1.CreateSessionResponse.user:type_name -> memos.api.v1.User
8, // 5: memos.api.v1.CreateSessionResponse.expires_at:type_name -> google.protobuf.Timestamp 8, // 5: memos.api.v1.CreateSessionResponse.last_accessed_at:type_name -> google.protobuf.Timestamp
0, // 6: memos.api.v1.AuthService.GetCurrentSession:input_type -> memos.api.v1.GetCurrentSessionRequest 0, // 6: memos.api.v1.AuthService.GetCurrentSession:input_type -> memos.api.v1.GetCurrentSessionRequest
2, // 7: memos.api.v1.AuthService.CreateSession:input_type -> memos.api.v1.CreateSessionRequest 2, // 7: memos.api.v1.AuthService.CreateSession:input_type -> memos.api.v1.CreateSessionRequest
4, // 8: memos.api.v1.AuthService.DeleteSession:input_type -> memos.api.v1.DeleteSessionRequest 4, // 8: memos.api.v1.AuthService.DeleteSession:input_type -> memos.api.v1.DeleteSessionRequest
......
This diff is collapsed.
...@@ -3282,21 +3282,18 @@ definitions: ...@@ -3282,21 +3282,18 @@ definitions:
ssoCredentials: ssoCredentials:
$ref: '#/definitions/CreateSessionRequestSSOCredentials' $ref: '#/definitions/CreateSessionRequestSSOCredentials'
description: SSO provider authentication method. description: SSO provider authentication method.
neverExpire:
type: boolean
description: |-
Whether the session should never expire.
Optional field that defaults to false for security.
v1CreateSessionResponse: v1CreateSessionResponse:
type: object type: object
properties: properties:
user: user:
$ref: '#/definitions/v1User' $ref: '#/definitions/v1User'
description: The authenticated user information. description: The authenticated user information.
expiresAt: lastAccessedAt:
type: string type: string
format: date-time format: date-time
description: Token expiration time. description: |-
Last time the session was accessed.
Used for sliding expiration calculation (last_accessed_time + 2 weeks).
v1EmbeddedContentNode: v1EmbeddedContentNode:
type: object type: object
properties: properties:
...@@ -3316,10 +3313,12 @@ definitions: ...@@ -3316,10 +3313,12 @@ definitions:
properties: properties:
user: user:
$ref: '#/definitions/v1User' $ref: '#/definitions/v1User'
expiresAt: lastAccessedAt:
type: string type: string
format: date-time format: date-time
description: Current session expiration time (if available). description: |-
Last time the session was accessed.
Used for sliding expiration calculation (last_accessed_time + 2 weeks).
v1HTMLElementNode: v1HTMLElementNode:
type: object type: object
properties: properties:
...@@ -4152,15 +4151,12 @@ definitions: ...@@ -4152,15 +4151,12 @@ definitions:
format: date-time format: date-time
description: The timestamp when the session was created. description: The timestamp when the session was created.
readOnly: true readOnly: true
expireTime:
type: string
format: date-time
description: The timestamp when the session expires.
readOnly: true
lastAccessedTime: lastAccessedTime:
type: string type: string
format: date-time format: date-time
description: The timestamp when the session was last accessed. description: |-
The timestamp when the session was last accessed.
Used for sliding expiration calculation (last_accessed_time + 2 weeks).
readOnly: true readOnly: true
clientInfo: clientInfo:
$ref: '#/definitions/v1UserSessionClientInfo' $ref: '#/definitions/v1UserSessionClientInfo'
......
...@@ -476,12 +476,11 @@ type SessionsUserSetting_Session struct { ...@@ -476,12 +476,11 @@ type SessionsUserSetting_Session struct {
SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"`
// Timestamp when the session was created. // Timestamp when the session was created.
CreateTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty"` CreateTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty"`
// Timestamp when the session expires.
ExpireTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=expire_time,json=expireTime,proto3" json:"expire_time,omitempty"`
// Timestamp when the session was last accessed. // Timestamp when the session was last accessed.
LastAccessedTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=last_accessed_time,json=lastAccessedTime,proto3" json:"last_accessed_time,omitempty"` // Used for sliding expiration calculation (last_accessed_time + 2 weeks).
LastAccessedTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=last_accessed_time,json=lastAccessedTime,proto3" json:"last_accessed_time,omitempty"`
// Client information associated with this session. // Client information associated with this session.
ClientInfo *SessionsUserSetting_ClientInfo `protobuf:"bytes,5,opt,name=client_info,json=clientInfo,proto3" json:"client_info,omitempty"` ClientInfo *SessionsUserSetting_ClientInfo `protobuf:"bytes,4,opt,name=client_info,json=clientInfo,proto3" json:"client_info,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
...@@ -530,13 +529,6 @@ func (x *SessionsUserSetting_Session) GetCreateTime() *timestamppb.Timestamp { ...@@ -530,13 +529,6 @@ func (x *SessionsUserSetting_Session) GetCreateTime() *timestamppb.Timestamp {
return nil return nil
} }
func (x *SessionsUserSetting_Session) GetExpireTime() *timestamppb.Timestamp {
if x != nil {
return x.ExpireTime
}
return nil
}
func (x *SessionsUserSetting_Session) GetLastAccessedTime() *timestamppb.Timestamp { func (x *SessionsUserSetting_Session) GetLastAccessedTime() *timestamppb.Timestamp {
if x != nil { if x != nil {
return x.LastAccessedTime return x.LastAccessedTime
...@@ -836,18 +828,16 @@ const file_store_user_setting_proto_rawDesc = "" + ...@@ -836,18 +828,16 @@ const file_store_user_setting_proto_rawDesc = "" +
"\n" + "\n" +
"appearance\x18\x02 \x01(\tR\n" + "appearance\x18\x02 \x01(\tR\n" +
"appearance\x12'\n" + "appearance\x12'\n" +
"\x0fmemo_visibility\x18\x03 \x01(\tR\x0ememoVisibility\"\xb0\x04\n" + "\x0fmemo_visibility\x18\x03 \x01(\tR\x0ememoVisibility\"\xf3\x03\n" +
"\x13SessionsUserSetting\x12D\n" + "\x13SessionsUserSetting\x12D\n" +
"\bsessions\x18\x01 \x03(\v2(.memos.store.SessionsUserSetting.SessionR\bsessions\x1a\xba\x02\n" + "\bsessions\x18\x01 \x03(\v2(.memos.store.SessionsUserSetting.SessionR\bsessions\x1a\xfd\x01\n" +
"\aSession\x12\x1d\n" + "\aSession\x12\x1d\n" +
"\n" + "\n" +
"session_id\x18\x01 \x01(\tR\tsessionId\x12;\n" + "session_id\x18\x01 \x01(\tR\tsessionId\x12;\n" +
"\vcreate_time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\n" + "\vcreate_time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\n" +
"createTime\x12;\n" + "createTime\x12H\n" +
"\vexpire_time\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\n" + "\x12last_accessed_time\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\x10lastAccessedTime\x12L\n" +
"expireTime\x12H\n" + "\vclient_info\x18\x04 \x01(\v2+.memos.store.SessionsUserSetting.ClientInfoR\n" +
"\x12last_accessed_time\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\x10lastAccessedTime\x12L\n" +
"\vclient_info\x18\x05 \x01(\v2+.memos.store.SessionsUserSetting.ClientInfoR\n" +
"clientInfo\x1a\x95\x01\n" + "clientInfo\x1a\x95\x01\n" +
"\n" + "\n" +
"ClientInfo\x12\x1d\n" + "ClientInfo\x12\x1d\n" +
...@@ -919,14 +909,13 @@ var file_store_user_setting_proto_depIdxs = []int32{ ...@@ -919,14 +909,13 @@ var file_store_user_setting_proto_depIdxs = []int32{
10, // 8: memos.store.ShortcutsUserSetting.shortcuts:type_name -> memos.store.ShortcutsUserSetting.Shortcut 10, // 8: memos.store.ShortcutsUserSetting.shortcuts:type_name -> memos.store.ShortcutsUserSetting.Shortcut
11, // 9: memos.store.WebhooksUserSetting.webhooks:type_name -> memos.store.WebhooksUserSetting.Webhook 11, // 9: memos.store.WebhooksUserSetting.webhooks:type_name -> memos.store.WebhooksUserSetting.Webhook
12, // 10: memos.store.SessionsUserSetting.Session.create_time:type_name -> google.protobuf.Timestamp 12, // 10: memos.store.SessionsUserSetting.Session.create_time:type_name -> google.protobuf.Timestamp
12, // 11: memos.store.SessionsUserSetting.Session.expire_time:type_name -> google.protobuf.Timestamp 12, // 11: memos.store.SessionsUserSetting.Session.last_accessed_time:type_name -> google.protobuf.Timestamp
12, // 12: memos.store.SessionsUserSetting.Session.last_accessed_time:type_name -> google.protobuf.Timestamp 8, // 12: memos.store.SessionsUserSetting.Session.client_info:type_name -> memos.store.SessionsUserSetting.ClientInfo
8, // 13: memos.store.SessionsUserSetting.Session.client_info:type_name -> memos.store.SessionsUserSetting.ClientInfo 13, // [13:13] is the sub-list for method output_type
14, // [14:14] is the sub-list for method output_type 13, // [13:13] is the sub-list for method input_type
14, // [14:14] is the sub-list for method input_type 13, // [13:13] is the sub-list for extension type_name
14, // [14:14] is the sub-list for extension type_name 13, // [13:13] is the sub-list for extension extendee
14, // [14:14] is the sub-list for extension extendee 0, // [0:13] is the sub-list for field type_name
0, // [0:14] is the sub-list for field type_name
} }
func init() { file_store_user_setting_proto_init() } func init() { file_store_user_setting_proto_init() }
......
...@@ -48,12 +48,11 @@ message SessionsUserSetting { ...@@ -48,12 +48,11 @@ message SessionsUserSetting {
string session_id = 1; string session_id = 1;
// Timestamp when the session was created. // Timestamp when the session was created.
google.protobuf.Timestamp create_time = 2; google.protobuf.Timestamp create_time = 2;
// Timestamp when the session expires.
google.protobuf.Timestamp expire_time = 3;
// Timestamp when the session was last accessed. // Timestamp when the session was last accessed.
google.protobuf.Timestamp last_accessed_time = 4; // Used for sliding expiration calculation (last_accessed_time + 2 weeks).
google.protobuf.Timestamp last_accessed_time = 3;
// Client information associated with this session. // Client information associated with this session.
ClientInfo client_info = 5; ClientInfo client_info = 4;
} }
message ClientInfo { message ClientInfo {
......
...@@ -203,13 +203,16 @@ func (in *GRPCAuthInterceptor) updateSessionLastAccessed(ctx context.Context, us ...@@ -203,13 +203,16 @@ func (in *GRPCAuthInterceptor) updateSessionLastAccessed(ctx context.Context, us
return in.Store.UpdateUserSessionLastAccessed(ctx, userID, sessionID, timestamppb.Now()) return in.Store.UpdateUserSessionLastAccessed(ctx, userID, sessionID, timestamppb.Now())
} }
// validateUserSession checks if a session exists and is still valid. // validateUserSession checks if a session exists and is still valid using sliding expiration.
func validateUserSession(sessionID string, userSessions []*storepb.SessionsUserSetting_Session) bool { func validateUserSession(sessionID string, userSessions []*storepb.SessionsUserSetting_Session) bool {
for _, session := range userSessions { for _, session := range userSessions {
if sessionID == session.SessionId { if sessionID == session.SessionId {
// Check if session has expired // Use sliding expiration: check if last_accessed_time + 2 weeks > current_time
if session.ExpireTime != nil && session.ExpireTime.AsTime().Before(time.Now()) { if session.LastAccessedTime != nil {
return false expirationTime := session.LastAccessedTime.AsTime().Add(SessionSlidingDuration)
if expirationTime.Before(time.Now()) {
return false
}
} }
return true return true
} }
......
...@@ -19,11 +19,10 @@ const ( ...@@ -19,11 +19,10 @@ const (
KeyID = "v1" KeyID = "v1"
// AccessTokenAudienceName is the audience name of the access token. // AccessTokenAudienceName is the audience name of the access token.
AccessTokenAudienceName = "user.access-token" AccessTokenAudienceName = "user.access-token"
AccessTokenDuration = 7 * 24 * time.Hour // SessionSlidingDuration is the sliding expiration duration for user sessions (2 weeks).
// Sessions are considered valid if last_accessed_time + SessionSlidingDuration > current_time.
SessionSlidingDuration = 14 * 24 * time.Hour
// CookieExpDuration expires slightly earlier than the jwt expiration. Client would be logged out if the user
// cookie expires, thus the client would always logout first before attempting to make a request with the expired jwt.
CookieExpDuration = AccessTokenDuration - 1*time.Minute
// SessionCookieName is the cookie name of user session ID. // SessionCookieName is the cookie name of user session ID.
SessionCookieName = "user_session" SessionCookieName = "user_session"
) )
......
...@@ -42,16 +42,20 @@ func (s *APIV1Service) GetCurrentSession(ctx context.Context, _ *v1pb.GetCurrent ...@@ -42,16 +42,20 @@ func (s *APIV1Service) GetCurrentSession(ctx context.Context, _ *v1pb.GetCurrent
return nil, status.Errorf(codes.Unauthenticated, "user not found") return nil, status.Errorf(codes.Unauthenticated, "user not found")
} }
// Update session last accessed time if we have a session ID var lastAccessedAt *timestamppb.Timestamp
// Update session last accessed time if we have a session ID and get the current session info
if sessionID, ok := ctx.Value(sessionIDContextKey).(string); ok && sessionID != "" { if sessionID, ok := ctx.Value(sessionIDContextKey).(string); ok && sessionID != "" {
if err := s.Store.UpdateUserSessionLastAccessed(ctx, user.ID, sessionID, timestamppb.Now()); err != nil { now := timestamppb.Now()
if err := s.Store.UpdateUserSessionLastAccessed(ctx, user.ID, sessionID, now); err != nil {
// Log error but don't fail the request // Log error but don't fail the request
slog.Error("failed to update session last accessed time", "error", err) slog.Error("failed to update session last accessed time", "error", err)
} }
lastAccessedAt = now
} }
return &v1pb.GetCurrentSessionResponse{ return &v1pb.GetCurrentSessionResponse{
User: convertUserFromStore(user), User: convertUserFromStore(user),
LastAccessedAt: lastAccessedAt,
}, nil }, nil
} }
...@@ -167,18 +171,15 @@ func (s *APIV1Service) CreateSession(ctx context.Context, request *v1pb.CreateSe ...@@ -167,18 +171,15 @@ func (s *APIV1Service) CreateSession(ctx context.Context, request *v1pb.CreateSe
return nil, status.Errorf(codes.PermissionDenied, "user has been archived with username %s", existingUser.Username) return nil, status.Errorf(codes.PermissionDenied, "user has been archived with username %s", existingUser.Username)
} }
expireTime := time.Now().Add(AccessTokenDuration) // Default session expiration time is 100 year
if request.NeverExpire { expireTime := time.Now().Add(100 * 365 * 24 * time.Hour)
// Set the expire time to 100 years.
expireTime = time.Now().Add(100 * 365 * 24 * time.Hour)
}
if err := s.doSignIn(ctx, existingUser, expireTime); err != nil { if err := s.doSignIn(ctx, existingUser, expireTime); err != nil {
return nil, status.Errorf(codes.Internal, "failed to sign in, error: %v", err) return nil, status.Errorf(codes.Internal, "failed to sign in, error: %v", err)
} }
return &v1pb.CreateSessionResponse{ return &v1pb.CreateSessionResponse{
User: convertUserFromStore(existingUser), User: convertUserFromStore(existingUser),
ExpiresAt: timestamppb.New(expireTime), LastAccessedAt: timestamppb.Now(),
}, nil }, nil
} }
...@@ -190,7 +191,7 @@ func (s *APIV1Service) doSignIn(ctx context.Context, user *store.User, expireTim ...@@ -190,7 +191,7 @@ func (s *APIV1Service) doSignIn(ctx context.Context, user *store.User, expireTim
} }
// Track session in user settings // Track session in user settings
if err := s.trackUserSession(ctx, user.ID, sessionID, expireTime); err != nil { if err := s.trackUserSession(ctx, user.ID, sessionID); err != nil {
// Log the error but don't fail the login if session tracking fails // Log the error but don't fail the login if session tracking fails
// This ensures backward compatibility // This ensures backward compatibility
slog.Error("failed to track user session", "error", err) slog.Error("failed to track user session", "error", err)
...@@ -308,14 +309,13 @@ func (s *APIV1Service) GetCurrentUser(ctx context.Context) (*store.User, error) ...@@ -308,14 +309,13 @@ func (s *APIV1Service) GetCurrentUser(ctx context.Context) (*store.User, error)
} }
// Helper function to track user session for session management. // Helper function to track user session for session management.
func (s *APIV1Service) trackUserSession(ctx context.Context, userID int32, sessionID string, expireTime time.Time) error { func (s *APIV1Service) trackUserSession(ctx context.Context, userID int32, sessionID string) error {
// Extract client information from the context // Extract client information from the context
clientInfo := s.extractClientInfo(ctx) clientInfo := s.extractClientInfo(ctx)
session := &storepb.SessionsUserSetting_Session{ session := &storepb.SessionsUserSetting_Session{
SessionId: sessionID, SessionId: sessionID,
CreateTime: timestamppb.Now(), CreateTime: timestamppb.Now(),
ExpireTime: timestamppb.New(expireTime),
LastAccessedTime: timestamppb.Now(), LastAccessedTime: timestamppb.Now(),
ClientInfo: clientInfo, ClientInfo: clientInfo,
} }
......
...@@ -650,7 +650,6 @@ func (s *APIV1Service) ListUserSessions(ctx context.Context, request *v1pb.ListU ...@@ -650,7 +650,6 @@ func (s *APIV1Service) ListUserSessions(ctx context.Context, request *v1pb.ListU
Name: fmt.Sprintf("users/%d/sessions/%s", userID, userSession.SessionId), Name: fmt.Sprintf("users/%d/sessions/%s", userID, userSession.SessionId),
SessionId: userSession.SessionId, SessionId: userSession.SessionId,
CreateTime: userSession.CreateTime, CreateTime: userSession.CreateTime,
ExpireTime: userSession.ExpireTime,
LastAccessedTime: userSession.LastAccessedTime, LastAccessedTime: userSession.LastAccessedTime,
} }
...@@ -715,7 +714,6 @@ func (s *APIV1Service) UpsertUserSession(ctx context.Context, userID int32, sess ...@@ -715,7 +714,6 @@ func (s *APIV1Service) UpsertUserSession(ctx context.Context, userID int32, sess
session := &storepb.SessionsUserSetting_Session{ session := &storepb.SessionsUserSetting_Session{
SessionId: sessionID, SessionId: sessionID,
CreateTime: timestamppb.Now(), CreateTime: timestamppb.Now(),
ExpireTime: timestamppb.New(time.Now().Add(30 * 24 * time.Hour)), // 30 days default
LastAccessedTime: timestamppb.Now(), LastAccessedTime: timestamppb.Now(),
ClientInfo: clientInfo, ClientInfo: clientInfo,
} }
......
import { Button, Checkbox, Input } from "@usememos/mui"; import { Button, Input } from "@usememos/mui";
import { LoaderIcon } from "lucide-react"; import { LoaderIcon } from "lucide-react";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { ClientError } from "nice-grpc-web"; import { ClientError } from "nice-grpc-web";
...@@ -17,7 +17,6 @@ const PasswordSignInForm = observer(() => { ...@@ -17,7 +17,6 @@ const PasswordSignInForm = observer(() => {
const actionBtnLoadingState = useLoading(false); const actionBtnLoadingState = useLoading(false);
const [username, setUsername] = useState(workspaceStore.state.profile.mode === "demo" ? "yourselfhosted" : ""); const [username, setUsername] = useState(workspaceStore.state.profile.mode === "demo" ? "yourselfhosted" : "");
const [password, setPassword] = useState(workspaceStore.state.profile.mode === "demo" ? "yourselfhosted" : ""); const [password, setPassword] = useState(workspaceStore.state.profile.mode === "demo" ? "yourselfhosted" : "");
const [remember, setRemember] = useState(true);
const handleUsernameInputChanged = (e: React.ChangeEvent<HTMLInputElement>) => { const handleUsernameInputChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
const text = e.target.value as string; const text = e.target.value as string;
...@@ -47,7 +46,6 @@ const PasswordSignInForm = observer(() => { ...@@ -47,7 +46,6 @@ const PasswordSignInForm = observer(() => {
actionBtnLoadingState.setLoading(); actionBtnLoadingState.setLoading();
await authServiceClient.createSession({ await authServiceClient.createSession({
passwordCredentials: { username, password }, passwordCredentials: { username, password },
neverExpire: remember,
}); });
await initialUserStore(); await initialUserStore();
navigateTo("/"); navigateTo("/");
...@@ -94,9 +92,6 @@ const PasswordSignInForm = observer(() => { ...@@ -94,9 +92,6 @@ const PasswordSignInForm = observer(() => {
/> />
</div> </div>
</div> </div>
<div className="flex flex-row justify-start items-center w-full mt-6">
<Checkbox label={t("common.remember-me")} checked={remember} onChange={(e) => setRemember(e.target.checked)} />
</div>
<div className="flex flex-row justify-end items-center w-full mt-6"> <div className="flex flex-row justify-end items-center w-full mt-6">
<Button <Button
type="submit" type="submit"
......
...@@ -60,6 +60,18 @@ const UserSessionsSection = () => { ...@@ -60,6 +60,18 @@ const UserSessionsSection = () => {
return parts.length > 0 ? parts.join(" • ") : "Unknown Device"; return parts.length > 0 ? parts.join(" • ") : "Unknown Device";
}; };
const getSessionExpirationDate = (session: UserSession) => {
if (!session.lastAccessedTime) return null;
// Calculate expiration as last_accessed_time + 2 weeks (14 days)
const expirationDate = new Date(session.lastAccessedTime.getTime() + 14 * 24 * 60 * 60 * 1000);
return expirationDate;
};
const isSessionExpired = (session: UserSession) => {
const expirationDate = getSessionExpirationDate(session);
return expirationDate ? expirationDate < new Date() : false;
};
const isCurrentSession = (session: UserSession) => { const isCurrentSession = (session: UserSession) => {
// A simple heuristic: the most recently accessed session is likely the current one // A simple heuristic: the most recently accessed session is likely the current one
if (userSessions.length === 0) return false; if (userSessions.length === 0) return false;
...@@ -126,7 +138,13 @@ const UserSessionsSection = () => { ...@@ -126,7 +138,13 @@ const UserSessionsSection = () => {
</div> </div>
</td> </td>
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500 dark:text-gray-400"> <td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500 dark:text-gray-400">
{userSession.expireTime?.toLocaleString() ?? t("setting.user-sessions-section.never")} <div className="flex items-center space-x-1">
<ClockIcon className="w-4 h-4" />
<span>
{getSessionExpirationDate(userSession)?.toLocaleString() ?? t("setting.user-sessions-section.never")}
{isSessionExpired(userSession) && <span className="ml-2 text-red-600 text-xs">(Expired)</span>}
</span>
</div>
</td> </td>
<td className="relative whitespace-nowrap py-2 pl-3 pr-4 text-right text-sm"> <td className="relative whitespace-nowrap py-2 pl-3 pr-4 text-right text-sm">
<Button <Button
......
...@@ -253,7 +253,7 @@ ...@@ -253,7 +253,7 @@
}, },
"user-sessions-section": { "user-sessions-section": {
"title": "Active Sessions", "title": "Active Sessions",
"description": "A list of all active sessions for your account. You can revoke any session except the current one.", "description": "A list of all active sessions for your account. Sessions automatically expire 2 weeks after the last activity. You can revoke any session except the current one.",
"device": "Device", "device": "Device",
"location": "Location", "location": "Location",
"last-active": "Last Active", "last-active": "Last Active",
...@@ -442,4 +442,4 @@ ...@@ -442,4 +442,4 @@
"rename-tag": "Rename tag", "rename-tag": "Rename tag",
"rename-tip": "All your memos with this tag will be updated." "rename-tip": "All your memos with this tag will be updated."
} }
} }
\ No newline at end of file
...@@ -19,8 +19,11 @@ export interface GetCurrentSessionResponse { ...@@ -19,8 +19,11 @@ export interface GetCurrentSessionResponse {
user?: user?:
| User | User
| undefined; | undefined;
/** Current session expiration time (if available). */ /**
expiresAt?: Date | undefined; * Last time the session was accessed.
* Used for sliding expiration calculation (last_accessed_time + 2 weeks).
*/
lastAccessedAt?: Date | undefined;
} }
export interface CreateSessionRequest { export interface CreateSessionRequest {
...@@ -29,14 +32,7 @@ export interface CreateSessionRequest { ...@@ -29,14 +32,7 @@ export interface CreateSessionRequest {
| CreateSessionRequest_PasswordCredentials | CreateSessionRequest_PasswordCredentials
| undefined; | undefined;
/** SSO provider authentication method. */ /** SSO provider authentication method. */
ssoCredentials?: ssoCredentials?: CreateSessionRequest_SSOCredentials | undefined;
| CreateSessionRequest_SSOCredentials
| undefined;
/**
* Whether the session should never expire.
* Optional field that defaults to false for security.
*/
neverExpire: boolean;
} }
/** Nested message for password-based authentication credentials. */ /** Nested message for password-based authentication credentials. */
...@@ -77,8 +73,11 @@ export interface CreateSessionResponse { ...@@ -77,8 +73,11 @@ export interface CreateSessionResponse {
user?: user?:
| User | User
| undefined; | undefined;
/** Token expiration time. */ /**
expiresAt?: Date | undefined; * Last time the session was accessed.
* Used for sliding expiration calculation (last_accessed_time + 2 weeks).
*/
lastAccessedAt?: Date | undefined;
} }
export interface DeleteSessionRequest { export interface DeleteSessionRequest {
...@@ -119,7 +118,7 @@ export const GetCurrentSessionRequest: MessageFns<GetCurrentSessionRequest> = { ...@@ -119,7 +118,7 @@ export const GetCurrentSessionRequest: MessageFns<GetCurrentSessionRequest> = {
}; };
function createBaseGetCurrentSessionResponse(): GetCurrentSessionResponse { function createBaseGetCurrentSessionResponse(): GetCurrentSessionResponse {
return { user: undefined, expiresAt: undefined }; return { user: undefined, lastAccessedAt: undefined };
} }
export const GetCurrentSessionResponse: MessageFns<GetCurrentSessionResponse> = { export const GetCurrentSessionResponse: MessageFns<GetCurrentSessionResponse> = {
...@@ -127,8 +126,8 @@ export const GetCurrentSessionResponse: MessageFns<GetCurrentSessionResponse> = ...@@ -127,8 +126,8 @@ export const GetCurrentSessionResponse: MessageFns<GetCurrentSessionResponse> =
if (message.user !== undefined) { if (message.user !== undefined) {
User.encode(message.user, writer.uint32(10).fork()).join(); User.encode(message.user, writer.uint32(10).fork()).join();
} }
if (message.expiresAt !== undefined) { if (message.lastAccessedAt !== undefined) {
Timestamp.encode(toTimestamp(message.expiresAt), writer.uint32(18).fork()).join(); Timestamp.encode(toTimestamp(message.lastAccessedAt), writer.uint32(18).fork()).join();
} }
return writer; return writer;
}, },
...@@ -153,7 +152,7 @@ export const GetCurrentSessionResponse: MessageFns<GetCurrentSessionResponse> = ...@@ -153,7 +152,7 @@ export const GetCurrentSessionResponse: MessageFns<GetCurrentSessionResponse> =
break; break;
} }
message.expiresAt = fromTimestamp(Timestamp.decode(reader, reader.uint32())); message.lastAccessedAt = fromTimestamp(Timestamp.decode(reader, reader.uint32()));
continue; continue;
} }
} }
...@@ -171,13 +170,13 @@ export const GetCurrentSessionResponse: MessageFns<GetCurrentSessionResponse> = ...@@ -171,13 +170,13 @@ export const GetCurrentSessionResponse: MessageFns<GetCurrentSessionResponse> =
fromPartial(object: DeepPartial<GetCurrentSessionResponse>): GetCurrentSessionResponse { fromPartial(object: DeepPartial<GetCurrentSessionResponse>): GetCurrentSessionResponse {
const message = createBaseGetCurrentSessionResponse(); const message = createBaseGetCurrentSessionResponse();
message.user = (object.user !== undefined && object.user !== null) ? User.fromPartial(object.user) : undefined; message.user = (object.user !== undefined && object.user !== null) ? User.fromPartial(object.user) : undefined;
message.expiresAt = object.expiresAt ?? undefined; message.lastAccessedAt = object.lastAccessedAt ?? undefined;
return message; return message;
}, },
}; };
function createBaseCreateSessionRequest(): CreateSessionRequest { function createBaseCreateSessionRequest(): CreateSessionRequest {
return { passwordCredentials: undefined, ssoCredentials: undefined, neverExpire: false }; return { passwordCredentials: undefined, ssoCredentials: undefined };
} }
export const CreateSessionRequest: MessageFns<CreateSessionRequest> = { export const CreateSessionRequest: MessageFns<CreateSessionRequest> = {
...@@ -188,9 +187,6 @@ export const CreateSessionRequest: MessageFns<CreateSessionRequest> = { ...@@ -188,9 +187,6 @@ export const CreateSessionRequest: MessageFns<CreateSessionRequest> = {
if (message.ssoCredentials !== undefined) { if (message.ssoCredentials !== undefined) {
CreateSessionRequest_SSOCredentials.encode(message.ssoCredentials, writer.uint32(18).fork()).join(); CreateSessionRequest_SSOCredentials.encode(message.ssoCredentials, writer.uint32(18).fork()).join();
} }
if (message.neverExpire !== false) {
writer.uint32(24).bool(message.neverExpire);
}
return writer; return writer;
}, },
...@@ -217,14 +213,6 @@ export const CreateSessionRequest: MessageFns<CreateSessionRequest> = { ...@@ -217,14 +213,6 @@ export const CreateSessionRequest: MessageFns<CreateSessionRequest> = {
message.ssoCredentials = CreateSessionRequest_SSOCredentials.decode(reader, reader.uint32()); message.ssoCredentials = CreateSessionRequest_SSOCredentials.decode(reader, reader.uint32());
continue; continue;
} }
case 3: {
if (tag !== 24) {
break;
}
message.neverExpire = reader.bool();
continue;
}
} }
if ((tag & 7) === 4 || tag === 0) { if ((tag & 7) === 4 || tag === 0) {
break; break;
...@@ -245,7 +233,6 @@ export const CreateSessionRequest: MessageFns<CreateSessionRequest> = { ...@@ -245,7 +233,6 @@ export const CreateSessionRequest: MessageFns<CreateSessionRequest> = {
message.ssoCredentials = (object.ssoCredentials !== undefined && object.ssoCredentials !== null) message.ssoCredentials = (object.ssoCredentials !== undefined && object.ssoCredentials !== null)
? CreateSessionRequest_SSOCredentials.fromPartial(object.ssoCredentials) ? CreateSessionRequest_SSOCredentials.fromPartial(object.ssoCredentials)
: undefined; : undefined;
message.neverExpire = object.neverExpire ?? false;
return message; return message;
}, },
}; };
...@@ -379,7 +366,7 @@ export const CreateSessionRequest_SSOCredentials: MessageFns<CreateSessionReques ...@@ -379,7 +366,7 @@ export const CreateSessionRequest_SSOCredentials: MessageFns<CreateSessionReques
}; };
function createBaseCreateSessionResponse(): CreateSessionResponse { function createBaseCreateSessionResponse(): CreateSessionResponse {
return { user: undefined, expiresAt: undefined }; return { user: undefined, lastAccessedAt: undefined };
} }
export const CreateSessionResponse: MessageFns<CreateSessionResponse> = { export const CreateSessionResponse: MessageFns<CreateSessionResponse> = {
...@@ -387,8 +374,8 @@ export const CreateSessionResponse: MessageFns<CreateSessionResponse> = { ...@@ -387,8 +374,8 @@ export const CreateSessionResponse: MessageFns<CreateSessionResponse> = {
if (message.user !== undefined) { if (message.user !== undefined) {
User.encode(message.user, writer.uint32(10).fork()).join(); User.encode(message.user, writer.uint32(10).fork()).join();
} }
if (message.expiresAt !== undefined) { if (message.lastAccessedAt !== undefined) {
Timestamp.encode(toTimestamp(message.expiresAt), writer.uint32(18).fork()).join(); Timestamp.encode(toTimestamp(message.lastAccessedAt), writer.uint32(18).fork()).join();
} }
return writer; return writer;
}, },
...@@ -413,7 +400,7 @@ export const CreateSessionResponse: MessageFns<CreateSessionResponse> = { ...@@ -413,7 +400,7 @@ export const CreateSessionResponse: MessageFns<CreateSessionResponse> = {
break; break;
} }
message.expiresAt = fromTimestamp(Timestamp.decode(reader, reader.uint32())); message.lastAccessedAt = fromTimestamp(Timestamp.decode(reader, reader.uint32()));
continue; continue;
} }
} }
...@@ -431,7 +418,7 @@ export const CreateSessionResponse: MessageFns<CreateSessionResponse> = { ...@@ -431,7 +418,7 @@ export const CreateSessionResponse: MessageFns<CreateSessionResponse> = {
fromPartial(object: DeepPartial<CreateSessionResponse>): CreateSessionResponse { fromPartial(object: DeepPartial<CreateSessionResponse>): CreateSessionResponse {
const message = createBaseCreateSessionResponse(); const message = createBaseCreateSessionResponse();
message.user = (object.user !== undefined && object.user !== null) ? User.fromPartial(object.user) : undefined; message.user = (object.user !== undefined && object.user !== null) ? User.fromPartial(object.user) : undefined;
message.expiresAt = object.expiresAt ?? undefined; message.lastAccessedAt = object.lastAccessedAt ?? undefined;
return message; return message;
}, },
}; };
......
...@@ -365,11 +365,10 @@ export interface UserSession { ...@@ -365,11 +365,10 @@ export interface UserSession {
createTime?: createTime?:
| Date | Date
| undefined; | undefined;
/** The timestamp when the session expires. */ /**
expireTime?: * The timestamp when the session was last accessed.
| Date * Used for sliding expiration calculation (last_accessed_time + 2 weeks).
| undefined; */
/** The timestamp when the session was last accessed. */
lastAccessedTime?: lastAccessedTime?:
| Date | Date
| undefined; | undefined;
...@@ -2073,14 +2072,7 @@ export const DeleteUserAccessTokenRequest: MessageFns<DeleteUserAccessTokenReque ...@@ -2073,14 +2072,7 @@ export const DeleteUserAccessTokenRequest: MessageFns<DeleteUserAccessTokenReque
}; };
function createBaseUserSession(): UserSession { function createBaseUserSession(): UserSession {
return { return { name: "", sessionId: "", createTime: undefined, lastAccessedTime: undefined, clientInfo: undefined };
name: "",
sessionId: "",
createTime: undefined,
expireTime: undefined,
lastAccessedTime: undefined,
clientInfo: undefined,
};
} }
export const UserSession: MessageFns<UserSession> = { export const UserSession: MessageFns<UserSession> = {
...@@ -2094,14 +2086,11 @@ export const UserSession: MessageFns<UserSession> = { ...@@ -2094,14 +2086,11 @@ export const UserSession: MessageFns<UserSession> = {
if (message.createTime !== undefined) { if (message.createTime !== undefined) {
Timestamp.encode(toTimestamp(message.createTime), writer.uint32(26).fork()).join(); Timestamp.encode(toTimestamp(message.createTime), writer.uint32(26).fork()).join();
} }
if (message.expireTime !== undefined) {
Timestamp.encode(toTimestamp(message.expireTime), writer.uint32(34).fork()).join();
}
if (message.lastAccessedTime !== undefined) { if (message.lastAccessedTime !== undefined) {
Timestamp.encode(toTimestamp(message.lastAccessedTime), writer.uint32(42).fork()).join(); Timestamp.encode(toTimestamp(message.lastAccessedTime), writer.uint32(34).fork()).join();
} }
if (message.clientInfo !== undefined) { if (message.clientInfo !== undefined) {
UserSession_ClientInfo.encode(message.clientInfo, writer.uint32(50).fork()).join(); UserSession_ClientInfo.encode(message.clientInfo, writer.uint32(42).fork()).join();
} }
return writer; return writer;
}, },
...@@ -2142,7 +2131,7 @@ export const UserSession: MessageFns<UserSession> = { ...@@ -2142,7 +2131,7 @@ export const UserSession: MessageFns<UserSession> = {
break; break;
} }
message.expireTime = fromTimestamp(Timestamp.decode(reader, reader.uint32())); message.lastAccessedTime = fromTimestamp(Timestamp.decode(reader, reader.uint32()));
continue; continue;
} }
case 5: { case 5: {
...@@ -2150,14 +2139,6 @@ export const UserSession: MessageFns<UserSession> = { ...@@ -2150,14 +2139,6 @@ export const UserSession: MessageFns<UserSession> = {
break; break;
} }
message.lastAccessedTime = fromTimestamp(Timestamp.decode(reader, reader.uint32()));
continue;
}
case 6: {
if (tag !== 50) {
break;
}
message.clientInfo = UserSession_ClientInfo.decode(reader, reader.uint32()); message.clientInfo = UserSession_ClientInfo.decode(reader, reader.uint32());
continue; continue;
} }
...@@ -2178,7 +2159,6 @@ export const UserSession: MessageFns<UserSession> = { ...@@ -2178,7 +2159,6 @@ export const UserSession: MessageFns<UserSession> = {
message.name = object.name ?? ""; message.name = object.name ?? "";
message.sessionId = object.sessionId ?? ""; message.sessionId = object.sessionId ?? "";
message.createTime = object.createTime ?? undefined; message.createTime = object.createTime ?? undefined;
message.expireTime = object.expireTime ?? undefined;
message.lastAccessedTime = object.lastAccessedTime ?? undefined; message.lastAccessedTime = object.lastAccessedTime ?? undefined;
message.clientInfo = (object.clientInfo !== undefined && object.clientInfo !== null) message.clientInfo = (object.clientInfo !== undefined && object.clientInfo !== null)
? UserSession_ClientInfo.fromPartial(object.clientInfo) ? UserSession_ClientInfo.fromPartial(object.clientInfo)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment