chore: tweak auth service

pull/4772/merge
Steven 2 days ago
parent 6e1b01cb68
commit 42d1650c6d

@ -6,19 +6,20 @@ import "api/v1/user_service.proto";
import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
option go_package = "gen/api/v1";
service AuthService {
// GetCurrentSession returns the current active session information.
// This method is idempotent and safe, suitable for checking current session state.
rpc GetCurrentSession(GetCurrentSessionRequest) returns (User) {
rpc GetCurrentSession(GetCurrentSessionRequest) returns (GetCurrentSessionResponse) {
option (google.api.http) = {get: "/api/v1/auth/sessions/current"};
}
// CreateSession authenticates a user and creates a new session.
// Returns the authenticated user information upon successful authentication.
rpc CreateSession(CreateSessionRequest) returns (User) {
rpc CreateSession(CreateSessionRequest) returns (CreateSessionResponse) {
option (google.api.http) = {
post: "/api/v1/auth/sessions"
body: "*"
@ -36,6 +37,9 @@ message GetCurrentSessionRequest {}
message GetCurrentSessionResponse {
User user = 1;
// Current session expiration time (if available).
google.protobuf.Timestamp expires_at = 2;
}
message CreateSessionRequest {
@ -67,7 +71,7 @@ message CreateSessionRequest {
// Provide one authentication method (username/password or SSO).
// Required field to specify the authentication method.
oneof method {
oneof credentials {
// Username and password authentication method.
PasswordCredentials password_credentials = 1;
@ -80,4 +84,12 @@ message CreateSessionRequest {
bool never_expire = 3 [(google.api.field_behavior) = OPTIONAL];
}
message CreateSessionResponse {
// The authenticated user information.
User user = 1;
// Token expiration time.
google.protobuf.Timestamp expires_at = 2;
}
message DeleteSessionRequest {}

@ -11,6 +11,7 @@ import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
emptypb "google.golang.org/protobuf/types/known/emptypb"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
unsafe "unsafe"
@ -60,8 +61,10 @@ func (*GetCurrentSessionRequest) Descriptor() ([]byte, []int) {
}
type GetCurrentSessionResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
state protoimpl.MessageState `protogen:"open.v1"`
User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
// Current session expiration time (if available).
ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@ -103,16 +106,23 @@ func (x *GetCurrentSessionResponse) GetUser() *User {
return nil
}
func (x *GetCurrentSessionResponse) GetExpiresAt() *timestamppb.Timestamp {
if x != nil {
return x.ExpiresAt
}
return nil
}
type CreateSessionRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Provide one authentication method (username/password or SSO).
// Required field to specify the authentication method.
//
// Types that are valid to be assigned to Method:
// Types that are valid to be assigned to Credentials:
//
// *CreateSessionRequest_PasswordCredentials_
// *CreateSessionRequest_SsoCredentials
Method isCreateSessionRequest_Method `protobuf_oneof:"method"`
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"`
@ -150,16 +160,16 @@ func (*CreateSessionRequest) Descriptor() ([]byte, []int) {
return file_api_v1_auth_service_proto_rawDescGZIP(), []int{2}
}
func (x *CreateSessionRequest) GetMethod() isCreateSessionRequest_Method {
func (x *CreateSessionRequest) GetCredentials() isCreateSessionRequest_Credentials {
if x != nil {
return x.Method
return x.Credentials
}
return nil
}
func (x *CreateSessionRequest) GetPasswordCredentials() *CreateSessionRequest_PasswordCredentials {
if x != nil {
if x, ok := x.Method.(*CreateSessionRequest_PasswordCredentials_); ok {
if x, ok := x.Credentials.(*CreateSessionRequest_PasswordCredentials_); ok {
return x.PasswordCredentials
}
}
@ -168,7 +178,7 @@ func (x *CreateSessionRequest) GetPasswordCredentials() *CreateSessionRequest_Pa
func (x *CreateSessionRequest) GetSsoCredentials() *CreateSessionRequest_SSOCredentials {
if x != nil {
if x, ok := x.Method.(*CreateSessionRequest_SsoCredentials); ok {
if x, ok := x.Credentials.(*CreateSessionRequest_SsoCredentials); ok {
return x.SsoCredentials
}
}
@ -182,8 +192,8 @@ func (x *CreateSessionRequest) GetNeverExpire() bool {
return false
}
type isCreateSessionRequest_Method interface {
isCreateSessionRequest_Method()
type isCreateSessionRequest_Credentials interface {
isCreateSessionRequest_Credentials()
}
type CreateSessionRequest_PasswordCredentials_ struct {
@ -196,9 +206,63 @@ type CreateSessionRequest_SsoCredentials struct {
SsoCredentials *CreateSessionRequest_SSOCredentials `protobuf:"bytes,2,opt,name=sso_credentials,json=ssoCredentials,proto3,oneof"`
}
func (*CreateSessionRequest_PasswordCredentials_) isCreateSessionRequest_Method() {}
func (*CreateSessionRequest_PasswordCredentials_) isCreateSessionRequest_Credentials() {}
func (*CreateSessionRequest_SsoCredentials) isCreateSessionRequest_Credentials() {}
type CreateSessionResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The authenticated user information.
User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
// Token expiration time.
ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *CreateSessionResponse) Reset() {
*x = CreateSessionResponse{}
mi := &file_api_v1_auth_service_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *CreateSessionResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateSessionRequest_SsoCredentials) isCreateSessionRequest_Method() {}
func (*CreateSessionResponse) ProtoMessage() {}
func (x *CreateSessionResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_auth_service_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CreateSessionResponse.ProtoReflect.Descriptor instead.
func (*CreateSessionResponse) Descriptor() ([]byte, []int) {
return file_api_v1_auth_service_proto_rawDescGZIP(), []int{3}
}
func (x *CreateSessionResponse) GetUser() *User {
if x != nil {
return x.User
}
return nil
}
func (x *CreateSessionResponse) GetExpiresAt() *timestamppb.Timestamp {
if x != nil {
return x.ExpiresAt
}
return nil
}
type DeleteSessionRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
@ -208,7 +272,7 @@ type DeleteSessionRequest struct {
func (x *DeleteSessionRequest) Reset() {
*x = DeleteSessionRequest{}
mi := &file_api_v1_auth_service_proto_msgTypes[3]
mi := &file_api_v1_auth_service_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -220,7 +284,7 @@ func (x *DeleteSessionRequest) String() string {
func (*DeleteSessionRequest) ProtoMessage() {}
func (x *DeleteSessionRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_auth_service_proto_msgTypes[3]
mi := &file_api_v1_auth_service_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -233,7 +297,7 @@ func (x *DeleteSessionRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use DeleteSessionRequest.ProtoReflect.Descriptor instead.
func (*DeleteSessionRequest) Descriptor() ([]byte, []int) {
return file_api_v1_auth_service_proto_rawDescGZIP(), []int{3}
return file_api_v1_auth_service_proto_rawDescGZIP(), []int{4}
}
// Nested message for password-based authentication credentials.
@ -251,7 +315,7 @@ type CreateSessionRequest_PasswordCredentials struct {
func (x *CreateSessionRequest_PasswordCredentials) Reset() {
*x = CreateSessionRequest_PasswordCredentials{}
mi := &file_api_v1_auth_service_proto_msgTypes[4]
mi := &file_api_v1_auth_service_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -263,7 +327,7 @@ func (x *CreateSessionRequest_PasswordCredentials) String() string {
func (*CreateSessionRequest_PasswordCredentials) ProtoMessage() {}
func (x *CreateSessionRequest_PasswordCredentials) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_auth_service_proto_msgTypes[4]
mi := &file_api_v1_auth_service_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -311,7 +375,7 @@ type CreateSessionRequest_SSOCredentials struct {
func (x *CreateSessionRequest_SSOCredentials) Reset() {
*x = CreateSessionRequest_SSOCredentials{}
mi := &file_api_v1_auth_service_proto_msgTypes[5]
mi := &file_api_v1_auth_service_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -323,7 +387,7 @@ func (x *CreateSessionRequest_SSOCredentials) String() string {
func (*CreateSessionRequest_SSOCredentials) ProtoMessage() {}
func (x *CreateSessionRequest_SSOCredentials) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_auth_service_proto_msgTypes[5]
mi := &file_api_v1_auth_service_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -364,10 +428,12 @@ var File_api_v1_auth_service_proto protoreflect.FileDescriptor
const file_api_v1_auth_service_proto_rawDesc = "" +
"\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\n" +
"\x18GetCurrentSessionRequest\"C\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" +
"\x19GetCurrentSessionResponse\x12&\n" +
"\x04user\x18\x01 \x01(\v2\x12.memos.api.v1.UserR\x04user\"\xdb\x03\n" +
"\x04user\x18\x01 \x01(\v2\x12.memos.api.v1.UserR\x04user\x129\n" +
"\n" +
"expires_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\"\xe0\x03\n" +
"\x14CreateSessionRequest\x12k\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" +
@ -378,12 +444,16 @@ const file_api_v1_auth_service_proto_rawDesc = "" +
"\x0eSSOCredentials\x12\x1a\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" +
"\fredirect_uri\x18\x03 \x01(\tB\x03\xe0A\x02R\vredirectUriB\b\n" +
"\x06method\"\x16\n" +
"\x14DeleteSessionRequest2\xe4\x02\n" +
"\vAuthService\x12v\n" +
"\x11GetCurrentSession\x12&.memos.api.v1.GetCurrentSessionRequest\x1a\x12.memos.api.v1.User\"%\x82\xd3\xe4\x93\x02\x1f\x12\x1d/api/v1/auth/sessions/current\x12i\n" +
"\rCreateSession\x12\".memos.api.v1.CreateSessionRequest\x1a\x12.memos.api.v1.User\" \x82\xd3\xe4\x93\x02\x1a:\x01*\"\x15/api/v1/auth/sessions\x12r\n" +
"\fredirect_uri\x18\x03 \x01(\tB\x03\xe0A\x02R\vredirectUriB\r\n" +
"\vcredentials\"z\n" +
"\x15CreateSessionResponse\x12&\n" +
"\x04user\x18\x01 \x01(\v2\x12.memos.api.v1.UserR\x04user\x129\n" +
"\n" +
"expires_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\"\x16\n" +
"\x14DeleteSessionRequest2\x8b\x03\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" +
"\rCreateSession\x12\".memos.api.v1.CreateSessionRequest\x1a#.memos.api.v1.CreateSessionResponse\" \x82\xd3\xe4\x93\x02\x1a:\x01*\"\x15/api/v1/auth/sessions\x12r\n" +
"\rDeleteSession\x12\".memos.api.v1.DeleteSessionRequest\x1a\x16.google.protobuf.Empty\"%\x82\xd3\xe4\x93\x02\x1f*\x1d/api/v1/auth/sessions/currentB\xa8\x01\n" +
"\x10com.memos.api.v1B\x10AuthServiceProtoP\x01Z0github.com/usememos/memos/proto/gen/api/v1;apiv1\xa2\x02\x03MAX\xaa\x02\fMemos.Api.V1\xca\x02\fMemos\\Api\\V1\xe2\x02\x18Memos\\Api\\V1\\GPBMetadata\xea\x02\x0eMemos::Api::V1b\x06proto3"
@ -399,32 +469,37 @@ func file_api_v1_auth_service_proto_rawDescGZIP() []byte {
return file_api_v1_auth_service_proto_rawDescData
}
var file_api_v1_auth_service_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_api_v1_auth_service_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_api_v1_auth_service_proto_goTypes = []any{
(*GetCurrentSessionRequest)(nil), // 0: memos.api.v1.GetCurrentSessionRequest
(*GetCurrentSessionResponse)(nil), // 1: memos.api.v1.GetCurrentSessionResponse
(*CreateSessionRequest)(nil), // 2: memos.api.v1.CreateSessionRequest
(*DeleteSessionRequest)(nil), // 3: memos.api.v1.DeleteSessionRequest
(*CreateSessionRequest_PasswordCredentials)(nil), // 4: memos.api.v1.CreateSessionRequest.PasswordCredentials
(*CreateSessionRequest_SSOCredentials)(nil), // 5: memos.api.v1.CreateSessionRequest.SSOCredentials
(*User)(nil), // 6: memos.api.v1.User
(*emptypb.Empty)(nil), // 7: google.protobuf.Empty
(*CreateSessionResponse)(nil), // 3: memos.api.v1.CreateSessionResponse
(*DeleteSessionRequest)(nil), // 4: memos.api.v1.DeleteSessionRequest
(*CreateSessionRequest_PasswordCredentials)(nil), // 5: memos.api.v1.CreateSessionRequest.PasswordCredentials
(*CreateSessionRequest_SSOCredentials)(nil), // 6: memos.api.v1.CreateSessionRequest.SSOCredentials
(*User)(nil), // 7: memos.api.v1.User
(*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp
(*emptypb.Empty)(nil), // 9: google.protobuf.Empty
}
var file_api_v1_auth_service_proto_depIdxs = []int32{
6, // 0: memos.api.v1.GetCurrentSessionResponse.user:type_name -> memos.api.v1.User
4, // 1: memos.api.v1.CreateSessionRequest.password_credentials:type_name -> memos.api.v1.CreateSessionRequest.PasswordCredentials
5, // 2: memos.api.v1.CreateSessionRequest.sso_credentials:type_name -> memos.api.v1.CreateSessionRequest.SSOCredentials
0, // 3: memos.api.v1.AuthService.GetCurrentSession:input_type -> memos.api.v1.GetCurrentSessionRequest
2, // 4: memos.api.v1.AuthService.CreateSession:input_type -> memos.api.v1.CreateSessionRequest
3, // 5: memos.api.v1.AuthService.DeleteSession:input_type -> memos.api.v1.DeleteSessionRequest
6, // 6: memos.api.v1.AuthService.GetCurrentSession:output_type -> memos.api.v1.User
6, // 7: memos.api.v1.AuthService.CreateSession:output_type -> memos.api.v1.User
7, // 8: memos.api.v1.AuthService.DeleteSession:output_type -> google.protobuf.Empty
6, // [6:9] is the sub-list for method output_type
3, // [3:6] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
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
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
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
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
4, // 8: memos.api.v1.AuthService.DeleteSession:input_type -> memos.api.v1.DeleteSessionRequest
1, // 9: memos.api.v1.AuthService.GetCurrentSession:output_type -> memos.api.v1.GetCurrentSessionResponse
3, // 10: memos.api.v1.AuthService.CreateSession:output_type -> memos.api.v1.CreateSessionResponse
9, // 11: memos.api.v1.AuthService.DeleteSession:output_type -> google.protobuf.Empty
9, // [9:12] is the sub-list for method output_type
6, // [6:9] is the sub-list for method input_type
6, // [6:6] is the sub-list for extension type_name
6, // [6:6] is the sub-list for extension extendee
0, // [0:6] is the sub-list for field type_name
}
func init() { file_api_v1_auth_service_proto_init() }
@ -443,7 +518,7 @@ func file_api_v1_auth_service_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_v1_auth_service_proto_rawDesc), len(file_api_v1_auth_service_proto_rawDesc)),
NumEnums: 0,
NumMessages: 6,
NumMessages: 7,
NumExtensions: 0,
NumServices: 1,
},

@ -31,10 +31,10 @@ const (
type AuthServiceClient interface {
// GetCurrentSession returns the current active session information.
// This method is idempotent and safe, suitable for checking current session state.
GetCurrentSession(ctx context.Context, in *GetCurrentSessionRequest, opts ...grpc.CallOption) (*User, error)
GetCurrentSession(ctx context.Context, in *GetCurrentSessionRequest, opts ...grpc.CallOption) (*GetCurrentSessionResponse, error)
// CreateSession authenticates a user and creates a new session.
// Returns the authenticated user information upon successful authentication.
CreateSession(ctx context.Context, in *CreateSessionRequest, opts ...grpc.CallOption) (*User, error)
CreateSession(ctx context.Context, in *CreateSessionRequest, opts ...grpc.CallOption) (*CreateSessionResponse, error)
// DeleteSession terminates the current user session.
// This is an idempotent operation that invalidates the user's authentication.
DeleteSession(ctx context.Context, in *DeleteSessionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
@ -48,9 +48,9 @@ func NewAuthServiceClient(cc grpc.ClientConnInterface) AuthServiceClient {
return &authServiceClient{cc}
}
func (c *authServiceClient) GetCurrentSession(ctx context.Context, in *GetCurrentSessionRequest, opts ...grpc.CallOption) (*User, error) {
func (c *authServiceClient) GetCurrentSession(ctx context.Context, in *GetCurrentSessionRequest, opts ...grpc.CallOption) (*GetCurrentSessionResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(User)
out := new(GetCurrentSessionResponse)
err := c.cc.Invoke(ctx, AuthService_GetCurrentSession_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
@ -58,9 +58,9 @@ func (c *authServiceClient) GetCurrentSession(ctx context.Context, in *GetCurren
return out, nil
}
func (c *authServiceClient) CreateSession(ctx context.Context, in *CreateSessionRequest, opts ...grpc.CallOption) (*User, error) {
func (c *authServiceClient) CreateSession(ctx context.Context, in *CreateSessionRequest, opts ...grpc.CallOption) (*CreateSessionResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(User)
out := new(CreateSessionResponse)
err := c.cc.Invoke(ctx, AuthService_CreateSession_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
@ -84,10 +84,10 @@ func (c *authServiceClient) DeleteSession(ctx context.Context, in *DeleteSession
type AuthServiceServer interface {
// GetCurrentSession returns the current active session information.
// This method is idempotent and safe, suitable for checking current session state.
GetCurrentSession(context.Context, *GetCurrentSessionRequest) (*User, error)
GetCurrentSession(context.Context, *GetCurrentSessionRequest) (*GetCurrentSessionResponse, error)
// CreateSession authenticates a user and creates a new session.
// Returns the authenticated user information upon successful authentication.
CreateSession(context.Context, *CreateSessionRequest) (*User, error)
CreateSession(context.Context, *CreateSessionRequest) (*CreateSessionResponse, error)
// DeleteSession terminates the current user session.
// This is an idempotent operation that invalidates the user's authentication.
DeleteSession(context.Context, *DeleteSessionRequest) (*emptypb.Empty, error)
@ -101,10 +101,10 @@ type AuthServiceServer interface {
// pointer dereference when methods are called.
type UnimplementedAuthServiceServer struct{}
func (UnimplementedAuthServiceServer) GetCurrentSession(context.Context, *GetCurrentSessionRequest) (*User, error) {
func (UnimplementedAuthServiceServer) GetCurrentSession(context.Context, *GetCurrentSessionRequest) (*GetCurrentSessionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetCurrentSession not implemented")
}
func (UnimplementedAuthServiceServer) CreateSession(context.Context, *CreateSessionRequest) (*User, error) {
func (UnimplementedAuthServiceServer) CreateSession(context.Context, *CreateSessionRequest) (*CreateSessionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateSession not implemented")
}
func (UnimplementedAuthServiceServer) DeleteSession(context.Context, *DeleteSessionRequest) (*emptypb.Empty, error) {

@ -141,7 +141,7 @@ paths:
"200":
description: A successful response.
schema:
$ref: '#/definitions/v1User'
$ref: '#/definitions/v1CreateSessionResponse'
default:
description: An unexpected error response.
schema:
@ -164,7 +164,7 @@ paths:
"200":
description: A successful response.
schema:
$ref: '#/definitions/v1User'
$ref: '#/definitions/v1GetCurrentSessionResponse'
default:
description: An unexpected error response.
schema:
@ -3287,6 +3287,16 @@ definitions:
description: |-
Whether the session should never expire.
Optional field that defaults to false for security.
v1CreateSessionResponse:
type: object
properties:
user:
$ref: '#/definitions/v1User'
description: The authenticated user information.
expiresAt:
type: string
format: date-time
description: Token expiration time.
v1EmbeddedContentNode:
type: object
properties:
@ -3301,6 +3311,15 @@ definitions:
properties:
symbol:
type: string
v1GetCurrentSessionResponse:
type: object
properties:
user:
$ref: '#/definitions/v1User'
expiresAt:
type: string
format: date-time
description: Current session expiration time (if available).
v1HTMLElementNode:
type: object
properties:

@ -3,7 +3,6 @@ package v1
var authenticationAllowlistMethods = map[string]bool{
"/memos.api.v1.WorkspaceService/GetWorkspaceProfile": true,
"/memos.api.v1.WorkspaceService/GetWorkspaceSetting": true,
"/memos.api.v1.IdentityProviderService/GetIdentityProvider": true,
"/memos.api.v1.IdentityProviderService/ListIdentityProviders": true,
"/memos.api.v1.AuthService/CreateSession": true,
"/memos.api.v1.AuthService/GetCurrentSession": true,

@ -29,7 +29,7 @@ const (
unmatchedUsernameAndPasswordError = "unmatched username and password"
)
func (s *APIV1Service) GetCurrentSession(ctx context.Context, _ *v1pb.GetCurrentSessionRequest) (*v1pb.User, error) {
func (s *APIV1Service) GetCurrentSession(ctx context.Context, _ *v1pb.GetCurrentSessionRequest) (*v1pb.GetCurrentSessionResponse, error) {
user, err := s.GetCurrentUser(ctx)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
@ -50,10 +50,12 @@ func (s *APIV1Service) GetCurrentSession(ctx context.Context, _ *v1pb.GetCurrent
}
}
return convertUserFromStore(user), nil
return &v1pb.GetCurrentSessionResponse{
User: convertUserFromStore(user),
}, nil
}
func (s *APIV1Service) CreateSession(ctx context.Context, request *v1pb.CreateSessionRequest) (*v1pb.User, error) {
func (s *APIV1Service) CreateSession(ctx context.Context, request *v1pb.CreateSessionRequest) (*v1pb.CreateSessionResponse, error) {
var existingUser *store.User
if passwordCredentials := request.GetPasswordCredentials(); passwordCredentials != nil {
user, err := s.Store.GetUser(ctx, &store.FindUser{
@ -173,7 +175,11 @@ func (s *APIV1Service) CreateSession(ctx context.Context, request *v1pb.CreateSe
if err := s.doSignIn(ctx, existingUser, expireTime); err != nil {
return nil, status.Errorf(codes.Internal, "failed to sign in, error: %v", err)
}
return convertUserFromStore(existingUser), nil
return &v1pb.CreateSessionResponse{
User: convertUserFromStore(existingUser),
ExpiresAt: timestamppb.New(expireTime),
}, nil
}
func (s *APIV1Service) doSignIn(ctx context.Context, user *store.User, expireTime time.Time) error {

@ -13,5 +13,5 @@ func TestGetCurrentSchemaVersion(t *testing.T) {
currentSchemaVersion, err := ts.GetCurrentSchemaVersion()
require.NoError(t, err)
require.Equal(t, "0.24.2", currentSchemaVersion)
require.Equal(t, "0.25.1", currentSchemaVersion)
}

@ -45,7 +45,10 @@ const PasswordSignInForm = observer(() => {
try {
actionBtnLoadingState.setLoading();
await authServiceClient.createSession({ passwordCredentials: { username, password }, neverExpire: remember });
await authServiceClient.createSession({
passwordCredentials: { username, password },
neverExpire: remember,
});
await initialUserStore();
navigateTo("/");
} catch (error: any) {

@ -231,7 +231,16 @@ const userStore = (() => {
export const initialUserStore = async () => {
try {
const currentUser = await authServiceClient.getCurrentSession({});
const { user: currentUser } = await authServiceClient.getCurrentSession({});
if (!currentUser) {
// If no user is authenticated, we can skip the rest of the initialization.
userStore.state.setPartial({
currentUser: undefined,
userSetting: undefined,
userMapByName: {},
});
return;
}
const userSetting = await userServiceClient.getUserSetting({ name: currentUser.name });
userStore.state.setPartial({
currentUser: currentUser.name,

@ -7,6 +7,7 @@
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { Empty } from "../../google/protobuf/empty";
import { Timestamp } from "../../google/protobuf/timestamp";
import { User } from "./user_service";
export const protobufPackage = "memos.api.v1";
@ -15,7 +16,11 @@ export interface GetCurrentSessionRequest {
}
export interface GetCurrentSessionResponse {
user?: User | undefined;
user?:
| User
| undefined;
/** Current session expiration time (if available). */
expiresAt?: Date | undefined;
}
export interface CreateSessionRequest {
@ -67,6 +72,15 @@ export interface CreateSessionRequest_SSOCredentials {
redirectUri: string;
}
export interface CreateSessionResponse {
/** The authenticated user information. */
user?:
| User
| undefined;
/** Token expiration time. */
expiresAt?: Date | undefined;
}
export interface DeleteSessionRequest {
}
@ -105,7 +119,7 @@ export const GetCurrentSessionRequest: MessageFns<GetCurrentSessionRequest> = {
};
function createBaseGetCurrentSessionResponse(): GetCurrentSessionResponse {
return { user: undefined };
return { user: undefined, expiresAt: undefined };
}
export const GetCurrentSessionResponse: MessageFns<GetCurrentSessionResponse> = {
@ -113,6 +127,9 @@ export const GetCurrentSessionResponse: MessageFns<GetCurrentSessionResponse> =
if (message.user !== undefined) {
User.encode(message.user, writer.uint32(10).fork()).join();
}
if (message.expiresAt !== undefined) {
Timestamp.encode(toTimestamp(message.expiresAt), writer.uint32(18).fork()).join();
}
return writer;
},
@ -131,6 +148,14 @@ export const GetCurrentSessionResponse: MessageFns<GetCurrentSessionResponse> =
message.user = User.decode(reader, reader.uint32());
continue;
}
case 2: {
if (tag !== 18) {
break;
}
message.expiresAt = fromTimestamp(Timestamp.decode(reader, reader.uint32()));
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
@ -146,6 +171,7 @@ export const GetCurrentSessionResponse: MessageFns<GetCurrentSessionResponse> =
fromPartial(object: DeepPartial<GetCurrentSessionResponse>): GetCurrentSessionResponse {
const message = createBaseGetCurrentSessionResponse();
message.user = (object.user !== undefined && object.user !== null) ? User.fromPartial(object.user) : undefined;
message.expiresAt = object.expiresAt ?? undefined;
return message;
},
};
@ -352,6 +378,64 @@ export const CreateSessionRequest_SSOCredentials: MessageFns<CreateSessionReques
},
};
function createBaseCreateSessionResponse(): CreateSessionResponse {
return { user: undefined, expiresAt: undefined };
}
export const CreateSessionResponse: MessageFns<CreateSessionResponse> = {
encode(message: CreateSessionResponse, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.user !== undefined) {
User.encode(message.user, writer.uint32(10).fork()).join();
}
if (message.expiresAt !== undefined) {
Timestamp.encode(toTimestamp(message.expiresAt), writer.uint32(18).fork()).join();
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): CreateSessionResponse {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseCreateSessionResponse();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.user = User.decode(reader, reader.uint32());
continue;
}
case 2: {
if (tag !== 18) {
break;
}
message.expiresAt = fromTimestamp(Timestamp.decode(reader, reader.uint32()));
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
create(base?: DeepPartial<CreateSessionResponse>): CreateSessionResponse {
return CreateSessionResponse.fromPartial(base ?? {});
},
fromPartial(object: DeepPartial<CreateSessionResponse>): CreateSessionResponse {
const message = createBaseCreateSessionResponse();
message.user = (object.user !== undefined && object.user !== null) ? User.fromPartial(object.user) : undefined;
message.expiresAt = object.expiresAt ?? undefined;
return message;
},
};
function createBaseDeleteSessionRequest(): DeleteSessionRequest {
return {};
}
@ -399,7 +483,7 @@ export const AuthServiceDefinition = {
name: "GetCurrentSession",
requestType: GetCurrentSessionRequest,
requestStream: false,
responseType: User,
responseType: GetCurrentSessionResponse,
responseStream: false,
options: {
_unknownFields: {
@ -450,7 +534,7 @@ export const AuthServiceDefinition = {
name: "CreateSession",
requestType: CreateSessionRequest,
requestStream: false,
responseType: User,
responseType: CreateSessionResponse,
responseStream: false,
options: {
_unknownFields: {
@ -550,6 +634,18 @@ export type DeepPartial<T> = T extends Builtin ? T
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
: Partial<T>;
function toTimestamp(date: Date): Timestamp {
const seconds = Math.trunc(date.getTime() / 1_000);
const nanos = (date.getTime() % 1_000) * 1_000_000;
return { seconds, nanos };
}
function fromTimestamp(t: Timestamp): Date {
let millis = (t.seconds || 0) * 1_000;
millis += (t.nanos || 0) / 1_000_000;
return new globalThis.Date(millis);
}
export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T;

@ -35,7 +35,7 @@ export enum Edition {
EDITION_2024 = "EDITION_2024",
/**
* EDITION_1_TEST_ONLY - Placeholder editions for testing feature resolution. These should not be
* used or relyed on outside of tests.
* used or relied on outside of tests.
*/
EDITION_1_TEST_ONLY = "EDITION_1_TEST_ONLY",
EDITION_2_TEST_ONLY = "EDITION_2_TEST_ONLY",
@ -177,11 +177,19 @@ export interface FileDescriptorProto {
* The supported values are "proto2", "proto3", and "editions".
*
* If `edition` is present, this value must be "editions".
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
syntax?:
| string
| undefined;
/** The edition of the proto file. */
/**
* The edition of the proto file.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
edition?: Edition | undefined;
}
@ -828,7 +836,12 @@ export interface FileOptions {
rubyPackage?:
| string
| undefined;
/** Any features defined in the specific edition. */
/**
* Any features defined in the specific edition.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
features?:
| FeatureSet
| undefined;
@ -966,7 +979,12 @@ export interface MessageOptions {
deprecatedLegacyJsonFieldConflicts?:
| boolean
| undefined;
/** Any features defined in the specific edition. */
/**
* Any features defined in the specific edition.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
features?:
| FeatureSet
| undefined;
@ -976,12 +994,13 @@ export interface MessageOptions {
export interface FieldOptions {
/**
* NOTE: ctype is deprecated. Use `features.(pb.cpp).string_type` instead.
* The ctype option instructs the C++ code generator to use a different
* representation of the field than it normally would. See the specific
* options below. This option is only implemented to support use of
* [ctype=CORD] and [ctype=STRING] (the default) on non-repeated fields of
* type "bytes" in the open source release -- sorry, we'll try to include
* other types in a future version!
* type "bytes" in the open source release.
* TODO: make ctype actually deprecated.
*/
ctype?:
| FieldOptions_CType
@ -1070,7 +1089,12 @@ export interface FieldOptions {
retention?: FieldOptions_OptionRetention | undefined;
targets: FieldOptions_OptionTargetType[];
editionDefaults: FieldOptions_EditionDefault[];
/** Any features defined in the specific edition. */
/**
* Any features defined in the specific edition.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
features?: FeatureSet | undefined;
featureSupport?:
| FieldOptions_FeatureSupport
@ -1169,11 +1193,7 @@ export function fieldOptions_JSTypeToNumber(object: FieldOptions_JSType): number
}
}
/**
* If set to RETENTION_SOURCE, the option will be omitted from the binary.
* Note: as of January 2023, support for this is in progress and does not yet
* have an effect (b/264593489).
*/
/** If set to RETENTION_SOURCE, the option will be omitted from the binary. */
export enum FieldOptions_OptionRetention {
RETENTION_UNKNOWN = "RETENTION_UNKNOWN",
RETENTION_RUNTIME = "RETENTION_RUNTIME",
@ -1216,8 +1236,7 @@ export function fieldOptions_OptionRetentionToNumber(object: FieldOptions_Option
/**
* This indicates the types of entities that the field may apply to when used
* as an option. If it is unset, then the field may be freely used as an
* option on any kind of entity. Note: as of January 2023, support for this is
* in progress and does not yet have an effect (b/264593489).
* option on any kind of entity.
*/
export enum FieldOptions_OptionTargetType {
TARGET_TYPE_UNKNOWN = "TARGET_TYPE_UNKNOWN",
@ -1341,7 +1360,12 @@ export interface FieldOptions_FeatureSupport {
}
export interface OneofOptions {
/** Any features defined in the specific edition. */
/**
* Any features defined in the specific edition.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
features?:
| FeatureSet
| undefined;
@ -1379,7 +1403,12 @@ export interface EnumOptions {
deprecatedLegacyJsonFieldConflicts?:
| boolean
| undefined;
/** Any features defined in the specific edition. */
/**
* Any features defined in the specific edition.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
features?:
| FeatureSet
| undefined;
@ -1397,7 +1426,12 @@ export interface EnumValueOptions {
deprecated?:
| boolean
| undefined;
/** Any features defined in the specific edition. */
/**
* Any features defined in the specific edition.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
features?:
| FeatureSet
| undefined;
@ -1418,7 +1452,12 @@ export interface EnumValueOptions {
}
export interface ServiceOptions {
/** Any features defined in the specific edition. */
/**
* Any features defined in the specific edition.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
features?:
| FeatureSet
| undefined;
@ -1446,7 +1485,12 @@ export interface MethodOptions {
idempotencyLevel?:
| MethodOptions_IdempotencyLevel
| undefined;
/** Any features defined in the specific edition. */
/**
* Any features defined in the specific edition.
* WARNING: This field should only be used by protobuf plugins or special
* cases like the proto compiler. Other uses are discouraged and
* developers should rely on the protoreflect APIs for their client language.
*/
features?:
| FeatureSet
| undefined;
@ -1549,6 +1593,7 @@ export interface FeatureSet {
utf8Validation?: FeatureSet_Utf8Validation | undefined;
messageEncoding?: FeatureSet_MessageEncoding | undefined;
jsonFormat?: FeatureSet_JsonFormat | undefined;
enforceNamingStyle?: FeatureSet_EnforceNamingStyle | undefined;
}
export enum FeatureSet_FieldPresence {
@ -1791,6 +1836,45 @@ export function featureSet_JsonFormatToNumber(object: FeatureSet_JsonFormat): nu
}
}
export enum FeatureSet_EnforceNamingStyle {
ENFORCE_NAMING_STYLE_UNKNOWN = "ENFORCE_NAMING_STYLE_UNKNOWN",
STYLE2024 = "STYLE2024",
STYLE_LEGACY = "STYLE_LEGACY",
UNRECOGNIZED = "UNRECOGNIZED",
}
export function featureSet_EnforceNamingStyleFromJSON(object: any): FeatureSet_EnforceNamingStyle {
switch (object) {
case 0:
case "ENFORCE_NAMING_STYLE_UNKNOWN":
return FeatureSet_EnforceNamingStyle.ENFORCE_NAMING_STYLE_UNKNOWN;
case 1:
case "STYLE2024":
return FeatureSet_EnforceNamingStyle.STYLE2024;
case 2:
case "STYLE_LEGACY":
return FeatureSet_EnforceNamingStyle.STYLE_LEGACY;
case -1:
case "UNRECOGNIZED":
default:
return FeatureSet_EnforceNamingStyle.UNRECOGNIZED;
}
}
export function featureSet_EnforceNamingStyleToNumber(object: FeatureSet_EnforceNamingStyle): number {
switch (object) {
case FeatureSet_EnforceNamingStyle.ENFORCE_NAMING_STYLE_UNKNOWN:
return 0;
case FeatureSet_EnforceNamingStyle.STYLE2024:
return 1;
case FeatureSet_EnforceNamingStyle.STYLE_LEGACY:
return 2;
case FeatureSet_EnforceNamingStyle.UNRECOGNIZED:
default:
return -1;
}
}
/**
* A compiled specification for the defaults of a set of features. These
* messages are generated from FeatureSet extensions and can be used to seed
@ -4914,6 +4998,7 @@ function createBaseFeatureSet(): FeatureSet {
utf8Validation: FeatureSet_Utf8Validation.UTF8_VALIDATION_UNKNOWN,
messageEncoding: FeatureSet_MessageEncoding.MESSAGE_ENCODING_UNKNOWN,
jsonFormat: FeatureSet_JsonFormat.JSON_FORMAT_UNKNOWN,
enforceNamingStyle: FeatureSet_EnforceNamingStyle.ENFORCE_NAMING_STYLE_UNKNOWN,
};
}
@ -4948,6 +5033,12 @@ export const FeatureSet: MessageFns<FeatureSet> = {
if (message.jsonFormat !== undefined && message.jsonFormat !== FeatureSet_JsonFormat.JSON_FORMAT_UNKNOWN) {
writer.uint32(48).int32(featureSet_JsonFormatToNumber(message.jsonFormat));
}
if (
message.enforceNamingStyle !== undefined &&
message.enforceNamingStyle !== FeatureSet_EnforceNamingStyle.ENFORCE_NAMING_STYLE_UNKNOWN
) {
writer.uint32(56).int32(featureSet_EnforceNamingStyleToNumber(message.enforceNamingStyle));
}
return writer;
},
@ -5006,6 +5097,14 @@ export const FeatureSet: MessageFns<FeatureSet> = {
message.jsonFormat = featureSet_JsonFormatFromJSON(reader.int32());
continue;
}
case 7: {
if (tag !== 56) {
break;
}
message.enforceNamingStyle = featureSet_EnforceNamingStyleFromJSON(reader.int32());
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
@ -5027,6 +5126,8 @@ export const FeatureSet: MessageFns<FeatureSet> = {
message.utf8Validation = object.utf8Validation ?? FeatureSet_Utf8Validation.UTF8_VALIDATION_UNKNOWN;
message.messageEncoding = object.messageEncoding ?? FeatureSet_MessageEncoding.MESSAGE_ENCODING_UNKNOWN;
message.jsonFormat = object.jsonFormat ?? FeatureSet_JsonFormat.JSON_FORMAT_UNKNOWN;
message.enforceNamingStyle = object.enforceNamingStyle ??
FeatureSet_EnforceNamingStyle.ENFORCE_NAMING_STYLE_UNKNOWN;
return message;
},
};

Loading…
Cancel
Save