mirror of https://github.com/usememos/memos.git
refactor: user auth improvements (#5360)
This commit is contained in:
parent
2c2ef53737
commit
7932f6d0d0
|
|
@ -7,6 +7,10 @@ web/dist
|
|||
# Build artifacts
|
||||
build/
|
||||
bin/
|
||||
memos
|
||||
|
||||
# Plan/design documents
|
||||
docs/plans/
|
||||
|
||||
.DS_Store
|
||||
|
||||
|
|
|
|||
|
|
@ -11,87 +11,104 @@ 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 (GetCurrentSessionResponse) {
|
||||
option (google.api.http) = {get: "/api/v1/auth/sessions/current"};
|
||||
// GetCurrentUser returns the authenticated user's information.
|
||||
// Validates the access token and returns user details.
|
||||
// Similar to OIDC's /userinfo endpoint.
|
||||
rpc GetCurrentUser(GetCurrentUserRequest) returns (GetCurrentUserResponse) {
|
||||
option (google.api.http) = {get: "/api/v1/auth/me"};
|
||||
}
|
||||
|
||||
// CreateSession authenticates a user and creates a new session.
|
||||
// Returns the authenticated user information upon successful authentication.
|
||||
rpc CreateSession(CreateSessionRequest) returns (CreateSessionResponse) {
|
||||
// SignIn authenticates a user with credentials and returns tokens.
|
||||
// On success, returns an access token and sets a refresh token cookie.
|
||||
// Supports password-based and SSO authentication methods.
|
||||
rpc SignIn(SignInRequest) returns (SignInResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v1/auth/sessions"
|
||||
post: "/api/v1/auth/signin"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// DeleteSession terminates the current user session.
|
||||
// This is an idempotent operation that invalidates the user's authentication.
|
||||
rpc DeleteSession(DeleteSessionRequest) returns (google.protobuf.Empty) {
|
||||
option (google.api.http) = {delete: "/api/v1/auth/sessions/current"};
|
||||
// SignOut terminates the user's authentication.
|
||||
// Revokes the refresh token and clears the authentication cookie.
|
||||
rpc SignOut(SignOutRequest) returns (google.protobuf.Empty) {
|
||||
option (google.api.http) = {post: "/api/v1/auth/signout"};
|
||||
}
|
||||
|
||||
// RefreshToken exchanges a valid refresh token for a new access token.
|
||||
// The refresh token is read from the HttpOnly cookie.
|
||||
// Returns a new short-lived access token.
|
||||
rpc RefreshToken(RefreshTokenRequest) returns (RefreshTokenResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v1/auth/refresh"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
message GetCurrentSessionRequest {}
|
||||
message GetCurrentUserRequest {}
|
||||
|
||||
message GetCurrentSessionResponse {
|
||||
message GetCurrentUserResponse {
|
||||
// The authenticated user's information.
|
||||
User user = 1;
|
||||
|
||||
// Last time the session was accessed.
|
||||
// Used for sliding expiration calculation (last_accessed_time + 2 weeks).
|
||||
google.protobuf.Timestamp last_accessed_at = 2;
|
||||
}
|
||||
|
||||
message CreateSessionRequest {
|
||||
message SignInRequest {
|
||||
// Nested message for password-based authentication credentials.
|
||||
message PasswordCredentials {
|
||||
// The username to sign in with.
|
||||
// Required field for password-based authentication.
|
||||
string username = 1 [(google.api.field_behavior) = REQUIRED];
|
||||
|
||||
// The password to sign in with.
|
||||
// Required field for password-based authentication.
|
||||
string password = 2 [(google.api.field_behavior) = REQUIRED];
|
||||
}
|
||||
|
||||
// Nested message for SSO authentication credentials.
|
||||
message SSOCredentials {
|
||||
// The ID of the SSO provider.
|
||||
// Required field to identify the SSO provider.
|
||||
int32 idp_id = 1 [(google.api.field_behavior) = REQUIRED];
|
||||
|
||||
// The authorization code from the SSO provider.
|
||||
// Required field for completing the SSO flow.
|
||||
string code = 2 [(google.api.field_behavior) = REQUIRED];
|
||||
|
||||
// The redirect URI used in the SSO flow.
|
||||
// Required field for security validation.
|
||||
string redirect_uri = 3 [(google.api.field_behavior) = REQUIRED];
|
||||
|
||||
// The PKCE code verifier for enhanced security (RFC 7636).
|
||||
// Optional field - if provided, enables PKCE flow protection against authorization code interception.
|
||||
// Optional - enables PKCE flow protection against authorization code interception.
|
||||
string code_verifier = 4 [(google.api.field_behavior) = OPTIONAL];
|
||||
}
|
||||
|
||||
// Provide one authentication method (username/password or SSO).
|
||||
// Required field to specify the authentication method.
|
||||
// Authentication credentials. Provide one method.
|
||||
oneof credentials {
|
||||
// Username and password authentication method.
|
||||
// Username and password authentication.
|
||||
PasswordCredentials password_credentials = 1;
|
||||
|
||||
// SSO provider authentication method.
|
||||
// SSO provider authentication.
|
||||
SSOCredentials sso_credentials = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message CreateSessionResponse {
|
||||
// The authenticated user information.
|
||||
message SignInResponse {
|
||||
// The authenticated user's information.
|
||||
User user = 1;
|
||||
|
||||
// Last time the session was accessed.
|
||||
// Used for sliding expiration calculation (last_accessed_time + 2 weeks).
|
||||
google.protobuf.Timestamp last_accessed_at = 2;
|
||||
// The short-lived access token for API requests.
|
||||
// Store in memory only, not in localStorage.
|
||||
string access_token = 2;
|
||||
|
||||
// When the access token expires.
|
||||
// Client should call RefreshToken before this time.
|
||||
google.protobuf.Timestamp access_token_expires_at = 3;
|
||||
}
|
||||
|
||||
message DeleteSessionRequest {}
|
||||
message SignOutRequest {}
|
||||
|
||||
message RefreshTokenRequest {}
|
||||
|
||||
message RefreshTokenResponse {
|
||||
// The new short-lived access token.
|
||||
string access_token = 1;
|
||||
|
||||
// When the access token expires.
|
||||
google.protobuf.Timestamp expires_at = 2;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,35 +84,39 @@ service UserService {
|
|||
option (google.api.method_signature) = "parent";
|
||||
}
|
||||
|
||||
// ListUserAccessTokens returns a list of access tokens for a user.
|
||||
rpc ListUserAccessTokens(ListUserAccessTokensRequest) returns (ListUserAccessTokensResponse) {
|
||||
option (google.api.http) = {get: "/api/v1/{parent=users/*}/accessTokens"};
|
||||
// ListPersonalAccessTokens returns a list of Personal Access Tokens (PATs) for a user.
|
||||
// PATs are long-lived tokens for API/script access, distinct from short-lived JWT access tokens.
|
||||
rpc ListPersonalAccessTokens(ListPersonalAccessTokensRequest) returns (ListPersonalAccessTokensResponse) {
|
||||
option (google.api.http) = {get: "/api/v1/{parent=users/*}/personalAccessTokens"};
|
||||
option (google.api.method_signature) = "parent";
|
||||
}
|
||||
|
||||
// CreateUserAccessToken creates a new access token for a user.
|
||||
rpc CreateUserAccessToken(CreateUserAccessTokenRequest) returns (UserAccessToken) {
|
||||
// CreatePersonalAccessToken creates a new Personal Access Token for a user.
|
||||
// The token value is only returned once upon creation.
|
||||
rpc CreatePersonalAccessToken(CreatePersonalAccessTokenRequest) returns (CreatePersonalAccessTokenResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v1/{parent=users/*}/accessTokens"
|
||||
body: "access_token"
|
||||
post: "/api/v1/{parent=users/*}/personalAccessTokens"
|
||||
body: "*"
|
||||
};
|
||||
option (google.api.method_signature) = "parent,access_token";
|
||||
}
|
||||
|
||||
// DeleteUserAccessToken deletes an access token.
|
||||
rpc DeleteUserAccessToken(DeleteUserAccessTokenRequest) returns (google.protobuf.Empty) {
|
||||
option (google.api.http) = {delete: "/api/v1/{name=users/*/accessTokens/*}"};
|
||||
// DeletePersonalAccessToken deletes a Personal Access Token.
|
||||
rpc DeletePersonalAccessToken(DeletePersonalAccessTokenRequest) returns (google.protobuf.Empty) {
|
||||
option (google.api.http) = {delete: "/api/v1/{name=users/*/personalAccessTokens/*}"};
|
||||
option (google.api.method_signature) = "name";
|
||||
}
|
||||
|
||||
// ListUserSessions returns a list of active sessions for a user.
|
||||
rpc ListUserSessions(ListUserSessionsRequest) returns (ListUserSessionsResponse) {
|
||||
// ListSessions returns a list of active login sessions for a user.
|
||||
// Each session represents a browser/device where the user is logged in.
|
||||
// Sessions are backed by refresh tokens with sliding expiration.
|
||||
rpc ListSessions(ListSessionsRequest) returns (ListSessionsResponse) {
|
||||
option (google.api.http) = {get: "/api/v1/{parent=users/*}/sessions"};
|
||||
option (google.api.method_signature) = "parent";
|
||||
}
|
||||
|
||||
// RevokeUserSession revokes a specific session for a user.
|
||||
rpc RevokeUserSession(RevokeUserSessionRequest) returns (google.protobuf.Empty) {
|
||||
// RevokeSession revokes a specific login session.
|
||||
// This invalidates the refresh token, forcing re-authentication on that device.
|
||||
rpc RevokeSession(RevokeSessionRequest) returns (google.protobuf.Empty) {
|
||||
option (google.api.http) = {delete: "/api/v1/{name=users/*/sessions/*}"};
|
||||
option (google.api.method_signature) = "name";
|
||||
}
|
||||
|
|
@ -398,9 +402,9 @@ message UserSetting {
|
|||
KEY_UNSPECIFIED = 0;
|
||||
// GENERAL is the key for general user settings.
|
||||
GENERAL = 1;
|
||||
// SESSIONS is the key for user authentication sessions.
|
||||
// SESSIONS is the key for user login sessions (refresh tokens).
|
||||
SESSIONS = 2;
|
||||
// ACCESS_TOKENS is the key for access tokens.
|
||||
// ACCESS_TOKENS is the key for Personal Access Tokens (PATs).
|
||||
ACCESS_TOKENS = 3;
|
||||
// WEBHOOKS is the key for user webhooks.
|
||||
WEBHOOKS = 4;
|
||||
|
|
@ -420,14 +424,14 @@ message UserSetting {
|
|||
|
||||
// User authentication sessions configuration.
|
||||
message SessionsSetting {
|
||||
// List of active user sessions.
|
||||
repeated UserSession sessions = 1;
|
||||
// List of active login sessions.
|
||||
repeated Session sessions = 1;
|
||||
}
|
||||
|
||||
// User access tokens configuration.
|
||||
// Personal access tokens configuration.
|
||||
message AccessTokensSetting {
|
||||
// List of user access tokens.
|
||||
repeated UserAccessToken access_tokens = 1;
|
||||
// List of personal access tokens (PATs).
|
||||
repeated PersonalAccessToken personal_access_tokens = 1;
|
||||
}
|
||||
|
||||
// User webhooks configuration.
|
||||
|
|
@ -487,85 +491,97 @@ message ListUserSettingsResponse {
|
|||
int32 total_size = 3;
|
||||
}
|
||||
|
||||
// User access token message
|
||||
message UserAccessToken {
|
||||
// PersonalAccessToken represents a long-lived token for API/script access.
|
||||
// PATs are distinct from short-lived JWT access tokens used for session authentication.
|
||||
message PersonalAccessToken {
|
||||
option (google.api.resource) = {
|
||||
type: "memos.api.v1/UserAccessToken"
|
||||
pattern: "users/{user}/accessTokens/{access_token}"
|
||||
singular: "userAccessToken"
|
||||
plural: "userAccessTokens"
|
||||
type: "memos.api.v1/PersonalAccessToken"
|
||||
pattern: "users/{user}/personalAccessTokens/{personal_access_token}"
|
||||
singular: "personalAccessToken"
|
||||
plural: "personalAccessTokens"
|
||||
};
|
||||
|
||||
// The resource name of the access token.
|
||||
// Format: users/{user}/accessTokens/{access_token}
|
||||
// The resource name of the personal access token.
|
||||
// Format: users/{user}/personalAccessTokens/{personal_access_token}
|
||||
string name = 1 [(google.api.field_behavior) = IDENTIFIER];
|
||||
|
||||
// Output only. The access token value.
|
||||
string access_token = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
|
||||
// The description of the token.
|
||||
string description = 2 [(google.api.field_behavior) = OPTIONAL];
|
||||
|
||||
// The description of the access token.
|
||||
string description = 3 [(google.api.field_behavior) = OPTIONAL];
|
||||
|
||||
// Output only. The issued timestamp.
|
||||
google.protobuf.Timestamp issued_at = 4 [(google.api.field_behavior) = OUTPUT_ONLY];
|
||||
// Output only. The creation timestamp.
|
||||
google.protobuf.Timestamp created_at = 3 [(google.api.field_behavior) = OUTPUT_ONLY];
|
||||
|
||||
// Optional. The expiration timestamp.
|
||||
google.protobuf.Timestamp expires_at = 5 [(google.api.field_behavior) = OPTIONAL];
|
||||
google.protobuf.Timestamp expires_at = 4 [(google.api.field_behavior) = OPTIONAL];
|
||||
|
||||
// Output only. The last used timestamp.
|
||||
google.protobuf.Timestamp last_used_at = 5 [(google.api.field_behavior) = OUTPUT_ONLY];
|
||||
}
|
||||
|
||||
message ListUserAccessTokensRequest {
|
||||
// Required. The parent resource whose access tokens will be listed.
|
||||
message ListPersonalAccessTokensRequest {
|
||||
// Required. The parent resource whose personal access tokens will be listed.
|
||||
// Format: users/{user}
|
||||
string parent = 1 [
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(google.api.resource_reference) = {type: "memos.api.v1/User"}
|
||||
];
|
||||
|
||||
// Optional. The maximum number of access tokens to return.
|
||||
// Optional. The maximum number of tokens to return.
|
||||
int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL];
|
||||
|
||||
// Optional. A page token for pagination.
|
||||
string page_token = 3 [(google.api.field_behavior) = OPTIONAL];
|
||||
}
|
||||
|
||||
message ListUserAccessTokensResponse {
|
||||
// The list of access tokens.
|
||||
repeated UserAccessToken access_tokens = 1;
|
||||
message ListPersonalAccessTokensResponse {
|
||||
// The list of personal access tokens.
|
||||
repeated PersonalAccessToken personal_access_tokens = 1;
|
||||
|
||||
// A token for the next page of results.
|
||||
string next_page_token = 2;
|
||||
|
||||
// The total count of access tokens.
|
||||
// The total count of personal access tokens.
|
||||
int32 total_size = 3;
|
||||
}
|
||||
|
||||
message CreateUserAccessTokenRequest {
|
||||
// Required. The parent resource where this access token will be created.
|
||||
message CreatePersonalAccessTokenRequest {
|
||||
// Required. The parent resource where this token will be created.
|
||||
// Format: users/{user}
|
||||
string parent = 1 [
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(google.api.resource_reference) = {type: "memos.api.v1/User"}
|
||||
];
|
||||
|
||||
// Required. The access token to create.
|
||||
UserAccessToken access_token = 2 [(google.api.field_behavior) = REQUIRED];
|
||||
// Optional. Description of the personal access token.
|
||||
string description = 2 [(google.api.field_behavior) = OPTIONAL];
|
||||
|
||||
// Optional. The access token ID to use.
|
||||
string access_token_id = 3 [(google.api.field_behavior) = OPTIONAL];
|
||||
// Optional. Expiration duration in days (0 = never expires).
|
||||
int32 expires_in_days = 3 [(google.api.field_behavior) = OPTIONAL];
|
||||
}
|
||||
|
||||
message DeleteUserAccessTokenRequest {
|
||||
// Required. The resource name of the access token to delete.
|
||||
// Format: users/{user}/accessTokens/{access_token}
|
||||
message CreatePersonalAccessTokenResponse {
|
||||
// The personal access token metadata.
|
||||
PersonalAccessToken personal_access_token = 1;
|
||||
|
||||
// The actual token value - only returned on creation.
|
||||
// This is the only time the token value will be visible.
|
||||
string token = 2;
|
||||
}
|
||||
|
||||
message DeletePersonalAccessTokenRequest {
|
||||
// Required. The resource name of the personal access token to delete.
|
||||
// Format: users/{user}/personalAccessTokens/{personal_access_token}
|
||||
string name = 1 [
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(google.api.resource_reference) = {type: "memos.api.v1/UserAccessToken"}
|
||||
(google.api.resource_reference) = {type: "memos.api.v1/PersonalAccessToken"}
|
||||
];
|
||||
}
|
||||
|
||||
message UserSession {
|
||||
// Session represents a user's login session on a specific device/browser.
|
||||
// Sessions are backed by refresh tokens with sliding expiration.
|
||||
message Session {
|
||||
option (google.api.resource) = {
|
||||
type: "memos.api.v1/UserSession"
|
||||
type: "memos.api.v1/Session"
|
||||
pattern: "users/{user}/sessions/{session}"
|
||||
name_field: "name"
|
||||
};
|
||||
|
|
@ -605,7 +621,7 @@ message UserSession {
|
|||
}
|
||||
}
|
||||
|
||||
message ListUserSessionsRequest {
|
||||
message ListSessionsRequest {
|
||||
// Required. The resource name of the parent.
|
||||
// Format: users/{user}
|
||||
string parent = 1 [
|
||||
|
|
@ -614,12 +630,12 @@ message ListUserSessionsRequest {
|
|||
];
|
||||
}
|
||||
|
||||
message ListUserSessionsResponse {
|
||||
// The list of user sessions.
|
||||
repeated UserSession sessions = 1;
|
||||
message ListSessionsResponse {
|
||||
// The list of sessions.
|
||||
repeated Session sessions = 1;
|
||||
}
|
||||
|
||||
message RevokeUserSessionRequest {
|
||||
message RevokeSessionRequest {
|
||||
// The name of the session to revoke.
|
||||
// Format: users/{user}/sessions/{session}
|
||||
string name = 1 [(google.api.field_behavior) = REQUIRED];
|
||||
|
|
|
|||
|
|
@ -34,28 +34,35 @@ const (
|
|||
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
|
||||
// period.
|
||||
const (
|
||||
// AuthServiceGetCurrentSessionProcedure is the fully-qualified name of the AuthService's
|
||||
// GetCurrentSession RPC.
|
||||
AuthServiceGetCurrentSessionProcedure = "/memos.api.v1.AuthService/GetCurrentSession"
|
||||
// AuthServiceCreateSessionProcedure is the fully-qualified name of the AuthService's CreateSession
|
||||
// AuthServiceGetCurrentUserProcedure is the fully-qualified name of the AuthService's
|
||||
// GetCurrentUser RPC.
|
||||
AuthServiceGetCurrentUserProcedure = "/memos.api.v1.AuthService/GetCurrentUser"
|
||||
// AuthServiceSignInProcedure is the fully-qualified name of the AuthService's SignIn RPC.
|
||||
AuthServiceSignInProcedure = "/memos.api.v1.AuthService/SignIn"
|
||||
// AuthServiceSignOutProcedure is the fully-qualified name of the AuthService's SignOut RPC.
|
||||
AuthServiceSignOutProcedure = "/memos.api.v1.AuthService/SignOut"
|
||||
// AuthServiceRefreshTokenProcedure is the fully-qualified name of the AuthService's RefreshToken
|
||||
// RPC.
|
||||
AuthServiceCreateSessionProcedure = "/memos.api.v1.AuthService/CreateSession"
|
||||
// AuthServiceDeleteSessionProcedure is the fully-qualified name of the AuthService's DeleteSession
|
||||
// RPC.
|
||||
AuthServiceDeleteSessionProcedure = "/memos.api.v1.AuthService/DeleteSession"
|
||||
AuthServiceRefreshTokenProcedure = "/memos.api.v1.AuthService/RefreshToken"
|
||||
)
|
||||
|
||||
// AuthServiceClient is a client for the memos.api.v1.AuthService service.
|
||||
type AuthServiceClient interface {
|
||||
// GetCurrentSession returns the current active session information.
|
||||
// This method is idempotent and safe, suitable for checking current session state.
|
||||
GetCurrentSession(context.Context, *connect.Request[v1.GetCurrentSessionRequest]) (*connect.Response[v1.GetCurrentSessionResponse], error)
|
||||
// CreateSession authenticates a user and creates a new session.
|
||||
// Returns the authenticated user information upon successful authentication.
|
||||
CreateSession(context.Context, *connect.Request[v1.CreateSessionRequest]) (*connect.Response[v1.CreateSessionResponse], error)
|
||||
// DeleteSession terminates the current user session.
|
||||
// This is an idempotent operation that invalidates the user's authentication.
|
||||
DeleteSession(context.Context, *connect.Request[v1.DeleteSessionRequest]) (*connect.Response[emptypb.Empty], error)
|
||||
// GetCurrentUser returns the authenticated user's information.
|
||||
// Validates the access token and returns user details.
|
||||
// Similar to OIDC's /userinfo endpoint.
|
||||
GetCurrentUser(context.Context, *connect.Request[v1.GetCurrentUserRequest]) (*connect.Response[v1.GetCurrentUserResponse], error)
|
||||
// SignIn authenticates a user with credentials and returns tokens.
|
||||
// On success, returns an access token and sets a refresh token cookie.
|
||||
// Supports password-based and SSO authentication methods.
|
||||
SignIn(context.Context, *connect.Request[v1.SignInRequest]) (*connect.Response[v1.SignInResponse], error)
|
||||
// SignOut terminates the user's authentication.
|
||||
// Revokes the refresh token and clears the authentication cookie.
|
||||
SignOut(context.Context, *connect.Request[v1.SignOutRequest]) (*connect.Response[emptypb.Empty], error)
|
||||
// RefreshToken exchanges a valid refresh token for a new access token.
|
||||
// The refresh token is read from the HttpOnly cookie.
|
||||
// Returns a new short-lived access token.
|
||||
RefreshToken(context.Context, *connect.Request[v1.RefreshTokenRequest]) (*connect.Response[v1.RefreshTokenResponse], error)
|
||||
}
|
||||
|
||||
// NewAuthServiceClient constructs a client for the memos.api.v1.AuthService service. By default, it
|
||||
|
|
@ -69,22 +76,28 @@ func NewAuthServiceClient(httpClient connect.HTTPClient, baseURL string, opts ..
|
|||
baseURL = strings.TrimRight(baseURL, "/")
|
||||
authServiceMethods := v1.File_api_v1_auth_service_proto.Services().ByName("AuthService").Methods()
|
||||
return &authServiceClient{
|
||||
getCurrentSession: connect.NewClient[v1.GetCurrentSessionRequest, v1.GetCurrentSessionResponse](
|
||||
getCurrentUser: connect.NewClient[v1.GetCurrentUserRequest, v1.GetCurrentUserResponse](
|
||||
httpClient,
|
||||
baseURL+AuthServiceGetCurrentSessionProcedure,
|
||||
connect.WithSchema(authServiceMethods.ByName("GetCurrentSession")),
|
||||
baseURL+AuthServiceGetCurrentUserProcedure,
|
||||
connect.WithSchema(authServiceMethods.ByName("GetCurrentUser")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
createSession: connect.NewClient[v1.CreateSessionRequest, v1.CreateSessionResponse](
|
||||
signIn: connect.NewClient[v1.SignInRequest, v1.SignInResponse](
|
||||
httpClient,
|
||||
baseURL+AuthServiceCreateSessionProcedure,
|
||||
connect.WithSchema(authServiceMethods.ByName("CreateSession")),
|
||||
baseURL+AuthServiceSignInProcedure,
|
||||
connect.WithSchema(authServiceMethods.ByName("SignIn")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
deleteSession: connect.NewClient[v1.DeleteSessionRequest, emptypb.Empty](
|
||||
signOut: connect.NewClient[v1.SignOutRequest, emptypb.Empty](
|
||||
httpClient,
|
||||
baseURL+AuthServiceDeleteSessionProcedure,
|
||||
connect.WithSchema(authServiceMethods.ByName("DeleteSession")),
|
||||
baseURL+AuthServiceSignOutProcedure,
|
||||
connect.WithSchema(authServiceMethods.ByName("SignOut")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
refreshToken: connect.NewClient[v1.RefreshTokenRequest, v1.RefreshTokenResponse](
|
||||
httpClient,
|
||||
baseURL+AuthServiceRefreshTokenProcedure,
|
||||
connect.WithSchema(authServiceMethods.ByName("RefreshToken")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
}
|
||||
|
|
@ -92,37 +105,49 @@ func NewAuthServiceClient(httpClient connect.HTTPClient, baseURL string, opts ..
|
|||
|
||||
// authServiceClient implements AuthServiceClient.
|
||||
type authServiceClient struct {
|
||||
getCurrentSession *connect.Client[v1.GetCurrentSessionRequest, v1.GetCurrentSessionResponse]
|
||||
createSession *connect.Client[v1.CreateSessionRequest, v1.CreateSessionResponse]
|
||||
deleteSession *connect.Client[v1.DeleteSessionRequest, emptypb.Empty]
|
||||
getCurrentUser *connect.Client[v1.GetCurrentUserRequest, v1.GetCurrentUserResponse]
|
||||
signIn *connect.Client[v1.SignInRequest, v1.SignInResponse]
|
||||
signOut *connect.Client[v1.SignOutRequest, emptypb.Empty]
|
||||
refreshToken *connect.Client[v1.RefreshTokenRequest, v1.RefreshTokenResponse]
|
||||
}
|
||||
|
||||
// GetCurrentSession calls memos.api.v1.AuthService.GetCurrentSession.
|
||||
func (c *authServiceClient) GetCurrentSession(ctx context.Context, req *connect.Request[v1.GetCurrentSessionRequest]) (*connect.Response[v1.GetCurrentSessionResponse], error) {
|
||||
return c.getCurrentSession.CallUnary(ctx, req)
|
||||
// GetCurrentUser calls memos.api.v1.AuthService.GetCurrentUser.
|
||||
func (c *authServiceClient) GetCurrentUser(ctx context.Context, req *connect.Request[v1.GetCurrentUserRequest]) (*connect.Response[v1.GetCurrentUserResponse], error) {
|
||||
return c.getCurrentUser.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// CreateSession calls memos.api.v1.AuthService.CreateSession.
|
||||
func (c *authServiceClient) CreateSession(ctx context.Context, req *connect.Request[v1.CreateSessionRequest]) (*connect.Response[v1.CreateSessionResponse], error) {
|
||||
return c.createSession.CallUnary(ctx, req)
|
||||
// SignIn calls memos.api.v1.AuthService.SignIn.
|
||||
func (c *authServiceClient) SignIn(ctx context.Context, req *connect.Request[v1.SignInRequest]) (*connect.Response[v1.SignInResponse], error) {
|
||||
return c.signIn.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// DeleteSession calls memos.api.v1.AuthService.DeleteSession.
|
||||
func (c *authServiceClient) DeleteSession(ctx context.Context, req *connect.Request[v1.DeleteSessionRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
return c.deleteSession.CallUnary(ctx, req)
|
||||
// SignOut calls memos.api.v1.AuthService.SignOut.
|
||||
func (c *authServiceClient) SignOut(ctx context.Context, req *connect.Request[v1.SignOutRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
return c.signOut.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// RefreshToken calls memos.api.v1.AuthService.RefreshToken.
|
||||
func (c *authServiceClient) RefreshToken(ctx context.Context, req *connect.Request[v1.RefreshTokenRequest]) (*connect.Response[v1.RefreshTokenResponse], error) {
|
||||
return c.refreshToken.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// AuthServiceHandler is an implementation of the memos.api.v1.AuthService service.
|
||||
type AuthServiceHandler interface {
|
||||
// GetCurrentSession returns the current active session information.
|
||||
// This method is idempotent and safe, suitable for checking current session state.
|
||||
GetCurrentSession(context.Context, *connect.Request[v1.GetCurrentSessionRequest]) (*connect.Response[v1.GetCurrentSessionResponse], error)
|
||||
// CreateSession authenticates a user and creates a new session.
|
||||
// Returns the authenticated user information upon successful authentication.
|
||||
CreateSession(context.Context, *connect.Request[v1.CreateSessionRequest]) (*connect.Response[v1.CreateSessionResponse], error)
|
||||
// DeleteSession terminates the current user session.
|
||||
// This is an idempotent operation that invalidates the user's authentication.
|
||||
DeleteSession(context.Context, *connect.Request[v1.DeleteSessionRequest]) (*connect.Response[emptypb.Empty], error)
|
||||
// GetCurrentUser returns the authenticated user's information.
|
||||
// Validates the access token and returns user details.
|
||||
// Similar to OIDC's /userinfo endpoint.
|
||||
GetCurrentUser(context.Context, *connect.Request[v1.GetCurrentUserRequest]) (*connect.Response[v1.GetCurrentUserResponse], error)
|
||||
// SignIn authenticates a user with credentials and returns tokens.
|
||||
// On success, returns an access token and sets a refresh token cookie.
|
||||
// Supports password-based and SSO authentication methods.
|
||||
SignIn(context.Context, *connect.Request[v1.SignInRequest]) (*connect.Response[v1.SignInResponse], error)
|
||||
// SignOut terminates the user's authentication.
|
||||
// Revokes the refresh token and clears the authentication cookie.
|
||||
SignOut(context.Context, *connect.Request[v1.SignOutRequest]) (*connect.Response[emptypb.Empty], error)
|
||||
// RefreshToken exchanges a valid refresh token for a new access token.
|
||||
// The refresh token is read from the HttpOnly cookie.
|
||||
// Returns a new short-lived access token.
|
||||
RefreshToken(context.Context, *connect.Request[v1.RefreshTokenRequest]) (*connect.Response[v1.RefreshTokenResponse], error)
|
||||
}
|
||||
|
||||
// NewAuthServiceHandler builds an HTTP handler from the service implementation. It returns the path
|
||||
|
|
@ -132,32 +157,40 @@ type AuthServiceHandler interface {
|
|||
// and JSON codecs. They also support gzip compression.
|
||||
func NewAuthServiceHandler(svc AuthServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
|
||||
authServiceMethods := v1.File_api_v1_auth_service_proto.Services().ByName("AuthService").Methods()
|
||||
authServiceGetCurrentSessionHandler := connect.NewUnaryHandler(
|
||||
AuthServiceGetCurrentSessionProcedure,
|
||||
svc.GetCurrentSession,
|
||||
connect.WithSchema(authServiceMethods.ByName("GetCurrentSession")),
|
||||
authServiceGetCurrentUserHandler := connect.NewUnaryHandler(
|
||||
AuthServiceGetCurrentUserProcedure,
|
||||
svc.GetCurrentUser,
|
||||
connect.WithSchema(authServiceMethods.ByName("GetCurrentUser")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
authServiceCreateSessionHandler := connect.NewUnaryHandler(
|
||||
AuthServiceCreateSessionProcedure,
|
||||
svc.CreateSession,
|
||||
connect.WithSchema(authServiceMethods.ByName("CreateSession")),
|
||||
authServiceSignInHandler := connect.NewUnaryHandler(
|
||||
AuthServiceSignInProcedure,
|
||||
svc.SignIn,
|
||||
connect.WithSchema(authServiceMethods.ByName("SignIn")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
authServiceDeleteSessionHandler := connect.NewUnaryHandler(
|
||||
AuthServiceDeleteSessionProcedure,
|
||||
svc.DeleteSession,
|
||||
connect.WithSchema(authServiceMethods.ByName("DeleteSession")),
|
||||
authServiceSignOutHandler := connect.NewUnaryHandler(
|
||||
AuthServiceSignOutProcedure,
|
||||
svc.SignOut,
|
||||
connect.WithSchema(authServiceMethods.ByName("SignOut")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
authServiceRefreshTokenHandler := connect.NewUnaryHandler(
|
||||
AuthServiceRefreshTokenProcedure,
|
||||
svc.RefreshToken,
|
||||
connect.WithSchema(authServiceMethods.ByName("RefreshToken")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
return "/memos.api.v1.AuthService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case AuthServiceGetCurrentSessionProcedure:
|
||||
authServiceGetCurrentSessionHandler.ServeHTTP(w, r)
|
||||
case AuthServiceCreateSessionProcedure:
|
||||
authServiceCreateSessionHandler.ServeHTTP(w, r)
|
||||
case AuthServiceDeleteSessionProcedure:
|
||||
authServiceDeleteSessionHandler.ServeHTTP(w, r)
|
||||
case AuthServiceGetCurrentUserProcedure:
|
||||
authServiceGetCurrentUserHandler.ServeHTTP(w, r)
|
||||
case AuthServiceSignInProcedure:
|
||||
authServiceSignInHandler.ServeHTTP(w, r)
|
||||
case AuthServiceSignOutProcedure:
|
||||
authServiceSignOutHandler.ServeHTTP(w, r)
|
||||
case AuthServiceRefreshTokenProcedure:
|
||||
authServiceRefreshTokenHandler.ServeHTTP(w, r)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
|
@ -167,14 +200,18 @@ func NewAuthServiceHandler(svc AuthServiceHandler, opts ...connect.HandlerOption
|
|||
// UnimplementedAuthServiceHandler returns CodeUnimplemented from all methods.
|
||||
type UnimplementedAuthServiceHandler struct{}
|
||||
|
||||
func (UnimplementedAuthServiceHandler) GetCurrentSession(context.Context, *connect.Request[v1.GetCurrentSessionRequest]) (*connect.Response[v1.GetCurrentSessionResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AuthService.GetCurrentSession is not implemented"))
|
||||
func (UnimplementedAuthServiceHandler) GetCurrentUser(context.Context, *connect.Request[v1.GetCurrentUserRequest]) (*connect.Response[v1.GetCurrentUserResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AuthService.GetCurrentUser is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedAuthServiceHandler) CreateSession(context.Context, *connect.Request[v1.CreateSessionRequest]) (*connect.Response[v1.CreateSessionResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AuthService.CreateSession is not implemented"))
|
||||
func (UnimplementedAuthServiceHandler) SignIn(context.Context, *connect.Request[v1.SignInRequest]) (*connect.Response[v1.SignInResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AuthService.SignIn is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedAuthServiceHandler) DeleteSession(context.Context, *connect.Request[v1.DeleteSessionRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AuthService.DeleteSession is not implemented"))
|
||||
func (UnimplementedAuthServiceHandler) SignOut(context.Context, *connect.Request[v1.SignOutRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AuthService.SignOut is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedAuthServiceHandler) RefreshToken(context.Context, *connect.Request[v1.RefreshTokenRequest]) (*connect.Response[v1.RefreshTokenResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AuthService.RefreshToken is not implemented"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,21 +59,21 @@ const (
|
|||
// UserServiceListUserSettingsProcedure is the fully-qualified name of the UserService's
|
||||
// ListUserSettings RPC.
|
||||
UserServiceListUserSettingsProcedure = "/memos.api.v1.UserService/ListUserSettings"
|
||||
// UserServiceListUserAccessTokensProcedure is the fully-qualified name of the UserService's
|
||||
// ListUserAccessTokens RPC.
|
||||
UserServiceListUserAccessTokensProcedure = "/memos.api.v1.UserService/ListUserAccessTokens"
|
||||
// UserServiceCreateUserAccessTokenProcedure is the fully-qualified name of the UserService's
|
||||
// CreateUserAccessToken RPC.
|
||||
UserServiceCreateUserAccessTokenProcedure = "/memos.api.v1.UserService/CreateUserAccessToken"
|
||||
// UserServiceDeleteUserAccessTokenProcedure is the fully-qualified name of the UserService's
|
||||
// DeleteUserAccessToken RPC.
|
||||
UserServiceDeleteUserAccessTokenProcedure = "/memos.api.v1.UserService/DeleteUserAccessToken"
|
||||
// UserServiceListUserSessionsProcedure is the fully-qualified name of the UserService's
|
||||
// ListUserSessions RPC.
|
||||
UserServiceListUserSessionsProcedure = "/memos.api.v1.UserService/ListUserSessions"
|
||||
// UserServiceRevokeUserSessionProcedure is the fully-qualified name of the UserService's
|
||||
// RevokeUserSession RPC.
|
||||
UserServiceRevokeUserSessionProcedure = "/memos.api.v1.UserService/RevokeUserSession"
|
||||
// UserServiceListPersonalAccessTokensProcedure is the fully-qualified name of the UserService's
|
||||
// ListPersonalAccessTokens RPC.
|
||||
UserServiceListPersonalAccessTokensProcedure = "/memos.api.v1.UserService/ListPersonalAccessTokens"
|
||||
// UserServiceCreatePersonalAccessTokenProcedure is the fully-qualified name of the UserService's
|
||||
// CreatePersonalAccessToken RPC.
|
||||
UserServiceCreatePersonalAccessTokenProcedure = "/memos.api.v1.UserService/CreatePersonalAccessToken"
|
||||
// UserServiceDeletePersonalAccessTokenProcedure is the fully-qualified name of the UserService's
|
||||
// DeletePersonalAccessToken RPC.
|
||||
UserServiceDeletePersonalAccessTokenProcedure = "/memos.api.v1.UserService/DeletePersonalAccessToken"
|
||||
// UserServiceListSessionsProcedure is the fully-qualified name of the UserService's ListSessions
|
||||
// RPC.
|
||||
UserServiceListSessionsProcedure = "/memos.api.v1.UserService/ListSessions"
|
||||
// UserServiceRevokeSessionProcedure is the fully-qualified name of the UserService's RevokeSession
|
||||
// RPC.
|
||||
UserServiceRevokeSessionProcedure = "/memos.api.v1.UserService/RevokeSession"
|
||||
// UserServiceListUserWebhooksProcedure is the fully-qualified name of the UserService's
|
||||
// ListUserWebhooks RPC.
|
||||
UserServiceListUserWebhooksProcedure = "/memos.api.v1.UserService/ListUserWebhooks"
|
||||
|
|
@ -122,16 +122,21 @@ type UserServiceClient interface {
|
|||
UpdateUserSetting(context.Context, *connect.Request[v1.UpdateUserSettingRequest]) (*connect.Response[v1.UserSetting], error)
|
||||
// ListUserSettings returns a list of user settings.
|
||||
ListUserSettings(context.Context, *connect.Request[v1.ListUserSettingsRequest]) (*connect.Response[v1.ListUserSettingsResponse], error)
|
||||
// ListUserAccessTokens returns a list of access tokens for a user.
|
||||
ListUserAccessTokens(context.Context, *connect.Request[v1.ListUserAccessTokensRequest]) (*connect.Response[v1.ListUserAccessTokensResponse], error)
|
||||
// CreateUserAccessToken creates a new access token for a user.
|
||||
CreateUserAccessToken(context.Context, *connect.Request[v1.CreateUserAccessTokenRequest]) (*connect.Response[v1.UserAccessToken], error)
|
||||
// DeleteUserAccessToken deletes an access token.
|
||||
DeleteUserAccessToken(context.Context, *connect.Request[v1.DeleteUserAccessTokenRequest]) (*connect.Response[emptypb.Empty], error)
|
||||
// ListUserSessions returns a list of active sessions for a user.
|
||||
ListUserSessions(context.Context, *connect.Request[v1.ListUserSessionsRequest]) (*connect.Response[v1.ListUserSessionsResponse], error)
|
||||
// RevokeUserSession revokes a specific session for a user.
|
||||
RevokeUserSession(context.Context, *connect.Request[v1.RevokeUserSessionRequest]) (*connect.Response[emptypb.Empty], error)
|
||||
// ListPersonalAccessTokens returns a list of Personal Access Tokens (PATs) for a user.
|
||||
// PATs are long-lived tokens for API/script access, distinct from short-lived JWT access tokens.
|
||||
ListPersonalAccessTokens(context.Context, *connect.Request[v1.ListPersonalAccessTokensRequest]) (*connect.Response[v1.ListPersonalAccessTokensResponse], error)
|
||||
// CreatePersonalAccessToken creates a new Personal Access Token for a user.
|
||||
// The token value is only returned once upon creation.
|
||||
CreatePersonalAccessToken(context.Context, *connect.Request[v1.CreatePersonalAccessTokenRequest]) (*connect.Response[v1.CreatePersonalAccessTokenResponse], error)
|
||||
// DeletePersonalAccessToken deletes a Personal Access Token.
|
||||
DeletePersonalAccessToken(context.Context, *connect.Request[v1.DeletePersonalAccessTokenRequest]) (*connect.Response[emptypb.Empty], error)
|
||||
// ListSessions returns a list of active login sessions for a user.
|
||||
// Each session represents a browser/device where the user is logged in.
|
||||
// Sessions are backed by refresh tokens with sliding expiration.
|
||||
ListSessions(context.Context, *connect.Request[v1.ListSessionsRequest]) (*connect.Response[v1.ListSessionsResponse], error)
|
||||
// RevokeSession revokes a specific login session.
|
||||
// This invalidates the refresh token, forcing re-authentication on that device.
|
||||
RevokeSession(context.Context, *connect.Request[v1.RevokeSessionRequest]) (*connect.Response[emptypb.Empty], error)
|
||||
// ListUserWebhooks returns a list of webhooks for a user.
|
||||
ListUserWebhooks(context.Context, *connect.Request[v1.ListUserWebhooksRequest]) (*connect.Response[v1.ListUserWebhooksResponse], error)
|
||||
// CreateUserWebhook creates a new webhook for a user.
|
||||
|
|
@ -219,34 +224,34 @@ func NewUserServiceClient(httpClient connect.HTTPClient, baseURL string, opts ..
|
|||
connect.WithSchema(userServiceMethods.ByName("ListUserSettings")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
listUserAccessTokens: connect.NewClient[v1.ListUserAccessTokensRequest, v1.ListUserAccessTokensResponse](
|
||||
listPersonalAccessTokens: connect.NewClient[v1.ListPersonalAccessTokensRequest, v1.ListPersonalAccessTokensResponse](
|
||||
httpClient,
|
||||
baseURL+UserServiceListUserAccessTokensProcedure,
|
||||
connect.WithSchema(userServiceMethods.ByName("ListUserAccessTokens")),
|
||||
baseURL+UserServiceListPersonalAccessTokensProcedure,
|
||||
connect.WithSchema(userServiceMethods.ByName("ListPersonalAccessTokens")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
createUserAccessToken: connect.NewClient[v1.CreateUserAccessTokenRequest, v1.UserAccessToken](
|
||||
createPersonalAccessToken: connect.NewClient[v1.CreatePersonalAccessTokenRequest, v1.CreatePersonalAccessTokenResponse](
|
||||
httpClient,
|
||||
baseURL+UserServiceCreateUserAccessTokenProcedure,
|
||||
connect.WithSchema(userServiceMethods.ByName("CreateUserAccessToken")),
|
||||
baseURL+UserServiceCreatePersonalAccessTokenProcedure,
|
||||
connect.WithSchema(userServiceMethods.ByName("CreatePersonalAccessToken")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
deleteUserAccessToken: connect.NewClient[v1.DeleteUserAccessTokenRequest, emptypb.Empty](
|
||||
deletePersonalAccessToken: connect.NewClient[v1.DeletePersonalAccessTokenRequest, emptypb.Empty](
|
||||
httpClient,
|
||||
baseURL+UserServiceDeleteUserAccessTokenProcedure,
|
||||
connect.WithSchema(userServiceMethods.ByName("DeleteUserAccessToken")),
|
||||
baseURL+UserServiceDeletePersonalAccessTokenProcedure,
|
||||
connect.WithSchema(userServiceMethods.ByName("DeletePersonalAccessToken")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
listUserSessions: connect.NewClient[v1.ListUserSessionsRequest, v1.ListUserSessionsResponse](
|
||||
listSessions: connect.NewClient[v1.ListSessionsRequest, v1.ListSessionsResponse](
|
||||
httpClient,
|
||||
baseURL+UserServiceListUserSessionsProcedure,
|
||||
connect.WithSchema(userServiceMethods.ByName("ListUserSessions")),
|
||||
baseURL+UserServiceListSessionsProcedure,
|
||||
connect.WithSchema(userServiceMethods.ByName("ListSessions")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
revokeUserSession: connect.NewClient[v1.RevokeUserSessionRequest, emptypb.Empty](
|
||||
revokeSession: connect.NewClient[v1.RevokeSessionRequest, emptypb.Empty](
|
||||
httpClient,
|
||||
baseURL+UserServiceRevokeUserSessionProcedure,
|
||||
connect.WithSchema(userServiceMethods.ByName("RevokeUserSession")),
|
||||
baseURL+UserServiceRevokeSessionProcedure,
|
||||
connect.WithSchema(userServiceMethods.ByName("RevokeSession")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
listUserWebhooks: connect.NewClient[v1.ListUserWebhooksRequest, v1.ListUserWebhooksResponse](
|
||||
|
|
@ -296,28 +301,28 @@ func NewUserServiceClient(httpClient connect.HTTPClient, baseURL string, opts ..
|
|||
|
||||
// userServiceClient implements UserServiceClient.
|
||||
type userServiceClient struct {
|
||||
listUsers *connect.Client[v1.ListUsersRequest, v1.ListUsersResponse]
|
||||
getUser *connect.Client[v1.GetUserRequest, v1.User]
|
||||
createUser *connect.Client[v1.CreateUserRequest, v1.User]
|
||||
updateUser *connect.Client[v1.UpdateUserRequest, v1.User]
|
||||
deleteUser *connect.Client[v1.DeleteUserRequest, emptypb.Empty]
|
||||
listAllUserStats *connect.Client[v1.ListAllUserStatsRequest, v1.ListAllUserStatsResponse]
|
||||
getUserStats *connect.Client[v1.GetUserStatsRequest, v1.UserStats]
|
||||
getUserSetting *connect.Client[v1.GetUserSettingRequest, v1.UserSetting]
|
||||
updateUserSetting *connect.Client[v1.UpdateUserSettingRequest, v1.UserSetting]
|
||||
listUserSettings *connect.Client[v1.ListUserSettingsRequest, v1.ListUserSettingsResponse]
|
||||
listUserAccessTokens *connect.Client[v1.ListUserAccessTokensRequest, v1.ListUserAccessTokensResponse]
|
||||
createUserAccessToken *connect.Client[v1.CreateUserAccessTokenRequest, v1.UserAccessToken]
|
||||
deleteUserAccessToken *connect.Client[v1.DeleteUserAccessTokenRequest, emptypb.Empty]
|
||||
listUserSessions *connect.Client[v1.ListUserSessionsRequest, v1.ListUserSessionsResponse]
|
||||
revokeUserSession *connect.Client[v1.RevokeUserSessionRequest, emptypb.Empty]
|
||||
listUserWebhooks *connect.Client[v1.ListUserWebhooksRequest, v1.ListUserWebhooksResponse]
|
||||
createUserWebhook *connect.Client[v1.CreateUserWebhookRequest, v1.UserWebhook]
|
||||
updateUserWebhook *connect.Client[v1.UpdateUserWebhookRequest, v1.UserWebhook]
|
||||
deleteUserWebhook *connect.Client[v1.DeleteUserWebhookRequest, emptypb.Empty]
|
||||
listUserNotifications *connect.Client[v1.ListUserNotificationsRequest, v1.ListUserNotificationsResponse]
|
||||
updateUserNotification *connect.Client[v1.UpdateUserNotificationRequest, v1.UserNotification]
|
||||
deleteUserNotification *connect.Client[v1.DeleteUserNotificationRequest, emptypb.Empty]
|
||||
listUsers *connect.Client[v1.ListUsersRequest, v1.ListUsersResponse]
|
||||
getUser *connect.Client[v1.GetUserRequest, v1.User]
|
||||
createUser *connect.Client[v1.CreateUserRequest, v1.User]
|
||||
updateUser *connect.Client[v1.UpdateUserRequest, v1.User]
|
||||
deleteUser *connect.Client[v1.DeleteUserRequest, emptypb.Empty]
|
||||
listAllUserStats *connect.Client[v1.ListAllUserStatsRequest, v1.ListAllUserStatsResponse]
|
||||
getUserStats *connect.Client[v1.GetUserStatsRequest, v1.UserStats]
|
||||
getUserSetting *connect.Client[v1.GetUserSettingRequest, v1.UserSetting]
|
||||
updateUserSetting *connect.Client[v1.UpdateUserSettingRequest, v1.UserSetting]
|
||||
listUserSettings *connect.Client[v1.ListUserSettingsRequest, v1.ListUserSettingsResponse]
|
||||
listPersonalAccessTokens *connect.Client[v1.ListPersonalAccessTokensRequest, v1.ListPersonalAccessTokensResponse]
|
||||
createPersonalAccessToken *connect.Client[v1.CreatePersonalAccessTokenRequest, v1.CreatePersonalAccessTokenResponse]
|
||||
deletePersonalAccessToken *connect.Client[v1.DeletePersonalAccessTokenRequest, emptypb.Empty]
|
||||
listSessions *connect.Client[v1.ListSessionsRequest, v1.ListSessionsResponse]
|
||||
revokeSession *connect.Client[v1.RevokeSessionRequest, emptypb.Empty]
|
||||
listUserWebhooks *connect.Client[v1.ListUserWebhooksRequest, v1.ListUserWebhooksResponse]
|
||||
createUserWebhook *connect.Client[v1.CreateUserWebhookRequest, v1.UserWebhook]
|
||||
updateUserWebhook *connect.Client[v1.UpdateUserWebhookRequest, v1.UserWebhook]
|
||||
deleteUserWebhook *connect.Client[v1.DeleteUserWebhookRequest, emptypb.Empty]
|
||||
listUserNotifications *connect.Client[v1.ListUserNotificationsRequest, v1.ListUserNotificationsResponse]
|
||||
updateUserNotification *connect.Client[v1.UpdateUserNotificationRequest, v1.UserNotification]
|
||||
deleteUserNotification *connect.Client[v1.DeleteUserNotificationRequest, emptypb.Empty]
|
||||
}
|
||||
|
||||
// ListUsers calls memos.api.v1.UserService.ListUsers.
|
||||
|
|
@ -370,29 +375,29 @@ func (c *userServiceClient) ListUserSettings(ctx context.Context, req *connect.R
|
|||
return c.listUserSettings.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// ListUserAccessTokens calls memos.api.v1.UserService.ListUserAccessTokens.
|
||||
func (c *userServiceClient) ListUserAccessTokens(ctx context.Context, req *connect.Request[v1.ListUserAccessTokensRequest]) (*connect.Response[v1.ListUserAccessTokensResponse], error) {
|
||||
return c.listUserAccessTokens.CallUnary(ctx, req)
|
||||
// ListPersonalAccessTokens calls memos.api.v1.UserService.ListPersonalAccessTokens.
|
||||
func (c *userServiceClient) ListPersonalAccessTokens(ctx context.Context, req *connect.Request[v1.ListPersonalAccessTokensRequest]) (*connect.Response[v1.ListPersonalAccessTokensResponse], error) {
|
||||
return c.listPersonalAccessTokens.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// CreateUserAccessToken calls memos.api.v1.UserService.CreateUserAccessToken.
|
||||
func (c *userServiceClient) CreateUserAccessToken(ctx context.Context, req *connect.Request[v1.CreateUserAccessTokenRequest]) (*connect.Response[v1.UserAccessToken], error) {
|
||||
return c.createUserAccessToken.CallUnary(ctx, req)
|
||||
// CreatePersonalAccessToken calls memos.api.v1.UserService.CreatePersonalAccessToken.
|
||||
func (c *userServiceClient) CreatePersonalAccessToken(ctx context.Context, req *connect.Request[v1.CreatePersonalAccessTokenRequest]) (*connect.Response[v1.CreatePersonalAccessTokenResponse], error) {
|
||||
return c.createPersonalAccessToken.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// DeleteUserAccessToken calls memos.api.v1.UserService.DeleteUserAccessToken.
|
||||
func (c *userServiceClient) DeleteUserAccessToken(ctx context.Context, req *connect.Request[v1.DeleteUserAccessTokenRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
return c.deleteUserAccessToken.CallUnary(ctx, req)
|
||||
// DeletePersonalAccessToken calls memos.api.v1.UserService.DeletePersonalAccessToken.
|
||||
func (c *userServiceClient) DeletePersonalAccessToken(ctx context.Context, req *connect.Request[v1.DeletePersonalAccessTokenRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
return c.deletePersonalAccessToken.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// ListUserSessions calls memos.api.v1.UserService.ListUserSessions.
|
||||
func (c *userServiceClient) ListUserSessions(ctx context.Context, req *connect.Request[v1.ListUserSessionsRequest]) (*connect.Response[v1.ListUserSessionsResponse], error) {
|
||||
return c.listUserSessions.CallUnary(ctx, req)
|
||||
// ListSessions calls memos.api.v1.UserService.ListSessions.
|
||||
func (c *userServiceClient) ListSessions(ctx context.Context, req *connect.Request[v1.ListSessionsRequest]) (*connect.Response[v1.ListSessionsResponse], error) {
|
||||
return c.listSessions.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// RevokeUserSession calls memos.api.v1.UserService.RevokeUserSession.
|
||||
func (c *userServiceClient) RevokeUserSession(ctx context.Context, req *connect.Request[v1.RevokeUserSessionRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
return c.revokeUserSession.CallUnary(ctx, req)
|
||||
// RevokeSession calls memos.api.v1.UserService.RevokeSession.
|
||||
func (c *userServiceClient) RevokeSession(ctx context.Context, req *connect.Request[v1.RevokeSessionRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
return c.revokeSession.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// ListUserWebhooks calls memos.api.v1.UserService.ListUserWebhooks.
|
||||
|
|
@ -455,16 +460,21 @@ type UserServiceHandler interface {
|
|||
UpdateUserSetting(context.Context, *connect.Request[v1.UpdateUserSettingRequest]) (*connect.Response[v1.UserSetting], error)
|
||||
// ListUserSettings returns a list of user settings.
|
||||
ListUserSettings(context.Context, *connect.Request[v1.ListUserSettingsRequest]) (*connect.Response[v1.ListUserSettingsResponse], error)
|
||||
// ListUserAccessTokens returns a list of access tokens for a user.
|
||||
ListUserAccessTokens(context.Context, *connect.Request[v1.ListUserAccessTokensRequest]) (*connect.Response[v1.ListUserAccessTokensResponse], error)
|
||||
// CreateUserAccessToken creates a new access token for a user.
|
||||
CreateUserAccessToken(context.Context, *connect.Request[v1.CreateUserAccessTokenRequest]) (*connect.Response[v1.UserAccessToken], error)
|
||||
// DeleteUserAccessToken deletes an access token.
|
||||
DeleteUserAccessToken(context.Context, *connect.Request[v1.DeleteUserAccessTokenRequest]) (*connect.Response[emptypb.Empty], error)
|
||||
// ListUserSessions returns a list of active sessions for a user.
|
||||
ListUserSessions(context.Context, *connect.Request[v1.ListUserSessionsRequest]) (*connect.Response[v1.ListUserSessionsResponse], error)
|
||||
// RevokeUserSession revokes a specific session for a user.
|
||||
RevokeUserSession(context.Context, *connect.Request[v1.RevokeUserSessionRequest]) (*connect.Response[emptypb.Empty], error)
|
||||
// ListPersonalAccessTokens returns a list of Personal Access Tokens (PATs) for a user.
|
||||
// PATs are long-lived tokens for API/script access, distinct from short-lived JWT access tokens.
|
||||
ListPersonalAccessTokens(context.Context, *connect.Request[v1.ListPersonalAccessTokensRequest]) (*connect.Response[v1.ListPersonalAccessTokensResponse], error)
|
||||
// CreatePersonalAccessToken creates a new Personal Access Token for a user.
|
||||
// The token value is only returned once upon creation.
|
||||
CreatePersonalAccessToken(context.Context, *connect.Request[v1.CreatePersonalAccessTokenRequest]) (*connect.Response[v1.CreatePersonalAccessTokenResponse], error)
|
||||
// DeletePersonalAccessToken deletes a Personal Access Token.
|
||||
DeletePersonalAccessToken(context.Context, *connect.Request[v1.DeletePersonalAccessTokenRequest]) (*connect.Response[emptypb.Empty], error)
|
||||
// ListSessions returns a list of active login sessions for a user.
|
||||
// Each session represents a browser/device where the user is logged in.
|
||||
// Sessions are backed by refresh tokens with sliding expiration.
|
||||
ListSessions(context.Context, *connect.Request[v1.ListSessionsRequest]) (*connect.Response[v1.ListSessionsResponse], error)
|
||||
// RevokeSession revokes a specific login session.
|
||||
// This invalidates the refresh token, forcing re-authentication on that device.
|
||||
RevokeSession(context.Context, *connect.Request[v1.RevokeSessionRequest]) (*connect.Response[emptypb.Empty], error)
|
||||
// ListUserWebhooks returns a list of webhooks for a user.
|
||||
ListUserWebhooks(context.Context, *connect.Request[v1.ListUserWebhooksRequest]) (*connect.Response[v1.ListUserWebhooksResponse], error)
|
||||
// CreateUserWebhook creates a new webhook for a user.
|
||||
|
|
@ -548,34 +558,34 @@ func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption
|
|||
connect.WithSchema(userServiceMethods.ByName("ListUserSettings")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
userServiceListUserAccessTokensHandler := connect.NewUnaryHandler(
|
||||
UserServiceListUserAccessTokensProcedure,
|
||||
svc.ListUserAccessTokens,
|
||||
connect.WithSchema(userServiceMethods.ByName("ListUserAccessTokens")),
|
||||
userServiceListPersonalAccessTokensHandler := connect.NewUnaryHandler(
|
||||
UserServiceListPersonalAccessTokensProcedure,
|
||||
svc.ListPersonalAccessTokens,
|
||||
connect.WithSchema(userServiceMethods.ByName("ListPersonalAccessTokens")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
userServiceCreateUserAccessTokenHandler := connect.NewUnaryHandler(
|
||||
UserServiceCreateUserAccessTokenProcedure,
|
||||
svc.CreateUserAccessToken,
|
||||
connect.WithSchema(userServiceMethods.ByName("CreateUserAccessToken")),
|
||||
userServiceCreatePersonalAccessTokenHandler := connect.NewUnaryHandler(
|
||||
UserServiceCreatePersonalAccessTokenProcedure,
|
||||
svc.CreatePersonalAccessToken,
|
||||
connect.WithSchema(userServiceMethods.ByName("CreatePersonalAccessToken")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
userServiceDeleteUserAccessTokenHandler := connect.NewUnaryHandler(
|
||||
UserServiceDeleteUserAccessTokenProcedure,
|
||||
svc.DeleteUserAccessToken,
|
||||
connect.WithSchema(userServiceMethods.ByName("DeleteUserAccessToken")),
|
||||
userServiceDeletePersonalAccessTokenHandler := connect.NewUnaryHandler(
|
||||
UserServiceDeletePersonalAccessTokenProcedure,
|
||||
svc.DeletePersonalAccessToken,
|
||||
connect.WithSchema(userServiceMethods.ByName("DeletePersonalAccessToken")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
userServiceListUserSessionsHandler := connect.NewUnaryHandler(
|
||||
UserServiceListUserSessionsProcedure,
|
||||
svc.ListUserSessions,
|
||||
connect.WithSchema(userServiceMethods.ByName("ListUserSessions")),
|
||||
userServiceListSessionsHandler := connect.NewUnaryHandler(
|
||||
UserServiceListSessionsProcedure,
|
||||
svc.ListSessions,
|
||||
connect.WithSchema(userServiceMethods.ByName("ListSessions")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
userServiceRevokeUserSessionHandler := connect.NewUnaryHandler(
|
||||
UserServiceRevokeUserSessionProcedure,
|
||||
svc.RevokeUserSession,
|
||||
connect.WithSchema(userServiceMethods.ByName("RevokeUserSession")),
|
||||
userServiceRevokeSessionHandler := connect.NewUnaryHandler(
|
||||
UserServiceRevokeSessionProcedure,
|
||||
svc.RevokeSession,
|
||||
connect.WithSchema(userServiceMethods.ByName("RevokeSession")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
userServiceListUserWebhooksHandler := connect.NewUnaryHandler(
|
||||
|
|
@ -642,16 +652,16 @@ func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption
|
|||
userServiceUpdateUserSettingHandler.ServeHTTP(w, r)
|
||||
case UserServiceListUserSettingsProcedure:
|
||||
userServiceListUserSettingsHandler.ServeHTTP(w, r)
|
||||
case UserServiceListUserAccessTokensProcedure:
|
||||
userServiceListUserAccessTokensHandler.ServeHTTP(w, r)
|
||||
case UserServiceCreateUserAccessTokenProcedure:
|
||||
userServiceCreateUserAccessTokenHandler.ServeHTTP(w, r)
|
||||
case UserServiceDeleteUserAccessTokenProcedure:
|
||||
userServiceDeleteUserAccessTokenHandler.ServeHTTP(w, r)
|
||||
case UserServiceListUserSessionsProcedure:
|
||||
userServiceListUserSessionsHandler.ServeHTTP(w, r)
|
||||
case UserServiceRevokeUserSessionProcedure:
|
||||
userServiceRevokeUserSessionHandler.ServeHTTP(w, r)
|
||||
case UserServiceListPersonalAccessTokensProcedure:
|
||||
userServiceListPersonalAccessTokensHandler.ServeHTTP(w, r)
|
||||
case UserServiceCreatePersonalAccessTokenProcedure:
|
||||
userServiceCreatePersonalAccessTokenHandler.ServeHTTP(w, r)
|
||||
case UserServiceDeletePersonalAccessTokenProcedure:
|
||||
userServiceDeletePersonalAccessTokenHandler.ServeHTTP(w, r)
|
||||
case UserServiceListSessionsProcedure:
|
||||
userServiceListSessionsHandler.ServeHTTP(w, r)
|
||||
case UserServiceRevokeSessionProcedure:
|
||||
userServiceRevokeSessionHandler.ServeHTTP(w, r)
|
||||
case UserServiceListUserWebhooksProcedure:
|
||||
userServiceListUserWebhooksHandler.ServeHTTP(w, r)
|
||||
case UserServiceCreateUserWebhookProcedure:
|
||||
|
|
@ -715,24 +725,24 @@ func (UnimplementedUserServiceHandler) ListUserSettings(context.Context, *connec
|
|||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListUserSettings is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedUserServiceHandler) ListUserAccessTokens(context.Context, *connect.Request[v1.ListUserAccessTokensRequest]) (*connect.Response[v1.ListUserAccessTokensResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListUserAccessTokens is not implemented"))
|
||||
func (UnimplementedUserServiceHandler) ListPersonalAccessTokens(context.Context, *connect.Request[v1.ListPersonalAccessTokensRequest]) (*connect.Response[v1.ListPersonalAccessTokensResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListPersonalAccessTokens is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedUserServiceHandler) CreateUserAccessToken(context.Context, *connect.Request[v1.CreateUserAccessTokenRequest]) (*connect.Response[v1.UserAccessToken], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.CreateUserAccessToken is not implemented"))
|
||||
func (UnimplementedUserServiceHandler) CreatePersonalAccessToken(context.Context, *connect.Request[v1.CreatePersonalAccessTokenRequest]) (*connect.Response[v1.CreatePersonalAccessTokenResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.CreatePersonalAccessToken is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedUserServiceHandler) DeleteUserAccessToken(context.Context, *connect.Request[v1.DeleteUserAccessTokenRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.DeleteUserAccessToken is not implemented"))
|
||||
func (UnimplementedUserServiceHandler) DeletePersonalAccessToken(context.Context, *connect.Request[v1.DeletePersonalAccessTokenRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.DeletePersonalAccessToken is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedUserServiceHandler) ListUserSessions(context.Context, *connect.Request[v1.ListUserSessionsRequest]) (*connect.Response[v1.ListUserSessionsResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListUserSessions is not implemented"))
|
||||
func (UnimplementedUserServiceHandler) ListSessions(context.Context, *connect.Request[v1.ListSessionsRequest]) (*connect.Response[v1.ListSessionsResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListSessions is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedUserServiceHandler) RevokeUserSession(context.Context, *connect.Request[v1.RevokeUserSessionRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.RevokeUserSession is not implemented"))
|
||||
func (UnimplementedUserServiceHandler) RevokeSession(context.Context, *connect.Request[v1.RevokeSessionRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.RevokeSession is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedUserServiceHandler) ListUserWebhooks(context.Context, *connect.Request[v1.ListUserWebhooksRequest]) (*connect.Response[v1.ListUserWebhooksResponse], error) {
|
||||
|
|
|
|||
|
|
@ -24,26 +24,26 @@ const (
|
|||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type GetCurrentSessionRequest struct {
|
||||
type GetCurrentUserRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GetCurrentSessionRequest) Reset() {
|
||||
*x = GetCurrentSessionRequest{}
|
||||
func (x *GetCurrentUserRequest) Reset() {
|
||||
*x = GetCurrentUserRequest{}
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GetCurrentSessionRequest) String() string {
|
||||
func (x *GetCurrentUserRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetCurrentSessionRequest) ProtoMessage() {}
|
||||
func (*GetCurrentUserRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetCurrentSessionRequest) ProtoReflect() protoreflect.Message {
|
||||
func (x *GetCurrentUserRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
|
|
@ -55,35 +55,33 @@ func (x *GetCurrentSessionRequest) ProtoReflect() protoreflect.Message {
|
|||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetCurrentSessionRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetCurrentSessionRequest) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use GetCurrentUserRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetCurrentUserRequest) Descriptor() ([]byte, []int) {
|
||||
return file_api_v1_auth_service_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
type GetCurrentSessionResponse struct {
|
||||
type GetCurrentUserResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
|
||||
// Last time the session was accessed.
|
||||
// Used for sliding expiration calculation (last_accessed_time + 2 weeks).
|
||||
LastAccessedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=last_accessed_at,json=lastAccessedAt,proto3" json:"last_accessed_at,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
// The authenticated user's information.
|
||||
User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GetCurrentSessionResponse) Reset() {
|
||||
*x = GetCurrentSessionResponse{}
|
||||
func (x *GetCurrentUserResponse) Reset() {
|
||||
*x = GetCurrentUserResponse{}
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GetCurrentSessionResponse) String() string {
|
||||
func (x *GetCurrentUserResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetCurrentSessionResponse) ProtoMessage() {}
|
||||
func (*GetCurrentUserResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetCurrentSessionResponse) ProtoReflect() protoreflect.Message {
|
||||
func (x *GetCurrentUserResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
|
|
@ -95,53 +93,45 @@ func (x *GetCurrentSessionResponse) ProtoReflect() protoreflect.Message {
|
|||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetCurrentSessionResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetCurrentSessionResponse) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use GetCurrentUserResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetCurrentUserResponse) Descriptor() ([]byte, []int) {
|
||||
return file_api_v1_auth_service_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *GetCurrentSessionResponse) GetUser() *User {
|
||||
func (x *GetCurrentUserResponse) GetUser() *User {
|
||||
if x != nil {
|
||||
return x.User
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *GetCurrentSessionResponse) GetLastAccessedAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.LastAccessedAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CreateSessionRequest struct {
|
||||
type SignInRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Provide one authentication method (username/password or SSO).
|
||||
// Required field to specify the authentication method.
|
||||
// Authentication credentials. Provide one method.
|
||||
//
|
||||
// Types that are valid to be assigned to Credentials:
|
||||
//
|
||||
// *CreateSessionRequest_PasswordCredentials_
|
||||
// *CreateSessionRequest_SsoCredentials
|
||||
Credentials isCreateSessionRequest_Credentials `protobuf_oneof:"credentials"`
|
||||
// *SignInRequest_PasswordCredentials_
|
||||
// *SignInRequest_SsoCredentials
|
||||
Credentials isSignInRequest_Credentials `protobuf_oneof:"credentials"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *CreateSessionRequest) Reset() {
|
||||
*x = CreateSessionRequest{}
|
||||
func (x *SignInRequest) Reset() {
|
||||
*x = SignInRequest{}
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *CreateSessionRequest) String() string {
|
||||
func (x *SignInRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CreateSessionRequest) ProtoMessage() {}
|
||||
func (*SignInRequest) ProtoMessage() {}
|
||||
|
||||
func (x *CreateSessionRequest) ProtoReflect() protoreflect.Message {
|
||||
func (x *SignInRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
|
|
@ -153,79 +143,82 @@ func (x *CreateSessionRequest) ProtoReflect() protoreflect.Message {
|
|||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CreateSessionRequest.ProtoReflect.Descriptor instead.
|
||||
func (*CreateSessionRequest) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use SignInRequest.ProtoReflect.Descriptor instead.
|
||||
func (*SignInRequest) Descriptor() ([]byte, []int) {
|
||||
return file_api_v1_auth_service_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *CreateSessionRequest) GetCredentials() isCreateSessionRequest_Credentials {
|
||||
func (x *SignInRequest) GetCredentials() isSignInRequest_Credentials {
|
||||
if x != nil {
|
||||
return x.Credentials
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CreateSessionRequest) GetPasswordCredentials() *CreateSessionRequest_PasswordCredentials {
|
||||
func (x *SignInRequest) GetPasswordCredentials() *SignInRequest_PasswordCredentials {
|
||||
if x != nil {
|
||||
if x, ok := x.Credentials.(*CreateSessionRequest_PasswordCredentials_); ok {
|
||||
if x, ok := x.Credentials.(*SignInRequest_PasswordCredentials_); ok {
|
||||
return x.PasswordCredentials
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CreateSessionRequest) GetSsoCredentials() *CreateSessionRequest_SSOCredentials {
|
||||
func (x *SignInRequest) GetSsoCredentials() *SignInRequest_SSOCredentials {
|
||||
if x != nil {
|
||||
if x, ok := x.Credentials.(*CreateSessionRequest_SsoCredentials); ok {
|
||||
if x, ok := x.Credentials.(*SignInRequest_SsoCredentials); ok {
|
||||
return x.SsoCredentials
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isCreateSessionRequest_Credentials interface {
|
||||
isCreateSessionRequest_Credentials()
|
||||
type isSignInRequest_Credentials interface {
|
||||
isSignInRequest_Credentials()
|
||||
}
|
||||
|
||||
type CreateSessionRequest_PasswordCredentials_ struct {
|
||||
// Username and password authentication method.
|
||||
PasswordCredentials *CreateSessionRequest_PasswordCredentials `protobuf:"bytes,1,opt,name=password_credentials,json=passwordCredentials,proto3,oneof"`
|
||||
type SignInRequest_PasswordCredentials_ struct {
|
||||
// Username and password authentication.
|
||||
PasswordCredentials *SignInRequest_PasswordCredentials `protobuf:"bytes,1,opt,name=password_credentials,json=passwordCredentials,proto3,oneof"`
|
||||
}
|
||||
|
||||
type CreateSessionRequest_SsoCredentials struct {
|
||||
// SSO provider authentication method.
|
||||
SsoCredentials *CreateSessionRequest_SSOCredentials `protobuf:"bytes,2,opt,name=sso_credentials,json=ssoCredentials,proto3,oneof"`
|
||||
type SignInRequest_SsoCredentials struct {
|
||||
// SSO provider authentication.
|
||||
SsoCredentials *SignInRequest_SSOCredentials `protobuf:"bytes,2,opt,name=sso_credentials,json=ssoCredentials,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*CreateSessionRequest_PasswordCredentials_) isCreateSessionRequest_Credentials() {}
|
||||
func (*SignInRequest_PasswordCredentials_) isSignInRequest_Credentials() {}
|
||||
|
||||
func (*CreateSessionRequest_SsoCredentials) isCreateSessionRequest_Credentials() {}
|
||||
func (*SignInRequest_SsoCredentials) isSignInRequest_Credentials() {}
|
||||
|
||||
type CreateSessionResponse struct {
|
||||
type SignInResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// The authenticated user information.
|
||||
// The authenticated user's information.
|
||||
User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
|
||||
// Last time the session was accessed.
|
||||
// Used for sliding expiration calculation (last_accessed_time + 2 weeks).
|
||||
LastAccessedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=last_accessed_at,json=lastAccessedAt,proto3" json:"last_accessed_at,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
// The short-lived access token for API requests.
|
||||
// Store in memory only, not in localStorage.
|
||||
AccessToken string `protobuf:"bytes,2,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
|
||||
// When the access token expires.
|
||||
// Client should call RefreshToken before this time.
|
||||
AccessTokenExpiresAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=access_token_expires_at,json=accessTokenExpiresAt,proto3" json:"access_token_expires_at,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *CreateSessionResponse) Reset() {
|
||||
*x = CreateSessionResponse{}
|
||||
func (x *SignInResponse) Reset() {
|
||||
*x = SignInResponse{}
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *CreateSessionResponse) String() string {
|
||||
func (x *SignInResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CreateSessionResponse) ProtoMessage() {}
|
||||
func (*SignInResponse) ProtoMessage() {}
|
||||
|
||||
func (x *CreateSessionResponse) ProtoReflect() protoreflect.Message {
|
||||
func (x *SignInResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
|
|
@ -237,45 +230,52 @@ func (x *CreateSessionResponse) ProtoReflect() protoreflect.Message {
|
|||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CreateSessionResponse.ProtoReflect.Descriptor instead.
|
||||
func (*CreateSessionResponse) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use SignInResponse.ProtoReflect.Descriptor instead.
|
||||
func (*SignInResponse) Descriptor() ([]byte, []int) {
|
||||
return file_api_v1_auth_service_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *CreateSessionResponse) GetUser() *User {
|
||||
func (x *SignInResponse) GetUser() *User {
|
||||
if x != nil {
|
||||
return x.User
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CreateSessionResponse) GetLastAccessedAt() *timestamppb.Timestamp {
|
||||
func (x *SignInResponse) GetAccessToken() string {
|
||||
if x != nil {
|
||||
return x.LastAccessedAt
|
||||
return x.AccessToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SignInResponse) GetAccessTokenExpiresAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.AccessTokenExpiresAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DeleteSessionRequest struct {
|
||||
type SignOutRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *DeleteSessionRequest) Reset() {
|
||||
*x = DeleteSessionRequest{}
|
||||
func (x *SignOutRequest) Reset() {
|
||||
*x = SignOutRequest{}
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *DeleteSessionRequest) String() string {
|
||||
func (x *SignOutRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*DeleteSessionRequest) ProtoMessage() {}
|
||||
func (*SignOutRequest) ProtoMessage() {}
|
||||
|
||||
func (x *DeleteSessionRequest) ProtoReflect() protoreflect.Message {
|
||||
func (x *SignOutRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
|
|
@ -287,39 +287,127 @@ func (x *DeleteSessionRequest) ProtoReflect() protoreflect.Message {
|
|||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use DeleteSessionRequest.ProtoReflect.Descriptor instead.
|
||||
func (*DeleteSessionRequest) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use SignOutRequest.ProtoReflect.Descriptor instead.
|
||||
func (*SignOutRequest) Descriptor() ([]byte, []int) {
|
||||
return file_api_v1_auth_service_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
type RefreshTokenRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RefreshTokenRequest) Reset() {
|
||||
*x = RefreshTokenRequest{}
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *RefreshTokenRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RefreshTokenRequest) ProtoMessage() {}
|
||||
|
||||
func (x *RefreshTokenRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[5]
|
||||
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 RefreshTokenRequest.ProtoReflect.Descriptor instead.
|
||||
func (*RefreshTokenRequest) Descriptor() ([]byte, []int) {
|
||||
return file_api_v1_auth_service_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
type RefreshTokenResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// The new short-lived access token.
|
||||
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
|
||||
// When the access token expires.
|
||||
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 *RefreshTokenResponse) Reset() {
|
||||
*x = RefreshTokenResponse{}
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *RefreshTokenResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RefreshTokenResponse) ProtoMessage() {}
|
||||
|
||||
func (x *RefreshTokenResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[6]
|
||||
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 RefreshTokenResponse.ProtoReflect.Descriptor instead.
|
||||
func (*RefreshTokenResponse) Descriptor() ([]byte, []int) {
|
||||
return file_api_v1_auth_service_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *RefreshTokenResponse) GetAccessToken() string {
|
||||
if x != nil {
|
||||
return x.AccessToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *RefreshTokenResponse) GetExpiresAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.ExpiresAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Nested message for password-based authentication credentials.
|
||||
type CreateSessionRequest_PasswordCredentials struct {
|
||||
type SignInRequest_PasswordCredentials struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// The username to sign in with.
|
||||
// Required field for password-based authentication.
|
||||
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
|
||||
// The password to sign in with.
|
||||
// Required field for password-based authentication.
|
||||
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *CreateSessionRequest_PasswordCredentials) Reset() {
|
||||
*x = CreateSessionRequest_PasswordCredentials{}
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[5]
|
||||
func (x *SignInRequest_PasswordCredentials) Reset() {
|
||||
*x = SignInRequest_PasswordCredentials{}
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *CreateSessionRequest_PasswordCredentials) String() string {
|
||||
func (x *SignInRequest_PasswordCredentials) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CreateSessionRequest_PasswordCredentials) ProtoMessage() {}
|
||||
func (*SignInRequest_PasswordCredentials) ProtoMessage() {}
|
||||
|
||||
func (x *CreateSessionRequest_PasswordCredentials) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[5]
|
||||
func (x *SignInRequest_PasswordCredentials) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[7]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -330,19 +418,19 @@ func (x *CreateSessionRequest_PasswordCredentials) ProtoReflect() protoreflect.M
|
|||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CreateSessionRequest_PasswordCredentials.ProtoReflect.Descriptor instead.
|
||||
func (*CreateSessionRequest_PasswordCredentials) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use SignInRequest_PasswordCredentials.ProtoReflect.Descriptor instead.
|
||||
func (*SignInRequest_PasswordCredentials) Descriptor() ([]byte, []int) {
|
||||
return file_api_v1_auth_service_proto_rawDescGZIP(), []int{2, 0}
|
||||
}
|
||||
|
||||
func (x *CreateSessionRequest_PasswordCredentials) GetUsername() string {
|
||||
func (x *SignInRequest_PasswordCredentials) GetUsername() string {
|
||||
if x != nil {
|
||||
return x.Username
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *CreateSessionRequest_PasswordCredentials) GetPassword() string {
|
||||
func (x *SignInRequest_PasswordCredentials) GetPassword() string {
|
||||
if x != nil {
|
||||
return x.Password
|
||||
}
|
||||
|
|
@ -350,39 +438,36 @@ func (x *CreateSessionRequest_PasswordCredentials) GetPassword() string {
|
|||
}
|
||||
|
||||
// Nested message for SSO authentication credentials.
|
||||
type CreateSessionRequest_SSOCredentials struct {
|
||||
type SignInRequest_SSOCredentials struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// The ID of the SSO provider.
|
||||
// Required field to identify the SSO provider.
|
||||
IdpId int32 `protobuf:"varint,1,opt,name=idp_id,json=idpId,proto3" json:"idp_id,omitempty"`
|
||||
// The authorization code from the SSO provider.
|
||||
// Required field for completing the SSO flow.
|
||||
Code string `protobuf:"bytes,2,opt,name=code,proto3" json:"code,omitempty"`
|
||||
// The redirect URI used in the SSO flow.
|
||||
// Required field for security validation.
|
||||
RedirectUri string `protobuf:"bytes,3,opt,name=redirect_uri,json=redirectUri,proto3" json:"redirect_uri,omitempty"`
|
||||
// The PKCE code verifier for enhanced security (RFC 7636).
|
||||
// Optional field - if provided, enables PKCE flow protection against authorization code interception.
|
||||
// Optional - enables PKCE flow protection against authorization code interception.
|
||||
CodeVerifier string `protobuf:"bytes,4,opt,name=code_verifier,json=codeVerifier,proto3" json:"code_verifier,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *CreateSessionRequest_SSOCredentials) Reset() {
|
||||
*x = CreateSessionRequest_SSOCredentials{}
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[6]
|
||||
func (x *SignInRequest_SSOCredentials) Reset() {
|
||||
*x = SignInRequest_SSOCredentials{}
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *CreateSessionRequest_SSOCredentials) String() string {
|
||||
func (x *SignInRequest_SSOCredentials) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CreateSessionRequest_SSOCredentials) ProtoMessage() {}
|
||||
func (*SignInRequest_SSOCredentials) ProtoMessage() {}
|
||||
|
||||
func (x *CreateSessionRequest_SSOCredentials) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[6]
|
||||
func (x *SignInRequest_SSOCredentials) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_v1_auth_service_proto_msgTypes[8]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -393,33 +478,33 @@ func (x *CreateSessionRequest_SSOCredentials) ProtoReflect() protoreflect.Messag
|
|||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CreateSessionRequest_SSOCredentials.ProtoReflect.Descriptor instead.
|
||||
func (*CreateSessionRequest_SSOCredentials) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use SignInRequest_SSOCredentials.ProtoReflect.Descriptor instead.
|
||||
func (*SignInRequest_SSOCredentials) Descriptor() ([]byte, []int) {
|
||||
return file_api_v1_auth_service_proto_rawDescGZIP(), []int{2, 1}
|
||||
}
|
||||
|
||||
func (x *CreateSessionRequest_SSOCredentials) GetIdpId() int32 {
|
||||
func (x *SignInRequest_SSOCredentials) GetIdpId() int32 {
|
||||
if x != nil {
|
||||
return x.IdpId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *CreateSessionRequest_SSOCredentials) GetCode() string {
|
||||
func (x *SignInRequest_SSOCredentials) GetCode() string {
|
||||
if x != nil {
|
||||
return x.Code
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *CreateSessionRequest_SSOCredentials) GetRedirectUri() string {
|
||||
func (x *SignInRequest_SSOCredentials) GetRedirectUri() string {
|
||||
if x != nil {
|
||||
return x.RedirectUri
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *CreateSessionRequest_SSOCredentials) GetCodeVerifier() string {
|
||||
func (x *SignInRequest_SSOCredentials) GetCodeVerifier() string {
|
||||
if x != nil {
|
||||
return x.CodeVerifier
|
||||
}
|
||||
|
|
@ -430,14 +515,13 @@ 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\x1fgoogle/protobuf/timestamp.proto\"\x1a\n" +
|
||||
"\x18GetCurrentSessionRequest\"\x89\x01\n" +
|
||||
"\x19GetCurrentSessionResponse\x12&\n" +
|
||||
"\x04user\x18\x01 \x01(\v2\x12.memos.api.v1.UserR\x04user\x12D\n" +
|
||||
"\x10last_accessed_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x0elastAccessedAt\"\xe3\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\x1aW\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\"\x17\n" +
|
||||
"\x15GetCurrentUserRequest\"@\n" +
|
||||
"\x16GetCurrentUserResponse\x12&\n" +
|
||||
"\x04user\x18\x01 \x01(\v2\x12.memos.api.v1.UserR\x04user\"\xce\x03\n" +
|
||||
"\rSignInRequest\x12d\n" +
|
||||
"\x14password_credentials\x18\x01 \x01(\v2/.memos.api.v1.SignInRequest.PasswordCredentialsH\x00R\x13passwordCredentials\x12U\n" +
|
||||
"\x0fsso_credentials\x18\x02 \x01(\v2*.memos.api.v1.SignInRequest.SSOCredentialsH\x00R\x0essoCredentials\x1aW\n" +
|
||||
"\x13PasswordCredentials\x12\x1f\n" +
|
||||
"\busername\x18\x01 \x01(\tB\x03\xe0A\x02R\busername\x12\x1f\n" +
|
||||
"\bpassword\x18\x02 \x01(\tB\x03\xe0A\x02R\bpassword\x1a\x97\x01\n" +
|
||||
|
|
@ -446,15 +530,22 @@ const file_api_v1_auth_service_proto_rawDesc = "" +
|
|||
"\x04code\x18\x02 \x01(\tB\x03\xe0A\x02R\x04code\x12&\n" +
|
||||
"\fredirect_uri\x18\x03 \x01(\tB\x03\xe0A\x02R\vredirectUri\x12(\n" +
|
||||
"\rcode_verifier\x18\x04 \x01(\tB\x03\xe0A\x01R\fcodeVerifierB\r\n" +
|
||||
"\vcredentials\"\x85\x01\n" +
|
||||
"\x15CreateSessionResponse\x12&\n" +
|
||||
"\x04user\x18\x01 \x01(\v2\x12.memos.api.v1.UserR\x04user\x12D\n" +
|
||||
"\x10last_accessed_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x0elastAccessedAt\"\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" +
|
||||
"\vcredentials\"\xae\x01\n" +
|
||||
"\x0eSignInResponse\x12&\n" +
|
||||
"\x04user\x18\x01 \x01(\v2\x12.memos.api.v1.UserR\x04user\x12!\n" +
|
||||
"\faccess_token\x18\x02 \x01(\tR\vaccessToken\x12Q\n" +
|
||||
"\x17access_token_expires_at\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\x14accessTokenExpiresAt\"\x10\n" +
|
||||
"\x0eSignOutRequest\"\x15\n" +
|
||||
"\x13RefreshTokenRequest\"t\n" +
|
||||
"\x14RefreshTokenResponse\x12!\n" +
|
||||
"\faccess_token\x18\x01 \x01(\tR\vaccessToken\x129\n" +
|
||||
"\n" +
|
||||
"expires_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt2\xbf\x03\n" +
|
||||
"\vAuthService\x12t\n" +
|
||||
"\x0eGetCurrentUser\x12#.memos.api.v1.GetCurrentUserRequest\x1a$.memos.api.v1.GetCurrentUserResponse\"\x17\x82\xd3\xe4\x93\x02\x11\x12\x0f/api/v1/auth/me\x12c\n" +
|
||||
"\x06SignIn\x12\x1b.memos.api.v1.SignInRequest\x1a\x1c.memos.api.v1.SignInResponse\"\x1e\x82\xd3\xe4\x93\x02\x18:\x01*\"\x13/api/v1/auth/signin\x12]\n" +
|
||||
"\aSignOut\x12\x1c.memos.api.v1.SignOutRequest\x1a\x16.google.protobuf.Empty\"\x1c\x82\xd3\xe4\x93\x02\x16\"\x14/api/v1/auth/signout\x12v\n" +
|
||||
"\fRefreshToken\x12!.memos.api.v1.RefreshTokenRequest\x1a\".memos.api.v1.RefreshTokenResponse\"\x1f\x82\xd3\xe4\x93\x02\x19:\x01*\"\x14/api/v1/auth/refreshB\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"
|
||||
|
||||
var (
|
||||
|
|
@ -469,37 +560,41 @@ 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, 7)
|
||||
var file_api_v1_auth_service_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
|
||||
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
|
||||
(*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
|
||||
(*GetCurrentUserRequest)(nil), // 0: memos.api.v1.GetCurrentUserRequest
|
||||
(*GetCurrentUserResponse)(nil), // 1: memos.api.v1.GetCurrentUserResponse
|
||||
(*SignInRequest)(nil), // 2: memos.api.v1.SignInRequest
|
||||
(*SignInResponse)(nil), // 3: memos.api.v1.SignInResponse
|
||||
(*SignOutRequest)(nil), // 4: memos.api.v1.SignOutRequest
|
||||
(*RefreshTokenRequest)(nil), // 5: memos.api.v1.RefreshTokenRequest
|
||||
(*RefreshTokenResponse)(nil), // 6: memos.api.v1.RefreshTokenResponse
|
||||
(*SignInRequest_PasswordCredentials)(nil), // 7: memos.api.v1.SignInRequest.PasswordCredentials
|
||||
(*SignInRequest_SSOCredentials)(nil), // 8: memos.api.v1.SignInRequest.SSOCredentials
|
||||
(*User)(nil), // 9: memos.api.v1.User
|
||||
(*timestamppb.Timestamp)(nil), // 10: google.protobuf.Timestamp
|
||||
(*emptypb.Empty)(nil), // 11: google.protobuf.Empty
|
||||
}
|
||||
var file_api_v1_auth_service_proto_depIdxs = []int32{
|
||||
7, // 0: memos.api.v1.GetCurrentSessionResponse.user:type_name -> memos.api.v1.User
|
||||
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
|
||||
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.last_accessed_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
|
||||
9, // 0: memos.api.v1.GetCurrentUserResponse.user:type_name -> memos.api.v1.User
|
||||
7, // 1: memos.api.v1.SignInRequest.password_credentials:type_name -> memos.api.v1.SignInRequest.PasswordCredentials
|
||||
8, // 2: memos.api.v1.SignInRequest.sso_credentials:type_name -> memos.api.v1.SignInRequest.SSOCredentials
|
||||
9, // 3: memos.api.v1.SignInResponse.user:type_name -> memos.api.v1.User
|
||||
10, // 4: memos.api.v1.SignInResponse.access_token_expires_at:type_name -> google.protobuf.Timestamp
|
||||
10, // 5: memos.api.v1.RefreshTokenResponse.expires_at:type_name -> google.protobuf.Timestamp
|
||||
0, // 6: memos.api.v1.AuthService.GetCurrentUser:input_type -> memos.api.v1.GetCurrentUserRequest
|
||||
2, // 7: memos.api.v1.AuthService.SignIn:input_type -> memos.api.v1.SignInRequest
|
||||
4, // 8: memos.api.v1.AuthService.SignOut:input_type -> memos.api.v1.SignOutRequest
|
||||
5, // 9: memos.api.v1.AuthService.RefreshToken:input_type -> memos.api.v1.RefreshTokenRequest
|
||||
1, // 10: memos.api.v1.AuthService.GetCurrentUser:output_type -> memos.api.v1.GetCurrentUserResponse
|
||||
3, // 11: memos.api.v1.AuthService.SignIn:output_type -> memos.api.v1.SignInResponse
|
||||
11, // 12: memos.api.v1.AuthService.SignOut:output_type -> google.protobuf.Empty
|
||||
6, // 13: memos.api.v1.AuthService.RefreshToken:output_type -> memos.api.v1.RefreshTokenResponse
|
||||
10, // [10:14] is the sub-list for method output_type
|
||||
6, // [6:10] 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() }
|
||||
|
|
@ -509,8 +604,8 @@ func file_api_v1_auth_service_proto_init() {
|
|||
}
|
||||
file_api_v1_user_service_proto_init()
|
||||
file_api_v1_auth_service_proto_msgTypes[2].OneofWrappers = []any{
|
||||
(*CreateSessionRequest_PasswordCredentials_)(nil),
|
||||
(*CreateSessionRequest_SsoCredentials)(nil),
|
||||
(*SignInRequest_PasswordCredentials_)(nil),
|
||||
(*SignInRequest_SsoCredentials)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
|
|
@ -518,7 +613,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: 7,
|
||||
NumMessages: 9,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -35,30 +35,30 @@ var (
|
|||
_ = metadata.Join
|
||||
)
|
||||
|
||||
func request_AuthService_GetCurrentSession_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
func request_AuthService_GetCurrentUser_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq GetCurrentSessionRequest
|
||||
protoReq GetCurrentUserRequest
|
||||
metadata runtime.ServerMetadata
|
||||
)
|
||||
if req.Body != nil {
|
||||
_, _ = io.Copy(io.Discard, req.Body)
|
||||
}
|
||||
msg, err := client.GetCurrentSession(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
msg, err := client.GetCurrentUser(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func local_request_AuthService_GetCurrentSession_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
func local_request_AuthService_GetCurrentUser_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq GetCurrentSessionRequest
|
||||
protoReq GetCurrentUserRequest
|
||||
metadata runtime.ServerMetadata
|
||||
)
|
||||
msg, err := server.GetCurrentSession(ctx, &protoReq)
|
||||
msg, err := server.GetCurrentUser(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func request_AuthService_CreateSession_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
func request_AuthService_SignIn_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq CreateSessionRequest
|
||||
protoReq SignInRequest
|
||||
metadata runtime.ServerMetadata
|
||||
)
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
|
||||
|
|
@ -67,40 +67,67 @@ func request_AuthService_CreateSession_0(ctx context.Context, marshaler runtime.
|
|||
if req.Body != nil {
|
||||
_, _ = io.Copy(io.Discard, req.Body)
|
||||
}
|
||||
msg, err := client.CreateSession(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
msg, err := client.SignIn(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func local_request_AuthService_CreateSession_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
func local_request_AuthService_SignIn_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq CreateSessionRequest
|
||||
protoReq SignInRequest
|
||||
metadata runtime.ServerMetadata
|
||||
)
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
msg, err := server.CreateSession(ctx, &protoReq)
|
||||
msg, err := server.SignIn(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func request_AuthService_DeleteSession_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
func request_AuthService_SignOut_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq DeleteSessionRequest
|
||||
protoReq SignOutRequest
|
||||
metadata runtime.ServerMetadata
|
||||
)
|
||||
if req.Body != nil {
|
||||
_, _ = io.Copy(io.Discard, req.Body)
|
||||
}
|
||||
msg, err := client.DeleteSession(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
msg, err := client.SignOut(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func local_request_AuthService_DeleteSession_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
func local_request_AuthService_SignOut_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq DeleteSessionRequest
|
||||
protoReq SignOutRequest
|
||||
metadata runtime.ServerMetadata
|
||||
)
|
||||
msg, err := server.DeleteSession(ctx, &protoReq)
|
||||
msg, err := server.SignOut(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func request_AuthService_RefreshToken_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq RefreshTokenRequest
|
||||
metadata runtime.ServerMetadata
|
||||
)
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if req.Body != nil {
|
||||
_, _ = io.Copy(io.Discard, req.Body)
|
||||
}
|
||||
msg, err := client.RefreshToken(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func local_request_AuthService_RefreshToken_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq RefreshTokenRequest
|
||||
metadata runtime.ServerMetadata
|
||||
)
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
msg, err := server.RefreshToken(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
|
|
@ -110,65 +137,85 @@ func local_request_AuthService_DeleteSession_0(ctx context.Context, marshaler ru
|
|||
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterAuthServiceHandlerFromEndpoint instead.
|
||||
// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call.
|
||||
func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server AuthServiceServer) error {
|
||||
mux.Handle(http.MethodGet, pattern_AuthService_GetCurrentSession_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle(http.MethodGet, pattern_AuthService_GetCurrentUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.AuthService/GetCurrentSession", runtime.WithHTTPPathPattern("/api/v1/auth/sessions/current"))
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.AuthService/GetCurrentUser", runtime.WithHTTPPathPattern("/api/v1/auth/me"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_AuthService_GetCurrentSession_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
resp, md, err := local_request_AuthService_GetCurrentUser_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_AuthService_GetCurrentSession_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_AuthService_GetCurrentUser_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodPost, pattern_AuthService_CreateSession_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle(http.MethodPost, pattern_AuthService_SignIn_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.AuthService/CreateSession", runtime.WithHTTPPathPattern("/api/v1/auth/sessions"))
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.AuthService/SignIn", runtime.WithHTTPPathPattern("/api/v1/auth/signin"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_AuthService_CreateSession_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
resp, md, err := local_request_AuthService_SignIn_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_AuthService_CreateSession_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_AuthService_SignIn_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodDelete, pattern_AuthService_DeleteSession_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle(http.MethodPost, pattern_AuthService_SignOut_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.AuthService/DeleteSession", runtime.WithHTTPPathPattern("/api/v1/auth/sessions/current"))
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.AuthService/SignOut", runtime.WithHTTPPathPattern("/api/v1/auth/signout"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_AuthService_DeleteSession_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
resp, md, err := local_request_AuthService_SignOut_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_AuthService_DeleteSession_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_AuthService_SignOut_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodPost, pattern_AuthService_RefreshToken_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.AuthService/RefreshToken", runtime.WithHTTPPathPattern("/api/v1/auth/refresh"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_AuthService_RefreshToken_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_AuthService_RefreshToken_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
|
||||
return nil
|
||||
|
|
@ -210,68 +257,87 @@ func RegisterAuthServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn
|
|||
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
|
||||
// "AuthServiceClient" to call the correct interceptors. This client ignores the HTTP middlewares.
|
||||
func RegisterAuthServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client AuthServiceClient) error {
|
||||
mux.Handle(http.MethodGet, pattern_AuthService_GetCurrentSession_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle(http.MethodGet, pattern_AuthService_GetCurrentUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.AuthService/GetCurrentSession", runtime.WithHTTPPathPattern("/api/v1/auth/sessions/current"))
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.AuthService/GetCurrentUser", runtime.WithHTTPPathPattern("/api/v1/auth/me"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_AuthService_GetCurrentSession_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
resp, md, err := request_AuthService_GetCurrentUser_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_AuthService_GetCurrentSession_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_AuthService_GetCurrentUser_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodPost, pattern_AuthService_CreateSession_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle(http.MethodPost, pattern_AuthService_SignIn_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.AuthService/CreateSession", runtime.WithHTTPPathPattern("/api/v1/auth/sessions"))
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.AuthService/SignIn", runtime.WithHTTPPathPattern("/api/v1/auth/signin"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_AuthService_CreateSession_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
resp, md, err := request_AuthService_SignIn_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_AuthService_CreateSession_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_AuthService_SignIn_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodDelete, pattern_AuthService_DeleteSession_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle(http.MethodPost, pattern_AuthService_SignOut_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.AuthService/DeleteSession", runtime.WithHTTPPathPattern("/api/v1/auth/sessions/current"))
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.AuthService/SignOut", runtime.WithHTTPPathPattern("/api/v1/auth/signout"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_AuthService_DeleteSession_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
resp, md, err := request_AuthService_SignOut_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_AuthService_DeleteSession_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_AuthService_SignOut_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodPost, pattern_AuthService_RefreshToken_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.AuthService/RefreshToken", runtime.WithHTTPPathPattern("/api/v1/auth/refresh"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_AuthService_RefreshToken_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_AuthService_RefreshToken_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
pattern_AuthService_GetCurrentSession_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"api", "v1", "auth", "sessions", "current"}, ""))
|
||||
pattern_AuthService_CreateSession_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "auth", "sessions"}, ""))
|
||||
pattern_AuthService_DeleteSession_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"api", "v1", "auth", "sessions", "current"}, ""))
|
||||
pattern_AuthService_GetCurrentUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "auth", "me"}, ""))
|
||||
pattern_AuthService_SignIn_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "auth", "signin"}, ""))
|
||||
pattern_AuthService_SignOut_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "auth", "signout"}, ""))
|
||||
pattern_AuthService_RefreshToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "auth", "refresh"}, ""))
|
||||
)
|
||||
|
||||
var (
|
||||
forward_AuthService_GetCurrentSession_0 = runtime.ForwardResponseMessage
|
||||
forward_AuthService_CreateSession_0 = runtime.ForwardResponseMessage
|
||||
forward_AuthService_DeleteSession_0 = runtime.ForwardResponseMessage
|
||||
forward_AuthService_GetCurrentUser_0 = runtime.ForwardResponseMessage
|
||||
forward_AuthService_SignIn_0 = runtime.ForwardResponseMessage
|
||||
forward_AuthService_SignOut_0 = runtime.ForwardResponseMessage
|
||||
forward_AuthService_RefreshToken_0 = runtime.ForwardResponseMessage
|
||||
)
|
||||
|
|
|
|||
|
|
@ -20,24 +20,31 @@ import (
|
|||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
AuthService_GetCurrentSession_FullMethodName = "/memos.api.v1.AuthService/GetCurrentSession"
|
||||
AuthService_CreateSession_FullMethodName = "/memos.api.v1.AuthService/CreateSession"
|
||||
AuthService_DeleteSession_FullMethodName = "/memos.api.v1.AuthService/DeleteSession"
|
||||
AuthService_GetCurrentUser_FullMethodName = "/memos.api.v1.AuthService/GetCurrentUser"
|
||||
AuthService_SignIn_FullMethodName = "/memos.api.v1.AuthService/SignIn"
|
||||
AuthService_SignOut_FullMethodName = "/memos.api.v1.AuthService/SignOut"
|
||||
AuthService_RefreshToken_FullMethodName = "/memos.api.v1.AuthService/RefreshToken"
|
||||
)
|
||||
|
||||
// AuthServiceClient is the client API for AuthService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
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) (*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) (*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)
|
||||
// GetCurrentUser returns the authenticated user's information.
|
||||
// Validates the access token and returns user details.
|
||||
// Similar to OIDC's /userinfo endpoint.
|
||||
GetCurrentUser(ctx context.Context, in *GetCurrentUserRequest, opts ...grpc.CallOption) (*GetCurrentUserResponse, error)
|
||||
// SignIn authenticates a user with credentials and returns tokens.
|
||||
// On success, returns an access token and sets a refresh token cookie.
|
||||
// Supports password-based and SSO authentication methods.
|
||||
SignIn(ctx context.Context, in *SignInRequest, opts ...grpc.CallOption) (*SignInResponse, error)
|
||||
// SignOut terminates the user's authentication.
|
||||
// Revokes the refresh token and clears the authentication cookie.
|
||||
SignOut(ctx context.Context, in *SignOutRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
// RefreshToken exchanges a valid refresh token for a new access token.
|
||||
// The refresh token is read from the HttpOnly cookie.
|
||||
// Returns a new short-lived access token.
|
||||
RefreshToken(ctx context.Context, in *RefreshTokenRequest, opts ...grpc.CallOption) (*RefreshTokenResponse, error)
|
||||
}
|
||||
|
||||
type authServiceClient struct {
|
||||
|
|
@ -48,30 +55,40 @@ func NewAuthServiceClient(cc grpc.ClientConnInterface) AuthServiceClient {
|
|||
return &authServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *authServiceClient) GetCurrentSession(ctx context.Context, in *GetCurrentSessionRequest, opts ...grpc.CallOption) (*GetCurrentSessionResponse, error) {
|
||||
func (c *authServiceClient) GetCurrentUser(ctx context.Context, in *GetCurrentUserRequest, opts ...grpc.CallOption) (*GetCurrentUserResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetCurrentSessionResponse)
|
||||
err := c.cc.Invoke(ctx, AuthService_GetCurrentSession_FullMethodName, in, out, cOpts...)
|
||||
out := new(GetCurrentUserResponse)
|
||||
err := c.cc.Invoke(ctx, AuthService_GetCurrentUser_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authServiceClient) CreateSession(ctx context.Context, in *CreateSessionRequest, opts ...grpc.CallOption) (*CreateSessionResponse, error) {
|
||||
func (c *authServiceClient) SignIn(ctx context.Context, in *SignInRequest, opts ...grpc.CallOption) (*SignInResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(CreateSessionResponse)
|
||||
err := c.cc.Invoke(ctx, AuthService_CreateSession_FullMethodName, in, out, cOpts...)
|
||||
out := new(SignInResponse)
|
||||
err := c.cc.Invoke(ctx, AuthService_SignIn_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authServiceClient) DeleteSession(ctx context.Context, in *DeleteSessionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
func (c *authServiceClient) SignOut(ctx context.Context, in *SignOutRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, AuthService_DeleteSession_FullMethodName, in, out, cOpts...)
|
||||
err := c.cc.Invoke(ctx, AuthService_SignOut_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authServiceClient) RefreshToken(ctx context.Context, in *RefreshTokenRequest, opts ...grpc.CallOption) (*RefreshTokenResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(RefreshTokenResponse)
|
||||
err := c.cc.Invoke(ctx, AuthService_RefreshToken_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -82,15 +99,21 @@ func (c *authServiceClient) DeleteSession(ctx context.Context, in *DeleteSession
|
|||
// All implementations must embed UnimplementedAuthServiceServer
|
||||
// for forward compatibility.
|
||||
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) (*GetCurrentSessionResponse, error)
|
||||
// CreateSession authenticates a user and creates a new session.
|
||||
// Returns the authenticated user information upon successful authentication.
|
||||
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)
|
||||
// GetCurrentUser returns the authenticated user's information.
|
||||
// Validates the access token and returns user details.
|
||||
// Similar to OIDC's /userinfo endpoint.
|
||||
GetCurrentUser(context.Context, *GetCurrentUserRequest) (*GetCurrentUserResponse, error)
|
||||
// SignIn authenticates a user with credentials and returns tokens.
|
||||
// On success, returns an access token and sets a refresh token cookie.
|
||||
// Supports password-based and SSO authentication methods.
|
||||
SignIn(context.Context, *SignInRequest) (*SignInResponse, error)
|
||||
// SignOut terminates the user's authentication.
|
||||
// Revokes the refresh token and clears the authentication cookie.
|
||||
SignOut(context.Context, *SignOutRequest) (*emptypb.Empty, error)
|
||||
// RefreshToken exchanges a valid refresh token for a new access token.
|
||||
// The refresh token is read from the HttpOnly cookie.
|
||||
// Returns a new short-lived access token.
|
||||
RefreshToken(context.Context, *RefreshTokenRequest) (*RefreshTokenResponse, error)
|
||||
mustEmbedUnimplementedAuthServiceServer()
|
||||
}
|
||||
|
||||
|
|
@ -101,14 +124,17 @@ type AuthServiceServer interface {
|
|||
// pointer dereference when methods are called.
|
||||
type UnimplementedAuthServiceServer struct{}
|
||||
|
||||
func (UnimplementedAuthServiceServer) GetCurrentSession(context.Context, *GetCurrentSessionRequest) (*GetCurrentSessionResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetCurrentSession not implemented")
|
||||
func (UnimplementedAuthServiceServer) GetCurrentUser(context.Context, *GetCurrentUserRequest) (*GetCurrentUserResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetCurrentUser not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) CreateSession(context.Context, *CreateSessionRequest) (*CreateSessionResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method CreateSession not implemented")
|
||||
func (UnimplementedAuthServiceServer) SignIn(context.Context, *SignInRequest) (*SignInResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method SignIn not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) DeleteSession(context.Context, *DeleteSessionRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method DeleteSession not implemented")
|
||||
func (UnimplementedAuthServiceServer) SignOut(context.Context, *SignOutRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method SignOut not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) RefreshToken(context.Context, *RefreshTokenRequest) (*RefreshTokenResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method RefreshToken not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {}
|
||||
func (UnimplementedAuthServiceServer) testEmbeddedByValue() {}
|
||||
|
|
@ -131,56 +157,74 @@ func RegisterAuthServiceServer(s grpc.ServiceRegistrar, srv AuthServiceServer) {
|
|||
s.RegisterService(&AuthService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _AuthService_GetCurrentSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetCurrentSessionRequest)
|
||||
func _AuthService_GetCurrentUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetCurrentUserRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthServiceServer).GetCurrentSession(ctx, in)
|
||||
return srv.(AuthServiceServer).GetCurrentUser(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthService_GetCurrentSession_FullMethodName,
|
||||
FullMethod: AuthService_GetCurrentUser_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthServiceServer).GetCurrentSession(ctx, req.(*GetCurrentSessionRequest))
|
||||
return srv.(AuthServiceServer).GetCurrentUser(ctx, req.(*GetCurrentUserRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthService_CreateSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreateSessionRequest)
|
||||
func _AuthService_SignIn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SignInRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthServiceServer).CreateSession(ctx, in)
|
||||
return srv.(AuthServiceServer).SignIn(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthService_CreateSession_FullMethodName,
|
||||
FullMethod: AuthService_SignIn_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthServiceServer).CreateSession(ctx, req.(*CreateSessionRequest))
|
||||
return srv.(AuthServiceServer).SignIn(ctx, req.(*SignInRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthService_DeleteSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeleteSessionRequest)
|
||||
func _AuthService_SignOut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SignOutRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthServiceServer).DeleteSession(ctx, in)
|
||||
return srv.(AuthServiceServer).SignOut(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthService_DeleteSession_FullMethodName,
|
||||
FullMethod: AuthService_SignOut_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthServiceServer).DeleteSession(ctx, req.(*DeleteSessionRequest))
|
||||
return srv.(AuthServiceServer).SignOut(ctx, req.(*SignOutRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthService_RefreshToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RefreshTokenRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthServiceServer).RefreshToken(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthService_RefreshToken_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthServiceServer).RefreshToken(ctx, req.(*RefreshTokenRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
|
@ -193,16 +237,20 @@ var AuthService_ServiceDesc = grpc.ServiceDesc{
|
|||
HandlerType: (*AuthServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetCurrentSession",
|
||||
Handler: _AuthService_GetCurrentSession_Handler,
|
||||
MethodName: "GetCurrentUser",
|
||||
Handler: _AuthService_GetCurrentUser_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CreateSession",
|
||||
Handler: _AuthService_CreateSession_Handler,
|
||||
MethodName: "SignIn",
|
||||
Handler: _AuthService_SignIn_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeleteSession",
|
||||
Handler: _AuthService_DeleteSession_Handler,
|
||||
MethodName: "SignOut",
|
||||
Handler: _AuthService_SignOut_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "RefreshToken",
|
||||
Handler: _AuthService_RefreshToken_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -531,11 +531,11 @@ func local_request_UserService_ListUserSettings_0(ctx context.Context, marshaler
|
|||
return msg, metadata, err
|
||||
}
|
||||
|
||||
var filter_UserService_ListUserAccessTokens_0 = &utilities.DoubleArray{Encoding: map[string]int{"parent": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
|
||||
var filter_UserService_ListPersonalAccessTokens_0 = &utilities.DoubleArray{Encoding: map[string]int{"parent": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
|
||||
|
||||
func request_UserService_ListUserAccessTokens_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
func request_UserService_ListPersonalAccessTokens_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq ListUserAccessTokensRequest
|
||||
protoReq ListPersonalAccessTokensRequest
|
||||
metadata runtime.ServerMetadata
|
||||
err error
|
||||
)
|
||||
|
|
@ -553,16 +553,16 @@ func request_UserService_ListUserAccessTokens_0(ctx context.Context, marshaler r
|
|||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_ListUserAccessTokens_0); err != nil {
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_ListPersonalAccessTokens_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
msg, err := client.ListUserAccessTokens(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
msg, err := client.ListPersonalAccessTokens(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func local_request_UserService_ListUserAccessTokens_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
func local_request_UserService_ListPersonalAccessTokens_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq ListUserAccessTokensRequest
|
||||
protoReq ListPersonalAccessTokensRequest
|
||||
metadata runtime.ServerMetadata
|
||||
err error
|
||||
)
|
||||
|
|
@ -577,22 +577,20 @@ func local_request_UserService_ListUserAccessTokens_0(ctx context.Context, marsh
|
|||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_ListUserAccessTokens_0); err != nil {
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_ListPersonalAccessTokens_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
msg, err := server.ListUserAccessTokens(ctx, &protoReq)
|
||||
msg, err := server.ListPersonalAccessTokens(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
var filter_UserService_CreateUserAccessToken_0 = &utilities.DoubleArray{Encoding: map[string]int{"access_token": 0, "parent": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}}
|
||||
|
||||
func request_UserService_CreateUserAccessToken_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
func request_UserService_CreatePersonalAccessToken_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq CreateUserAccessTokenRequest
|
||||
protoReq CreatePersonalAccessTokenRequest
|
||||
metadata runtime.ServerMetadata
|
||||
err error
|
||||
)
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq.AccessToken); err != nil && !errors.Is(err, io.EOF) {
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if req.Body != nil {
|
||||
|
|
@ -606,23 +604,17 @@ func request_UserService_CreateUserAccessToken_0(ctx context.Context, marshaler
|
|||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
|
||||
}
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_CreateUserAccessToken_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
msg, err := client.CreateUserAccessToken(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
msg, err := client.CreatePersonalAccessToken(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func local_request_UserService_CreateUserAccessToken_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
func local_request_UserService_CreatePersonalAccessToken_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq CreateUserAccessTokenRequest
|
||||
protoReq CreatePersonalAccessTokenRequest
|
||||
metadata runtime.ServerMetadata
|
||||
err error
|
||||
)
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq.AccessToken); err != nil && !errors.Is(err, io.EOF) {
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
val, ok := pathParams["parent"]
|
||||
|
|
@ -633,19 +625,13 @@ func local_request_UserService_CreateUserAccessToken_0(ctx context.Context, mars
|
|||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
|
||||
}
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_CreateUserAccessToken_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
msg, err := server.CreateUserAccessToken(ctx, &protoReq)
|
||||
msg, err := server.CreatePersonalAccessToken(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func request_UserService_DeleteUserAccessToken_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
func request_UserService_DeletePersonalAccessToken_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq DeleteUserAccessTokenRequest
|
||||
protoReq DeletePersonalAccessTokenRequest
|
||||
metadata runtime.ServerMetadata
|
||||
err error
|
||||
)
|
||||
|
|
@ -660,13 +646,13 @@ func request_UserService_DeleteUserAccessToken_0(ctx context.Context, marshaler
|
|||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
|
||||
}
|
||||
msg, err := client.DeleteUserAccessToken(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
msg, err := client.DeletePersonalAccessToken(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func local_request_UserService_DeleteUserAccessToken_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
func local_request_UserService_DeletePersonalAccessToken_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq DeleteUserAccessTokenRequest
|
||||
protoReq DeletePersonalAccessTokenRequest
|
||||
metadata runtime.ServerMetadata
|
||||
err error
|
||||
)
|
||||
|
|
@ -678,13 +664,13 @@ func local_request_UserService_DeleteUserAccessToken_0(ctx context.Context, mars
|
|||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
|
||||
}
|
||||
msg, err := server.DeleteUserAccessToken(ctx, &protoReq)
|
||||
msg, err := server.DeletePersonalAccessToken(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func request_UserService_ListUserSessions_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
func request_UserService_ListSessions_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq ListUserSessionsRequest
|
||||
protoReq ListSessionsRequest
|
||||
metadata runtime.ServerMetadata
|
||||
err error
|
||||
)
|
||||
|
|
@ -699,13 +685,13 @@ func request_UserService_ListUserSessions_0(ctx context.Context, marshaler runti
|
|||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
|
||||
}
|
||||
msg, err := client.ListUserSessions(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
msg, err := client.ListSessions(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func local_request_UserService_ListUserSessions_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
func local_request_UserService_ListSessions_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq ListUserSessionsRequest
|
||||
protoReq ListSessionsRequest
|
||||
metadata runtime.ServerMetadata
|
||||
err error
|
||||
)
|
||||
|
|
@ -717,13 +703,13 @@ func local_request_UserService_ListUserSessions_0(ctx context.Context, marshaler
|
|||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
|
||||
}
|
||||
msg, err := server.ListUserSessions(ctx, &protoReq)
|
||||
msg, err := server.ListSessions(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func request_UserService_RevokeUserSession_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
func request_UserService_RevokeSession_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq RevokeUserSessionRequest
|
||||
protoReq RevokeSessionRequest
|
||||
metadata runtime.ServerMetadata
|
||||
err error
|
||||
)
|
||||
|
|
@ -738,13 +724,13 @@ func request_UserService_RevokeUserSession_0(ctx context.Context, marshaler runt
|
|||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
|
||||
}
|
||||
msg, err := client.RevokeUserSession(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
msg, err := client.RevokeSession(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func local_request_UserService_RevokeUserSession_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
func local_request_UserService_RevokeSession_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq RevokeUserSessionRequest
|
||||
protoReq RevokeSessionRequest
|
||||
metadata runtime.ServerMetadata
|
||||
err error
|
||||
)
|
||||
|
|
@ -756,7 +742,7 @@ func local_request_UserService_RevokeUserSession_0(ctx context.Context, marshale
|
|||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
|
||||
}
|
||||
msg, err := server.RevokeUserSession(ctx, &protoReq)
|
||||
msg, err := server.RevokeSession(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
|
|
@ -1343,105 +1329,105 @@ func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
|
|||
}
|
||||
forward_UserService_ListUserSettings_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodGet, pattern_UserService_ListUserAccessTokens_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle(http.MethodGet, pattern_UserService_ListPersonalAccessTokens_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/ListUserAccessTokens", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/accessTokens"))
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/ListPersonalAccessTokens", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/personalAccessTokens"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_UserService_ListUserAccessTokens_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
resp, md, err := local_request_UserService_ListPersonalAccessTokens_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_UserService_ListUserAccessTokens_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_UserService_ListPersonalAccessTokens_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodPost, pattern_UserService_CreateUserAccessToken_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle(http.MethodPost, pattern_UserService_CreatePersonalAccessToken_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/CreateUserAccessToken", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/accessTokens"))
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/CreatePersonalAccessToken", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/personalAccessTokens"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_UserService_CreateUserAccessToken_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
resp, md, err := local_request_UserService_CreatePersonalAccessToken_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_UserService_CreateUserAccessToken_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_UserService_CreatePersonalAccessToken_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodDelete, pattern_UserService_DeleteUserAccessToken_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle(http.MethodDelete, pattern_UserService_DeletePersonalAccessToken_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/DeleteUserAccessToken", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/accessTokens/*}"))
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/DeletePersonalAccessToken", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/personalAccessTokens/*}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_UserService_DeleteUserAccessToken_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
resp, md, err := local_request_UserService_DeletePersonalAccessToken_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_UserService_DeleteUserAccessToken_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_UserService_DeletePersonalAccessToken_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodGet, pattern_UserService_ListUserSessions_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle(http.MethodGet, pattern_UserService_ListSessions_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/ListUserSessions", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/sessions"))
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/ListSessions", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/sessions"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_UserService_ListUserSessions_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
resp, md, err := local_request_UserService_ListSessions_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_UserService_ListUserSessions_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_UserService_ListSessions_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodDelete, pattern_UserService_RevokeUserSession_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle(http.MethodDelete, pattern_UserService_RevokeSession_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/RevokeUserSession", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/sessions/*}"))
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/RevokeSession", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/sessions/*}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_UserService_RevokeUserSession_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
resp, md, err := local_request_UserService_RevokeSession_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_UserService_RevokeUserSession_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_UserService_RevokeSession_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodGet, pattern_UserService_ListUserWebhooks_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
|
|
@ -1793,90 +1779,90 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
|
|||
}
|
||||
forward_UserService_ListUserSettings_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodGet, pattern_UserService_ListUserAccessTokens_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle(http.MethodGet, pattern_UserService_ListPersonalAccessTokens_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/ListUserAccessTokens", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/accessTokens"))
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/ListPersonalAccessTokens", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/personalAccessTokens"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_UserService_ListUserAccessTokens_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
resp, md, err := request_UserService_ListPersonalAccessTokens_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_UserService_ListUserAccessTokens_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_UserService_ListPersonalAccessTokens_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodPost, pattern_UserService_CreateUserAccessToken_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle(http.MethodPost, pattern_UserService_CreatePersonalAccessToken_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/CreateUserAccessToken", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/accessTokens"))
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/CreatePersonalAccessToken", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/personalAccessTokens"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_UserService_CreateUserAccessToken_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
resp, md, err := request_UserService_CreatePersonalAccessToken_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_UserService_CreateUserAccessToken_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_UserService_CreatePersonalAccessToken_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodDelete, pattern_UserService_DeleteUserAccessToken_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle(http.MethodDelete, pattern_UserService_DeletePersonalAccessToken_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/DeleteUserAccessToken", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/accessTokens/*}"))
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/DeletePersonalAccessToken", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/personalAccessTokens/*}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_UserService_DeleteUserAccessToken_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
resp, md, err := request_UserService_DeletePersonalAccessToken_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_UserService_DeleteUserAccessToken_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_UserService_DeletePersonalAccessToken_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodGet, pattern_UserService_ListUserSessions_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle(http.MethodGet, pattern_UserService_ListSessions_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/ListUserSessions", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/sessions"))
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/ListSessions", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/sessions"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_UserService_ListUserSessions_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
resp, md, err := request_UserService_ListSessions_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_UserService_ListUserSessions_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_UserService_ListSessions_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodDelete, pattern_UserService_RevokeUserSession_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle(http.MethodDelete, pattern_UserService_RevokeSession_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/RevokeUserSession", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/sessions/*}"))
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/RevokeSession", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/sessions/*}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_UserService_RevokeUserSession_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
resp, md, err := request_UserService_RevokeSession_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_UserService_RevokeUserSession_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_UserService_RevokeSession_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodGet, pattern_UserService_ListUserWebhooks_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
|
|
@ -2001,51 +1987,51 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
|
|||
}
|
||||
|
||||
var (
|
||||
pattern_UserService_ListUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, ""))
|
||||
pattern_UserService_GetUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, ""))
|
||||
pattern_UserService_CreateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, ""))
|
||||
pattern_UserService_UpdateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "user.name"}, ""))
|
||||
pattern_UserService_DeleteUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, ""))
|
||||
pattern_UserService_ListAllUserStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "stats"))
|
||||
pattern_UserService_GetUserStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "getStats"))
|
||||
pattern_UserService_GetUserSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "settings", "name"}, ""))
|
||||
pattern_UserService_UpdateUserSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "settings", "setting.name"}, ""))
|
||||
pattern_UserService_ListUserSettings_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "settings"}, ""))
|
||||
pattern_UserService_ListUserAccessTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "accessTokens"}, ""))
|
||||
pattern_UserService_CreateUserAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "accessTokens"}, ""))
|
||||
pattern_UserService_DeleteUserAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "accessTokens", "name"}, ""))
|
||||
pattern_UserService_ListUserSessions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "sessions"}, ""))
|
||||
pattern_UserService_RevokeUserSession_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "sessions", "name"}, ""))
|
||||
pattern_UserService_ListUserWebhooks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, ""))
|
||||
pattern_UserService_CreateUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, ""))
|
||||
pattern_UserService_UpdateUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "webhook.name"}, ""))
|
||||
pattern_UserService_DeleteUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "name"}, ""))
|
||||
pattern_UserService_ListUserNotifications_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "notifications"}, ""))
|
||||
pattern_UserService_UpdateUserNotification_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "notifications", "notification.name"}, ""))
|
||||
pattern_UserService_DeleteUserNotification_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "notifications", "name"}, ""))
|
||||
pattern_UserService_ListUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, ""))
|
||||
pattern_UserService_GetUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, ""))
|
||||
pattern_UserService_CreateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, ""))
|
||||
pattern_UserService_UpdateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "user.name"}, ""))
|
||||
pattern_UserService_DeleteUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, ""))
|
||||
pattern_UserService_ListAllUserStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "stats"))
|
||||
pattern_UserService_GetUserStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "getStats"))
|
||||
pattern_UserService_GetUserSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "settings", "name"}, ""))
|
||||
pattern_UserService_UpdateUserSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "settings", "setting.name"}, ""))
|
||||
pattern_UserService_ListUserSettings_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "settings"}, ""))
|
||||
pattern_UserService_ListPersonalAccessTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "personalAccessTokens"}, ""))
|
||||
pattern_UserService_CreatePersonalAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "personalAccessTokens"}, ""))
|
||||
pattern_UserService_DeletePersonalAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "personalAccessTokens", "name"}, ""))
|
||||
pattern_UserService_ListSessions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "sessions"}, ""))
|
||||
pattern_UserService_RevokeSession_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "sessions", "name"}, ""))
|
||||
pattern_UserService_ListUserWebhooks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, ""))
|
||||
pattern_UserService_CreateUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, ""))
|
||||
pattern_UserService_UpdateUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "webhook.name"}, ""))
|
||||
pattern_UserService_DeleteUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "name"}, ""))
|
||||
pattern_UserService_ListUserNotifications_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "notifications"}, ""))
|
||||
pattern_UserService_UpdateUserNotification_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "notifications", "notification.name"}, ""))
|
||||
pattern_UserService_DeleteUserNotification_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "notifications", "name"}, ""))
|
||||
)
|
||||
|
||||
var (
|
||||
forward_UserService_ListUsers_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_GetUser_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_CreateUser_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_UpdateUser_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_DeleteUser_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_ListAllUserStats_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_GetUserStats_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_GetUserSetting_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_UpdateUserSetting_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_ListUserSettings_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_ListUserAccessTokens_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_CreateUserAccessToken_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_DeleteUserAccessToken_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_ListUserSessions_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_RevokeUserSession_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_ListUserWebhooks_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_CreateUserWebhook_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_UpdateUserWebhook_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_DeleteUserWebhook_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_ListUserNotifications_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_UpdateUserNotification_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_DeleteUserNotification_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_ListUsers_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_GetUser_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_CreateUser_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_UpdateUser_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_DeleteUser_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_ListAllUserStats_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_GetUserStats_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_GetUserSetting_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_UpdateUserSetting_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_ListUserSettings_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_ListPersonalAccessTokens_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_CreatePersonalAccessToken_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_DeletePersonalAccessToken_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_ListSessions_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_RevokeSession_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_ListUserWebhooks_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_CreateUserWebhook_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_UpdateUserWebhook_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_DeleteUserWebhook_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_ListUserNotifications_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_UpdateUserNotification_0 = runtime.ForwardResponseMessage
|
||||
forward_UserService_DeleteUserNotification_0 = runtime.ForwardResponseMessage
|
||||
)
|
||||
|
|
|
|||
|
|
@ -20,28 +20,28 @@ import (
|
|||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
UserService_ListUsers_FullMethodName = "/memos.api.v1.UserService/ListUsers"
|
||||
UserService_GetUser_FullMethodName = "/memos.api.v1.UserService/GetUser"
|
||||
UserService_CreateUser_FullMethodName = "/memos.api.v1.UserService/CreateUser"
|
||||
UserService_UpdateUser_FullMethodName = "/memos.api.v1.UserService/UpdateUser"
|
||||
UserService_DeleteUser_FullMethodName = "/memos.api.v1.UserService/DeleteUser"
|
||||
UserService_ListAllUserStats_FullMethodName = "/memos.api.v1.UserService/ListAllUserStats"
|
||||
UserService_GetUserStats_FullMethodName = "/memos.api.v1.UserService/GetUserStats"
|
||||
UserService_GetUserSetting_FullMethodName = "/memos.api.v1.UserService/GetUserSetting"
|
||||
UserService_UpdateUserSetting_FullMethodName = "/memos.api.v1.UserService/UpdateUserSetting"
|
||||
UserService_ListUserSettings_FullMethodName = "/memos.api.v1.UserService/ListUserSettings"
|
||||
UserService_ListUserAccessTokens_FullMethodName = "/memos.api.v1.UserService/ListUserAccessTokens"
|
||||
UserService_CreateUserAccessToken_FullMethodName = "/memos.api.v1.UserService/CreateUserAccessToken"
|
||||
UserService_DeleteUserAccessToken_FullMethodName = "/memos.api.v1.UserService/DeleteUserAccessToken"
|
||||
UserService_ListUserSessions_FullMethodName = "/memos.api.v1.UserService/ListUserSessions"
|
||||
UserService_RevokeUserSession_FullMethodName = "/memos.api.v1.UserService/RevokeUserSession"
|
||||
UserService_ListUserWebhooks_FullMethodName = "/memos.api.v1.UserService/ListUserWebhooks"
|
||||
UserService_CreateUserWebhook_FullMethodName = "/memos.api.v1.UserService/CreateUserWebhook"
|
||||
UserService_UpdateUserWebhook_FullMethodName = "/memos.api.v1.UserService/UpdateUserWebhook"
|
||||
UserService_DeleteUserWebhook_FullMethodName = "/memos.api.v1.UserService/DeleteUserWebhook"
|
||||
UserService_ListUserNotifications_FullMethodName = "/memos.api.v1.UserService/ListUserNotifications"
|
||||
UserService_UpdateUserNotification_FullMethodName = "/memos.api.v1.UserService/UpdateUserNotification"
|
||||
UserService_DeleteUserNotification_FullMethodName = "/memos.api.v1.UserService/DeleteUserNotification"
|
||||
UserService_ListUsers_FullMethodName = "/memos.api.v1.UserService/ListUsers"
|
||||
UserService_GetUser_FullMethodName = "/memos.api.v1.UserService/GetUser"
|
||||
UserService_CreateUser_FullMethodName = "/memos.api.v1.UserService/CreateUser"
|
||||
UserService_UpdateUser_FullMethodName = "/memos.api.v1.UserService/UpdateUser"
|
||||
UserService_DeleteUser_FullMethodName = "/memos.api.v1.UserService/DeleteUser"
|
||||
UserService_ListAllUserStats_FullMethodName = "/memos.api.v1.UserService/ListAllUserStats"
|
||||
UserService_GetUserStats_FullMethodName = "/memos.api.v1.UserService/GetUserStats"
|
||||
UserService_GetUserSetting_FullMethodName = "/memos.api.v1.UserService/GetUserSetting"
|
||||
UserService_UpdateUserSetting_FullMethodName = "/memos.api.v1.UserService/UpdateUserSetting"
|
||||
UserService_ListUserSettings_FullMethodName = "/memos.api.v1.UserService/ListUserSettings"
|
||||
UserService_ListPersonalAccessTokens_FullMethodName = "/memos.api.v1.UserService/ListPersonalAccessTokens"
|
||||
UserService_CreatePersonalAccessToken_FullMethodName = "/memos.api.v1.UserService/CreatePersonalAccessToken"
|
||||
UserService_DeletePersonalAccessToken_FullMethodName = "/memos.api.v1.UserService/DeletePersonalAccessToken"
|
||||
UserService_ListSessions_FullMethodName = "/memos.api.v1.UserService/ListSessions"
|
||||
UserService_RevokeSession_FullMethodName = "/memos.api.v1.UserService/RevokeSession"
|
||||
UserService_ListUserWebhooks_FullMethodName = "/memos.api.v1.UserService/ListUserWebhooks"
|
||||
UserService_CreateUserWebhook_FullMethodName = "/memos.api.v1.UserService/CreateUserWebhook"
|
||||
UserService_UpdateUserWebhook_FullMethodName = "/memos.api.v1.UserService/UpdateUserWebhook"
|
||||
UserService_DeleteUserWebhook_FullMethodName = "/memos.api.v1.UserService/DeleteUserWebhook"
|
||||
UserService_ListUserNotifications_FullMethodName = "/memos.api.v1.UserService/ListUserNotifications"
|
||||
UserService_UpdateUserNotification_FullMethodName = "/memos.api.v1.UserService/UpdateUserNotification"
|
||||
UserService_DeleteUserNotification_FullMethodName = "/memos.api.v1.UserService/DeleteUserNotification"
|
||||
)
|
||||
|
||||
// UserServiceClient is the client API for UserService service.
|
||||
|
|
@ -71,16 +71,21 @@ type UserServiceClient interface {
|
|||
UpdateUserSetting(ctx context.Context, in *UpdateUserSettingRequest, opts ...grpc.CallOption) (*UserSetting, error)
|
||||
// ListUserSettings returns a list of user settings.
|
||||
ListUserSettings(ctx context.Context, in *ListUserSettingsRequest, opts ...grpc.CallOption) (*ListUserSettingsResponse, error)
|
||||
// ListUserAccessTokens returns a list of access tokens for a user.
|
||||
ListUserAccessTokens(ctx context.Context, in *ListUserAccessTokensRequest, opts ...grpc.CallOption) (*ListUserAccessTokensResponse, error)
|
||||
// CreateUserAccessToken creates a new access token for a user.
|
||||
CreateUserAccessToken(ctx context.Context, in *CreateUserAccessTokenRequest, opts ...grpc.CallOption) (*UserAccessToken, error)
|
||||
// DeleteUserAccessToken deletes an access token.
|
||||
DeleteUserAccessToken(ctx context.Context, in *DeleteUserAccessTokenRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
// ListUserSessions returns a list of active sessions for a user.
|
||||
ListUserSessions(ctx context.Context, in *ListUserSessionsRequest, opts ...grpc.CallOption) (*ListUserSessionsResponse, error)
|
||||
// RevokeUserSession revokes a specific session for a user.
|
||||
RevokeUserSession(ctx context.Context, in *RevokeUserSessionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
// ListPersonalAccessTokens returns a list of Personal Access Tokens (PATs) for a user.
|
||||
// PATs are long-lived tokens for API/script access, distinct from short-lived JWT access tokens.
|
||||
ListPersonalAccessTokens(ctx context.Context, in *ListPersonalAccessTokensRequest, opts ...grpc.CallOption) (*ListPersonalAccessTokensResponse, error)
|
||||
// CreatePersonalAccessToken creates a new Personal Access Token for a user.
|
||||
// The token value is only returned once upon creation.
|
||||
CreatePersonalAccessToken(ctx context.Context, in *CreatePersonalAccessTokenRequest, opts ...grpc.CallOption) (*CreatePersonalAccessTokenResponse, error)
|
||||
// DeletePersonalAccessToken deletes a Personal Access Token.
|
||||
DeletePersonalAccessToken(ctx context.Context, in *DeletePersonalAccessTokenRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
// ListSessions returns a list of active login sessions for a user.
|
||||
// Each session represents a browser/device where the user is logged in.
|
||||
// Sessions are backed by refresh tokens with sliding expiration.
|
||||
ListSessions(ctx context.Context, in *ListSessionsRequest, opts ...grpc.CallOption) (*ListSessionsResponse, error)
|
||||
// RevokeSession revokes a specific login session.
|
||||
// This invalidates the refresh token, forcing re-authentication on that device.
|
||||
RevokeSession(ctx context.Context, in *RevokeSessionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
// ListUserWebhooks returns a list of webhooks for a user.
|
||||
ListUserWebhooks(ctx context.Context, in *ListUserWebhooksRequest, opts ...grpc.CallOption) (*ListUserWebhooksResponse, error)
|
||||
// CreateUserWebhook creates a new webhook for a user.
|
||||
|
|
@ -205,50 +210,50 @@ func (c *userServiceClient) ListUserSettings(ctx context.Context, in *ListUserSe
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (c *userServiceClient) ListUserAccessTokens(ctx context.Context, in *ListUserAccessTokensRequest, opts ...grpc.CallOption) (*ListUserAccessTokensResponse, error) {
|
||||
func (c *userServiceClient) ListPersonalAccessTokens(ctx context.Context, in *ListPersonalAccessTokensRequest, opts ...grpc.CallOption) (*ListPersonalAccessTokensResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListUserAccessTokensResponse)
|
||||
err := c.cc.Invoke(ctx, UserService_ListUserAccessTokens_FullMethodName, in, out, cOpts...)
|
||||
out := new(ListPersonalAccessTokensResponse)
|
||||
err := c.cc.Invoke(ctx, UserService_ListPersonalAccessTokens_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *userServiceClient) CreateUserAccessToken(ctx context.Context, in *CreateUserAccessTokenRequest, opts ...grpc.CallOption) (*UserAccessToken, error) {
|
||||
func (c *userServiceClient) CreatePersonalAccessToken(ctx context.Context, in *CreatePersonalAccessTokenRequest, opts ...grpc.CallOption) (*CreatePersonalAccessTokenResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(UserAccessToken)
|
||||
err := c.cc.Invoke(ctx, UserService_CreateUserAccessToken_FullMethodName, in, out, cOpts...)
|
||||
out := new(CreatePersonalAccessTokenResponse)
|
||||
err := c.cc.Invoke(ctx, UserService_CreatePersonalAccessToken_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *userServiceClient) DeleteUserAccessToken(ctx context.Context, in *DeleteUserAccessTokenRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
func (c *userServiceClient) DeletePersonalAccessToken(ctx context.Context, in *DeletePersonalAccessTokenRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, UserService_DeleteUserAccessToken_FullMethodName, in, out, cOpts...)
|
||||
err := c.cc.Invoke(ctx, UserService_DeletePersonalAccessToken_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *userServiceClient) ListUserSessions(ctx context.Context, in *ListUserSessionsRequest, opts ...grpc.CallOption) (*ListUserSessionsResponse, error) {
|
||||
func (c *userServiceClient) ListSessions(ctx context.Context, in *ListSessionsRequest, opts ...grpc.CallOption) (*ListSessionsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListUserSessionsResponse)
|
||||
err := c.cc.Invoke(ctx, UserService_ListUserSessions_FullMethodName, in, out, cOpts...)
|
||||
out := new(ListSessionsResponse)
|
||||
err := c.cc.Invoke(ctx, UserService_ListSessions_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *userServiceClient) RevokeUserSession(ctx context.Context, in *RevokeUserSessionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
func (c *userServiceClient) RevokeSession(ctx context.Context, in *RevokeSessionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, UserService_RevokeUserSession_FullMethodName, in, out, cOpts...)
|
||||
err := c.cc.Invoke(ctx, UserService_RevokeSession_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -352,16 +357,21 @@ type UserServiceServer interface {
|
|||
UpdateUserSetting(context.Context, *UpdateUserSettingRequest) (*UserSetting, error)
|
||||
// ListUserSettings returns a list of user settings.
|
||||
ListUserSettings(context.Context, *ListUserSettingsRequest) (*ListUserSettingsResponse, error)
|
||||
// ListUserAccessTokens returns a list of access tokens for a user.
|
||||
ListUserAccessTokens(context.Context, *ListUserAccessTokensRequest) (*ListUserAccessTokensResponse, error)
|
||||
// CreateUserAccessToken creates a new access token for a user.
|
||||
CreateUserAccessToken(context.Context, *CreateUserAccessTokenRequest) (*UserAccessToken, error)
|
||||
// DeleteUserAccessToken deletes an access token.
|
||||
DeleteUserAccessToken(context.Context, *DeleteUserAccessTokenRequest) (*emptypb.Empty, error)
|
||||
// ListUserSessions returns a list of active sessions for a user.
|
||||
ListUserSessions(context.Context, *ListUserSessionsRequest) (*ListUserSessionsResponse, error)
|
||||
// RevokeUserSession revokes a specific session for a user.
|
||||
RevokeUserSession(context.Context, *RevokeUserSessionRequest) (*emptypb.Empty, error)
|
||||
// ListPersonalAccessTokens returns a list of Personal Access Tokens (PATs) for a user.
|
||||
// PATs are long-lived tokens for API/script access, distinct from short-lived JWT access tokens.
|
||||
ListPersonalAccessTokens(context.Context, *ListPersonalAccessTokensRequest) (*ListPersonalAccessTokensResponse, error)
|
||||
// CreatePersonalAccessToken creates a new Personal Access Token for a user.
|
||||
// The token value is only returned once upon creation.
|
||||
CreatePersonalAccessToken(context.Context, *CreatePersonalAccessTokenRequest) (*CreatePersonalAccessTokenResponse, error)
|
||||
// DeletePersonalAccessToken deletes a Personal Access Token.
|
||||
DeletePersonalAccessToken(context.Context, *DeletePersonalAccessTokenRequest) (*emptypb.Empty, error)
|
||||
// ListSessions returns a list of active login sessions for a user.
|
||||
// Each session represents a browser/device where the user is logged in.
|
||||
// Sessions are backed by refresh tokens with sliding expiration.
|
||||
ListSessions(context.Context, *ListSessionsRequest) (*ListSessionsResponse, error)
|
||||
// RevokeSession revokes a specific login session.
|
||||
// This invalidates the refresh token, forcing re-authentication on that device.
|
||||
RevokeSession(context.Context, *RevokeSessionRequest) (*emptypb.Empty, error)
|
||||
// ListUserWebhooks returns a list of webhooks for a user.
|
||||
ListUserWebhooks(context.Context, *ListUserWebhooksRequest) (*ListUserWebhooksResponse, error)
|
||||
// CreateUserWebhook creates a new webhook for a user.
|
||||
|
|
@ -416,20 +426,20 @@ func (UnimplementedUserServiceServer) UpdateUserSetting(context.Context, *Update
|
|||
func (UnimplementedUserServiceServer) ListUserSettings(context.Context, *ListUserSettingsRequest) (*ListUserSettingsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListUserSettings not implemented")
|
||||
}
|
||||
func (UnimplementedUserServiceServer) ListUserAccessTokens(context.Context, *ListUserAccessTokensRequest) (*ListUserAccessTokensResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListUserAccessTokens not implemented")
|
||||
func (UnimplementedUserServiceServer) ListPersonalAccessTokens(context.Context, *ListPersonalAccessTokensRequest) (*ListPersonalAccessTokensResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListPersonalAccessTokens not implemented")
|
||||
}
|
||||
func (UnimplementedUserServiceServer) CreateUserAccessToken(context.Context, *CreateUserAccessTokenRequest) (*UserAccessToken, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method CreateUserAccessToken not implemented")
|
||||
func (UnimplementedUserServiceServer) CreatePersonalAccessToken(context.Context, *CreatePersonalAccessTokenRequest) (*CreatePersonalAccessTokenResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method CreatePersonalAccessToken not implemented")
|
||||
}
|
||||
func (UnimplementedUserServiceServer) DeleteUserAccessToken(context.Context, *DeleteUserAccessTokenRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method DeleteUserAccessToken not implemented")
|
||||
func (UnimplementedUserServiceServer) DeletePersonalAccessToken(context.Context, *DeletePersonalAccessTokenRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method DeletePersonalAccessToken not implemented")
|
||||
}
|
||||
func (UnimplementedUserServiceServer) ListUserSessions(context.Context, *ListUserSessionsRequest) (*ListUserSessionsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListUserSessions not implemented")
|
||||
func (UnimplementedUserServiceServer) ListSessions(context.Context, *ListSessionsRequest) (*ListSessionsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListSessions not implemented")
|
||||
}
|
||||
func (UnimplementedUserServiceServer) RevokeUserSession(context.Context, *RevokeUserSessionRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method RevokeUserSession not implemented")
|
||||
func (UnimplementedUserServiceServer) RevokeSession(context.Context, *RevokeSessionRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method RevokeSession not implemented")
|
||||
}
|
||||
func (UnimplementedUserServiceServer) ListUserWebhooks(context.Context, *ListUserWebhooksRequest) (*ListUserWebhooksResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListUserWebhooks not implemented")
|
||||
|
|
@ -653,92 +663,92 @@ func _UserService_ListUserSettings_Handler(srv interface{}, ctx context.Context,
|
|||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _UserService_ListUserAccessTokens_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListUserAccessTokensRequest)
|
||||
func _UserService_ListPersonalAccessTokens_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListPersonalAccessTokensRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UserServiceServer).ListUserAccessTokens(ctx, in)
|
||||
return srv.(UserServiceServer).ListPersonalAccessTokens(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: UserService_ListUserAccessTokens_FullMethodName,
|
||||
FullMethod: UserService_ListPersonalAccessTokens_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UserServiceServer).ListUserAccessTokens(ctx, req.(*ListUserAccessTokensRequest))
|
||||
return srv.(UserServiceServer).ListPersonalAccessTokens(ctx, req.(*ListPersonalAccessTokensRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _UserService_CreateUserAccessToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreateUserAccessTokenRequest)
|
||||
func _UserService_CreatePersonalAccessToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreatePersonalAccessTokenRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UserServiceServer).CreateUserAccessToken(ctx, in)
|
||||
return srv.(UserServiceServer).CreatePersonalAccessToken(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: UserService_CreateUserAccessToken_FullMethodName,
|
||||
FullMethod: UserService_CreatePersonalAccessToken_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UserServiceServer).CreateUserAccessToken(ctx, req.(*CreateUserAccessTokenRequest))
|
||||
return srv.(UserServiceServer).CreatePersonalAccessToken(ctx, req.(*CreatePersonalAccessTokenRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _UserService_DeleteUserAccessToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeleteUserAccessTokenRequest)
|
||||
func _UserService_DeletePersonalAccessToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeletePersonalAccessTokenRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UserServiceServer).DeleteUserAccessToken(ctx, in)
|
||||
return srv.(UserServiceServer).DeletePersonalAccessToken(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: UserService_DeleteUserAccessToken_FullMethodName,
|
||||
FullMethod: UserService_DeletePersonalAccessToken_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UserServiceServer).DeleteUserAccessToken(ctx, req.(*DeleteUserAccessTokenRequest))
|
||||
return srv.(UserServiceServer).DeletePersonalAccessToken(ctx, req.(*DeletePersonalAccessTokenRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _UserService_ListUserSessions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListUserSessionsRequest)
|
||||
func _UserService_ListSessions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListSessionsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UserServiceServer).ListUserSessions(ctx, in)
|
||||
return srv.(UserServiceServer).ListSessions(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: UserService_ListUserSessions_FullMethodName,
|
||||
FullMethod: UserService_ListSessions_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UserServiceServer).ListUserSessions(ctx, req.(*ListUserSessionsRequest))
|
||||
return srv.(UserServiceServer).ListSessions(ctx, req.(*ListSessionsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _UserService_RevokeUserSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RevokeUserSessionRequest)
|
||||
func _UserService_RevokeSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RevokeSessionRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UserServiceServer).RevokeUserSession(ctx, in)
|
||||
return srv.(UserServiceServer).RevokeSession(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: UserService_RevokeUserSession_FullMethodName,
|
||||
FullMethod: UserService_RevokeSession_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UserServiceServer).RevokeUserSession(ctx, req.(*RevokeUserSessionRequest))
|
||||
return srv.(UserServiceServer).RevokeSession(ctx, req.(*RevokeSessionRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
|
@ -917,24 +927,24 @@ var UserService_ServiceDesc = grpc.ServiceDesc{
|
|||
Handler: _UserService_ListUserSettings_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListUserAccessTokens",
|
||||
Handler: _UserService_ListUserAccessTokens_Handler,
|
||||
MethodName: "ListPersonalAccessTokens",
|
||||
Handler: _UserService_ListPersonalAccessTokens_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CreateUserAccessToken",
|
||||
Handler: _UserService_CreateUserAccessToken_Handler,
|
||||
MethodName: "CreatePersonalAccessToken",
|
||||
Handler: _UserService_CreatePersonalAccessToken_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeleteUserAccessToken",
|
||||
Handler: _UserService_DeleteUserAccessToken_Handler,
|
||||
MethodName: "DeletePersonalAccessToken",
|
||||
Handler: _UserService_DeletePersonalAccessToken_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListUserSessions",
|
||||
Handler: _UserService_ListUserSessions_Handler,
|
||||
MethodName: "ListSessions",
|
||||
Handler: _UserService_ListSessions_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "RevokeUserSession",
|
||||
Handler: _UserService_RevokeUserSession_Handler,
|
||||
MethodName: "RevokeSession",
|
||||
Handler: _UserService_RevokeSession_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListUserWebhooks",
|
||||
|
|
|
|||
|
|
@ -239,19 +239,42 @@ paths:
|
|||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Status'
|
||||
/api/v1/auth/sessions:
|
||||
/api/v1/auth/me:
|
||||
get:
|
||||
tags:
|
||||
- AuthService
|
||||
description: |-
|
||||
GetCurrentUser returns the authenticated user's information.
|
||||
Validates the access token and returns user details.
|
||||
Similar to OIDC's /userinfo endpoint.
|
||||
operationId: AuthService_GetCurrentUser
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GetCurrentUserResponse'
|
||||
default:
|
||||
description: Default error response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Status'
|
||||
/api/v1/auth/refresh:
|
||||
post:
|
||||
tags:
|
||||
- AuthService
|
||||
description: |-
|
||||
CreateSession authenticates a user and creates a new session.
|
||||
Returns the authenticated user information upon successful authentication.
|
||||
operationId: AuthService_CreateSession
|
||||
RefreshToken exchanges a valid refresh token for a new access token.
|
||||
The refresh token is read from the HttpOnly cookie.
|
||||
Returns a new short-lived access token.
|
||||
operationId: AuthService_RefreshToken
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CreateSessionRequest'
|
||||
$ref: '#/components/schemas/RefreshTokenRequest'
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
|
|
@ -259,41 +282,49 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CreateSessionResponse'
|
||||
$ref: '#/components/schemas/RefreshTokenResponse'
|
||||
default:
|
||||
description: Default error response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Status'
|
||||
/api/v1/auth/sessions/current:
|
||||
get:
|
||||
/api/v1/auth/signin:
|
||||
post:
|
||||
tags:
|
||||
- AuthService
|
||||
description: |-
|
||||
GetCurrentSession returns the current active session information.
|
||||
This method is idempotent and safe, suitable for checking current session state.
|
||||
operationId: AuthService_GetCurrentSession
|
||||
SignIn authenticates a user with credentials and returns tokens.
|
||||
On success, returns an access token and sets a refresh token cookie.
|
||||
Supports password-based and SSO authentication methods.
|
||||
operationId: AuthService_SignIn
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SignInRequest'
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GetCurrentSessionResponse'
|
||||
$ref: '#/components/schemas/SignInResponse'
|
||||
default:
|
||||
description: Default error response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Status'
|
||||
delete:
|
||||
/api/v1/auth/signout:
|
||||
post:
|
||||
tags:
|
||||
- AuthService
|
||||
description: |-
|
||||
DeleteSession terminates the current user session.
|
||||
This is an idempotent operation that invalidates the user's authentication.
|
||||
operationId: AuthService_DeleteSession
|
||||
SignOut terminates the user's authentication.
|
||||
Revokes the refresh token and clears the authentication cookie.
|
||||
operationId: AuthService_SignOut
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
|
|
@ -1220,108 +1251,6 @@ paths:
|
|||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Status'
|
||||
/api/v1/users/{user}/accessTokens:
|
||||
get:
|
||||
tags:
|
||||
- UserService
|
||||
description: ListUserAccessTokens returns a list of access tokens for a user.
|
||||
operationId: UserService_ListUserAccessTokens
|
||||
parameters:
|
||||
- name: user
|
||||
in: path
|
||||
description: The user id.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: pageSize
|
||||
in: query
|
||||
description: Optional. The maximum number of access tokens to return.
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
- name: pageToken
|
||||
in: query
|
||||
description: Optional. A page token for pagination.
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ListUserAccessTokensResponse'
|
||||
default:
|
||||
description: Default error response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Status'
|
||||
post:
|
||||
tags:
|
||||
- UserService
|
||||
description: CreateUserAccessToken creates a new access token for a user.
|
||||
operationId: UserService_CreateUserAccessToken
|
||||
parameters:
|
||||
- name: user
|
||||
in: path
|
||||
description: The user id.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: accessTokenId
|
||||
in: query
|
||||
description: Optional. The access token ID to use.
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UserAccessToken'
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UserAccessToken'
|
||||
default:
|
||||
description: Default error response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Status'
|
||||
/api/v1/users/{user}/accessTokens/{accessToken}:
|
||||
delete:
|
||||
tags:
|
||||
- UserService
|
||||
description: DeleteUserAccessToken deletes an access token.
|
||||
operationId: UserService_DeleteUserAccessToken
|
||||
parameters:
|
||||
- name: user
|
||||
in: path
|
||||
description: The user id.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: accessToken
|
||||
in: path
|
||||
description: The accessToken id.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content: {}
|
||||
default:
|
||||
description: Default error response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Status'
|
||||
/api/v1/users/{user}/notifications:
|
||||
get:
|
||||
tags:
|
||||
|
|
@ -1432,12 +1361,116 @@ paths:
|
|||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Status'
|
||||
/api/v1/users/{user}/personalAccessTokens:
|
||||
get:
|
||||
tags:
|
||||
- UserService
|
||||
description: |-
|
||||
ListPersonalAccessTokens returns a list of Personal Access Tokens (PATs) for a user.
|
||||
PATs are long-lived tokens for API/script access, distinct from short-lived JWT access tokens.
|
||||
operationId: UserService_ListPersonalAccessTokens
|
||||
parameters:
|
||||
- name: user
|
||||
in: path
|
||||
description: The user id.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: pageSize
|
||||
in: query
|
||||
description: Optional. The maximum number of tokens to return.
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
- name: pageToken
|
||||
in: query
|
||||
description: Optional. A page token for pagination.
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ListPersonalAccessTokensResponse'
|
||||
default:
|
||||
description: Default error response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Status'
|
||||
post:
|
||||
tags:
|
||||
- UserService
|
||||
description: |-
|
||||
CreatePersonalAccessToken creates a new Personal Access Token for a user.
|
||||
The token value is only returned once upon creation.
|
||||
operationId: UserService_CreatePersonalAccessToken
|
||||
parameters:
|
||||
- name: user
|
||||
in: path
|
||||
description: The user id.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CreatePersonalAccessTokenRequest'
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CreatePersonalAccessTokenResponse'
|
||||
default:
|
||||
description: Default error response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Status'
|
||||
/api/v1/users/{user}/personalAccessTokens/{personalAccessToken}:
|
||||
delete:
|
||||
tags:
|
||||
- UserService
|
||||
description: DeletePersonalAccessToken deletes a Personal Access Token.
|
||||
operationId: UserService_DeletePersonalAccessToken
|
||||
parameters:
|
||||
- name: user
|
||||
in: path
|
||||
description: The user id.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: personalAccessToken
|
||||
in: path
|
||||
description: The personalAccessToken id.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content: {}
|
||||
default:
|
||||
description: Default error response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Status'
|
||||
/api/v1/users/{user}/sessions:
|
||||
get:
|
||||
tags:
|
||||
- UserService
|
||||
description: ListUserSessions returns a list of active sessions for a user.
|
||||
operationId: UserService_ListUserSessions
|
||||
description: |-
|
||||
ListSessions returns a list of active login sessions for a user.
|
||||
Each session represents a browser/device where the user is logged in.
|
||||
Sessions are backed by refresh tokens with sliding expiration.
|
||||
operationId: UserService_ListSessions
|
||||
parameters:
|
||||
- name: user
|
||||
in: path
|
||||
|
|
@ -1451,7 +1484,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ListUserSessionsResponse'
|
||||
$ref: '#/components/schemas/ListSessionsResponse'
|
||||
default:
|
||||
description: Default error response
|
||||
content:
|
||||
|
|
@ -1462,8 +1495,10 @@ paths:
|
|||
delete:
|
||||
tags:
|
||||
- UserService
|
||||
description: RevokeUserSession revokes a specific session for a user.
|
||||
operationId: UserService_RevokeUserSession
|
||||
description: |-
|
||||
RevokeSession revokes a specific login session.
|
||||
This invalidates the refresh token, forcing re-authentication on that device.
|
||||
operationId: UserService_RevokeSession
|
||||
parameters:
|
||||
- name: user
|
||||
in: path
|
||||
|
|
@ -2049,76 +2084,35 @@ components:
|
|||
description: |-
|
||||
Optional. The related memo. Refer to `Memo.name`.
|
||||
Format: memos/{memo}
|
||||
CreateSessionRequest:
|
||||
type: object
|
||||
properties:
|
||||
passwordCredentials:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/CreateSessionRequest_PasswordCredentials'
|
||||
description: Username and password authentication method.
|
||||
ssoCredentials:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/CreateSessionRequest_SSOCredentials'
|
||||
description: SSO provider authentication method.
|
||||
CreateSessionRequest_PasswordCredentials:
|
||||
CreatePersonalAccessTokenRequest:
|
||||
required:
|
||||
- username
|
||||
- password
|
||||
- parent
|
||||
type: object
|
||||
properties:
|
||||
username:
|
||||
parent:
|
||||
type: string
|
||||
description: |-
|
||||
The username to sign in with.
|
||||
Required field for password-based authentication.
|
||||
password:
|
||||
Required. The parent resource where this token will be created.
|
||||
Format: users/{user}
|
||||
description:
|
||||
type: string
|
||||
description: |-
|
||||
The password to sign in with.
|
||||
Required field for password-based authentication.
|
||||
description: Nested message for password-based authentication credentials.
|
||||
CreateSessionRequest_SSOCredentials:
|
||||
required:
|
||||
- idpId
|
||||
- code
|
||||
- redirectUri
|
||||
type: object
|
||||
properties:
|
||||
idpId:
|
||||
description: Optional. Description of the personal access token.
|
||||
expiresInDays:
|
||||
type: integer
|
||||
description: |-
|
||||
The ID of the SSO provider.
|
||||
Required field to identify the SSO provider.
|
||||
description: Optional. Expiration duration in days (0 = never expires).
|
||||
format: int32
|
||||
code:
|
||||
type: string
|
||||
description: |-
|
||||
The authorization code from the SSO provider.
|
||||
Required field for completing the SSO flow.
|
||||
redirectUri:
|
||||
type: string
|
||||
description: |-
|
||||
The redirect URI used in the SSO flow.
|
||||
Required field for security validation.
|
||||
codeVerifier:
|
||||
type: string
|
||||
description: |-
|
||||
The PKCE code verifier for enhanced security (RFC 7636).
|
||||
Optional field - if provided, enables PKCE flow protection against authorization code interception.
|
||||
description: Nested message for SSO authentication credentials.
|
||||
CreateSessionResponse:
|
||||
CreatePersonalAccessTokenResponse:
|
||||
type: object
|
||||
properties:
|
||||
user:
|
||||
personalAccessToken:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/User'
|
||||
description: The authenticated user information.
|
||||
lastAccessedAt:
|
||||
- $ref: '#/components/schemas/PersonalAccessToken'
|
||||
description: The personal access token metadata.
|
||||
token:
|
||||
type: string
|
||||
description: |-
|
||||
Last time the session was accessed.
|
||||
Used for sliding expiration calculation (last_accessed_time + 2 weeks).
|
||||
format: date-time
|
||||
The actual token value - only returned on creation.
|
||||
This is the only time the token value will be visible.
|
||||
FieldMapping:
|
||||
type: object
|
||||
properties:
|
||||
|
|
@ -2140,17 +2134,13 @@ components:
|
|||
logoUrl:
|
||||
type: string
|
||||
description: Custom profile configuration for instance branding.
|
||||
GetCurrentSessionResponse:
|
||||
GetCurrentUserResponse:
|
||||
type: object
|
||||
properties:
|
||||
user:
|
||||
$ref: '#/components/schemas/User'
|
||||
lastAccessedAt:
|
||||
type: string
|
||||
description: |-
|
||||
Last time the session was accessed.
|
||||
Used for sliding expiration calculation (last_accessed_time + 2 weeks).
|
||||
format: date-time
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/User'
|
||||
description: The authenticated user's information.
|
||||
GoogleProtobufAny:
|
||||
type: object
|
||||
properties:
|
||||
|
|
@ -2426,6 +2416,29 @@ components:
|
|||
description: |-
|
||||
A token that can be sent as `page_token` to retrieve the next page.
|
||||
If this field is omitted, there are no subsequent pages.
|
||||
ListPersonalAccessTokensResponse:
|
||||
type: object
|
||||
properties:
|
||||
personalAccessTokens:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/PersonalAccessToken'
|
||||
description: The list of personal access tokens.
|
||||
nextPageToken:
|
||||
type: string
|
||||
description: A token for the next page of results.
|
||||
totalSize:
|
||||
type: integer
|
||||
description: The total count of personal access tokens.
|
||||
format: int32
|
||||
ListSessionsResponse:
|
||||
type: object
|
||||
properties:
|
||||
sessions:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Session'
|
||||
description: The list of sessions.
|
||||
ListShortcutsResponse:
|
||||
type: object
|
||||
properties:
|
||||
|
|
@ -2434,21 +2447,6 @@ components:
|
|||
items:
|
||||
$ref: '#/components/schemas/Shortcut'
|
||||
description: The list of shortcuts.
|
||||
ListUserAccessTokensResponse:
|
||||
type: object
|
||||
properties:
|
||||
accessTokens:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UserAccessToken'
|
||||
description: The list of access tokens.
|
||||
nextPageToken:
|
||||
type: string
|
||||
description: A token for the next page of results.
|
||||
totalSize:
|
||||
type: integer
|
||||
description: The total count of access tokens.
|
||||
format: int32
|
||||
ListUserNotificationsResponse:
|
||||
type: object
|
||||
properties:
|
||||
|
|
@ -2458,14 +2456,6 @@ components:
|
|||
$ref: '#/components/schemas/UserNotification'
|
||||
nextPageToken:
|
||||
type: string
|
||||
ListUserSessionsResponse:
|
||||
type: object
|
||||
properties:
|
||||
sessions:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UserSession'
|
||||
description: The list of user sessions.
|
||||
ListUserSettingsResponse:
|
||||
type: object
|
||||
properties:
|
||||
|
|
@ -2687,6 +2677,34 @@ components:
|
|||
type: string
|
||||
fieldMapping:
|
||||
$ref: '#/components/schemas/FieldMapping'
|
||||
PersonalAccessToken:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
The resource name of the personal access token.
|
||||
Format: users/{user}/personalAccessTokens/{personal_access_token}
|
||||
description:
|
||||
type: string
|
||||
description: The description of the token.
|
||||
createdAt:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: Output only. The creation timestamp.
|
||||
format: date-time
|
||||
expiresAt:
|
||||
type: string
|
||||
description: Optional. The expiration timestamp.
|
||||
format: date-time
|
||||
lastUsedAt:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: Output only. The last used timestamp.
|
||||
format: date-time
|
||||
description: |-
|
||||
PersonalAccessToken represents a long-lived token for API/script access.
|
||||
PATs are distinct from short-lived JWT access tokens used for session authentication.
|
||||
Reaction:
|
||||
required:
|
||||
- contentId
|
||||
|
|
@ -2719,6 +2737,69 @@ components:
|
|||
type: string
|
||||
description: Output only. The creation timestamp.
|
||||
format: date-time
|
||||
RefreshTokenRequest:
|
||||
type: object
|
||||
properties: {}
|
||||
RefreshTokenResponse:
|
||||
type: object
|
||||
properties:
|
||||
accessToken:
|
||||
type: string
|
||||
description: The new short-lived access token.
|
||||
expiresAt:
|
||||
type: string
|
||||
description: When the access token expires.
|
||||
format: date-time
|
||||
Session:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
The resource name of the session.
|
||||
Format: users/{user}/sessions/{session}
|
||||
sessionId:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: The session ID.
|
||||
createTime:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: The timestamp when the session was created.
|
||||
format: date-time
|
||||
lastAccessedTime:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: |-
|
||||
The timestamp when the session was last accessed.
|
||||
Used for sliding expiration calculation (last_accessed_time + 2 weeks).
|
||||
format: date-time
|
||||
clientInfo:
|
||||
readOnly: true
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Session_ClientInfo'
|
||||
description: Client information associated with this session.
|
||||
description: |-
|
||||
Session represents a user's login session on a specific device/browser.
|
||||
Sessions are backed by refresh tokens with sliding expiration.
|
||||
Session_ClientInfo:
|
||||
type: object
|
||||
properties:
|
||||
userAgent:
|
||||
type: string
|
||||
description: User agent string of the client.
|
||||
ipAddress:
|
||||
type: string
|
||||
description: IP address of the client.
|
||||
deviceType:
|
||||
type: string
|
||||
description: Optional. Device type (e.g., "mobile", "desktop", "tablet").
|
||||
os:
|
||||
type: string
|
||||
description: Optional. Operating system (e.g., "iOS 17.0", "Windows 11").
|
||||
browser:
|
||||
type: string
|
||||
description: Optional. Browser name and version (e.g., "Chrome 119.0").
|
||||
SetMemoAttachmentsRequest:
|
||||
required:
|
||||
- name
|
||||
|
|
@ -2767,6 +2848,71 @@ components:
|
|||
filter:
|
||||
type: string
|
||||
description: The filter expression for the shortcut.
|
||||
SignInRequest:
|
||||
type: object
|
||||
properties:
|
||||
passwordCredentials:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/SignInRequest_PasswordCredentials'
|
||||
description: Username and password authentication.
|
||||
ssoCredentials:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/SignInRequest_SSOCredentials'
|
||||
description: SSO provider authentication.
|
||||
SignInRequest_PasswordCredentials:
|
||||
required:
|
||||
- username
|
||||
- password
|
||||
type: object
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
description: The username to sign in with.
|
||||
password:
|
||||
type: string
|
||||
description: The password to sign in with.
|
||||
description: Nested message for password-based authentication credentials.
|
||||
SignInRequest_SSOCredentials:
|
||||
required:
|
||||
- idpId
|
||||
- code
|
||||
- redirectUri
|
||||
type: object
|
||||
properties:
|
||||
idpId:
|
||||
type: integer
|
||||
description: The ID of the SSO provider.
|
||||
format: int32
|
||||
code:
|
||||
type: string
|
||||
description: The authorization code from the SSO provider.
|
||||
redirectUri:
|
||||
type: string
|
||||
description: The redirect URI used in the SSO flow.
|
||||
codeVerifier:
|
||||
type: string
|
||||
description: |-
|
||||
The PKCE code verifier for enhanced security (RFC 7636).
|
||||
Optional - enables PKCE flow protection against authorization code interception.
|
||||
description: Nested message for SSO authentication credentials.
|
||||
SignInResponse:
|
||||
type: object
|
||||
properties:
|
||||
user:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/User'
|
||||
description: The authenticated user's information.
|
||||
accessToken:
|
||||
type: string
|
||||
description: |-
|
||||
The short-lived access token for API requests.
|
||||
Store in memory only, not in localStorage.
|
||||
accessTokenExpiresAt:
|
||||
type: string
|
||||
description: |-
|
||||
When the access token expires.
|
||||
Client should call RefreshToken before this time.
|
||||
format: date-time
|
||||
Status:
|
||||
type: object
|
||||
properties:
|
||||
|
|
@ -2874,31 +3020,6 @@ components:
|
|||
type: string
|
||||
description: Output only. The last update timestamp.
|
||||
format: date-time
|
||||
UserAccessToken:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
The resource name of the access token.
|
||||
Format: users/{user}/accessTokens/{access_token}
|
||||
accessToken:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: Output only. The access token value.
|
||||
description:
|
||||
type: string
|
||||
description: The description of the access token.
|
||||
issuedAt:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: Output only. The issued timestamp.
|
||||
format: date-time
|
||||
expiresAt:
|
||||
type: string
|
||||
description: Optional. The expiration timestamp.
|
||||
format: date-time
|
||||
description: User access token message
|
||||
UserNotification:
|
||||
type: object
|
||||
properties:
|
||||
|
|
@ -2939,53 +3060,6 @@ components:
|
|||
type: integer
|
||||
description: The activity ID associated with this notification.
|
||||
format: int32
|
||||
UserSession:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
The resource name of the session.
|
||||
Format: users/{user}/sessions/{session}
|
||||
sessionId:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: The session ID.
|
||||
createTime:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: The timestamp when the session was created.
|
||||
format: date-time
|
||||
lastAccessedTime:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: |-
|
||||
The timestamp when the session was last accessed.
|
||||
Used for sliding expiration calculation (last_accessed_time + 2 weeks).
|
||||
format: date-time
|
||||
clientInfo:
|
||||
readOnly: true
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/UserSession_ClientInfo'
|
||||
description: Client information associated with this session.
|
||||
UserSession_ClientInfo:
|
||||
type: object
|
||||
properties:
|
||||
userAgent:
|
||||
type: string
|
||||
description: User agent string of the client.
|
||||
ipAddress:
|
||||
type: string
|
||||
description: IP address of the client.
|
||||
deviceType:
|
||||
type: string
|
||||
description: Optional. Device type (e.g., "mobile", "desktop", "tablet").
|
||||
os:
|
||||
type: string
|
||||
description: Optional. Operating system (e.g., "iOS 17.0", "Windows 11").
|
||||
browser:
|
||||
type: string
|
||||
description: Optional. Browser name and version (e.g., "Chrome 119.0").
|
||||
UserSetting:
|
||||
type: object
|
||||
properties:
|
||||
|
|
@ -3007,12 +3081,12 @@ components:
|
|||
UserSetting_AccessTokensSetting:
|
||||
type: object
|
||||
properties:
|
||||
accessTokens:
|
||||
personalAccessTokens:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UserAccessToken'
|
||||
description: List of user access tokens.
|
||||
description: User access tokens configuration.
|
||||
$ref: '#/components/schemas/PersonalAccessToken'
|
||||
description: List of personal access tokens (PATs).
|
||||
description: Personal access tokens configuration.
|
||||
UserSetting_GeneralSetting:
|
||||
type: object
|
||||
properties:
|
||||
|
|
@ -3035,8 +3109,8 @@ components:
|
|||
sessions:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UserSession'
|
||||
description: List of active user sessions.
|
||||
$ref: '#/components/schemas/Session'
|
||||
description: List of active login sessions.
|
||||
description: User authentication sessions configuration.
|
||||
UserSetting_WebhooksSetting:
|
||||
type: object
|
||||
|
|
|
|||
|
|
@ -36,6 +36,10 @@ const (
|
|||
UserSetting_SHORTCUTS UserSetting_Key = 4
|
||||
// The webhooks of the user.
|
||||
UserSetting_WEBHOOKS UserSetting_Key = 5
|
||||
// Refresh tokens for the user.
|
||||
UserSetting_REFRESH_TOKENS UserSetting_Key = 6
|
||||
// Personal access tokens for the user.
|
||||
UserSetting_PERSONAL_ACCESS_TOKENS UserSetting_Key = 7
|
||||
)
|
||||
|
||||
// Enum value maps for UserSetting_Key.
|
||||
|
|
@ -47,14 +51,18 @@ var (
|
|||
3: "ACCESS_TOKENS",
|
||||
4: "SHORTCUTS",
|
||||
5: "WEBHOOKS",
|
||||
6: "REFRESH_TOKENS",
|
||||
7: "PERSONAL_ACCESS_TOKENS",
|
||||
}
|
||||
UserSetting_Key_value = map[string]int32{
|
||||
"KEY_UNSPECIFIED": 0,
|
||||
"GENERAL": 1,
|
||||
"SESSIONS": 2,
|
||||
"ACCESS_TOKENS": 3,
|
||||
"SHORTCUTS": 4,
|
||||
"WEBHOOKS": 5,
|
||||
"KEY_UNSPECIFIED": 0,
|
||||
"GENERAL": 1,
|
||||
"SESSIONS": 2,
|
||||
"ACCESS_TOKENS": 3,
|
||||
"SHORTCUTS": 4,
|
||||
"WEBHOOKS": 5,
|
||||
"REFRESH_TOKENS": 6,
|
||||
"PERSONAL_ACCESS_TOKENS": 7,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -96,6 +104,8 @@ type UserSetting struct {
|
|||
// *UserSetting_AccessTokens
|
||||
// *UserSetting_Shortcuts
|
||||
// *UserSetting_Webhooks
|
||||
// *UserSetting_RefreshTokens
|
||||
// *UserSetting_PersonalAccessTokens
|
||||
Value isUserSetting_Value `protobuf_oneof:"value"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
|
|
@ -197,6 +207,24 @@ func (x *UserSetting) GetWebhooks() *WebhooksUserSetting {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (x *UserSetting) GetRefreshTokens() *RefreshTokensUserSetting {
|
||||
if x != nil {
|
||||
if x, ok := x.Value.(*UserSetting_RefreshTokens); ok {
|
||||
return x.RefreshTokens
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *UserSetting) GetPersonalAccessTokens() *PersonalAccessTokensUserSetting {
|
||||
if x != nil {
|
||||
if x, ok := x.Value.(*UserSetting_PersonalAccessTokens); ok {
|
||||
return x.PersonalAccessTokens
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isUserSetting_Value interface {
|
||||
isUserSetting_Value()
|
||||
}
|
||||
|
|
@ -221,6 +249,14 @@ type UserSetting_Webhooks struct {
|
|||
Webhooks *WebhooksUserSetting `protobuf:"bytes,7,opt,name=webhooks,proto3,oneof"`
|
||||
}
|
||||
|
||||
type UserSetting_RefreshTokens struct {
|
||||
RefreshTokens *RefreshTokensUserSetting `protobuf:"bytes,8,opt,name=refresh_tokens,json=refreshTokens,proto3,oneof"`
|
||||
}
|
||||
|
||||
type UserSetting_PersonalAccessTokens struct {
|
||||
PersonalAccessTokens *PersonalAccessTokensUserSetting `protobuf:"bytes,9,opt,name=personal_access_tokens,json=personalAccessTokens,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*UserSetting_General) isUserSetting_Value() {}
|
||||
|
||||
func (*UserSetting_Sessions) isUserSetting_Value() {}
|
||||
|
|
@ -231,6 +267,10 @@ func (*UserSetting_Shortcuts) isUserSetting_Value() {}
|
|||
|
||||
func (*UserSetting_Webhooks) isUserSetting_Value() {}
|
||||
|
||||
func (*UserSetting_RefreshTokens) isUserSetting_Value() {}
|
||||
|
||||
func (*UserSetting_PersonalAccessTokens) isUserSetting_Value() {}
|
||||
|
||||
type GeneralUserSetting struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// The user's locale.
|
||||
|
|
@ -383,6 +423,94 @@ func (x *AccessTokensUserSetting) GetAccessTokens() []*AccessTokensUserSetting_A
|
|||
return nil
|
||||
}
|
||||
|
||||
type RefreshTokensUserSetting struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
RefreshTokens []*RefreshTokensUserSetting_RefreshToken `protobuf:"bytes,1,rep,name=refresh_tokens,json=refreshTokens,proto3" json:"refresh_tokens,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RefreshTokensUserSetting) Reset() {
|
||||
*x = RefreshTokensUserSetting{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *RefreshTokensUserSetting) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RefreshTokensUserSetting) ProtoMessage() {}
|
||||
|
||||
func (x *RefreshTokensUserSetting) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[4]
|
||||
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 RefreshTokensUserSetting.ProtoReflect.Descriptor instead.
|
||||
func (*RefreshTokensUserSetting) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *RefreshTokensUserSetting) GetRefreshTokens() []*RefreshTokensUserSetting_RefreshToken {
|
||||
if x != nil {
|
||||
return x.RefreshTokens
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PersonalAccessTokensUserSetting struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Tokens []*PersonalAccessTokensUserSetting_PersonalAccessToken `protobuf:"bytes,1,rep,name=tokens,proto3" json:"tokens,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting) Reset() {
|
||||
*x = PersonalAccessTokensUserSetting{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PersonalAccessTokensUserSetting) ProtoMessage() {}
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[5]
|
||||
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 PersonalAccessTokensUserSetting.ProtoReflect.Descriptor instead.
|
||||
func (*PersonalAccessTokensUserSetting) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting) GetTokens() []*PersonalAccessTokensUserSetting_PersonalAccessToken {
|
||||
if x != nil {
|
||||
return x.Tokens
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ShortcutsUserSetting struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Shortcuts []*ShortcutsUserSetting_Shortcut `protobuf:"bytes,1,rep,name=shortcuts,proto3" json:"shortcuts,omitempty"`
|
||||
|
|
@ -392,7 +520,7 @@ type ShortcutsUserSetting struct {
|
|||
|
||||
func (x *ShortcutsUserSetting) Reset() {
|
||||
*x = ShortcutsUserSetting{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[4]
|
||||
mi := &file_store_user_setting_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -404,7 +532,7 @@ func (x *ShortcutsUserSetting) String() string {
|
|||
func (*ShortcutsUserSetting) ProtoMessage() {}
|
||||
|
||||
func (x *ShortcutsUserSetting) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[4]
|
||||
mi := &file_store_user_setting_proto_msgTypes[6]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -417,7 +545,7 @@ func (x *ShortcutsUserSetting) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use ShortcutsUserSetting.ProtoReflect.Descriptor instead.
|
||||
func (*ShortcutsUserSetting) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{4}
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *ShortcutsUserSetting) GetShortcuts() []*ShortcutsUserSetting_Shortcut {
|
||||
|
|
@ -436,7 +564,7 @@ type WebhooksUserSetting struct {
|
|||
|
||||
func (x *WebhooksUserSetting) Reset() {
|
||||
*x = WebhooksUserSetting{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[5]
|
||||
mi := &file_store_user_setting_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -448,7 +576,7 @@ func (x *WebhooksUserSetting) String() string {
|
|||
func (*WebhooksUserSetting) ProtoMessage() {}
|
||||
|
||||
func (x *WebhooksUserSetting) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[5]
|
||||
mi := &file_store_user_setting_proto_msgTypes[7]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -461,7 +589,7 @@ func (x *WebhooksUserSetting) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use WebhooksUserSetting.ProtoReflect.Descriptor instead.
|
||||
func (*WebhooksUserSetting) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{5}
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *WebhooksUserSetting) GetWebhooks() []*WebhooksUserSetting_Webhook {
|
||||
|
|
@ -488,7 +616,7 @@ type SessionsUserSetting_Session struct {
|
|||
|
||||
func (x *SessionsUserSetting_Session) Reset() {
|
||||
*x = SessionsUserSetting_Session{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[6]
|
||||
mi := &file_store_user_setting_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -500,7 +628,7 @@ func (x *SessionsUserSetting_Session) String() string {
|
|||
func (*SessionsUserSetting_Session) ProtoMessage() {}
|
||||
|
||||
func (x *SessionsUserSetting_Session) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[6]
|
||||
mi := &file_store_user_setting_proto_msgTypes[8]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -562,7 +690,7 @@ type SessionsUserSetting_ClientInfo struct {
|
|||
|
||||
func (x *SessionsUserSetting_ClientInfo) Reset() {
|
||||
*x = SessionsUserSetting_ClientInfo{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[7]
|
||||
mi := &file_store_user_setting_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -574,7 +702,7 @@ func (x *SessionsUserSetting_ClientInfo) String() string {
|
|||
func (*SessionsUserSetting_ClientInfo) ProtoMessage() {}
|
||||
|
||||
func (x *SessionsUserSetting_ClientInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[7]
|
||||
mi := &file_store_user_setting_proto_msgTypes[9]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -638,7 +766,7 @@ type AccessTokensUserSetting_AccessToken struct {
|
|||
|
||||
func (x *AccessTokensUserSetting_AccessToken) Reset() {
|
||||
*x = AccessTokensUserSetting_AccessToken{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[8]
|
||||
mi := &file_store_user_setting_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -650,7 +778,7 @@ func (x *AccessTokensUserSetting_AccessToken) String() string {
|
|||
func (*AccessTokensUserSetting_AccessToken) ProtoMessage() {}
|
||||
|
||||
func (x *AccessTokensUserSetting_AccessToken) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[8]
|
||||
mi := &file_store_user_setting_proto_msgTypes[10]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -680,6 +808,177 @@ func (x *AccessTokensUserSetting_AccessToken) GetDescription() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
type RefreshTokensUserSetting_RefreshToken struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Unique identifier (matches 'tid' claim in JWT)
|
||||
TokenId string `protobuf:"bytes,1,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"`
|
||||
// When the token expires
|
||||
ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
|
||||
// When the token was created
|
||||
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
|
||||
// Client information for session management UI
|
||||
ClientInfo *SessionsUserSetting_ClientInfo `protobuf:"bytes,4,opt,name=client_info,json=clientInfo,proto3" json:"client_info,omitempty"`
|
||||
// Optional description
|
||||
Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RefreshTokensUserSetting_RefreshToken) Reset() {
|
||||
*x = RefreshTokensUserSetting_RefreshToken{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *RefreshTokensUserSetting_RefreshToken) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RefreshTokensUserSetting_RefreshToken) ProtoMessage() {}
|
||||
|
||||
func (x *RefreshTokensUserSetting_RefreshToken) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[11]
|
||||
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 RefreshTokensUserSetting_RefreshToken.ProtoReflect.Descriptor instead.
|
||||
func (*RefreshTokensUserSetting_RefreshToken) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{4, 0}
|
||||
}
|
||||
|
||||
func (x *RefreshTokensUserSetting_RefreshToken) GetTokenId() string {
|
||||
if x != nil {
|
||||
return x.TokenId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *RefreshTokensUserSetting_RefreshToken) GetExpiresAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.ExpiresAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *RefreshTokensUserSetting_RefreshToken) GetCreatedAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreatedAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *RefreshTokensUserSetting_RefreshToken) GetClientInfo() *SessionsUserSetting_ClientInfo {
|
||||
if x != nil {
|
||||
return x.ClientInfo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *RefreshTokensUserSetting_RefreshToken) GetDescription() string {
|
||||
if x != nil {
|
||||
return x.Description
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type PersonalAccessTokensUserSetting_PersonalAccessToken struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Unique identifier for this token
|
||||
TokenId string `protobuf:"bytes,1,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"`
|
||||
// SHA-256 hash of the actual token
|
||||
TokenHash string `protobuf:"bytes,2,opt,name=token_hash,json=tokenHash,proto3" json:"token_hash,omitempty"`
|
||||
// User-provided description
|
||||
Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
|
||||
// When the token expires (null = never)
|
||||
ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
|
||||
// When the token was created
|
||||
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
|
||||
// When the token was last used
|
||||
LastUsedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=last_used_at,json=lastUsedAt,proto3" json:"last_used_at,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting_PersonalAccessToken) Reset() {
|
||||
*x = PersonalAccessTokensUserSetting_PersonalAccessToken{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[12]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting_PersonalAccessToken) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PersonalAccessTokensUserSetting_PersonalAccessToken) ProtoMessage() {}
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting_PersonalAccessToken) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[12]
|
||||
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 PersonalAccessTokensUserSetting_PersonalAccessToken.ProtoReflect.Descriptor instead.
|
||||
func (*PersonalAccessTokensUserSetting_PersonalAccessToken) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{5, 0}
|
||||
}
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting_PersonalAccessToken) GetTokenId() string {
|
||||
if x != nil {
|
||||
return x.TokenId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting_PersonalAccessToken) GetTokenHash() string {
|
||||
if x != nil {
|
||||
return x.TokenHash
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting_PersonalAccessToken) GetDescription() string {
|
||||
if x != nil {
|
||||
return x.Description
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting_PersonalAccessToken) GetExpiresAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.ExpiresAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting_PersonalAccessToken) GetCreatedAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreatedAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting_PersonalAccessToken) GetLastUsedAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.LastUsedAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ShortcutsUserSetting_Shortcut struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
|
|
@ -691,7 +990,7 @@ type ShortcutsUserSetting_Shortcut struct {
|
|||
|
||||
func (x *ShortcutsUserSetting_Shortcut) Reset() {
|
||||
*x = ShortcutsUserSetting_Shortcut{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[9]
|
||||
mi := &file_store_user_setting_proto_msgTypes[13]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -703,7 +1002,7 @@ func (x *ShortcutsUserSetting_Shortcut) String() string {
|
|||
func (*ShortcutsUserSetting_Shortcut) ProtoMessage() {}
|
||||
|
||||
func (x *ShortcutsUserSetting_Shortcut) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[9]
|
||||
mi := &file_store_user_setting_proto_msgTypes[13]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -716,7 +1015,7 @@ func (x *ShortcutsUserSetting_Shortcut) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use ShortcutsUserSetting_Shortcut.ProtoReflect.Descriptor instead.
|
||||
func (*ShortcutsUserSetting_Shortcut) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{4, 0}
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{6, 0}
|
||||
}
|
||||
|
||||
func (x *ShortcutsUserSetting_Shortcut) GetId() string {
|
||||
|
|
@ -754,7 +1053,7 @@ type WebhooksUserSetting_Webhook struct {
|
|||
|
||||
func (x *WebhooksUserSetting_Webhook) Reset() {
|
||||
*x = WebhooksUserSetting_Webhook{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[10]
|
||||
mi := &file_store_user_setting_proto_msgTypes[14]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -766,7 +1065,7 @@ func (x *WebhooksUserSetting_Webhook) String() string {
|
|||
func (*WebhooksUserSetting_Webhook) ProtoMessage() {}
|
||||
|
||||
func (x *WebhooksUserSetting_Webhook) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[10]
|
||||
mi := &file_store_user_setting_proto_msgTypes[14]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -779,7 +1078,7 @@ func (x *WebhooksUserSetting_Webhook) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use WebhooksUserSetting_Webhook.ProtoReflect.Descriptor instead.
|
||||
func (*WebhooksUserSetting_Webhook) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{5, 0}
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{7, 0}
|
||||
}
|
||||
|
||||
func (x *WebhooksUserSetting_Webhook) GetId() string {
|
||||
|
|
@ -807,7 +1106,7 @@ var File_store_user_setting_proto protoreflect.FileDescriptor
|
|||
|
||||
const file_store_user_setting_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x18store/user_setting.proto\x12\vmemos.store\x1a\x1fgoogle/protobuf/timestamp.proto\"\x93\x04\n" +
|
||||
"\x18store/user_setting.proto\x12\vmemos.store\x1a\x1fgoogle/protobuf/timestamp.proto\"\xfa\x05\n" +
|
||||
"\vUserSetting\x12\x17\n" +
|
||||
"\auser_id\x18\x01 \x01(\x05R\x06userId\x12.\n" +
|
||||
"\x03key\x18\x02 \x01(\x0e2\x1c.memos.store.UserSetting.KeyR\x03key\x12;\n" +
|
||||
|
|
@ -815,14 +1114,18 @@ const file_store_user_setting_proto_rawDesc = "" +
|
|||
"\bsessions\x18\x04 \x01(\v2 .memos.store.SessionsUserSettingH\x00R\bsessions\x12K\n" +
|
||||
"\raccess_tokens\x18\x05 \x01(\v2$.memos.store.AccessTokensUserSettingH\x00R\faccessTokens\x12A\n" +
|
||||
"\tshortcuts\x18\x06 \x01(\v2!.memos.store.ShortcutsUserSettingH\x00R\tshortcuts\x12>\n" +
|
||||
"\bwebhooks\x18\a \x01(\v2 .memos.store.WebhooksUserSettingH\x00R\bwebhooks\"e\n" +
|
||||
"\bwebhooks\x18\a \x01(\v2 .memos.store.WebhooksUserSettingH\x00R\bwebhooks\x12N\n" +
|
||||
"\x0erefresh_tokens\x18\b \x01(\v2%.memos.store.RefreshTokensUserSettingH\x00R\rrefreshTokens\x12d\n" +
|
||||
"\x16personal_access_tokens\x18\t \x01(\v2,.memos.store.PersonalAccessTokensUserSettingH\x00R\x14personalAccessTokens\"\x95\x01\n" +
|
||||
"\x03Key\x12\x13\n" +
|
||||
"\x0fKEY_UNSPECIFIED\x10\x00\x12\v\n" +
|
||||
"\aGENERAL\x10\x01\x12\f\n" +
|
||||
"\bSESSIONS\x10\x02\x12\x11\n" +
|
||||
"\rACCESS_TOKENS\x10\x03\x12\r\n" +
|
||||
"\tSHORTCUTS\x10\x04\x12\f\n" +
|
||||
"\bWEBHOOKS\x10\x05B\a\n" +
|
||||
"\bWEBHOOKS\x10\x05\x12\x12\n" +
|
||||
"\x0eREFRESH_TOKENS\x10\x06\x12\x1a\n" +
|
||||
"\x16PERSONAL_ACCESS_TOKENS\x10\aB\a\n" +
|
||||
"\x05value\"k\n" +
|
||||
"\x12GeneralUserSetting\x12\x16\n" +
|
||||
"\x06locale\x18\x01 \x01(\tR\x06locale\x12'\n" +
|
||||
|
|
@ -852,7 +1155,31 @@ const file_store_user_setting_proto_rawDesc = "" +
|
|||
"\raccess_tokens\x18\x01 \x03(\v20.memos.store.AccessTokensUserSetting.AccessTokenR\faccessTokens\x1aR\n" +
|
||||
"\vAccessToken\x12!\n" +
|
||||
"\faccess_token\x18\x01 \x01(\tR\vaccessToken\x12 \n" +
|
||||
"\vdescription\x18\x02 \x01(\tR\vdescription\"\xaa\x01\n" +
|
||||
"\vdescription\x18\x02 \x01(\tR\vdescription\"\x87\x03\n" +
|
||||
"\x18RefreshTokensUserSetting\x12Y\n" +
|
||||
"\x0erefresh_tokens\x18\x01 \x03(\v22.memos.store.RefreshTokensUserSetting.RefreshTokenR\rrefreshTokens\x1a\x8f\x02\n" +
|
||||
"\fRefreshToken\x12\x19\n" +
|
||||
"\btoken_id\x18\x01 \x01(\tR\atokenId\x129\n" +
|
||||
"\n" +
|
||||
"expires_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\x129\n" +
|
||||
"\n" +
|
||||
"created_at\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x12L\n" +
|
||||
"\vclient_info\x18\x04 \x01(\v2+.memos.store.SessionsUserSetting.ClientInfoR\n" +
|
||||
"clientInfo\x12 \n" +
|
||||
"\vdescription\x18\x05 \x01(\tR\vdescription\"\xa3\x03\n" +
|
||||
"\x1fPersonalAccessTokensUserSetting\x12X\n" +
|
||||
"\x06tokens\x18\x01 \x03(\v2@.memos.store.PersonalAccessTokensUserSetting.PersonalAccessTokenR\x06tokens\x1a\xa5\x02\n" +
|
||||
"\x13PersonalAccessToken\x12\x19\n" +
|
||||
"\btoken_id\x18\x01 \x01(\tR\atokenId\x12\x1d\n" +
|
||||
"\n" +
|
||||
"token_hash\x18\x02 \x01(\tR\ttokenHash\x12 \n" +
|
||||
"\vdescription\x18\x03 \x01(\tR\vdescription\x129\n" +
|
||||
"\n" +
|
||||
"expires_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\x129\n" +
|
||||
"\n" +
|
||||
"created_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x12<\n" +
|
||||
"\flast_used_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\n" +
|
||||
"lastUsedAt\"\xaa\x01\n" +
|
||||
"\x14ShortcutsUserSetting\x12H\n" +
|
||||
"\tshortcuts\x18\x01 \x03(\v2*.memos.store.ShortcutsUserSetting.ShortcutR\tshortcuts\x1aH\n" +
|
||||
"\bShortcut\x12\x0e\n" +
|
||||
|
|
@ -880,41 +1207,55 @@ func file_store_user_setting_proto_rawDescGZIP() []byte {
|
|||
}
|
||||
|
||||
var file_store_user_setting_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_store_user_setting_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
|
||||
var file_store_user_setting_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
|
||||
var file_store_user_setting_proto_goTypes = []any{
|
||||
(UserSetting_Key)(0), // 0: memos.store.UserSetting.Key
|
||||
(*UserSetting)(nil), // 1: memos.store.UserSetting
|
||||
(*GeneralUserSetting)(nil), // 2: memos.store.GeneralUserSetting
|
||||
(*SessionsUserSetting)(nil), // 3: memos.store.SessionsUserSetting
|
||||
(*AccessTokensUserSetting)(nil), // 4: memos.store.AccessTokensUserSetting
|
||||
(*ShortcutsUserSetting)(nil), // 5: memos.store.ShortcutsUserSetting
|
||||
(*WebhooksUserSetting)(nil), // 6: memos.store.WebhooksUserSetting
|
||||
(*SessionsUserSetting_Session)(nil), // 7: memos.store.SessionsUserSetting.Session
|
||||
(*SessionsUserSetting_ClientInfo)(nil), // 8: memos.store.SessionsUserSetting.ClientInfo
|
||||
(*AccessTokensUserSetting_AccessToken)(nil), // 9: memos.store.AccessTokensUserSetting.AccessToken
|
||||
(*ShortcutsUserSetting_Shortcut)(nil), // 10: memos.store.ShortcutsUserSetting.Shortcut
|
||||
(*WebhooksUserSetting_Webhook)(nil), // 11: memos.store.WebhooksUserSetting.Webhook
|
||||
(*timestamppb.Timestamp)(nil), // 12: google.protobuf.Timestamp
|
||||
(UserSetting_Key)(0), // 0: memos.store.UserSetting.Key
|
||||
(*UserSetting)(nil), // 1: memos.store.UserSetting
|
||||
(*GeneralUserSetting)(nil), // 2: memos.store.GeneralUserSetting
|
||||
(*SessionsUserSetting)(nil), // 3: memos.store.SessionsUserSetting
|
||||
(*AccessTokensUserSetting)(nil), // 4: memos.store.AccessTokensUserSetting
|
||||
(*RefreshTokensUserSetting)(nil), // 5: memos.store.RefreshTokensUserSetting
|
||||
(*PersonalAccessTokensUserSetting)(nil), // 6: memos.store.PersonalAccessTokensUserSetting
|
||||
(*ShortcutsUserSetting)(nil), // 7: memos.store.ShortcutsUserSetting
|
||||
(*WebhooksUserSetting)(nil), // 8: memos.store.WebhooksUserSetting
|
||||
(*SessionsUserSetting_Session)(nil), // 9: memos.store.SessionsUserSetting.Session
|
||||
(*SessionsUserSetting_ClientInfo)(nil), // 10: memos.store.SessionsUserSetting.ClientInfo
|
||||
(*AccessTokensUserSetting_AccessToken)(nil), // 11: memos.store.AccessTokensUserSetting.AccessToken
|
||||
(*RefreshTokensUserSetting_RefreshToken)(nil), // 12: memos.store.RefreshTokensUserSetting.RefreshToken
|
||||
(*PersonalAccessTokensUserSetting_PersonalAccessToken)(nil), // 13: memos.store.PersonalAccessTokensUserSetting.PersonalAccessToken
|
||||
(*ShortcutsUserSetting_Shortcut)(nil), // 14: memos.store.ShortcutsUserSetting.Shortcut
|
||||
(*WebhooksUserSetting_Webhook)(nil), // 15: memos.store.WebhooksUserSetting.Webhook
|
||||
(*timestamppb.Timestamp)(nil), // 16: google.protobuf.Timestamp
|
||||
}
|
||||
var file_store_user_setting_proto_depIdxs = []int32{
|
||||
0, // 0: memos.store.UserSetting.key:type_name -> memos.store.UserSetting.Key
|
||||
2, // 1: memos.store.UserSetting.general:type_name -> memos.store.GeneralUserSetting
|
||||
3, // 2: memos.store.UserSetting.sessions:type_name -> memos.store.SessionsUserSetting
|
||||
4, // 3: memos.store.UserSetting.access_tokens:type_name -> memos.store.AccessTokensUserSetting
|
||||
5, // 4: memos.store.UserSetting.shortcuts:type_name -> memos.store.ShortcutsUserSetting
|
||||
6, // 5: memos.store.UserSetting.webhooks:type_name -> memos.store.WebhooksUserSetting
|
||||
7, // 6: memos.store.SessionsUserSetting.sessions:type_name -> memos.store.SessionsUserSetting.Session
|
||||
9, // 7: memos.store.AccessTokensUserSetting.access_tokens:type_name -> memos.store.AccessTokensUserSetting.AccessToken
|
||||
10, // 8: memos.store.ShortcutsUserSetting.shortcuts:type_name -> memos.store.ShortcutsUserSetting.Shortcut
|
||||
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, // 11: 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
|
||||
13, // [13:13] is the sub-list for method output_type
|
||||
13, // [13:13] is the sub-list for method input_type
|
||||
13, // [13:13] is the sub-list for extension type_name
|
||||
13, // [13:13] is the sub-list for extension extendee
|
||||
0, // [0:13] is the sub-list for field type_name
|
||||
7, // 4: memos.store.UserSetting.shortcuts:type_name -> memos.store.ShortcutsUserSetting
|
||||
8, // 5: memos.store.UserSetting.webhooks:type_name -> memos.store.WebhooksUserSetting
|
||||
5, // 6: memos.store.UserSetting.refresh_tokens:type_name -> memos.store.RefreshTokensUserSetting
|
||||
6, // 7: memos.store.UserSetting.personal_access_tokens:type_name -> memos.store.PersonalAccessTokensUserSetting
|
||||
9, // 8: memos.store.SessionsUserSetting.sessions:type_name -> memos.store.SessionsUserSetting.Session
|
||||
11, // 9: memos.store.AccessTokensUserSetting.access_tokens:type_name -> memos.store.AccessTokensUserSetting.AccessToken
|
||||
12, // 10: memos.store.RefreshTokensUserSetting.refresh_tokens:type_name -> memos.store.RefreshTokensUserSetting.RefreshToken
|
||||
13, // 11: memos.store.PersonalAccessTokensUserSetting.tokens:type_name -> memos.store.PersonalAccessTokensUserSetting.PersonalAccessToken
|
||||
14, // 12: memos.store.ShortcutsUserSetting.shortcuts:type_name -> memos.store.ShortcutsUserSetting.Shortcut
|
||||
15, // 13: memos.store.WebhooksUserSetting.webhooks:type_name -> memos.store.WebhooksUserSetting.Webhook
|
||||
16, // 14: memos.store.SessionsUserSetting.Session.create_time:type_name -> google.protobuf.Timestamp
|
||||
16, // 15: memos.store.SessionsUserSetting.Session.last_accessed_time:type_name -> google.protobuf.Timestamp
|
||||
10, // 16: memos.store.SessionsUserSetting.Session.client_info:type_name -> memos.store.SessionsUserSetting.ClientInfo
|
||||
16, // 17: memos.store.RefreshTokensUserSetting.RefreshToken.expires_at:type_name -> google.protobuf.Timestamp
|
||||
16, // 18: memos.store.RefreshTokensUserSetting.RefreshToken.created_at:type_name -> google.protobuf.Timestamp
|
||||
10, // 19: memos.store.RefreshTokensUserSetting.RefreshToken.client_info:type_name -> memos.store.SessionsUserSetting.ClientInfo
|
||||
16, // 20: memos.store.PersonalAccessTokensUserSetting.PersonalAccessToken.expires_at:type_name -> google.protobuf.Timestamp
|
||||
16, // 21: memos.store.PersonalAccessTokensUserSetting.PersonalAccessToken.created_at:type_name -> google.protobuf.Timestamp
|
||||
16, // 22: memos.store.PersonalAccessTokensUserSetting.PersonalAccessToken.last_used_at:type_name -> google.protobuf.Timestamp
|
||||
23, // [23:23] is the sub-list for method output_type
|
||||
23, // [23:23] is the sub-list for method input_type
|
||||
23, // [23:23] is the sub-list for extension type_name
|
||||
23, // [23:23] is the sub-list for extension extendee
|
||||
0, // [0:23] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_store_user_setting_proto_init() }
|
||||
|
|
@ -928,6 +1269,8 @@ func file_store_user_setting_proto_init() {
|
|||
(*UserSetting_AccessTokens)(nil),
|
||||
(*UserSetting_Shortcuts)(nil),
|
||||
(*UserSetting_Webhooks)(nil),
|
||||
(*UserSetting_RefreshTokens)(nil),
|
||||
(*UserSetting_PersonalAccessTokens)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
|
|
@ -935,7 +1278,7 @@ func file_store_user_setting_proto_init() {
|
|||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_store_user_setting_proto_rawDesc), len(file_store_user_setting_proto_rawDesc)),
|
||||
NumEnums: 1,
|
||||
NumMessages: 11,
|
||||
NumMessages: 15,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ message UserSetting {
|
|||
SHORTCUTS = 4;
|
||||
// The webhooks of the user.
|
||||
WEBHOOKS = 5;
|
||||
// Refresh tokens for the user.
|
||||
REFRESH_TOKENS = 6;
|
||||
// Personal access tokens for the user.
|
||||
PERSONAL_ACCESS_TOKENS = 7;
|
||||
}
|
||||
|
||||
int32 user_id = 1;
|
||||
|
|
@ -30,6 +34,8 @@ message UserSetting {
|
|||
AccessTokensUserSetting access_tokens = 5;
|
||||
ShortcutsUserSetting shortcuts = 6;
|
||||
WebhooksUserSetting webhooks = 7;
|
||||
RefreshTokensUserSetting refresh_tokens = 8;
|
||||
PersonalAccessTokensUserSetting personal_access_tokens = 9;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -83,6 +89,40 @@ message AccessTokensUserSetting {
|
|||
repeated AccessToken access_tokens = 1;
|
||||
}
|
||||
|
||||
message RefreshTokensUserSetting {
|
||||
message RefreshToken {
|
||||
// Unique identifier (matches 'tid' claim in JWT)
|
||||
string token_id = 1;
|
||||
// When the token expires
|
||||
google.protobuf.Timestamp expires_at = 2;
|
||||
// When the token was created
|
||||
google.protobuf.Timestamp created_at = 3;
|
||||
// Client information for session management UI
|
||||
SessionsUserSetting.ClientInfo client_info = 4;
|
||||
// Optional description
|
||||
string description = 5;
|
||||
}
|
||||
repeated RefreshToken refresh_tokens = 1;
|
||||
}
|
||||
|
||||
message PersonalAccessTokensUserSetting {
|
||||
message PersonalAccessToken {
|
||||
// Unique identifier for this token
|
||||
string token_id = 1;
|
||||
// SHA-256 hash of the actual token
|
||||
string token_hash = 2;
|
||||
// User-provided description
|
||||
string description = 3;
|
||||
// When the token expires (null = never)
|
||||
google.protobuf.Timestamp expires_at = 4;
|
||||
// When the token was created
|
||||
google.protobuf.Timestamp created_at = 5;
|
||||
// When the token was last used
|
||||
google.protobuf.Timestamp last_used_at = 6;
|
||||
}
|
||||
repeated PersonalAccessToken tokens = 1;
|
||||
}
|
||||
|
||||
message ShortcutsUserSetting {
|
||||
message Shortcut {
|
||||
string id = 1;
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ package auth
|
|||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
|
|
@ -35,182 +36,128 @@ func NewAuthenticator(store *store.Store, secret string) *Authenticator {
|
|||
}
|
||||
}
|
||||
|
||||
// AuthenticateBySession validates a session cookie and returns the authenticated user.
|
||||
//
|
||||
// Validation steps:
|
||||
// 1. Use session ID to find the user and session details (single DB query)
|
||||
// 2. Verify user exists and is not archived
|
||||
// 3. Check session hasn't expired (sliding expiration: 14 days from last access)
|
||||
//
|
||||
// Returns the user if authentication succeeds, or an error describing the failure.
|
||||
func (a *Authenticator) AuthenticateBySession(ctx context.Context, sessionID string) (*store.User, error) {
|
||||
if sessionID == "" {
|
||||
return nil, errors.New("session ID not found")
|
||||
}
|
||||
|
||||
// Find the session and user in a single database query
|
||||
result, err := a.store.GetUserSessionByID(ctx, sessionID)
|
||||
// AuthenticateByAccessTokenV2 validates a short-lived access token.
|
||||
// Returns claims without database query (stateless validation).
|
||||
func (a *Authenticator) AuthenticateByAccessTokenV2(accessToken string) (*UserClaims, error) {
|
||||
claims, err := ParseAccessTokenV2(accessToken, []byte(a.secret))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "session not found")
|
||||
}
|
||||
|
||||
user, err := a.store.GetUser(ctx, &store.FindUser{ID: &result.UserID})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get user")
|
||||
}
|
||||
if user == nil {
|
||||
return nil, errors.New("user not found")
|
||||
}
|
||||
if user.RowStatus == store.Archived {
|
||||
return nil, errors.New("user is archived")
|
||||
}
|
||||
|
||||
// Validate session expiration
|
||||
if result.Session.LastAccessedTime != nil {
|
||||
expiration := result.Session.LastAccessedTime.AsTime().Add(SessionSlidingDuration)
|
||||
if expiration.Before(time.Now()) {
|
||||
return nil, errors.New("session expired")
|
||||
}
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// AuthenticateByJWT validates a JWT access token and returns the authenticated user.
|
||||
//
|
||||
// Validation steps:
|
||||
// 1. Parse and verify JWT signature using server secret
|
||||
// 2. Verify key ID matches expected version
|
||||
// 3. Extract user ID from JWT claims (subject field)
|
||||
// 4. Verify user exists and is not archived
|
||||
// 5. Verify token exists in user's access_tokens list (for revocation support)
|
||||
//
|
||||
// Returns the user if authentication succeeds, or an error describing the failure.
|
||||
func (a *Authenticator) AuthenticateByJWT(ctx context.Context, accessToken string) (*store.User, error) {
|
||||
if accessToken == "" {
|
||||
return nil, errors.New("access token not found")
|
||||
}
|
||||
|
||||
claims := &ClaimsMessage{}
|
||||
_, err := jwt.ParseWithClaims(accessToken, claims, func(t *jwt.Token) (any, error) {
|
||||
if t.Method.Alg() != jwt.SigningMethodHS256.Name {
|
||||
return nil, errors.Errorf("unexpected signing method: %v", t.Header["alg"])
|
||||
}
|
||||
kid, ok := t.Header["kid"].(string)
|
||||
if !ok || kid != KeyID {
|
||||
return nil, errors.Errorf("unexpected kid: %v", t.Header["kid"])
|
||||
}
|
||||
return []byte(a.secret), nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid or expired access token")
|
||||
return nil, errors.Wrap(err, "invalid access token")
|
||||
}
|
||||
|
||||
userID, err := util.ConvertStringToInt32(claims.Subject)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "malformed ID in token")
|
||||
return nil, errors.Wrap(err, "invalid user ID in token")
|
||||
}
|
||||
|
||||
return &UserClaims{
|
||||
UserID: userID,
|
||||
Username: claims.Username,
|
||||
Role: claims.Role,
|
||||
Status: claims.Status,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AuthenticateByRefreshToken validates a refresh token against the database.
|
||||
func (a *Authenticator) AuthenticateByRefreshToken(ctx context.Context, refreshToken string) (*store.User, string, error) {
|
||||
claims, err := ParseRefreshToken(refreshToken, []byte(a.secret))
|
||||
if err != nil {
|
||||
return nil, "", errors.Wrap(err, "invalid refresh token")
|
||||
}
|
||||
|
||||
userID, err := util.ConvertStringToInt32(claims.Subject)
|
||||
if err != nil {
|
||||
return nil, "", errors.Wrap(err, "invalid user ID in token")
|
||||
}
|
||||
|
||||
// Check token exists in database (revocation check)
|
||||
token, err := a.store.GetUserRefreshTokenByID(ctx, userID, claims.TokenID)
|
||||
if err != nil {
|
||||
return nil, "", errors.Wrap(err, "failed to get refresh token")
|
||||
}
|
||||
if token == nil {
|
||||
return nil, "", errors.New("refresh token revoked")
|
||||
}
|
||||
|
||||
// Check token not expired
|
||||
if token.ExpiresAt != nil && token.ExpiresAt.AsTime().Before(time.Now()) {
|
||||
return nil, "", errors.New("refresh token expired")
|
||||
}
|
||||
|
||||
// Get user
|
||||
user, err := a.store.GetUser(ctx, &store.FindUser{ID: &userID})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get user")
|
||||
return nil, "", errors.Wrap(err, "failed to get user")
|
||||
}
|
||||
if user == nil {
|
||||
return nil, errors.Errorf("user %d not found", userID)
|
||||
return nil, "", errors.New("user not found")
|
||||
}
|
||||
if user.RowStatus == store.Archived {
|
||||
return nil, errors.Errorf("user %d is archived", userID)
|
||||
return nil, "", errors.New("user is archived")
|
||||
}
|
||||
|
||||
accessTokens, err := a.store.GetUserAccessTokens(ctx, user.ID)
|
||||
return user, claims.TokenID, nil
|
||||
}
|
||||
|
||||
// AuthenticateByPAT validates a Personal Access Token.
|
||||
func (a *Authenticator) AuthenticateByPAT(ctx context.Context, token string) (*store.User, *storepb.PersonalAccessTokensUserSetting_PersonalAccessToken, error) {
|
||||
if !strings.HasPrefix(token, PersonalAccessTokenPrefix) {
|
||||
return nil, nil, errors.New("invalid PAT format")
|
||||
}
|
||||
|
||||
tokenHash := HashPersonalAccessToken(token)
|
||||
result, err := a.store.GetUserByPATHash(ctx, tokenHash)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get user access tokens")
|
||||
}
|
||||
if !validateAccessToken(accessToken, accessTokens) {
|
||||
return nil, errors.New("invalid access token")
|
||||
return nil, nil, errors.Wrap(err, "invalid PAT")
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// AuthorizeAndSetContext checks user authorization for the given procedure and sets context values.
|
||||
//
|
||||
// Authorization checks:
|
||||
// - Admin-only methods require Host or Admin role (checked via isAdminOnly function)
|
||||
//
|
||||
// Context values set:
|
||||
// - UserIDContextKey: Always set with the user's ID
|
||||
// - SessionIDContextKey: Set if authenticated via session cookie
|
||||
// - AccessTokenContextKey: Set if authenticated via JWT token
|
||||
//
|
||||
// Also updates session last accessed time for session-based auth (sliding expiration).
|
||||
//
|
||||
// Returns the updated context or an error if authorization fails.
|
||||
func (a *Authenticator) AuthorizeAndSetContext(ctx context.Context, procedure string, user *store.User, sessionID, accessToken string, isAdminOnly func(string) bool) (context.Context, error) {
|
||||
// Check admin-only method authorization
|
||||
if isAdminOnly != nil && isAdminOnly(procedure) && user.Role != store.RoleHost && user.Role != store.RoleAdmin {
|
||||
return nil, errors.Errorf("user %q is not authorized for this operation", user.Username)
|
||||
// Check expiry
|
||||
if result.PAT.ExpiresAt != nil && result.PAT.ExpiresAt.AsTime().Before(time.Now()) {
|
||||
return nil, nil, errors.New("PAT expired")
|
||||
}
|
||||
|
||||
// Set user ID in context (always)
|
||||
ctx = context.WithValue(ctx, UserIDContextKey, user.ID)
|
||||
|
||||
// Set authentication method specific context values
|
||||
if sessionID != "" {
|
||||
ctx = context.WithValue(ctx, SessionIDContextKey, sessionID)
|
||||
// Update session last accessed time for sliding expiration
|
||||
_ = a.store.UpdateUserSessionLastAccessed(ctx, user.ID, sessionID, timestamppb.Now())
|
||||
} else if accessToken != "" {
|
||||
ctx = context.WithValue(ctx, AccessTokenContextKey, accessToken)
|
||||
// Check user status
|
||||
if result.User.RowStatus == store.Archived {
|
||||
return nil, nil, errors.New("user is archived")
|
||||
}
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// validateAccessToken checks if the token exists in the user's access tokens list.
|
||||
// This enables token revocation: deleted tokens are removed from the list.
|
||||
func validateAccessToken(token string, tokens []*storepb.AccessTokensUserSetting_AccessToken) bool {
|
||||
for _, t := range tokens {
|
||||
if token == t.AccessToken {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// UpdateSessionLastAccessed updates the last accessed time for a session.
|
||||
// This implements sliding expiration - sessions remain valid as long as they're used.
|
||||
// Should be called after successful session-based authentication.
|
||||
func (a *Authenticator) UpdateSessionLastAccessed(ctx context.Context, userID int32, sessionID string) {
|
||||
// Fire-and-forget update; failures are logged but don't block the request
|
||||
_ = a.store.UpdateUserSessionLastAccessed(ctx, userID, sessionID, timestamppb.Now())
|
||||
return result.User, result.PAT, nil
|
||||
}
|
||||
|
||||
// AuthResult contains the result of an authentication attempt.
|
||||
type AuthResult struct {
|
||||
User *store.User
|
||||
SessionID string // Non-empty if authenticated via session cookie
|
||||
AccessToken string // Non-empty if authenticated via JWT
|
||||
User *store.User // Set for PAT and legacy auth
|
||||
Claims *UserClaims // Set for Access Token V2 (stateless)
|
||||
SessionID string // Non-empty if authenticated via session cookie
|
||||
AccessToken string // Non-empty if authenticated via JWT
|
||||
}
|
||||
|
||||
// Authenticate tries to authenticate using the provided credentials.
|
||||
// It tries session cookie first, then JWT token.
|
||||
// Priority: 1. Access Token V2, 2. PAT
|
||||
// Returns nil if no valid credentials are provided.
|
||||
// On successful session auth, it also updates the session sliding expiration.
|
||||
func (a *Authenticator) Authenticate(ctx context.Context, sessionID, authHeader string) *AuthResult {
|
||||
// Try session cookie authentication first
|
||||
if sessionID != "" {
|
||||
user, err := a.AuthenticateBySession(ctx, sessionID)
|
||||
if err == nil && user != nil {
|
||||
a.UpdateSessionLastAccessed(ctx, user.ID, sessionID)
|
||||
return &AuthResult{User: user, SessionID: sessionID}
|
||||
func (a *Authenticator) Authenticate(ctx context.Context, _, authHeader string) *AuthResult {
|
||||
token := ExtractBearerToken(authHeader)
|
||||
|
||||
// Try Access Token V2 (stateless)
|
||||
if token != "" && !strings.HasPrefix(token, PersonalAccessTokenPrefix) {
|
||||
claims, err := a.AuthenticateByAccessTokenV2(token)
|
||||
if err == nil && claims != nil {
|
||||
return &AuthResult{
|
||||
Claims: claims,
|
||||
AccessToken: token,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try JWT token authentication
|
||||
if token := ExtractBearerToken(authHeader); token != "" {
|
||||
user, err := a.AuthenticateByJWT(ctx, token)
|
||||
// Try PAT
|
||||
if token != "" && strings.HasPrefix(token, PersonalAccessTokenPrefix) {
|
||||
user, pat, err := a.AuthenticateByPAT(ctx, token)
|
||||
if err == nil && user != nil {
|
||||
// Update last used (fire-and-forget with logging)
|
||||
go func() {
|
||||
if err := a.store.UpdatePATLastUsed(context.Background(), user.ID, pat.TokenId, timestamppb.Now()); err != nil {
|
||||
slog.Warn("failed to update PAT last used time", "error", err, "userID", user.ID)
|
||||
}
|
||||
}()
|
||||
return &AuthResult{User: user, AccessToken: token}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@ const (
|
|||
// AccessTokenContextKey stores the JWT token for token-based auth.
|
||||
// Only set when authenticated via Bearer token.
|
||||
AccessTokenContextKey
|
||||
|
||||
// UserClaimsContextKey stores the claims from access token.
|
||||
UserClaimsContextKey
|
||||
|
||||
// RefreshTokenIDContextKey stores the refresh token ID.
|
||||
RefreshTokenIDContextKey
|
||||
)
|
||||
|
||||
// GetUserID retrieves the authenticated user's ID from the context.
|
||||
|
|
@ -70,3 +76,25 @@ func SetUserInContext(ctx context.Context, user *store.User, sessionID, accessTo
|
|||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
// UserClaims represents authenticated user info from access token.
|
||||
type UserClaims struct {
|
||||
UserID int32
|
||||
Username string
|
||||
Role string
|
||||
Status string
|
||||
}
|
||||
|
||||
// GetUserClaims retrieves the user claims from context.
|
||||
// Returns nil if not authenticated via access token.
|
||||
func GetUserClaims(ctx context.Context) *UserClaims {
|
||||
if v, ok := ctx.Value(UserClaimsContextKey).(*UserClaims); ok {
|
||||
return v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUserClaimsInContext sets the user claims in context.
|
||||
func SetUserClaimsInContext(ctx context.Context, claims *UserClaims) context.Context {
|
||||
return context.WithValue(ctx, UserClaimsContextKey, claims)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,3 +33,16 @@ func ExtractBearerToken(authHeader string) string {
|
|||
}
|
||||
return parts[1]
|
||||
}
|
||||
|
||||
// ExtractRefreshTokenFromCookie extracts the refresh token from cookie header.
|
||||
func ExtractRefreshTokenFromCookie(cookieHeader string) string {
|
||||
if cookieHeader == "" {
|
||||
return ""
|
||||
}
|
||||
req := &http.Request{Header: http.Header{"Cookie": []string{cookieHeader}}}
|
||||
cookie, err := req.Cookie(RefreshTokenCookieName)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return cookie.Value
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,13 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/usememos/memos/internal/util"
|
||||
)
|
||||
|
|
@ -40,6 +43,21 @@ const (
|
|||
// SessionCookieName is the HTTP cookie name used to store session information.
|
||||
// Cookie value is the session ID (UUID).
|
||||
SessionCookieName = "user_session"
|
||||
|
||||
// AccessTokenDuration is the lifetime of access tokens (15 minutes).
|
||||
AccessTokenDuration = 15 * time.Minute
|
||||
|
||||
// RefreshTokenDuration is the lifetime of refresh tokens (30 days).
|
||||
RefreshTokenDuration = 30 * 24 * time.Hour
|
||||
|
||||
// RefreshTokenAudienceName is the audience claim for refresh tokens.
|
||||
RefreshTokenAudienceName = "user.refresh-token"
|
||||
|
||||
// RefreshTokenCookieName is the cookie name for refresh tokens.
|
||||
RefreshTokenCookieName = "memos_refresh"
|
||||
|
||||
// PersonalAccessTokenPrefix is the prefix for PAT tokens.
|
||||
PersonalAccessTokenPrefix = "memos_pat_"
|
||||
)
|
||||
|
||||
// ClaimsMessage represents the claims structure in a JWT token.
|
||||
|
|
@ -56,6 +74,24 @@ type ClaimsMessage struct {
|
|||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// AccessTokenClaims contains claims for short-lived access tokens.
|
||||
// These tokens are validated by signature only (stateless).
|
||||
type AccessTokenClaims struct {
|
||||
Type string `json:"type"` // "access"
|
||||
Role string `json:"role"` // User role
|
||||
Status string `json:"status"` // User status
|
||||
Username string `json:"username"` // Username for display
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// RefreshTokenClaims contains claims for long-lived refresh tokens.
|
||||
// These tokens are validated against the database for revocation.
|
||||
type RefreshTokenClaims struct {
|
||||
Type string `json:"type"` // "refresh"
|
||||
TokenID string `json:"tid"` // Token ID for revocation lookup
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// GenerateAccessToken generates a JWT access token for a user.
|
||||
//
|
||||
// Parameters:
|
||||
|
|
@ -110,3 +146,121 @@ func generateToken(username string, userID int32, audience string, expirationTim
|
|||
func GenerateSessionID() string {
|
||||
return util.GenUUID()
|
||||
}
|
||||
|
||||
// GenerateAccessTokenV2 generates a short-lived access token with user claims.
|
||||
func GenerateAccessTokenV2(userID int32, username, role, status string, secret []byte) (string, time.Time, error) {
|
||||
expiresAt := time.Now().Add(AccessTokenDuration)
|
||||
|
||||
claims := &AccessTokenClaims{
|
||||
Type: "access",
|
||||
Role: role,
|
||||
Status: status,
|
||||
Username: username,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
Issuer: Issuer,
|
||||
Audience: jwt.ClaimStrings{AccessTokenAudienceName},
|
||||
Subject: fmt.Sprint(userID),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
ExpiresAt: jwt.NewNumericDate(expiresAt),
|
||||
},
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
token.Header["kid"] = KeyID
|
||||
|
||||
tokenString, err := token.SignedString(secret)
|
||||
if err != nil {
|
||||
return "", time.Time{}, err
|
||||
}
|
||||
|
||||
return tokenString, expiresAt, nil
|
||||
}
|
||||
|
||||
// GenerateRefreshToken generates a long-lived refresh token.
|
||||
func GenerateRefreshToken(userID int32, tokenID string, secret []byte) (string, time.Time, error) {
|
||||
expiresAt := time.Now().Add(RefreshTokenDuration)
|
||||
|
||||
claims := &RefreshTokenClaims{
|
||||
Type: "refresh",
|
||||
TokenID: tokenID,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
Issuer: Issuer,
|
||||
Audience: jwt.ClaimStrings{RefreshTokenAudienceName},
|
||||
Subject: fmt.Sprint(userID),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
ExpiresAt: jwt.NewNumericDate(expiresAt),
|
||||
},
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
token.Header["kid"] = KeyID
|
||||
|
||||
tokenString, err := token.SignedString(secret)
|
||||
if err != nil {
|
||||
return "", time.Time{}, err
|
||||
}
|
||||
|
||||
return tokenString, expiresAt, nil
|
||||
}
|
||||
|
||||
// GeneratePersonalAccessToken generates a random PAT string.
|
||||
func GeneratePersonalAccessToken() string {
|
||||
randomStr, err := util.RandomString(32)
|
||||
if err != nil {
|
||||
// Fallback to UUID if RandomString fails
|
||||
return PersonalAccessTokenPrefix + util.GenUUID()
|
||||
}
|
||||
return PersonalAccessTokenPrefix + randomStr
|
||||
}
|
||||
|
||||
// HashPersonalAccessToken returns SHA-256 hash of a PAT.
|
||||
func HashPersonalAccessToken(token string) string {
|
||||
hash := sha256.Sum256([]byte(token))
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
// verifyJWTKeyFunc returns a jwt.Keyfunc that validates the signing method and key ID.
|
||||
func verifyJWTKeyFunc(secret []byte) jwt.Keyfunc {
|
||||
return func(t *jwt.Token) (any, error) {
|
||||
if t.Method.Alg() != jwt.SigningMethodHS256.Name {
|
||||
return nil, errors.Errorf("unexpected signing method: %v", t.Header["alg"])
|
||||
}
|
||||
kid, ok := t.Header["kid"].(string)
|
||||
if !ok || kid != KeyID {
|
||||
return nil, errors.Errorf("unexpected kid: %v", t.Header["kid"])
|
||||
}
|
||||
return secret, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ParseAccessTokenV2 parses and validates a short-lived access token.
|
||||
func ParseAccessTokenV2(tokenString string, secret []byte) (*AccessTokenClaims, error) {
|
||||
claims := &AccessTokenClaims{}
|
||||
_, err := jwt.ParseWithClaims(tokenString, claims, verifyJWTKeyFunc(secret),
|
||||
jwt.WithIssuer(Issuer),
|
||||
jwt.WithAudience(AccessTokenAudienceName),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if claims.Type != "access" {
|
||||
return nil, errors.New("invalid token type: expected access token")
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
// ParseRefreshToken parses and validates a refresh token.
|
||||
func ParseRefreshToken(tokenString string, secret []byte) (*RefreshTokenClaims, error) {
|
||||
claims := &RefreshTokenClaims{}
|
||||
_, err := jwt.ParseWithClaims(tokenString, claims, verifyJWTKeyFunc(secret),
|
||||
jwt.WithIssuer(Issuer),
|
||||
jwt.WithAudience(RefreshTokenAudienceName),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if claims.Type != "refresh" {
|
||||
return nil, errors.New("invalid token type: expected refresh token")
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,306 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGenerateAccessTokenV2(t *testing.T) {
|
||||
secret := []byte("test-secret")
|
||||
|
||||
t.Run("generates valid access token", func(t *testing.T) {
|
||||
token, expiresAt, err := GenerateAccessTokenV2(1, "testuser", "USER", "ACTIVE", secret)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, token)
|
||||
assert.True(t, expiresAt.After(time.Now()))
|
||||
assert.True(t, expiresAt.Before(time.Now().Add(AccessTokenDuration+time.Minute)))
|
||||
})
|
||||
|
||||
t.Run("generates different tokens for same user", func(t *testing.T) {
|
||||
token1, _, err := GenerateAccessTokenV2(1, "testuser", "USER", "ACTIVE", secret)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(2 * time.Second) // Ensure different timestamps (tokens have 1s precision)
|
||||
|
||||
token2, _, err := GenerateAccessTokenV2(1, "testuser", "USER", "ACTIVE", secret)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.NotEqual(t, token1, token2, "tokens should be different due to different timestamps")
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseAccessTokenV2(t *testing.T) {
|
||||
secret := []byte("test-secret")
|
||||
|
||||
t.Run("parses valid access token", func(t *testing.T) {
|
||||
token, _, err := GenerateAccessTokenV2(1, "testuser", "USER", "ACTIVE", secret)
|
||||
require.NoError(t, err)
|
||||
|
||||
claims, err := ParseAccessTokenV2(token, secret)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "1", claims.Subject)
|
||||
assert.Equal(t, "testuser", claims.Username)
|
||||
assert.Equal(t, "USER", claims.Role)
|
||||
assert.Equal(t, "ACTIVE", claims.Status)
|
||||
assert.Equal(t, "access", claims.Type)
|
||||
})
|
||||
|
||||
t.Run("fails with wrong secret", func(t *testing.T) {
|
||||
token, _, err := GenerateAccessTokenV2(1, "testuser", "USER", "ACTIVE", secret)
|
||||
require.NoError(t, err)
|
||||
|
||||
wrongSecret := []byte("wrong-secret")
|
||||
_, err = ParseAccessTokenV2(token, wrongSecret)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("fails with invalid token", func(t *testing.T) {
|
||||
_, err := ParseAccessTokenV2("invalid-token", secret)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("fails with refresh token", func(t *testing.T) {
|
||||
// Generate a refresh token and try to parse it as access token
|
||||
// Should fail because audience mismatch is caught before type check
|
||||
refreshToken, _, err := GenerateRefreshToken(1, "token-id", secret)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = ParseAccessTokenV2(refreshToken, secret)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid audience")
|
||||
})
|
||||
|
||||
t.Run("parses token with different roles", func(t *testing.T) {
|
||||
roles := []string{"USER", "ADMIN", "HOST"}
|
||||
for _, role := range roles {
|
||||
token, _, err := GenerateAccessTokenV2(1, "testuser", role, "ACTIVE", secret)
|
||||
require.NoError(t, err)
|
||||
|
||||
claims, err := ParseAccessTokenV2(token, secret)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, role, claims.Role)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGenerateRefreshToken(t *testing.T) {
|
||||
secret := []byte("test-secret")
|
||||
|
||||
t.Run("generates valid refresh token", func(t *testing.T) {
|
||||
token, expiresAt, err := GenerateRefreshToken(1, "token-id-123", secret)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, token)
|
||||
assert.True(t, expiresAt.After(time.Now().Add(29*24*time.Hour)))
|
||||
})
|
||||
|
||||
t.Run("generates different tokens for different token IDs", func(t *testing.T) {
|
||||
token1, _, err := GenerateRefreshToken(1, "token-id-1", secret)
|
||||
require.NoError(t, err)
|
||||
|
||||
token2, _, err := GenerateRefreshToken(1, "token-id-2", secret)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.NotEqual(t, token1, token2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseRefreshToken(t *testing.T) {
|
||||
secret := []byte("test-secret")
|
||||
|
||||
t.Run("parses valid refresh token", func(t *testing.T) {
|
||||
token, _, err := GenerateRefreshToken(1, "token-id-123", secret)
|
||||
require.NoError(t, err)
|
||||
|
||||
claims, err := ParseRefreshToken(token, secret)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "1", claims.Subject)
|
||||
assert.Equal(t, "token-id-123", claims.TokenID)
|
||||
assert.Equal(t, "refresh", claims.Type)
|
||||
})
|
||||
|
||||
t.Run("fails with wrong secret", func(t *testing.T) {
|
||||
token, _, err := GenerateRefreshToken(1, "token-id-123", secret)
|
||||
require.NoError(t, err)
|
||||
|
||||
wrongSecret := []byte("wrong-secret")
|
||||
_, err = ParseRefreshToken(token, wrongSecret)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("fails with invalid token", func(t *testing.T) {
|
||||
_, err := ParseRefreshToken("invalid-token", secret)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("fails with access token", func(t *testing.T) {
|
||||
// Generate an access token and try to parse it as refresh token
|
||||
// Should fail because audience mismatch is caught before type check
|
||||
accessToken, _, err := GenerateAccessTokenV2(1, "testuser", "USER", "ACTIVE", secret)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = ParseRefreshToken(accessToken, secret)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid audience")
|
||||
})
|
||||
}
|
||||
|
||||
func TestGeneratePersonalAccessToken(t *testing.T) {
|
||||
t.Run("generates token with correct prefix", func(t *testing.T) {
|
||||
token := GeneratePersonalAccessToken()
|
||||
assert.NotEmpty(t, token)
|
||||
assert.True(t, len(token) > len(PersonalAccessTokenPrefix))
|
||||
assert.Equal(t, PersonalAccessTokenPrefix, token[:len(PersonalAccessTokenPrefix)])
|
||||
})
|
||||
|
||||
t.Run("generates unique tokens", func(t *testing.T) {
|
||||
token1 := GeneratePersonalAccessToken()
|
||||
token2 := GeneratePersonalAccessToken()
|
||||
assert.NotEqual(t, token1, token2)
|
||||
})
|
||||
|
||||
t.Run("generates token of sufficient length", func(t *testing.T) {
|
||||
token := GeneratePersonalAccessToken()
|
||||
// Prefix is "memos_pat_" (10 chars) + 32 random chars = at least 42 chars
|
||||
assert.True(t, len(token) >= 42, "token should be at least 42 characters")
|
||||
})
|
||||
}
|
||||
|
||||
func TestHashPersonalAccessToken(t *testing.T) {
|
||||
t.Run("generates SHA-256 hash", func(t *testing.T) {
|
||||
token := "memos_pat_abc123"
|
||||
hash := HashPersonalAccessToken(token)
|
||||
assert.NotEmpty(t, hash)
|
||||
assert.Len(t, hash, 64, "SHA-256 hex should be 64 characters")
|
||||
})
|
||||
|
||||
t.Run("same input produces same hash", func(t *testing.T) {
|
||||
token := "memos_pat_abc123"
|
||||
hash1 := HashPersonalAccessToken(token)
|
||||
hash2 := HashPersonalAccessToken(token)
|
||||
assert.Equal(t, hash1, hash2)
|
||||
})
|
||||
|
||||
t.Run("different inputs produce different hashes", func(t *testing.T) {
|
||||
token1 := "memos_pat_abc123"
|
||||
token2 := "memos_pat_xyz789"
|
||||
hash1 := HashPersonalAccessToken(token1)
|
||||
hash2 := HashPersonalAccessToken(token2)
|
||||
assert.NotEqual(t, hash1, hash2)
|
||||
})
|
||||
|
||||
t.Run("hash is deterministic", func(t *testing.T) {
|
||||
token := GeneratePersonalAccessToken()
|
||||
hash1 := HashPersonalAccessToken(token)
|
||||
hash2 := HashPersonalAccessToken(token)
|
||||
assert.Equal(t, hash1, hash2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccessTokenV2Integration(t *testing.T) {
|
||||
secret := []byte("test-secret")
|
||||
|
||||
t.Run("full lifecycle: generate, parse, validate", func(t *testing.T) {
|
||||
userID := int32(42)
|
||||
username := "john_doe"
|
||||
role := "ADMIN"
|
||||
status := "ACTIVE"
|
||||
|
||||
// Generate token
|
||||
token, expiresAt, err := GenerateAccessTokenV2(userID, username, role, status, secret)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, token)
|
||||
|
||||
// Parse token
|
||||
claims, err := ParseAccessTokenV2(token, secret)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Validate claims
|
||||
assert.Equal(t, "42", claims.Subject)
|
||||
assert.Equal(t, username, claims.Username)
|
||||
assert.Equal(t, role, claims.Role)
|
||||
assert.Equal(t, status, claims.Status)
|
||||
assert.Equal(t, "access", claims.Type)
|
||||
assert.Equal(t, Issuer, claims.Issuer)
|
||||
assert.NotNil(t, claims.IssuedAt)
|
||||
assert.NotNil(t, claims.ExpiresAt)
|
||||
|
||||
// Validate expiration
|
||||
assert.True(t, claims.ExpiresAt.Equal(expiresAt) || claims.ExpiresAt.Before(expiresAt))
|
||||
})
|
||||
}
|
||||
|
||||
func TestRefreshTokenIntegration(t *testing.T) {
|
||||
secret := []byte("test-secret")
|
||||
|
||||
t.Run("full lifecycle: generate, parse, validate", func(t *testing.T) {
|
||||
userID := int32(42)
|
||||
tokenID := "unique-token-id-456"
|
||||
|
||||
// Generate token
|
||||
token, expiresAt, err := GenerateRefreshToken(userID, tokenID, secret)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, token)
|
||||
|
||||
// Parse token
|
||||
claims, err := ParseRefreshToken(token, secret)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Validate claims
|
||||
assert.Equal(t, "42", claims.Subject)
|
||||
assert.Equal(t, tokenID, claims.TokenID)
|
||||
assert.Equal(t, "refresh", claims.Type)
|
||||
assert.Equal(t, Issuer, claims.Issuer)
|
||||
assert.NotNil(t, claims.IssuedAt)
|
||||
assert.NotNil(t, claims.ExpiresAt)
|
||||
|
||||
// Validate expiration
|
||||
assert.True(t, claims.ExpiresAt.Equal(expiresAt) || claims.ExpiresAt.Before(expiresAt))
|
||||
})
|
||||
}
|
||||
|
||||
func TestPersonalAccessTokenIntegration(t *testing.T) {
|
||||
t.Run("full lifecycle: generate, hash, verify", func(t *testing.T) {
|
||||
// Generate token
|
||||
token := GeneratePersonalAccessToken()
|
||||
assert.NotEmpty(t, token)
|
||||
assert.True(t, len(token) > len(PersonalAccessTokenPrefix))
|
||||
|
||||
// Hash token
|
||||
hash := HashPersonalAccessToken(token)
|
||||
assert.Len(t, hash, 64)
|
||||
|
||||
// Verify same token produces same hash
|
||||
hashAgain := HashPersonalAccessToken(token)
|
||||
assert.Equal(t, hash, hashAgain)
|
||||
|
||||
// Verify different token produces different hash
|
||||
token2 := GeneratePersonalAccessToken()
|
||||
hash2 := HashPersonalAccessToken(token2)
|
||||
assert.NotEqual(t, hash, hash2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTokenExpiration(t *testing.T) {
|
||||
secret := []byte("test-secret")
|
||||
|
||||
t.Run("access token expires after AccessTokenDuration", func(t *testing.T) {
|
||||
_, expiresAt, err := GenerateAccessTokenV2(1, "testuser", "USER", "ACTIVE", secret)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedExpiry := time.Now().Add(AccessTokenDuration)
|
||||
delta := expiresAt.Sub(expectedExpiry)
|
||||
assert.True(t, delta < time.Second, "expiration should be within 1 second of expected")
|
||||
})
|
||||
|
||||
t.Run("refresh token expires after RefreshTokenDuration", func(t *testing.T) {
|
||||
_, expiresAt, err := GenerateRefreshToken(1, "token-id", secret)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedExpiry := time.Now().Add(RefreshTokenDuration)
|
||||
delta := expiresAt.Sub(expectedExpiry)
|
||||
assert.True(t, delta < time.Second, "expiration should be within 1 second of expected")
|
||||
})
|
||||
}
|
||||
|
|
@ -9,9 +9,9 @@ package v1
|
|||
// Format: Full gRPC procedure path as returned by req.Spec().Procedure (Connect)
|
||||
// or info.FullMethod (gRPC interceptor).
|
||||
var PublicMethods = map[string]struct{}{
|
||||
// Auth Service - login flow must be accessible without auth
|
||||
"/memos.api.v1.AuthService/CreateSession": {},
|
||||
"/memos.api.v1.AuthService/GetCurrentSession": {},
|
||||
// Auth Service - login/token endpoints must be accessible without auth
|
||||
"/memos.api.v1.AuthService/SignIn": {},
|
||||
"/memos.api.v1.AuthService/RefreshToken": {}, // Token refresh uses cookie, must be accessible when access token expired
|
||||
|
||||
// Instance Service - needed before login to show instance info
|
||||
"/memos.api.v1.InstanceService/GetInstanceProfile": {},
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ import (
|
|||
func TestPublicMethodsArePublic(t *testing.T) {
|
||||
publicMethods := []string{
|
||||
// Auth Service
|
||||
"/memos.api.v1.AuthService/CreateSession",
|
||||
"/memos.api.v1.AuthService/GetCurrentSession",
|
||||
"/memos.api.v1.AuthService/SignIn",
|
||||
"/memos.api.v1.AuthService/RefreshToken",
|
||||
// Instance Service
|
||||
"/memos.api.v1.InstanceService/GetInstanceProfile",
|
||||
"/memos.api.v1.InstanceService/GetInstanceSetting",
|
||||
|
|
@ -39,8 +39,9 @@ func TestPublicMethodsArePublic(t *testing.T) {
|
|||
// TestProtectedMethodsRequireAuth verifies that non-public methods are recognized as protected.
|
||||
func TestProtectedMethodsRequireAuth(t *testing.T) {
|
||||
protectedMethods := []string{
|
||||
// Auth Service - logout requires auth
|
||||
"/memos.api.v1.AuthService/DeleteSession",
|
||||
// Auth Service - logout and get current user require auth
|
||||
"/memos.api.v1.AuthService/SignOut",
|
||||
"/memos.api.v1.AuthService/GetCurrentUser",
|
||||
// Instance Service - admin operations
|
||||
"/memos.api.v1.InstanceService/UpdateInstanceSetting",
|
||||
// User Service - modification operations
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ var SupportedThumbnailMimeTypes = []string{
|
|||
}
|
||||
|
||||
func (s *APIV1Service) CreateAttachment(ctx context.Context, request *v1pb.CreateAttachmentRequest) (*v1pb.Attachment, error) {
|
||||
user, err := s.GetCurrentUser(ctx)
|
||||
user, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -123,7 +123,7 @@ func (s *APIV1Service) CreateAttachment(ctx context.Context, request *v1pb.Creat
|
|||
}
|
||||
|
||||
func (s *APIV1Service) ListAttachments(ctx context.Context, request *v1pb.ListAttachmentsRequest) (*v1pb.ListAttachmentsResponse, error) {
|
||||
user, err := s.GetCurrentUser(ctx)
|
||||
user, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -234,7 +234,7 @@ func (s *APIV1Service) DeleteAttachment(ctx context.Context, request *v1pb.Delet
|
|||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid attachment id: %v", err)
|
||||
}
|
||||
user, err := s.GetCurrentUser(ctx)
|
||||
user, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,17 +29,13 @@ const (
|
|||
unmatchedUsernameAndPasswordError = "unmatched username and password"
|
||||
)
|
||||
|
||||
// GetCurrentSession retrieves the current authenticated session information.
|
||||
// GetCurrentUser returns the authenticated user's information.
|
||||
// Validates the access token and returns user details.
|
||||
//
|
||||
// This endpoint is used to:
|
||||
// - Check if a user is currently authenticated
|
||||
// - Get the current user's information
|
||||
// - Retrieve the last accessed time of the session
|
||||
//
|
||||
// Authentication: Required (session cookie or access token)
|
||||
// Returns: User information and last accessed timestamp.
|
||||
func (s *APIV1Service) GetCurrentSession(ctx context.Context, _ *v1pb.GetCurrentSessionRequest) (*v1pb.GetCurrentSessionResponse, error) {
|
||||
user, err := s.GetCurrentUser(ctx)
|
||||
// Authentication: Required (access token).
|
||||
// Returns: User information.
|
||||
func (s *APIV1Service) GetCurrentUser(ctx context.Context, _ *v1pb.GetCurrentUserRequest) (*v1pb.GetCurrentUserResponse, error) {
|
||||
user, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -51,37 +47,21 @@ func (s *APIV1Service) GetCurrentSession(ctx context.Context, _ *v1pb.GetCurrent
|
|||
return nil, status.Errorf(codes.Unauthenticated, "user not found")
|
||||
}
|
||||
|
||||
var lastAccessedAt *timestamppb.Timestamp
|
||||
// Update session last accessed time if we have a session ID and get the current session info
|
||||
if sessionID := auth.GetSessionID(ctx); sessionID != "" {
|
||||
now := timestamppb.Now()
|
||||
if err := s.Store.UpdateUserSessionLastAccessed(ctx, user.ID, sessionID, now); err != nil {
|
||||
// Log error but don't fail the request
|
||||
slog.Error("failed to update session last accessed time", "error", err)
|
||||
}
|
||||
lastAccessedAt = now
|
||||
}
|
||||
|
||||
return &v1pb.GetCurrentSessionResponse{
|
||||
User: convertUserFromStore(user),
|
||||
LastAccessedAt: lastAccessedAt,
|
||||
return &v1pb.GetCurrentUserResponse{
|
||||
User: convertUserFromStore(user),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateSession authenticates a user and establishes a new session.
|
||||
// SignIn authenticates a user with credentials and returns tokens.
|
||||
// On success, returns an access token and sets a refresh token cookie.
|
||||
//
|
||||
// This endpoint supports two authentication methods:
|
||||
// 1. Password-based authentication (username + password)
|
||||
// 2. SSO authentication (OAuth2 authorization code)
|
||||
// Supports two authentication methods:
|
||||
// 1. Password-based authentication (username + password).
|
||||
// 2. SSO authentication (OAuth2 authorization code).
|
||||
//
|
||||
// On successful authentication:
|
||||
// - A session cookie is set for web browsers (cookie: user_session={userID}-{sessionID})
|
||||
// - Session information is stored including client details (IP, user agent, device type)
|
||||
// - Sessions use sliding expiration: 14 days from last access
|
||||
//
|
||||
// Authentication: Not required (public endpoint)
|
||||
// Returns: Authenticated user information and last accessed timestamp.
|
||||
func (s *APIV1Service) CreateSession(ctx context.Context, request *v1pb.CreateSessionRequest) (*v1pb.CreateSessionResponse, error) {
|
||||
// Authentication: Not required (public endpoint).
|
||||
// Returns: User info, access token, and token expiry.
|
||||
func (s *APIV1Service) SignIn(ctx context.Context, request *v1pb.SignInRequest) (*v1pb.SignInResponse, error) {
|
||||
var existingUser *store.User
|
||||
|
||||
// Authentication Method 1: Password-based authentication
|
||||
|
|
@ -197,99 +177,162 @@ 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)
|
||||
}
|
||||
|
||||
// Default session expiration time is 100 year
|
||||
expireTime := time.Now().Add(100 * 365 * 24 * time.Hour)
|
||||
if err := s.doSignIn(ctx, existingUser, expireTime); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to sign in, error: %v", err)
|
||||
accessToken, accessExpiresAt, err := s.doSignIn(ctx, existingUser)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to sign in: %v", err)
|
||||
}
|
||||
|
||||
return &v1pb.CreateSessionResponse{
|
||||
User: convertUserFromStore(existingUser),
|
||||
LastAccessedAt: timestamppb.Now(),
|
||||
return &v1pb.SignInResponse{
|
||||
User: convertUserFromStore(existingUser),
|
||||
AccessToken: accessToken,
|
||||
AccessTokenExpiresAt: timestamppb.New(accessExpiresAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// doSignIn performs the actual sign-in operation by creating a session and setting the cookie.
|
||||
//
|
||||
// This function:
|
||||
// 1. Generates a unique session ID (UUID)
|
||||
// 2. Tracks the session in user settings with client information
|
||||
// 3. Sets a session cookie in the format: {userID}-{sessionID}
|
||||
// 4. Configures cookie security settings (HttpOnly, Secure, SameSite)
|
||||
//
|
||||
// Cookie lifetime is 100 years, but actual session validity is controlled by
|
||||
// sliding expiration (14 days from last access) checked during authentication.
|
||||
func (s *APIV1Service) doSignIn(ctx context.Context, user *store.User, expireTime time.Time) error {
|
||||
// Generate unique session ID for web use
|
||||
sessionID := auth.GenerateSessionID()
|
||||
|
||||
// Track session in user settings
|
||||
if err := s.trackUserSession(ctx, user.ID, sessionID); err != nil {
|
||||
// Log the error but don't fail the login if session tracking fails
|
||||
// This ensures backward compatibility
|
||||
slog.Error("failed to track user session", "error", err)
|
||||
}
|
||||
|
||||
// Set session cookie for web use
|
||||
sessionCookie, err := s.buildSessionCookie(ctx, sessionID, expireTime)
|
||||
// 1. Generates refresh token and access token.
|
||||
// 2. Stores refresh token metadata in user_setting.
|
||||
// 3. Sets refresh token as HttpOnly cookie.
|
||||
// 4. Returns access token and its expiry time.
|
||||
func (s *APIV1Service) doSignIn(ctx context.Context, user *store.User) (string, time.Time, error) {
|
||||
// Generate refresh token
|
||||
tokenID := util.GenUUID()
|
||||
refreshToken, refreshExpiresAt, err := auth.GenerateRefreshToken(user.ID, tokenID, []byte(s.Secret))
|
||||
if err != nil {
|
||||
return status.Errorf(codes.Internal, "failed to build session cookie, error: %v", err)
|
||||
}
|
||||
if err := SetResponseHeader(ctx, "Set-Cookie", sessionCookie); err != nil {
|
||||
return status.Errorf(codes.Internal, "failed to set response header, error: %v", err)
|
||||
return "", time.Time{}, status.Errorf(codes.Internal, "failed to generate refresh token: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
// Store refresh token metadata
|
||||
clientInfo := s.extractClientInfo(ctx)
|
||||
refreshTokenRecord := &storepb.RefreshTokensUserSetting_RefreshToken{
|
||||
TokenId: tokenID,
|
||||
ExpiresAt: timestamppb.New(refreshExpiresAt),
|
||||
CreatedAt: timestamppb.Now(),
|
||||
ClientInfo: clientInfo,
|
||||
}
|
||||
if err := s.Store.AddUserRefreshToken(ctx, user.ID, refreshTokenRecord); err != nil {
|
||||
slog.Error("failed to store refresh token", "error", err)
|
||||
}
|
||||
|
||||
// Set refresh token cookie
|
||||
refreshCookie := s.buildRefreshTokenCookie(ctx, refreshToken, refreshExpiresAt)
|
||||
if err := SetResponseHeader(ctx, "Set-Cookie", refreshCookie); err != nil {
|
||||
return "", time.Time{}, status.Errorf(codes.Internal, "failed to set refresh token cookie: %v", err)
|
||||
}
|
||||
|
||||
// Generate access token
|
||||
accessToken, accessExpiresAt, err := auth.GenerateAccessTokenV2(
|
||||
user.ID,
|
||||
user.Username,
|
||||
string(user.Role),
|
||||
string(user.RowStatus),
|
||||
[]byte(s.Secret),
|
||||
)
|
||||
if err != nil {
|
||||
return "", time.Time{}, status.Errorf(codes.Internal, "failed to generate access token: %v", err)
|
||||
}
|
||||
|
||||
return accessToken, accessExpiresAt, nil
|
||||
}
|
||||
|
||||
// DeleteSession terminates the current user session (logout).
|
||||
// SignOut terminates the user's authentication.
|
||||
// Revokes the refresh token and clears the authentication cookie.
|
||||
//
|
||||
// This endpoint:
|
||||
// 1. Removes the session from the user's sessions list in the database
|
||||
// 2. Clears the session cookie by setting it to expire immediately
|
||||
//
|
||||
// Authentication: Required (session cookie or access token)
|
||||
// Authentication: Required (access token).
|
||||
// Returns: Empty response on success.
|
||||
func (s *APIV1Service) DeleteSession(ctx context.Context, _ *v1pb.DeleteSessionRequest) (*emptypb.Empty, error) {
|
||||
user, err := s.GetCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
|
||||
}
|
||||
if user == nil {
|
||||
return nil, status.Errorf(codes.Unauthenticated, "user not found")
|
||||
}
|
||||
|
||||
// Check if we have a session ID (from cookie-based auth)
|
||||
if sessionID := auth.GetSessionID(ctx); sessionID != "" {
|
||||
// Remove session from user settings
|
||||
if err := s.Store.RemoveUserSession(ctx, user.ID, sessionID); err != nil {
|
||||
slog.Error("failed to remove user session", "error", err)
|
||||
func (s *APIV1Service) SignOut(ctx context.Context, _ *v1pb.SignOutRequest) (*emptypb.Empty, error) {
|
||||
// Get user from access token claims
|
||||
claims := auth.GetUserClaims(ctx)
|
||||
if claims != nil {
|
||||
// Revoke refresh token if we can identify it
|
||||
refreshToken := ""
|
||||
if md, ok := metadata.FromIncomingContext(ctx); ok {
|
||||
if cookies := md.Get("cookie"); len(cookies) > 0 {
|
||||
refreshToken = auth.ExtractRefreshTokenFromCookie(cookies[0])
|
||||
}
|
||||
}
|
||||
if refreshToken != "" {
|
||||
refreshClaims, err := auth.ParseRefreshToken(refreshToken, []byte(s.Secret))
|
||||
if err == nil {
|
||||
// Remove refresh token from user_setting by token_id
|
||||
_ = s.Store.RemoveUserRefreshToken(ctx, claims.UserID, refreshClaims.TokenID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear refresh token cookie
|
||||
if err := s.clearAuthCookies(ctx); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to clear auth cookies, error: %v", err)
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *APIV1Service) clearAuthCookies(ctx context.Context) error {
|
||||
// Clear session cookie
|
||||
sessionCookie, err := s.buildSessionCookie(ctx, "", time.Time{})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to build session cookie")
|
||||
// RefreshToken exchanges a valid refresh token for a new access token.
|
||||
//
|
||||
// This endpoint:
|
||||
// 1. Extracts the refresh token from the HttpOnly cookie (memos_refresh)
|
||||
// 2. Validates the refresh token against the database (checking expiry and revocation)
|
||||
// 3. Generates a new short-lived access token (15 minutes)
|
||||
// 4. Returns the new access token and its expiry time
|
||||
//
|
||||
// The refresh token remains valid and is not rotated.
|
||||
// Client should store the new access token in memory and use it for API requests.
|
||||
//
|
||||
// Authentication: Requires valid refresh token in cookie (public endpoint)
|
||||
// Returns: New access token and expiry timestamp.
|
||||
func (s *APIV1Service) RefreshToken(ctx context.Context, _ *v1pb.RefreshTokenRequest) (*v1pb.RefreshTokenResponse, error) {
|
||||
// Extract refresh token from cookie
|
||||
refreshToken := ""
|
||||
if md, ok := metadata.FromIncomingContext(ctx); ok {
|
||||
if cookies := md.Get("cookie"); len(cookies) > 0 {
|
||||
refreshToken = auth.ExtractRefreshTokenFromCookie(cookies[0])
|
||||
}
|
||||
}
|
||||
|
||||
// Set cookie in the response
|
||||
if err := SetResponseHeader(ctx, "Set-Cookie", sessionCookie); err != nil {
|
||||
return errors.Wrap(err, "failed to set response header")
|
||||
if refreshToken == "" {
|
||||
return nil, status.Errorf(codes.Unauthenticated, "refresh token not found")
|
||||
}
|
||||
|
||||
// Validate refresh token
|
||||
authenticator := auth.NewAuthenticator(s.Store, s.Secret)
|
||||
user, _, err := authenticator.AuthenticateByRefreshToken(ctx, refreshToken)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Unauthenticated, "invalid refresh token: %v", err)
|
||||
}
|
||||
|
||||
// Generate new access token
|
||||
accessToken, expiresAt, err := auth.GenerateAccessTokenV2(
|
||||
user.ID,
|
||||
user.Username,
|
||||
string(user.Role),
|
||||
string(user.RowStatus),
|
||||
[]byte(s.Secret),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to generate access token: %v", err)
|
||||
}
|
||||
|
||||
return &v1pb.RefreshTokenResponse{
|
||||
AccessToken: accessToken,
|
||||
ExpiresAt: timestamppb.New(expiresAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *APIV1Service) clearAuthCookies(ctx context.Context) error {
|
||||
// Clear refresh token cookie
|
||||
refreshCookie := s.buildRefreshTokenCookie(ctx, "", time.Time{})
|
||||
if err := SetResponseHeader(ctx, "Set-Cookie", refreshCookie); err != nil {
|
||||
return errors.Wrap(err, "failed to set refresh cookie")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*APIV1Service) buildSessionCookie(ctx context.Context, sessionCookieValue string, expireTime time.Time) (string, error) {
|
||||
func (*APIV1Service) buildRefreshTokenCookie(ctx context.Context, refreshToken string, expireTime time.Time) string {
|
||||
attrs := []string{
|
||||
fmt.Sprintf("%s=%s", auth.SessionCookieName, sessionCookieValue),
|
||||
fmt.Sprintf("%s=%s", auth.RefreshTokenCookieName, refreshToken),
|
||||
"Path=/",
|
||||
"HttpOnly",
|
||||
}
|
||||
|
|
@ -300,7 +343,7 @@ func (*APIV1Service) buildSessionCookie(ctx context.Context, sessionCookieValue
|
|||
}
|
||||
|
||||
// Try to determine if the request is HTTPS by checking the origin header
|
||||
// Default to non-HTTPS (Strict SameSite) if metadata is not available
|
||||
// Default to non-HTTPS (Lax SameSite) if metadata is not available
|
||||
isHTTPS := false
|
||||
if md, ok := metadata.FromIncomingContext(ctx); ok {
|
||||
for _, v := range md.Get("origin") {
|
||||
|
|
@ -312,15 +355,14 @@ func (*APIV1Service) buildSessionCookie(ctx context.Context, sessionCookieValue
|
|||
}
|
||||
|
||||
if isHTTPS {
|
||||
attrs = append(attrs, "SameSite=None")
|
||||
attrs = append(attrs, "Secure")
|
||||
attrs = append(attrs, "SameSite=Lax", "Secure")
|
||||
} else {
|
||||
attrs = append(attrs, "SameSite=Strict")
|
||||
attrs = append(attrs, "SameSite=Lax")
|
||||
}
|
||||
return strings.Join(attrs, "; "), nil
|
||||
return strings.Join(attrs, "; ")
|
||||
}
|
||||
|
||||
func (s *APIV1Service) GetCurrentUser(ctx context.Context) (*store.User, error) {
|
||||
func (s *APIV1Service) fetchCurrentUser(ctx context.Context) (*store.User, error) {
|
||||
userID := auth.GetUserID(ctx)
|
||||
if userID == 0 {
|
||||
return nil, nil
|
||||
|
|
@ -337,27 +379,6 @@ func (s *APIV1Service) GetCurrentUser(ctx context.Context) (*store.User, error)
|
|||
return user, nil
|
||||
}
|
||||
|
||||
// trackUserSession creates a new session record in the user's settings.
|
||||
//
|
||||
// Session information includes:
|
||||
// - session_id: Unique UUID for this session
|
||||
// - create_time: When the session was created
|
||||
// - last_accessed_time: When the session was last used (for sliding expiration)
|
||||
// - client_info: Device details (user agent, IP, device type, OS, browser).
|
||||
func (s *APIV1Service) trackUserSession(ctx context.Context, userID int32, sessionID string) error {
|
||||
// Extract client information from the context
|
||||
clientInfo := s.extractClientInfo(ctx)
|
||||
|
||||
session := &storepb.SessionsUserSetting_Session{
|
||||
SessionId: sessionID,
|
||||
CreateTime: timestamppb.Now(),
|
||||
LastAccessedTime: timestamppb.Now(),
|
||||
ClientInfo: clientInfo,
|
||||
}
|
||||
|
||||
return s.Store.AddUserSession(ctx, userID, session)
|
||||
}
|
||||
|
||||
// extractClientInfo extracts comprehensive client information from the request context.
|
||||
//
|
||||
// This function parses metadata from the gRPC context to extract:
|
||||
|
|
|
|||
|
|
@ -43,6 +43,10 @@ func (*MetadataInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFunc
|
|||
if xri := header.Get("X-Real-Ip"); xri != "" {
|
||||
md.Set("x-real-ip", xri)
|
||||
}
|
||||
// Forward Cookie header for authentication methods that need it (e.g., RefreshToken)
|
||||
if cookie := header.Get("Cookie"); cookie != "" {
|
||||
md.Set("cookie", cookie)
|
||||
}
|
||||
|
||||
// Set metadata in context so services can use metadata.FromIncomingContext()
|
||||
ctx = metadata.NewIncomingContext(ctx, md)
|
||||
|
|
@ -198,9 +202,16 @@ func (in *AuthInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFunc {
|
|||
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("authentication required"))
|
||||
}
|
||||
|
||||
// Set user in context (may be nil for public endpoints)
|
||||
// Set context based on auth result
|
||||
if result != nil {
|
||||
ctx = auth.SetUserInContext(ctx, result.User, result.SessionID, result.AccessToken)
|
||||
if result.Claims != nil {
|
||||
// Access Token V2 - stateless, use claims
|
||||
ctx = auth.SetUserClaimsInContext(ctx, result.Claims)
|
||||
ctx = context.WithValue(ctx, auth.UserIDContextKey, result.Claims.UserID)
|
||||
} else if result.User != nil {
|
||||
// PAT or legacy auth - have full user
|
||||
ctx = auth.SetUserInContext(ctx, result.User, result.SessionID, result.AccessToken)
|
||||
}
|
||||
}
|
||||
|
||||
return next(ctx, req)
|
||||
|
|
|
|||
|
|
@ -45,21 +45,27 @@ func (s *ConnectServiceHandler) UpdateInstanceSetting(ctx context.Context, req *
|
|||
// We use connectWithHeaderCarrier helper to inject a header carrier into the context,
|
||||
// which allows the service to set headers in a protocol-agnostic way.
|
||||
|
||||
func (s *ConnectServiceHandler) GetCurrentSession(ctx context.Context, req *connect.Request[v1pb.GetCurrentSessionRequest]) (*connect.Response[v1pb.GetCurrentSessionResponse], error) {
|
||||
return connectWithHeaderCarrier(ctx, func(ctx context.Context) (*v1pb.GetCurrentSessionResponse, error) {
|
||||
return s.APIV1Service.GetCurrentSession(ctx, req.Msg)
|
||||
func (s *ConnectServiceHandler) GetCurrentUser(ctx context.Context, req *connect.Request[v1pb.GetCurrentUserRequest]) (*connect.Response[v1pb.GetCurrentUserResponse], error) {
|
||||
return connectWithHeaderCarrier(ctx, func(ctx context.Context) (*v1pb.GetCurrentUserResponse, error) {
|
||||
return s.APIV1Service.GetCurrentUser(ctx, req.Msg)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *ConnectServiceHandler) CreateSession(ctx context.Context, req *connect.Request[v1pb.CreateSessionRequest]) (*connect.Response[v1pb.CreateSessionResponse], error) {
|
||||
return connectWithHeaderCarrier(ctx, func(ctx context.Context) (*v1pb.CreateSessionResponse, error) {
|
||||
return s.APIV1Service.CreateSession(ctx, req.Msg)
|
||||
func (s *ConnectServiceHandler) SignIn(ctx context.Context, req *connect.Request[v1pb.SignInRequest]) (*connect.Response[v1pb.SignInResponse], error) {
|
||||
return connectWithHeaderCarrier(ctx, func(ctx context.Context) (*v1pb.SignInResponse, error) {
|
||||
return s.APIV1Service.SignIn(ctx, req.Msg)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *ConnectServiceHandler) DeleteSession(ctx context.Context, req *connect.Request[v1pb.DeleteSessionRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
func (s *ConnectServiceHandler) SignOut(ctx context.Context, req *connect.Request[v1pb.SignOutRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
return connectWithHeaderCarrier(ctx, func(ctx context.Context) (*emptypb.Empty, error) {
|
||||
return s.APIV1Service.DeleteSession(ctx, req.Msg)
|
||||
return s.APIV1Service.SignOut(ctx, req.Msg)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *ConnectServiceHandler) RefreshToken(ctx context.Context, req *connect.Request[v1pb.RefreshTokenRequest]) (*connect.Response[v1pb.RefreshTokenResponse], error) {
|
||||
return connectWithHeaderCarrier(ctx, func(ctx context.Context) (*v1pb.RefreshTokenResponse, error) {
|
||||
return s.APIV1Service.RefreshToken(ctx, req.Msg)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -145,40 +151,40 @@ func (s *ConnectServiceHandler) ListUserSettings(ctx context.Context, req *conne
|
|||
return connect.NewResponse(resp), nil
|
||||
}
|
||||
|
||||
func (s *ConnectServiceHandler) ListUserAccessTokens(ctx context.Context, req *connect.Request[v1pb.ListUserAccessTokensRequest]) (*connect.Response[v1pb.ListUserAccessTokensResponse], error) {
|
||||
resp, err := s.APIV1Service.ListUserAccessTokens(ctx, req.Msg)
|
||||
func (s *ConnectServiceHandler) ListPersonalAccessTokens(ctx context.Context, req *connect.Request[v1pb.ListPersonalAccessTokensRequest]) (*connect.Response[v1pb.ListPersonalAccessTokensResponse], error) {
|
||||
resp, err := s.APIV1Service.ListPersonalAccessTokens(ctx, req.Msg)
|
||||
if err != nil {
|
||||
return nil, convertGRPCError(err)
|
||||
}
|
||||
return connect.NewResponse(resp), nil
|
||||
}
|
||||
|
||||
func (s *ConnectServiceHandler) CreateUserAccessToken(ctx context.Context, req *connect.Request[v1pb.CreateUserAccessTokenRequest]) (*connect.Response[v1pb.UserAccessToken], error) {
|
||||
resp, err := s.APIV1Service.CreateUserAccessToken(ctx, req.Msg)
|
||||
func (s *ConnectServiceHandler) CreatePersonalAccessToken(ctx context.Context, req *connect.Request[v1pb.CreatePersonalAccessTokenRequest]) (*connect.Response[v1pb.CreatePersonalAccessTokenResponse], error) {
|
||||
resp, err := s.APIV1Service.CreatePersonalAccessToken(ctx, req.Msg)
|
||||
if err != nil {
|
||||
return nil, convertGRPCError(err)
|
||||
}
|
||||
return connect.NewResponse(resp), nil
|
||||
}
|
||||
|
||||
func (s *ConnectServiceHandler) DeleteUserAccessToken(ctx context.Context, req *connect.Request[v1pb.DeleteUserAccessTokenRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
resp, err := s.APIV1Service.DeleteUserAccessToken(ctx, req.Msg)
|
||||
func (s *ConnectServiceHandler) DeletePersonalAccessToken(ctx context.Context, req *connect.Request[v1pb.DeletePersonalAccessTokenRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
resp, err := s.APIV1Service.DeletePersonalAccessToken(ctx, req.Msg)
|
||||
if err != nil {
|
||||
return nil, convertGRPCError(err)
|
||||
}
|
||||
return connect.NewResponse(resp), nil
|
||||
}
|
||||
|
||||
func (s *ConnectServiceHandler) ListUserSessions(ctx context.Context, req *connect.Request[v1pb.ListUserSessionsRequest]) (*connect.Response[v1pb.ListUserSessionsResponse], error) {
|
||||
resp, err := s.APIV1Service.ListUserSessions(ctx, req.Msg)
|
||||
func (s *ConnectServiceHandler) ListSessions(ctx context.Context, req *connect.Request[v1pb.ListSessionsRequest]) (*connect.Response[v1pb.ListSessionsResponse], error) {
|
||||
resp, err := s.APIV1Service.ListSessions(ctx, req.Msg)
|
||||
if err != nil {
|
||||
return nil, convertGRPCError(err)
|
||||
}
|
||||
return connect.NewResponse(resp), nil
|
||||
}
|
||||
|
||||
func (s *ConnectServiceHandler) RevokeUserSession(ctx context.Context, req *connect.Request[v1pb.RevokeUserSessionRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
resp, err := s.APIV1Service.RevokeUserSession(ctx, req.Msg)
|
||||
func (s *ConnectServiceHandler) RevokeSession(ctx context.Context, req *connect.Request[v1pb.RevokeSessionRequest]) (*connect.Response[emptypb.Empty], error) {
|
||||
resp, err := s.APIV1Service.RevokeSession(ctx, req.Msg)
|
||||
if err != nil {
|
||||
return nil, convertGRPCError(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import (
|
|||
)
|
||||
|
||||
func (s *APIV1Service) CreateIdentityProvider(ctx context.Context, request *v1pb.CreateIdentityProviderRequest) (*v1pb.IdentityProvider, error) {
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get user: %v", err)
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ func (s *APIV1Service) ListIdentityProviders(ctx context.Context, _ *v1pb.ListId
|
|||
|
||||
// Default to lowest-privilege role, update later based on real role
|
||||
currentUserRole := store.RoleUser
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err == nil && currentUser != nil {
|
||||
currentUserRole = currentUser.Role
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ func (s *APIV1Service) GetIdentityProvider(ctx context.Context, request *v1pb.Ge
|
|||
|
||||
// Default to lowest-privilege role, update later based on real role
|
||||
currentUserRole := store.RoleUser
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err == nil && currentUser != nil {
|
||||
currentUserRole = currentUser.Role
|
||||
}
|
||||
|
|
@ -80,7 +80,7 @@ func (s *APIV1Service) GetIdentityProvider(ctx context.Context, request *v1pb.Ge
|
|||
}
|
||||
|
||||
func (s *APIV1Service) UpdateIdentityProvider(ctx context.Context, request *v1pb.UpdateIdentityProviderRequest) (*v1pb.IdentityProvider, error) {
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get user: %v", err)
|
||||
}
|
||||
|
|
@ -121,7 +121,7 @@ func (s *APIV1Service) UpdateIdentityProvider(ctx context.Context, request *v1pb
|
|||
}
|
||||
|
||||
func (s *APIV1Service) DeleteIdentityProvider(ctx context.Context, request *v1pb.DeleteIdentityProviderRequest) (*emptypb.Empty, error) {
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get user: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ func (s *APIV1Service) GetInstanceSetting(ctx context.Context, request *v1pb.Get
|
|||
|
||||
// For storage setting, only host can get it.
|
||||
if instanceSetting.Key == storepb.InstanceSettingKey_STORAGE {
|
||||
user, err := s.GetCurrentUser(ctx)
|
||||
user, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -79,7 +79,7 @@ func (s *APIV1Service) GetInstanceSetting(ctx context.Context, request *v1pb.Get
|
|||
}
|
||||
|
||||
func (s *APIV1Service) UpdateInstanceSetting(ctx context.Context, request *v1pb.UpdateInstanceSettingRequest) (*v1pb.InstanceSetting, error) {
|
||||
user, err := s.GetCurrentUser(ctx)
|
||||
user, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import (
|
|||
)
|
||||
|
||||
func (s *APIV1Service) SetMemoAttachments(ctx context.Context, request *v1pb.SetMemoAttachmentsRequest) (*emptypb.Empty, error) {
|
||||
user, err := s.GetCurrentUser(ctx)
|
||||
user, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import (
|
|||
)
|
||||
|
||||
func (s *APIV1Service) SetMemoRelations(ctx context.Context, request *v1pb.SetMemoRelationsRequest) (*emptypb.Empty, error) {
|
||||
user, err := s.GetCurrentUser(ctx)
|
||||
user, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -84,7 +84,7 @@ func (s *APIV1Service) ListMemoRelations(ctx context.Context, request *v1pb.List
|
|||
return nil, status.Errorf(codes.Internal, "failed to get memo")
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get user")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import (
|
|||
)
|
||||
|
||||
func (s *APIV1Service) CreateMemo(ctx context.Context, request *v1pb.CreateMemoRequest) (*v1pb.Memo, error) {
|
||||
user, err := s.GetCurrentUser(ctx)
|
||||
user, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get user")
|
||||
}
|
||||
|
|
@ -149,7 +149,7 @@ func (s *APIV1Service) ListMemos(ctx context.Context, request *v1pb.ListMemosReq
|
|||
memoFind.Filters = append(memoFind.Filters, request.Filter)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get user")
|
||||
}
|
||||
|
|
@ -276,7 +276,7 @@ func (s *APIV1Service) GetMemo(ctx context.Context, request *v1pb.GetMemoRequest
|
|||
return nil, status.Errorf(codes.NotFound, "memo not found")
|
||||
}
|
||||
if memo.Visibility != store.Public {
|
||||
user, err := s.GetCurrentUser(ctx)
|
||||
user, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get user")
|
||||
}
|
||||
|
|
@ -326,7 +326,7 @@ func (s *APIV1Service) UpdateMemo(ctx context.Context, request *v1pb.UpdateMemoR
|
|||
return nil, status.Errorf(codes.NotFound, "memo not found")
|
||||
}
|
||||
|
||||
user, err := s.GetCurrentUser(ctx)
|
||||
user, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user")
|
||||
}
|
||||
|
|
@ -464,7 +464,7 @@ func (s *APIV1Service) DeleteMemo(ctx context.Context, request *v1pb.DeleteMemoR
|
|||
return nil, status.Errorf(codes.NotFound, "memo not found")
|
||||
}
|
||||
|
||||
user, err := s.GetCurrentUser(ctx)
|
||||
user, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user")
|
||||
}
|
||||
|
|
@ -615,7 +615,7 @@ func (s *APIV1Service) ListMemoComments(ctx context.Context, request *v1pb.ListM
|
|||
return nil, status.Errorf(codes.Internal, "failed to get memo")
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get user")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ func (s *APIV1Service) ListMemoReactions(ctx context.Context, request *v1pb.List
|
|||
}
|
||||
|
||||
func (s *APIV1Service) UpsertMemoReaction(ctx context.Context, request *v1pb.UpsertMemoReactionRequest) (*v1pb.Reaction, error) {
|
||||
user, err := s.GetCurrentUser(ctx)
|
||||
user, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user")
|
||||
}
|
||||
|
|
@ -55,7 +55,7 @@ func (s *APIV1Service) UpsertMemoReaction(ctx context.Context, request *v1pb.Ups
|
|||
}
|
||||
|
||||
func (s *APIV1Service) DeleteMemoReaction(ctx context.Context, request *v1pb.DeleteMemoReactionRequest) (*emptypb.Empty, error) {
|
||||
user, err := s.GetCurrentUser(ctx)
|
||||
user, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ func (s *APIV1Service) ListShortcuts(ctx context.Context, request *v1pb.ListShor
|
|||
return nil, status.Errorf(codes.InvalidArgument, "invalid user name: %v", err)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -91,7 +91,7 @@ func (s *APIV1Service) GetShortcut(ctx context.Context, request *v1pb.GetShortcu
|
|||
return nil, status.Errorf(codes.InvalidArgument, "invalid shortcut name: %v", err)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -130,7 +130,7 @@ func (s *APIV1Service) CreateShortcut(ctx context.Context, request *v1pb.CreateS
|
|||
return nil, status.Errorf(codes.InvalidArgument, "invalid user name: %v", err)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -202,7 +202,7 @@ func (s *APIV1Service) UpdateShortcut(ctx context.Context, request *v1pb.UpdateS
|
|||
return nil, status.Errorf(codes.InvalidArgument, "invalid shortcut name: %v", err)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -274,7 +274,7 @@ func (s *APIV1Service) DeleteShortcut(ctx context.Context, request *v1pb.DeleteS
|
|||
return nil, status.Errorf(codes.InvalidArgument, "invalid shortcut name: %v", err)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,655 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/usememos/memos/internal/util"
|
||||
storepb "github.com/usememos/memos/proto/gen/store"
|
||||
"github.com/usememos/memos/server/auth"
|
||||
"github.com/usememos/memos/store"
|
||||
)
|
||||
|
||||
func TestAuthenticatorAccessTokenV2(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("authenticates valid access token v2", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
// Create a test user
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Generate access token v2
|
||||
token, _, err := auth.GenerateAccessTokenV2(
|
||||
user.ID,
|
||||
user.Username,
|
||||
string(user.Role),
|
||||
string(user.RowStatus),
|
||||
[]byte(ts.Secret),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Authenticate
|
||||
authenticator := auth.NewAuthenticator(ts.Store, ts.Secret)
|
||||
claims, err := authenticator.AuthenticateByAccessTokenV2(token)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, claims)
|
||||
assert.Equal(t, user.ID, claims.UserID)
|
||||
assert.Equal(t, user.Username, claims.Username)
|
||||
assert.Equal(t, string(user.Role), claims.Role)
|
||||
assert.Equal(t, string(user.RowStatus), claims.Status)
|
||||
})
|
||||
|
||||
t.Run("fails with invalid token", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
authenticator := auth.NewAuthenticator(ts.Store, ts.Secret)
|
||||
_, err := authenticator.AuthenticateByAccessTokenV2("invalid-token")
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("fails with wrong secret", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Generate token with one secret
|
||||
token, _, err := auth.GenerateAccessTokenV2(
|
||||
user.ID,
|
||||
user.Username,
|
||||
string(user.Role),
|
||||
string(user.RowStatus),
|
||||
[]byte("secret-1"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Try to authenticate with different secret
|
||||
authenticator := auth.NewAuthenticator(ts.Store, "secret-2")
|
||||
_, err = authenticator.AuthenticateByAccessTokenV2(token)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAuthenticatorRefreshToken(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("authenticates valid refresh token", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
// Create a test user
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create refresh token record in store
|
||||
tokenID := util.GenUUID()
|
||||
refreshTokenRecord := &storepb.RefreshTokensUserSetting_RefreshToken{
|
||||
TokenId: tokenID,
|
||||
ExpiresAt: timestamppb.New(time.Now().Add(auth.RefreshTokenDuration)),
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
err = ts.Store.AddUserRefreshToken(ctx, user.ID, refreshTokenRecord)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Generate refresh token JWT
|
||||
token, _, err := auth.GenerateRefreshToken(user.ID, tokenID, []byte(ts.Secret))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Authenticate
|
||||
authenticator := auth.NewAuthenticator(ts.Store, ts.Secret)
|
||||
authenticatedUser, returnedTokenID, err := authenticator.AuthenticateByRefreshToken(ctx, token)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, authenticatedUser)
|
||||
assert.Equal(t, user.ID, authenticatedUser.ID)
|
||||
assert.Equal(t, tokenID, returnedTokenID)
|
||||
})
|
||||
|
||||
t.Run("fails with revoked token", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
tokenID := util.GenUUID()
|
||||
|
||||
// Generate refresh token JWT but don't store it in database (simulates revocation)
|
||||
token, _, err := auth.GenerateRefreshToken(user.ID, tokenID, []byte(ts.Secret))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Try to authenticate
|
||||
authenticator := auth.NewAuthenticator(ts.Store, ts.Secret)
|
||||
_, _, err = authenticator.AuthenticateByRefreshToken(ctx, token)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "revoked")
|
||||
})
|
||||
|
||||
t.Run("fails with expired token", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create expired refresh token record in store
|
||||
tokenID := util.GenUUID()
|
||||
expiredToken := &storepb.RefreshTokensUserSetting_RefreshToken{
|
||||
TokenId: tokenID,
|
||||
ExpiresAt: timestamppb.New(time.Now().Add(-1 * time.Hour)), // Expired
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
err = ts.Store.AddUserRefreshToken(ctx, user.ID, expiredToken)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Generate refresh token JWT (JWT itself isn't expired yet)
|
||||
token, _, err := auth.GenerateRefreshToken(user.ID, tokenID, []byte(ts.Secret))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Try to authenticate
|
||||
authenticator := auth.NewAuthenticator(ts.Store, ts.Secret)
|
||||
_, _, err = authenticator.AuthenticateByRefreshToken(ctx, token)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "expired")
|
||||
})
|
||||
|
||||
t.Run("fails with archived user", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create valid refresh token
|
||||
tokenID := util.GenUUID()
|
||||
refreshTokenRecord := &storepb.RefreshTokensUserSetting_RefreshToken{
|
||||
TokenId: tokenID,
|
||||
ExpiresAt: timestamppb.New(time.Now().Add(auth.RefreshTokenDuration)),
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
err = ts.Store.AddUserRefreshToken(ctx, user.ID, refreshTokenRecord)
|
||||
require.NoError(t, err)
|
||||
|
||||
token, _, err := auth.GenerateRefreshToken(user.ID, tokenID, []byte(ts.Secret))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Archive the user
|
||||
archivedStatus := store.Archived
|
||||
_, err = ts.Store.UpdateUser(ctx, &store.UpdateUser{
|
||||
ID: user.ID,
|
||||
RowStatus: &archivedStatus,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Try to authenticate
|
||||
authenticator := auth.NewAuthenticator(ts.Store, ts.Secret)
|
||||
_, _, err = authenticator.AuthenticateByRefreshToken(ctx, token)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "archived")
|
||||
})
|
||||
}
|
||||
|
||||
func TestAuthenticatorPAT(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("authenticates valid PAT", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
// Create a test user
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Generate PAT
|
||||
token := auth.GeneratePersonalAccessToken()
|
||||
tokenHash := auth.HashPersonalAccessToken(token)
|
||||
tokenID := util.GenUUID()
|
||||
|
||||
// Store PAT in database
|
||||
patRecord := &storepb.PersonalAccessTokensUserSetting_PersonalAccessToken{
|
||||
TokenId: tokenID,
|
||||
TokenHash: tokenHash,
|
||||
Description: "Test PAT",
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
err = ts.Store.AddUserPersonalAccessToken(ctx, user.ID, patRecord)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Authenticate
|
||||
authenticator := auth.NewAuthenticator(ts.Store, ts.Secret)
|
||||
authenticatedUser, pat, err := authenticator.AuthenticateByPAT(ctx, token)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, authenticatedUser)
|
||||
assert.NotNil(t, pat)
|
||||
assert.Equal(t, user.ID, authenticatedUser.ID)
|
||||
assert.Equal(t, tokenID, pat.TokenId)
|
||||
})
|
||||
|
||||
t.Run("fails with invalid PAT format", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
authenticator := auth.NewAuthenticator(ts.Store, ts.Secret)
|
||||
_, _, err := authenticator.AuthenticateByPAT(ctx, "invalid-token-without-prefix")
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid PAT format")
|
||||
})
|
||||
|
||||
t.Run("fails with non-existent PAT", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
// Generate a PAT but don't store it
|
||||
token := auth.GeneratePersonalAccessToken()
|
||||
|
||||
authenticator := auth.NewAuthenticator(ts.Store, ts.Secret)
|
||||
_, _, err := authenticator.AuthenticateByPAT(ctx, token)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("fails with expired PAT", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Generate and store expired PAT
|
||||
token := auth.GeneratePersonalAccessToken()
|
||||
tokenHash := auth.HashPersonalAccessToken(token)
|
||||
tokenID := util.GenUUID()
|
||||
|
||||
expiredPAT := &storepb.PersonalAccessTokensUserSetting_PersonalAccessToken{
|
||||
TokenId: tokenID,
|
||||
TokenHash: tokenHash,
|
||||
Description: "Expired PAT",
|
||||
ExpiresAt: timestamppb.New(time.Now().Add(-1 * time.Hour)), // Expired
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
err = ts.Store.AddUserPersonalAccessToken(ctx, user.ID, expiredPAT)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Try to authenticate
|
||||
authenticator := auth.NewAuthenticator(ts.Store, ts.Secret)
|
||||
_, _, err = authenticator.AuthenticateByPAT(ctx, token)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "expired")
|
||||
})
|
||||
|
||||
t.Run("succeeds with non-expiring PAT", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Generate and store PAT without expiration
|
||||
token := auth.GeneratePersonalAccessToken()
|
||||
tokenHash := auth.HashPersonalAccessToken(token)
|
||||
tokenID := util.GenUUID()
|
||||
|
||||
patRecord := &storepb.PersonalAccessTokensUserSetting_PersonalAccessToken{
|
||||
TokenId: tokenID,
|
||||
TokenHash: tokenHash,
|
||||
Description: "Never-expiring PAT",
|
||||
ExpiresAt: nil, // No expiration
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
err = ts.Store.AddUserPersonalAccessToken(ctx, user.ID, patRecord)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Authenticate
|
||||
authenticator := auth.NewAuthenticator(ts.Store, ts.Secret)
|
||||
authenticatedUser, pat, err := authenticator.AuthenticateByPAT(ctx, token)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, authenticatedUser)
|
||||
assert.NotNil(t, pat)
|
||||
assert.Nil(t, pat.ExpiresAt)
|
||||
})
|
||||
|
||||
t.Run("fails with archived user", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Generate and store PAT
|
||||
token := auth.GeneratePersonalAccessToken()
|
||||
tokenHash := auth.HashPersonalAccessToken(token)
|
||||
tokenID := util.GenUUID()
|
||||
|
||||
patRecord := &storepb.PersonalAccessTokensUserSetting_PersonalAccessToken{
|
||||
TokenId: tokenID,
|
||||
TokenHash: tokenHash,
|
||||
Description: "Test PAT",
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
err = ts.Store.AddUserPersonalAccessToken(ctx, user.ID, patRecord)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Archive the user
|
||||
archivedStatus := store.Archived
|
||||
_, err = ts.Store.UpdateUser(ctx, &store.UpdateUser{
|
||||
ID: user.ID,
|
||||
RowStatus: &archivedStatus,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Try to authenticate
|
||||
authenticator := auth.NewAuthenticator(ts.Store, ts.Secret)
|
||||
_, _, err = authenticator.AuthenticateByPAT(ctx, token)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "archived")
|
||||
})
|
||||
}
|
||||
|
||||
func TestStoreRefreshTokenMethods(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("adds and retrieves refresh token", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
tokenID := util.GenUUID()
|
||||
token := &storepb.RefreshTokensUserSetting_RefreshToken{
|
||||
TokenId: tokenID,
|
||||
ExpiresAt: timestamppb.New(time.Now().Add(30 * 24 * time.Hour)),
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
|
||||
err = ts.Store.AddUserRefreshToken(ctx, user.ID, token)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Retrieve tokens
|
||||
tokens, err := ts.Store.GetUserRefreshTokens(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, tokens, 1)
|
||||
assert.Equal(t, tokenID, tokens[0].TokenId)
|
||||
})
|
||||
|
||||
t.Run("retrieves specific refresh token by ID", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
tokenID := util.GenUUID()
|
||||
token := &storepb.RefreshTokensUserSetting_RefreshToken{
|
||||
TokenId: tokenID,
|
||||
ExpiresAt: timestamppb.New(time.Now().Add(30 * 24 * time.Hour)),
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
|
||||
err = ts.Store.AddUserRefreshToken(ctx, user.ID, token)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Retrieve specific token
|
||||
retrievedToken, err := ts.Store.GetUserRefreshTokenByID(ctx, user.ID, tokenID)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, retrievedToken)
|
||||
assert.Equal(t, tokenID, retrievedToken.TokenId)
|
||||
})
|
||||
|
||||
t.Run("removes refresh token", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
tokenID := util.GenUUID()
|
||||
token := &storepb.RefreshTokensUserSetting_RefreshToken{
|
||||
TokenId: tokenID,
|
||||
ExpiresAt: timestamppb.New(time.Now().Add(30 * 24 * time.Hour)),
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
|
||||
err = ts.Store.AddUserRefreshToken(ctx, user.ID, token)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Remove token
|
||||
err = ts.Store.RemoveUserRefreshToken(ctx, user.ID, tokenID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify removal
|
||||
tokens, err := ts.Store.GetUserRefreshTokens(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, tokens, 0)
|
||||
})
|
||||
|
||||
t.Run("handles multiple refresh tokens", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add multiple tokens
|
||||
tokenID1 := util.GenUUID()
|
||||
tokenID2 := util.GenUUID()
|
||||
|
||||
token1 := &storepb.RefreshTokensUserSetting_RefreshToken{
|
||||
TokenId: tokenID1,
|
||||
ExpiresAt: timestamppb.New(time.Now().Add(30 * 24 * time.Hour)),
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
token2 := &storepb.RefreshTokensUserSetting_RefreshToken{
|
||||
TokenId: tokenID2,
|
||||
ExpiresAt: timestamppb.New(time.Now().Add(30 * 24 * time.Hour)),
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
|
||||
err = ts.Store.AddUserRefreshToken(ctx, user.ID, token1)
|
||||
require.NoError(t, err)
|
||||
err = ts.Store.AddUserRefreshToken(ctx, user.ID, token2)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Retrieve all tokens
|
||||
tokens, err := ts.Store.GetUserRefreshTokens(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, tokens, 2)
|
||||
|
||||
// Remove one token
|
||||
err = ts.Store.RemoveUserRefreshToken(ctx, user.ID, tokenID1)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify only one token remains
|
||||
tokens, err = ts.Store.GetUserRefreshTokens(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, tokens, 1)
|
||||
assert.Equal(t, tokenID2, tokens[0].TokenId)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStorePersonalAccessTokenMethods(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("adds and retrieves PAT", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
token := auth.GeneratePersonalAccessToken()
|
||||
tokenHash := auth.HashPersonalAccessToken(token)
|
||||
tokenID := util.GenUUID()
|
||||
|
||||
pat := &storepb.PersonalAccessTokensUserSetting_PersonalAccessToken{
|
||||
TokenId: tokenID,
|
||||
TokenHash: tokenHash,
|
||||
Description: "Test PAT",
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
|
||||
err = ts.Store.AddUserPersonalAccessToken(ctx, user.ID, pat)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Retrieve PATs
|
||||
pats, err := ts.Store.GetUserPersonalAccessTokens(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pats, 1)
|
||||
assert.Equal(t, tokenID, pats[0].TokenId)
|
||||
assert.Equal(t, tokenHash, pats[0].TokenHash)
|
||||
})
|
||||
|
||||
t.Run("removes PAT", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
token := auth.GeneratePersonalAccessToken()
|
||||
tokenHash := auth.HashPersonalAccessToken(token)
|
||||
tokenID := util.GenUUID()
|
||||
|
||||
pat := &storepb.PersonalAccessTokensUserSetting_PersonalAccessToken{
|
||||
TokenId: tokenID,
|
||||
TokenHash: tokenHash,
|
||||
Description: "Test PAT",
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
|
||||
err = ts.Store.AddUserPersonalAccessToken(ctx, user.ID, pat)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Remove PAT
|
||||
err = ts.Store.RemoveUserPersonalAccessToken(ctx, user.ID, tokenID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify removal
|
||||
pats, err := ts.Store.GetUserPersonalAccessTokens(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pats, 0)
|
||||
})
|
||||
|
||||
t.Run("updates PAT last used time", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
token := auth.GeneratePersonalAccessToken()
|
||||
tokenHash := auth.HashPersonalAccessToken(token)
|
||||
tokenID := util.GenUUID()
|
||||
|
||||
pat := &storepb.PersonalAccessTokensUserSetting_PersonalAccessToken{
|
||||
TokenId: tokenID,
|
||||
TokenHash: tokenHash,
|
||||
Description: "Test PAT",
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
|
||||
err = ts.Store.AddUserPersonalAccessToken(ctx, user.ID, pat)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Update last used time
|
||||
lastUsed := timestamppb.Now()
|
||||
err = ts.Store.UpdatePATLastUsed(ctx, user.ID, tokenID, lastUsed)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify update
|
||||
pats, err := ts.Store.GetUserPersonalAccessTokens(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pats, 1)
|
||||
assert.NotNil(t, pats[0].LastUsedAt)
|
||||
})
|
||||
|
||||
t.Run("handles multiple PATs", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add multiple PATs
|
||||
token1 := auth.GeneratePersonalAccessToken()
|
||||
tokenHash1 := auth.HashPersonalAccessToken(token1)
|
||||
tokenID1 := util.GenUUID()
|
||||
|
||||
token2 := auth.GeneratePersonalAccessToken()
|
||||
tokenHash2 := auth.HashPersonalAccessToken(token2)
|
||||
tokenID2 := util.GenUUID()
|
||||
|
||||
pat1 := &storepb.PersonalAccessTokensUserSetting_PersonalAccessToken{
|
||||
TokenId: tokenID1,
|
||||
TokenHash: tokenHash1,
|
||||
Description: "PAT 1",
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
pat2 := &storepb.PersonalAccessTokensUserSetting_PersonalAccessToken{
|
||||
TokenId: tokenID2,
|
||||
TokenHash: tokenHash2,
|
||||
Description: "PAT 2",
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
|
||||
err = ts.Store.AddUserPersonalAccessToken(ctx, user.ID, pat1)
|
||||
require.NoError(t, err)
|
||||
err = ts.Store.AddUserPersonalAccessToken(ctx, user.ID, pat2)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Retrieve all PATs
|
||||
pats, err := ts.Store.GetUserPersonalAccessTokens(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pats, 2)
|
||||
|
||||
// Remove one PAT
|
||||
err = ts.Store.RemoveUserPersonalAccessToken(ctx, user.ID, tokenID1)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify only one PAT remains
|
||||
pats, err = ts.Store.GetUserPersonalAccessTokens(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pats, 1)
|
||||
assert.Equal(t, tokenID2, pats[0].TokenId)
|
||||
})
|
||||
|
||||
t.Run("finds user by PAT hash", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "testuser")
|
||||
require.NoError(t, err)
|
||||
|
||||
token := auth.GeneratePersonalAccessToken()
|
||||
tokenHash := auth.HashPersonalAccessToken(token)
|
||||
tokenID := util.GenUUID()
|
||||
|
||||
pat := &storepb.PersonalAccessTokensUserSetting_PersonalAccessToken{
|
||||
TokenId: tokenID,
|
||||
TokenHash: tokenHash,
|
||||
Description: "Test PAT",
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
|
||||
err = ts.Store.AddUserPersonalAccessToken(ctx, user.ID, pat)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Find user by PAT hash
|
||||
result, err := ts.Store.GetUserByPATHash(ctx, tokenHash)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, user.ID, result.UserID)
|
||||
assert.NotNil(t, result.User)
|
||||
assert.Equal(t, user.Username, result.User.Username)
|
||||
assert.NotNil(t, result.PAT)
|
||||
assert.Equal(t, tokenID, result.PAT.TokenId)
|
||||
})
|
||||
}
|
||||
|
|
@ -7,12 +7,10 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/cel-go/cel"
|
||||
"github.com/google/cel-go/common/ast"
|
||||
"github.com/labstack/echo/v4"
|
||||
|
|
@ -32,7 +30,7 @@ import (
|
|||
)
|
||||
|
||||
func (s *APIV1Service) ListUsers(ctx context.Context, request *v1pb.ListUsersRequest) (*v1pb.ListUsersResponse, error) {
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get user: %v", err)
|
||||
}
|
||||
|
|
@ -107,7 +105,7 @@ func (s *APIV1Service) GetUser(ctx context.Context, request *v1pb.GetUserRequest
|
|||
|
||||
func (s *APIV1Service) CreateUser(ctx context.Context, request *v1pb.CreateUserRequest) (*v1pb.User, error) {
|
||||
// Get current user (might be nil for unauthenticated requests)
|
||||
currentUser, _ := s.GetCurrentUser(ctx)
|
||||
currentUser, _ := s.fetchCurrentUser(ctx)
|
||||
|
||||
// Check if there are any existing users (for first-time setup detection)
|
||||
limitOne := 1
|
||||
|
|
@ -190,7 +188,7 @@ func (s *APIV1Service) UpdateUser(ctx context.Context, request *v1pb.UpdateUserR
|
|||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid user name: %v", err)
|
||||
}
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get user: %v", err)
|
||||
}
|
||||
|
|
@ -278,7 +276,7 @@ func (s *APIV1Service) DeleteUser(ctx context.Context, request *v1pb.DeleteUserR
|
|||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid user name: %v", err)
|
||||
}
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get user: %v", err)
|
||||
}
|
||||
|
|
@ -318,7 +316,7 @@ func (s *APIV1Service) GetUserSetting(ctx context.Context, request *v1pb.GetUser
|
|||
return nil, status.Errorf(codes.InvalidArgument, "invalid resource name: %v", err)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -355,7 +353,7 @@ func (s *APIV1Service) UpdateUserSetting(ctx context.Context, request *v1pb.Upda
|
|||
return nil, status.Errorf(codes.InvalidArgument, "invalid resource name: %v", err)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -444,7 +442,7 @@ func (s *APIV1Service) ListUserSettings(ctx context.Context, request *v1pb.ListU
|
|||
return nil, status.Errorf(codes.InvalidArgument, "invalid parent name: %v", err)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -498,7 +496,7 @@ func (s *APIV1Service) ListUserSettings(ctx context.Context, request *v1pb.ListU
|
|||
return response, nil
|
||||
}
|
||||
|
||||
// ListUserAccessTokens retrieves all Personal Access Tokens (PATs) for a user.
|
||||
// ListPersonalAccessTokens retrieves all Personal Access Tokens (PATs) for a user.
|
||||
//
|
||||
// Personal Access Tokens are used for:
|
||||
// - Mobile app authentication
|
||||
|
|
@ -508,75 +506,46 @@ func (s *APIV1Service) ListUserSettings(ctx context.Context, request *v1pb.ListU
|
|||
//
|
||||
// Security:
|
||||
// - Only the token owner can list their tokens
|
||||
// - Returns full token strings (so users can manage/revoke them)
|
||||
// - Returns token metadata only (not the actual token value)
|
||||
// - Invalid or expired tokens are filtered out
|
||||
//
|
||||
// Authentication: Required (session cookie or access token)
|
||||
// Authorization: User can only list their own tokens.
|
||||
func (s *APIV1Service) ListUserAccessTokens(ctx context.Context, request *v1pb.ListUserAccessTokensRequest) (*v1pb.ListUserAccessTokensResponse, error) {
|
||||
func (s *APIV1Service) ListPersonalAccessTokens(ctx context.Context, request *v1pb.ListPersonalAccessTokensRequest) (*v1pb.ListPersonalAccessTokensResponse, error) {
|
||||
userID, err := ExtractUserIDFromName(request.Parent)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid user name: %v", err)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
// Verify permission
|
||||
claims := auth.GetUserClaims(ctx)
|
||||
if claims == nil || claims.UserID != userID {
|
||||
currentUser, _ := s.fetchCurrentUser(ctx)
|
||||
if currentUser == nil || (currentUser.ID != userID && currentUser.Role != store.RoleHost && currentUser.Role != store.RoleAdmin) {
|
||||
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
|
||||
}
|
||||
}
|
||||
|
||||
tokens, err := s.Store.GetUserPersonalAccessTokens(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
if currentUser == nil {
|
||||
return nil, status.Errorf(codes.Unauthenticated, "user not authenticated")
|
||||
}
|
||||
if currentUser.ID != userID {
|
||||
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
|
||||
return nil, status.Errorf(codes.Internal, "failed to get access tokens: %v", err)
|
||||
}
|
||||
|
||||
userAccessTokens, err := s.Store.GetUserAccessTokens(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to list access tokens: %v", err)
|
||||
}
|
||||
|
||||
accessTokens := []*v1pb.UserAccessToken{}
|
||||
for _, userAccessToken := range userAccessTokens {
|
||||
claims := &auth.ClaimsMessage{}
|
||||
_, err := jwt.ParseWithClaims(userAccessToken.AccessToken, claims, func(t *jwt.Token) (any, error) {
|
||||
if t.Method.Alg() != jwt.SigningMethodHS256.Name {
|
||||
return nil, errors.Errorf("unexpected access token signing method=%v, expect %v", t.Header["alg"], jwt.SigningMethodHS256)
|
||||
}
|
||||
if kid, ok := t.Header["kid"].(string); ok {
|
||||
if kid == "v1" {
|
||||
return []byte(s.Secret), nil
|
||||
}
|
||||
}
|
||||
return nil, errors.Errorf("unexpected access token kid=%v", t.Header["kid"])
|
||||
})
|
||||
if err != nil {
|
||||
// If the access token is invalid or expired, just ignore it.
|
||||
continue
|
||||
personalAccessTokens := make([]*v1pb.PersonalAccessToken, len(tokens))
|
||||
for i, token := range tokens {
|
||||
personalAccessTokens[i] = &v1pb.PersonalAccessToken{
|
||||
Name: fmt.Sprintf("%s/personalAccessTokens/%s", request.Parent, token.TokenId),
|
||||
Description: token.Description,
|
||||
ExpiresAt: token.ExpiresAt,
|
||||
CreatedAt: token.CreatedAt,
|
||||
LastUsedAt: token.LastUsedAt,
|
||||
}
|
||||
|
||||
accessTokenResponse := &v1pb.UserAccessToken{
|
||||
Name: fmt.Sprintf("users/%d/accessTokens/%s", userID, userAccessToken.AccessToken),
|
||||
AccessToken: userAccessToken.AccessToken,
|
||||
Description: userAccessToken.Description,
|
||||
IssuedAt: timestamppb.New(claims.IssuedAt.Time),
|
||||
}
|
||||
if claims.ExpiresAt != nil {
|
||||
accessTokenResponse.ExpiresAt = timestamppb.New(claims.ExpiresAt.Time)
|
||||
}
|
||||
accessTokens = append(accessTokens, accessTokenResponse)
|
||||
}
|
||||
|
||||
// Sort by issued time in descending order.
|
||||
slices.SortFunc(accessTokens, func(i, j *v1pb.UserAccessToken) int {
|
||||
return int(i.IssuedAt.Seconds - j.IssuedAt.Seconds)
|
||||
})
|
||||
response := &v1pb.ListUserAccessTokensResponse{
|
||||
AccessTokens: accessTokens,
|
||||
}
|
||||
return response, nil
|
||||
return &v1pb.ListPersonalAccessTokensResponse{PersonalAccessTokens: personalAccessTokens}, nil
|
||||
}
|
||||
|
||||
// CreateUserAccessToken creates a new Personal Access Token (PAT) for a user.
|
||||
// CreatePersonalAccessToken creates a new Personal Access Token (PAT) for a user.
|
||||
//
|
||||
// Use cases:
|
||||
// - User manually creates token in settings for mobile app
|
||||
|
|
@ -584,8 +553,8 @@ func (s *APIV1Service) ListUserAccessTokens(ctx context.Context, request *v1pb.L
|
|||
// - User creates token for third-party integration
|
||||
//
|
||||
// Token properties:
|
||||
// - JWT format signed with server secret
|
||||
// - Contains user ID and username in claims
|
||||
// - Random string with memos_pat_ prefix
|
||||
// - SHA-256 hash stored in database
|
||||
// - Optional expiration time (can be never-expiring)
|
||||
// - User-provided description for identification
|
||||
//
|
||||
|
|
@ -596,66 +565,55 @@ func (s *APIV1Service) ListUserAccessTokens(ctx context.Context, request *v1pb.L
|
|||
//
|
||||
// Authentication: Required (session cookie or access token)
|
||||
// Authorization: User can only create tokens for themselves.
|
||||
func (s *APIV1Service) CreateUserAccessToken(ctx context.Context, request *v1pb.CreateUserAccessTokenRequest) (*v1pb.UserAccessToken, error) {
|
||||
func (s *APIV1Service) CreatePersonalAccessToken(ctx context.Context, request *v1pb.CreatePersonalAccessTokenRequest) (*v1pb.CreatePersonalAccessTokenResponse, error) {
|
||||
userID, err := ExtractUserIDFromName(request.Parent)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid user name: %v", err)
|
||||
}
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
if currentUser == nil {
|
||||
return nil, status.Errorf(codes.Unauthenticated, "user not authenticated")
|
||||
}
|
||||
if currentUser.ID != userID {
|
||||
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
|
||||
}
|
||||
|
||||
expiresAt := time.Time{}
|
||||
if request.AccessToken.ExpiresAt != nil {
|
||||
expiresAt = request.AccessToken.ExpiresAt.AsTime()
|
||||
}
|
||||
|
||||
accessToken, err := auth.GenerateAccessToken(currentUser.Username, currentUser.ID, expiresAt, []byte(s.Secret))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to generate access token: %v", err)
|
||||
}
|
||||
|
||||
claims := &auth.ClaimsMessage{}
|
||||
_, err = jwt.ParseWithClaims(accessToken, claims, func(t *jwt.Token) (any, error) {
|
||||
if t.Method.Alg() != jwt.SigningMethodHS256.Name {
|
||||
return nil, errors.Errorf("unexpected access token signing method=%v, expect %v", t.Header["alg"], jwt.SigningMethodHS256)
|
||||
// Verify permission
|
||||
claims := auth.GetUserClaims(ctx)
|
||||
if claims == nil || claims.UserID != userID {
|
||||
currentUser, _ := s.fetchCurrentUser(ctx)
|
||||
if currentUser == nil || currentUser.ID != userID {
|
||||
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
|
||||
}
|
||||
if kid, ok := t.Header["kid"].(string); ok {
|
||||
if kid == "v1" {
|
||||
return []byte(s.Secret), nil
|
||||
}
|
||||
}
|
||||
return nil, errors.Errorf("unexpected access token kid=%v", t.Header["kid"])
|
||||
})
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to parse access token: %v", err)
|
||||
}
|
||||
|
||||
// Upsert the access token to user setting store.
|
||||
if err := s.UpsertAccessTokenToStore(ctx, currentUser, accessToken, request.AccessToken.Description); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to upsert access token to store: %v", err)
|
||||
// Generate PAT
|
||||
tokenID := util.GenUUID()
|
||||
token := auth.GeneratePersonalAccessToken()
|
||||
tokenHash := auth.HashPersonalAccessToken(token)
|
||||
|
||||
var expiresAt *timestamppb.Timestamp
|
||||
if request.ExpiresInDays > 0 {
|
||||
expiresAt = timestamppb.New(time.Now().AddDate(0, 0, int(request.ExpiresInDays)))
|
||||
}
|
||||
|
||||
userAccessToken := &v1pb.UserAccessToken{
|
||||
Name: fmt.Sprintf("users/%d/accessTokens/%s", userID, accessToken),
|
||||
AccessToken: accessToken,
|
||||
Description: request.AccessToken.Description,
|
||||
IssuedAt: timestamppb.New(claims.IssuedAt.Time),
|
||||
patRecord := &storepb.PersonalAccessTokensUserSetting_PersonalAccessToken{
|
||||
TokenId: tokenID,
|
||||
TokenHash: tokenHash,
|
||||
Description: request.Description,
|
||||
ExpiresAt: expiresAt,
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
if claims.ExpiresAt != nil {
|
||||
userAccessToken.ExpiresAt = timestamppb.New(claims.ExpiresAt.Time)
|
||||
|
||||
if err := s.Store.AddUserPersonalAccessToken(ctx, userID, patRecord); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to create access token: %v", err)
|
||||
}
|
||||
return userAccessToken, nil
|
||||
|
||||
return &v1pb.CreatePersonalAccessTokenResponse{
|
||||
PersonalAccessToken: &v1pb.PersonalAccessToken{
|
||||
Name: fmt.Sprintf("%s/personalAccessTokens/%s", request.Parent, tokenID),
|
||||
Description: request.Description,
|
||||
ExpiresAt: expiresAt,
|
||||
CreatedAt: patRecord.CreatedAt,
|
||||
},
|
||||
Token: token, // Only returned on creation
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteUserAccessToken revokes a Personal Access Token.
|
||||
// DeletePersonalAccessToken revokes a Personal Access Token.
|
||||
//
|
||||
// This endpoint:
|
||||
// 1. Removes the token from the user's access tokens list
|
||||
|
|
@ -668,220 +626,118 @@ func (s *APIV1Service) CreateUserAccessToken(ctx context.Context, request *v1pb.
|
|||
//
|
||||
// Authentication: Required (session cookie or access token)
|
||||
// Authorization: User can only delete their own tokens.
|
||||
func (s *APIV1Service) DeleteUserAccessToken(ctx context.Context, request *v1pb.DeleteUserAccessTokenRequest) (*emptypb.Empty, error) {
|
||||
// Extract user ID from the access token resource name
|
||||
// Format: users/{user}/accessTokens/{access_token}
|
||||
func (s *APIV1Service) DeletePersonalAccessToken(ctx context.Context, request *v1pb.DeletePersonalAccessTokenRequest) (*emptypb.Empty, error) {
|
||||
// Parse name: users/{user_id}/personalAccessTokens/{token_id}
|
||||
parts := strings.Split(request.Name, "/")
|
||||
if len(parts) != 4 || parts[0] != "users" || parts[2] != "accessTokens" {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid access token name format: %s", request.Name)
|
||||
if len(parts) != 4 || parts[0] != "users" || parts[2] != "personalAccessTokens" {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid personal access token name")
|
||||
}
|
||||
|
||||
userID, err := ExtractUserIDFromName(fmt.Sprintf("users/%s", parts[1]))
|
||||
userID, err := util.ConvertStringToInt32(parts[1])
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid user name: %v", err)
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid user ID: %v", err)
|
||||
}
|
||||
accessTokenToDelete := parts[3]
|
||||
tokenID := parts[3]
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
if currentUser == nil {
|
||||
return nil, status.Errorf(codes.Unauthenticated, "user not authenticated")
|
||||
}
|
||||
if currentUser.ID != userID {
|
||||
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
|
||||
}
|
||||
|
||||
userAccessTokens, err := s.Store.GetUserAccessTokens(ctx, currentUser.ID)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to list access tokens: %v", err)
|
||||
}
|
||||
updatedUserAccessTokens := []*storepb.AccessTokensUserSetting_AccessToken{}
|
||||
for _, userAccessToken := range userAccessTokens {
|
||||
if userAccessToken.AccessToken == accessTokenToDelete {
|
||||
continue
|
||||
// Verify permission
|
||||
claims := auth.GetUserClaims(ctx)
|
||||
if claims == nil || claims.UserID != userID {
|
||||
currentUser, _ := s.fetchCurrentUser(ctx)
|
||||
if currentUser == nil || currentUser.ID != userID {
|
||||
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
|
||||
}
|
||||
updatedUserAccessTokens = append(updatedUserAccessTokens, userAccessToken)
|
||||
}
|
||||
if _, err := s.Store.UpsertUserSetting(ctx, &storepb.UserSetting{
|
||||
UserId: currentUser.ID,
|
||||
Key: storepb.UserSetting_ACCESS_TOKENS,
|
||||
Value: &storepb.UserSetting_AccessTokens{
|
||||
AccessTokens: &storepb.AccessTokensUserSetting{
|
||||
AccessTokens: updatedUserAccessTokens,
|
||||
},
|
||||
},
|
||||
}); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to upsert user setting: %v", err)
|
||||
|
||||
if err := s.Store.RemoveUserPersonalAccessToken(ctx, userID, tokenID); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to delete access token: %v", err)
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
// ListUserSessions retrieves all active sessions for a user.
|
||||
//
|
||||
// Sessions represent active browser logins. Each session includes:
|
||||
// - session_id: Unique identifier
|
||||
// - create_time: When the session was created
|
||||
// - last_accessed_time: Last API call time (for sliding expiration)
|
||||
// - client_info: Device details (browser, OS, IP address, device type)
|
||||
//
|
||||
// Use cases:
|
||||
// - User reviews where they're logged in
|
||||
// - User identifies suspicious login attempts
|
||||
// - User prepares to revoke specific sessions
|
||||
//
|
||||
// Authentication: Required (session cookie or access token)
|
||||
// Authorization: User can only list their own sessions.
|
||||
func (s *APIV1Service) ListUserSessions(ctx context.Context, request *v1pb.ListUserSessionsRequest) (*v1pb.ListUserSessionsResponse, error) {
|
||||
// ListSessions returns all active login sessions for a user.
|
||||
// Each session represents a device/browser where the user is logged in,
|
||||
// backed by refresh tokens with sliding expiration.
|
||||
func (s *APIV1Service) ListSessions(ctx context.Context, request *v1pb.ListSessionsRequest) (*v1pb.ListSessionsResponse, error) {
|
||||
userID, err := ExtractUserIDFromName(request.Parent)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid user name: %v", err)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
if currentUser == nil {
|
||||
return nil, status.Errorf(codes.Unauthenticated, "user not authenticated")
|
||||
}
|
||||
if currentUser.ID != userID {
|
||||
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
|
||||
}
|
||||
|
||||
userSessions, err := s.Store.GetUserSessions(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to list sessions: %v", err)
|
||||
}
|
||||
|
||||
sessions := []*v1pb.UserSession{}
|
||||
for _, userSession := range userSessions {
|
||||
sessionResponse := &v1pb.UserSession{
|
||||
Name: fmt.Sprintf("users/%d/sessions/%s", userID, userSession.SessionId),
|
||||
SessionId: userSession.SessionId,
|
||||
CreateTime: userSession.CreateTime,
|
||||
LastAccessedTime: userSession.LastAccessedTime,
|
||||
// Verify permission.
|
||||
claims := auth.GetUserClaims(ctx)
|
||||
if claims == nil || claims.UserID != userID {
|
||||
currentUser, _ := s.fetchCurrentUser(ctx)
|
||||
if currentUser == nil || (currentUser.ID != userID && currentUser.Role != store.RoleHost && currentUser.Role != store.RoleAdmin) {
|
||||
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
|
||||
}
|
||||
}
|
||||
|
||||
if userSession.ClientInfo != nil {
|
||||
sessionResponse.ClientInfo = &v1pb.UserSession_ClientInfo{
|
||||
UserAgent: userSession.ClientInfo.UserAgent,
|
||||
IpAddress: userSession.ClientInfo.IpAddress,
|
||||
DeviceType: userSession.ClientInfo.DeviceType,
|
||||
Os: userSession.ClientInfo.Os,
|
||||
Browser: userSession.ClientInfo.Browser,
|
||||
refreshTokens, err := s.Store.GetUserRefreshTokens(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get sessions: %v", err)
|
||||
}
|
||||
|
||||
sessions := make([]*v1pb.Session, 0, len(refreshTokens))
|
||||
for _, token := range refreshTokens {
|
||||
session := &v1pb.Session{
|
||||
Name: fmt.Sprintf("%s/sessions/%s", request.Parent, token.TokenId),
|
||||
SessionId: token.TokenId,
|
||||
CreateTime: token.CreatedAt,
|
||||
}
|
||||
if token.ClientInfo != nil {
|
||||
session.ClientInfo = &v1pb.Session_ClientInfo{
|
||||
UserAgent: token.ClientInfo.UserAgent,
|
||||
IpAddress: token.ClientInfo.IpAddress,
|
||||
DeviceType: token.ClientInfo.DeviceType,
|
||||
Os: token.ClientInfo.Os,
|
||||
Browser: token.ClientInfo.Browser,
|
||||
}
|
||||
}
|
||||
|
||||
sessions = append(sessions, sessionResponse)
|
||||
sessions = append(sessions, session)
|
||||
}
|
||||
|
||||
// Sort by last accessed time in descending order.
|
||||
slices.SortFunc(sessions, func(i, j *v1pb.UserSession) int {
|
||||
return int(j.LastAccessedTime.Seconds - i.LastAccessedTime.Seconds)
|
||||
})
|
||||
|
||||
response := &v1pb.ListUserSessionsResponse{
|
||||
Sessions: sessions,
|
||||
}
|
||||
return response, nil
|
||||
return &v1pb.ListSessionsResponse{Sessions: sessions}, nil
|
||||
}
|
||||
|
||||
// RevokeUserSession terminates a specific session for a user.
|
||||
//
|
||||
// This endpoint:
|
||||
// 1. Removes the session from the user's sessions list
|
||||
// 2. Immediately invalidates the session
|
||||
// 3. Forces the device to re-login on next request
|
||||
//
|
||||
// Use cases:
|
||||
// - User logs out from a specific device (e.g., "Log out my phone")
|
||||
// - User removes suspicious/unknown session
|
||||
// - User logs out from all devices except current one
|
||||
//
|
||||
// Note: This is different from DeleteSession (logout current session).
|
||||
// This endpoint allows revoking ANY session, not just the current one.
|
||||
//
|
||||
// Authentication: Required (session cookie or access token)
|
||||
// Authorization: User can only revoke their own sessions.
|
||||
func (s *APIV1Service) RevokeUserSession(ctx context.Context, request *v1pb.RevokeUserSessionRequest) (*emptypb.Empty, error) {
|
||||
// Extract user ID and session ID from the session resource name
|
||||
// Format: users/{user}/sessions/{session}
|
||||
// RevokeSession revokes a specific login session.
|
||||
// This invalidates the refresh token, forcing re-authentication on that device.
|
||||
func (s *APIV1Service) RevokeSession(ctx context.Context, request *v1pb.RevokeSessionRequest) (*emptypb.Empty, error) {
|
||||
// Parse name: users/{user_id}/sessions/{session_id}
|
||||
parts := strings.Split(request.Name, "/")
|
||||
if len(parts) != 4 || parts[0] != "users" || parts[2] != "sessions" {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid session name format: %s", request.Name)
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid session name")
|
||||
}
|
||||
|
||||
userID, err := ExtractUserIDFromName(fmt.Sprintf("users/%s", parts[1]))
|
||||
userID, err := util.ConvertStringToInt32(parts[1])
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid user name: %v", err)
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid user ID: %v", err)
|
||||
}
|
||||
sessionIDToRevoke := parts[3]
|
||||
sessionID := parts[3]
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
if currentUser == nil {
|
||||
return nil, status.Errorf(codes.Unauthenticated, "user not authenticated")
|
||||
}
|
||||
if currentUser.ID != userID {
|
||||
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
|
||||
// Verify permission.
|
||||
claims := auth.GetUserClaims(ctx)
|
||||
if claims == nil || claims.UserID != userID {
|
||||
currentUser, _ := s.fetchCurrentUser(ctx)
|
||||
if currentUser == nil || (currentUser.ID != userID && currentUser.Role != store.RoleHost && currentUser.Role != store.RoleAdmin) {
|
||||
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.Store.RemoveUserSession(ctx, userID, sessionIDToRevoke); err != nil {
|
||||
if err := s.Store.RemoveUserRefreshToken(ctx, userID, sessionID); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to revoke session: %v", err)
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
// UpsertUserSession adds or updates a user session.
|
||||
func (s *APIV1Service) UpsertUserSession(ctx context.Context, userID int32, sessionID string, clientInfo *storepb.SessionsUserSetting_ClientInfo) error {
|
||||
session := &storepb.SessionsUserSetting_Session{
|
||||
SessionId: sessionID,
|
||||
CreateTime: timestamppb.Now(),
|
||||
LastAccessedTime: timestamppb.Now(),
|
||||
ClientInfo: clientInfo,
|
||||
}
|
||||
|
||||
return s.Store.AddUserSession(ctx, userID, session)
|
||||
}
|
||||
|
||||
func (s *APIV1Service) UpsertAccessTokenToStore(ctx context.Context, user *store.User, accessToken, description string) error {
|
||||
userAccessTokens, err := s.Store.GetUserAccessTokens(ctx, user.ID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get user access tokens")
|
||||
}
|
||||
userAccessToken := storepb.AccessTokensUserSetting_AccessToken{
|
||||
AccessToken: accessToken,
|
||||
Description: description,
|
||||
}
|
||||
userAccessTokens = append(userAccessTokens, &userAccessToken)
|
||||
|
||||
if _, err := s.Store.UpsertUserSetting(ctx, &storepb.UserSetting{
|
||||
UserId: user.ID,
|
||||
Key: storepb.UserSetting_ACCESS_TOKENS,
|
||||
Value: &storepb.UserSetting_AccessTokens{
|
||||
AccessTokens: &storepb.AccessTokensUserSetting{
|
||||
AccessTokens: userAccessTokens,
|
||||
},
|
||||
},
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "failed to upsert user setting")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *APIV1Service) ListUserWebhooks(ctx context.Context, request *v1pb.ListUserWebhooksRequest) (*v1pb.ListUserWebhooksResponse, error) {
|
||||
userID, err := ExtractUserIDFromName(request.Parent)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid parent: %v", err)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -913,7 +769,7 @@ func (s *APIV1Service) CreateUserWebhook(ctx context.Context, request *v1pb.Crea
|
|||
return nil, status.Errorf(codes.InvalidArgument, "invalid parent: %v", err)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -953,7 +809,7 @@ func (s *APIV1Service) UpdateUserWebhook(ctx context.Context, request *v1pb.Upda
|
|||
return nil, status.Errorf(codes.InvalidArgument, "invalid webhook name: %v", err)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -1025,7 +881,7 @@ func (s *APIV1Service) DeleteUserWebhook(ctx context.Context, request *v1pb.Dele
|
|||
return nil, status.Errorf(codes.InvalidArgument, "invalid webhook name: %v", err)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -1187,10 +1043,6 @@ func convertSettingKeyToStore(key string) (storepb.UserSetting_Key, error) {
|
|||
switch key {
|
||||
case v1pb.UserSetting_Key_name[int32(v1pb.UserSetting_GENERAL)]:
|
||||
return storepb.UserSetting_GENERAL, nil
|
||||
case v1pb.UserSetting_Key_name[int32(v1pb.UserSetting_SESSIONS)]:
|
||||
return storepb.UserSetting_SESSIONS, nil
|
||||
case v1pb.UserSetting_Key_name[int32(v1pb.UserSetting_ACCESS_TOKENS)]:
|
||||
return storepb.UserSetting_ACCESS_TOKENS, nil
|
||||
case v1pb.UserSetting_Key_name[int32(v1pb.UserSetting_WEBHOOKS)]:
|
||||
return storepb.UserSetting_WEBHOOKS, nil
|
||||
default:
|
||||
|
|
@ -1203,10 +1055,6 @@ func convertSettingKeyFromStore(key storepb.UserSetting_Key) string {
|
|||
switch key {
|
||||
case storepb.UserSetting_GENERAL:
|
||||
return v1pb.UserSetting_Key_name[int32(v1pb.UserSetting_GENERAL)]
|
||||
case storepb.UserSetting_SESSIONS:
|
||||
return v1pb.UserSetting_Key_name[int32(v1pb.UserSetting_SESSIONS)]
|
||||
case storepb.UserSetting_ACCESS_TOKENS:
|
||||
return v1pb.UserSetting_Key_name[int32(v1pb.UserSetting_ACCESS_TOKENS)]
|
||||
case storepb.UserSetting_SHORTCUTS:
|
||||
return "SHORTCUTS" // Not defined in API proto
|
||||
case storepb.UserSetting_WEBHOOKS:
|
||||
|
|
@ -1226,18 +1074,6 @@ func convertUserSettingFromStore(storeSetting *storepb.UserSetting, userID int32
|
|||
}
|
||||
|
||||
switch key {
|
||||
case storepb.UserSetting_SESSIONS:
|
||||
setting.Value = &v1pb.UserSetting_SessionsSetting_{
|
||||
SessionsSetting: &v1pb.UserSetting_SessionsSetting{
|
||||
Sessions: []*v1pb.UserSession{},
|
||||
},
|
||||
}
|
||||
case storepb.UserSetting_ACCESS_TOKENS:
|
||||
setting.Value = &v1pb.UserSetting_AccessTokensSetting_{
|
||||
AccessTokensSetting: &v1pb.UserSetting_AccessTokensSetting{
|
||||
AccessTokens: []*v1pb.UserAccessToken{},
|
||||
},
|
||||
}
|
||||
case storepb.UserSetting_WEBHOOKS:
|
||||
setting.Value = &v1pb.UserSetting_WebhooksSetting_{
|
||||
WebhooksSetting: &v1pb.UserSetting_WebhooksSetting{
|
||||
|
|
@ -1273,46 +1109,6 @@ func convertUserSettingFromStore(storeSetting *storepb.UserSetting, userID int32
|
|||
GeneralSetting: getDefaultUserGeneralSetting(),
|
||||
}
|
||||
}
|
||||
case storepb.UserSetting_SESSIONS:
|
||||
sessions := storeSetting.GetSessions()
|
||||
apiSessions := make([]*v1pb.UserSession, 0, len(sessions.Sessions))
|
||||
for _, session := range sessions.Sessions {
|
||||
apiSession := &v1pb.UserSession{
|
||||
Name: fmt.Sprintf("users/%d/sessions/%s", userID, session.SessionId),
|
||||
SessionId: session.SessionId,
|
||||
CreateTime: session.CreateTime,
|
||||
LastAccessedTime: session.LastAccessedTime,
|
||||
ClientInfo: &v1pb.UserSession_ClientInfo{
|
||||
UserAgent: session.ClientInfo.UserAgent,
|
||||
IpAddress: session.ClientInfo.IpAddress,
|
||||
DeviceType: session.ClientInfo.DeviceType,
|
||||
Os: session.ClientInfo.Os,
|
||||
Browser: session.ClientInfo.Browser,
|
||||
},
|
||||
}
|
||||
apiSessions = append(apiSessions, apiSession)
|
||||
}
|
||||
setting.Value = &v1pb.UserSetting_SessionsSetting_{
|
||||
SessionsSetting: &v1pb.UserSetting_SessionsSetting{
|
||||
Sessions: apiSessions,
|
||||
},
|
||||
}
|
||||
case storepb.UserSetting_ACCESS_TOKENS:
|
||||
accessTokens := storeSetting.GetAccessTokens()
|
||||
apiTokens := make([]*v1pb.UserAccessToken, 0, len(accessTokens.AccessTokens))
|
||||
for _, token := range accessTokens.AccessTokens {
|
||||
apiToken := &v1pb.UserAccessToken{
|
||||
Name: fmt.Sprintf("users/%d/accessTokens/%s", userID, token.AccessToken),
|
||||
AccessToken: token.AccessToken,
|
||||
Description: token.Description,
|
||||
}
|
||||
apiTokens = append(apiTokens, apiToken)
|
||||
}
|
||||
setting.Value = &v1pb.UserSetting_AccessTokensSetting_{
|
||||
AccessTokensSetting: &v1pb.UserSetting_AccessTokensSetting{
|
||||
AccessTokens: apiTokens,
|
||||
},
|
||||
}
|
||||
case storepb.UserSetting_WEBHOOKS:
|
||||
webhooks := storeSetting.GetWebhooks()
|
||||
apiWebhooks := make([]*v1pb.UserWebhook, 0, len(webhooks.Webhooks))
|
||||
|
|
@ -1359,50 +1155,6 @@ func convertUserSettingToStore(apiSetting *v1pb.UserSetting, userID int32, key s
|
|||
} else {
|
||||
return nil, errors.Errorf("general setting is required")
|
||||
}
|
||||
case storepb.UserSetting_SESSIONS:
|
||||
if sessions := apiSetting.GetSessionsSetting(); sessions != nil {
|
||||
storeSessions := make([]*storepb.SessionsUserSetting_Session, 0, len(sessions.Sessions))
|
||||
for _, session := range sessions.Sessions {
|
||||
storeSession := &storepb.SessionsUserSetting_Session{
|
||||
SessionId: session.SessionId,
|
||||
CreateTime: session.CreateTime,
|
||||
LastAccessedTime: session.LastAccessedTime,
|
||||
ClientInfo: &storepb.SessionsUserSetting_ClientInfo{
|
||||
UserAgent: session.ClientInfo.UserAgent,
|
||||
IpAddress: session.ClientInfo.IpAddress,
|
||||
DeviceType: session.ClientInfo.DeviceType,
|
||||
Os: session.ClientInfo.Os,
|
||||
Browser: session.ClientInfo.Browser,
|
||||
},
|
||||
}
|
||||
storeSessions = append(storeSessions, storeSession)
|
||||
}
|
||||
storeSetting.Value = &storepb.UserSetting_Sessions{
|
||||
Sessions: &storepb.SessionsUserSetting{
|
||||
Sessions: storeSessions,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
return nil, errors.Errorf("sessions setting is required")
|
||||
}
|
||||
case storepb.UserSetting_ACCESS_TOKENS:
|
||||
if accessTokens := apiSetting.GetAccessTokensSetting(); accessTokens != nil {
|
||||
storeTokens := make([]*storepb.AccessTokensUserSetting_AccessToken, 0, len(accessTokens.AccessTokens))
|
||||
for _, token := range accessTokens.AccessTokens {
|
||||
storeToken := &storepb.AccessTokensUserSetting_AccessToken{
|
||||
AccessToken: token.AccessToken,
|
||||
Description: token.Description,
|
||||
}
|
||||
storeTokens = append(storeTokens, storeToken)
|
||||
}
|
||||
storeSetting.Value = &storepb.UserSetting_AccessTokens{
|
||||
AccessTokens: &storepb.AccessTokensUserSetting{
|
||||
AccessTokens: storeTokens,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
return nil, errors.Errorf("access tokens setting is required")
|
||||
}
|
||||
case storepb.UserSetting_WEBHOOKS:
|
||||
if webhooks := apiSetting.GetWebhooksSetting(); webhooks != nil {
|
||||
storeWebhooks := make([]*storepb.WebhooksUserSetting_Webhook, 0, len(webhooks.Webhooks))
|
||||
|
|
@ -1542,7 +1294,7 @@ func (s *APIV1Service) ListUserNotifications(ctx context.Context, request *v1pb.
|
|||
}
|
||||
|
||||
// Verify the requesting user has permission to view these notifications
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -1588,7 +1340,7 @@ func (s *APIV1Service) UpdateUserNotification(ctx context.Context, request *v1pb
|
|||
return nil, status.Errorf(codes.InvalidArgument, "invalid notification name: %v", err)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
@ -1653,7 +1405,7 @@ func (s *APIV1Service) DeleteUserNotification(ctx context.Context, request *v1pb
|
|||
return nil, status.Errorf(codes.InvalidArgument, "invalid notification name: %v", err)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ func (s *APIV1Service) ListAllUserStats(ctx context.Context, _ *v1pb.ListAllUser
|
|||
RowStatus: &normalStatus,
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get user: %v", err)
|
||||
}
|
||||
|
|
@ -121,7 +121,7 @@ func (s *APIV1Service) GetUserStats(ctx context.Context, request *v1pb.GetUserSt
|
|||
return nil, status.Errorf(codes.InvalidArgument, "invalid user name: %v", err)
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get user: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,9 +76,16 @@ func (s *APIV1Service) RegisterGateway(ctx context.Context, echoServer *echo.Ech
|
|||
return
|
||||
}
|
||||
|
||||
// Set user in context (may be nil for public endpoints)
|
||||
// Set context based on auth result (may be nil for public endpoints)
|
||||
if result != nil {
|
||||
ctx = auth.SetUserInContext(ctx, result.User, result.SessionID, result.AccessToken)
|
||||
if result.Claims != nil {
|
||||
// Access Token V2 - stateless, use claims
|
||||
ctx = auth.SetUserClaimsInContext(ctx, result.Claims)
|
||||
ctx = context.WithValue(ctx, auth.UserIDContextKey, result.Claims.UserID)
|
||||
} else if result.User != nil {
|
||||
// PAT - have full user
|
||||
ctx = auth.SetUserInContext(ctx, result.User, result.SessionID, result.AccessToken)
|
||||
}
|
||||
r = r.WithContext(ctx)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -287,25 +287,32 @@ func (s *FileServerService) checkAttachmentPermission(ctx context.Context, c ech
|
|||
}
|
||||
|
||||
// getCurrentUser retrieves the current authenticated user from the Echo context.
|
||||
// It checks both session cookies and Bearer tokens for authentication.
|
||||
// It checks Bearer tokens for authentication (Access Token V2 or PAT).
|
||||
// Uses the shared Authenticator for consistent authentication logic.
|
||||
func (s *FileServerService) getCurrentUser(ctx context.Context, c echo.Context) (*store.User, error) {
|
||||
// Try session cookie authentication first
|
||||
if cookie, err := c.Cookie(auth.SessionCookieName); err == nil && cookie.Value != "" {
|
||||
user, err := s.authenticator.AuthenticateBySession(ctx, cookie.Value)
|
||||
if err == nil && user != nil {
|
||||
return user, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Try JWT Bearer token authentication
|
||||
// Try Bearer token authentication
|
||||
authHeader := c.Request().Header.Get("Authorization")
|
||||
if authHeader != "" {
|
||||
parts := strings.Fields(authHeader)
|
||||
if len(parts) == 2 && strings.EqualFold(parts[0], "bearer") {
|
||||
user, err := s.authenticator.AuthenticateByJWT(ctx, parts[1])
|
||||
if err == nil && user != nil {
|
||||
return user, nil
|
||||
token := auth.ExtractBearerToken(authHeader)
|
||||
if token != "" {
|
||||
// Try Access Token V2 (stateless)
|
||||
if !strings.HasPrefix(token, auth.PersonalAccessTokenPrefix) {
|
||||
claims, err := s.authenticator.AuthenticateByAccessTokenV2(token)
|
||||
if err == nil && claims != nil {
|
||||
// Get user from claims
|
||||
user, err := s.Store.GetUser(ctx, &store.FindUser{ID: &claims.UserID})
|
||||
if err == nil && user != nil {
|
||||
return user, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try PAT
|
||||
if strings.HasPrefix(token, auth.PersonalAccessTokenPrefix) {
|
||||
user, _, err := s.authenticator.AuthenticateByPAT(ctx, token)
|
||||
if err == nil && user != nil {
|
||||
return user, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,41 +57,37 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting)
|
|||
return userSettingList, nil
|
||||
}
|
||||
|
||||
func (d *DB) GetUserSessionByID(ctx context.Context, sessionID string) (*store.UserSessionQueryResult, error) {
|
||||
// Query user_setting that contains this sessionID in the sessions array
|
||||
// Use JSON_SEARCH to check if sessionID exists in the array
|
||||
func (d *DB) GetUserByPATHash(ctx context.Context, tokenHash string) (*store.PATQueryResult, error) {
|
||||
query := `
|
||||
SELECT
|
||||
SELECT
|
||||
user_id,
|
||||
value
|
||||
FROM user_setting
|
||||
WHERE ` + "`key`" + ` = 'SESSIONS'
|
||||
AND JSON_SEARCH(value, 'one', ?, NULL, '$.sessions[*].sessionId') IS NOT NULL
|
||||
WHERE ` + "`key`" + ` = 'PERSONAL_ACCESS_TOKENS'
|
||||
AND JSON_SEARCH(value, 'one', ?, NULL, '$.tokens[*].tokenHash') IS NOT NULL
|
||||
`
|
||||
|
||||
var userID int32
|
||||
var sessionsJSON string
|
||||
var tokensJSON string
|
||||
|
||||
err := d.db.QueryRowContext(ctx, query, sessionID).Scan(&userID, &sessionsJSON)
|
||||
err := d.db.QueryRowContext(ctx, query, tokenHash).Scan(&userID, &tokensJSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse the entire sessions list using protobuf unmarshaler
|
||||
sessionsUserSetting := &storepb.SessionsUserSetting{}
|
||||
if err := protojsonUnmarshaler.Unmarshal([]byte(sessionsJSON), sessionsUserSetting); err != nil {
|
||||
patsUserSetting := &storepb.PersonalAccessTokensUserSetting{}
|
||||
if err := protojsonUnmarshaler.Unmarshal([]byte(tokensJSON), patsUserSetting); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Find the specific session by ID
|
||||
for _, session := range sessionsUserSetting.Sessions {
|
||||
if session.SessionId == sessionID {
|
||||
return &store.UserSessionQueryResult{
|
||||
UserID: userID,
|
||||
Session: session,
|
||||
for _, pat := range patsUserSetting.Tokens {
|
||||
if pat.TokenHash == tokenHash {
|
||||
return &store.PATQueryResult{
|
||||
UserID: userID,
|
||||
PAT: pat,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("session not found")
|
||||
return nil, errors.New("PAT not found")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,45 +70,41 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting)
|
|||
return userSettingList, nil
|
||||
}
|
||||
|
||||
func (d *DB) GetUserSessionByID(ctx context.Context, sessionID string) (*store.UserSessionQueryResult, error) {
|
||||
// Query user_setting that contains this sessionID in the sessions array
|
||||
// Use EXISTS with jsonb_array_elements to check array membership
|
||||
func (d *DB) GetUserByPATHash(ctx context.Context, tokenHash string) (*store.PATQueryResult, error) {
|
||||
query := `
|
||||
SELECT
|
||||
SELECT
|
||||
user_setting.user_id,
|
||||
user_setting.value
|
||||
FROM user_setting
|
||||
WHERE user_setting.key = 'SESSIONS'
|
||||
WHERE user_setting.key = 'PERSONAL_ACCESS_TOKENS'
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM jsonb_array_elements(user_setting.value::jsonb->'sessions') AS session
|
||||
WHERE session->>'sessionId' = $1
|
||||
SELECT 1
|
||||
FROM jsonb_array_elements(user_setting.value::jsonb->'tokens') AS token
|
||||
WHERE token->>'tokenHash' = $1
|
||||
)
|
||||
`
|
||||
|
||||
var userID int32
|
||||
var sessionsJSON string
|
||||
var tokensJSON string
|
||||
|
||||
err := d.db.QueryRowContext(ctx, query, sessionID).Scan(&userID, &sessionsJSON)
|
||||
err := d.db.QueryRowContext(ctx, query, tokenHash).Scan(&userID, &tokensJSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse the entire sessions list using protobuf unmarshaler
|
||||
sessionsUserSetting := &storepb.SessionsUserSetting{}
|
||||
if err := protojsonUnmarshaler.Unmarshal([]byte(sessionsJSON), sessionsUserSetting); err != nil {
|
||||
patsUserSetting := &storepb.PersonalAccessTokensUserSetting{}
|
||||
if err := protojsonUnmarshaler.Unmarshal([]byte(tokensJSON), patsUserSetting); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Find the specific session by ID
|
||||
for _, session := range sessionsUserSetting.Sessions {
|
||||
if session.SessionId == sessionID {
|
||||
return &store.UserSessionQueryResult{
|
||||
UserID: userID,
|
||||
Session: session,
|
||||
for _, pat := range patsUserSetting.Tokens {
|
||||
if pat.TokenHash == tokenHash {
|
||||
return &store.PATQueryResult{
|
||||
UserID: userID,
|
||||
PAT: pat,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("session not found")
|
||||
return nil, errors.New("PAT not found")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,45 +69,41 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting)
|
|||
return userSettingList, nil
|
||||
}
|
||||
|
||||
func (d *DB) GetUserSessionByID(ctx context.Context, sessionID string) (*store.UserSessionQueryResult, error) {
|
||||
// Query user_setting that contains this sessionID in the sessions array
|
||||
// Use EXISTS with json_each to properly check array membership
|
||||
func (d *DB) GetUserByPATHash(ctx context.Context, tokenHash string) (*store.PATQueryResult, error) {
|
||||
query := `
|
||||
SELECT
|
||||
SELECT
|
||||
user_setting.user_id,
|
||||
user_setting.value
|
||||
FROM user_setting
|
||||
WHERE user_setting.key = 'SESSIONS'
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM json_each(json_extract(user_setting.value, '$.sessions')) AS session
|
||||
WHERE json_extract(session.value, '$.sessionId') = ?
|
||||
)
|
||||
WHERE user_setting.key = 'PERSONAL_ACCESS_TOKENS'
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM json_each(json_extract(user_setting.value, '$.tokens')) AS token
|
||||
WHERE json_extract(token.value, '$.tokenHash') = ?
|
||||
)
|
||||
`
|
||||
|
||||
var userID int32
|
||||
var sessionsJSON string
|
||||
var tokensJSON string
|
||||
|
||||
err := d.db.QueryRowContext(ctx, query, sessionID).Scan(&userID, &sessionsJSON)
|
||||
err := d.db.QueryRowContext(ctx, query, tokenHash).Scan(&userID, &tokensJSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse the entire sessions list using protobuf unmarshaler
|
||||
sessionsUserSetting := &storepb.SessionsUserSetting{}
|
||||
if err := protojsonUnmarshaler.Unmarshal([]byte(sessionsJSON), sessionsUserSetting); err != nil {
|
||||
patsUserSetting := &storepb.PersonalAccessTokensUserSetting{}
|
||||
if err := protojsonUnmarshaler.Unmarshal([]byte(tokensJSON), patsUserSetting); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Find the specific session by ID
|
||||
for _, session := range sessionsUserSetting.Sessions {
|
||||
if session.SessionId == sessionID {
|
||||
return &store.UserSessionQueryResult{
|
||||
UserID: userID,
|
||||
Session: session,
|
||||
for _, pat := range patsUserSetting.Tokens {
|
||||
if pat.TokenHash == tokenHash {
|
||||
return &store.PATQueryResult{
|
||||
UserID: userID,
|
||||
PAT: pat,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("session not found")
|
||||
return nil, errors.New("PAT not found")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ type Driver interface {
|
|||
// UserSetting model related methods.
|
||||
UpsertUserSetting(ctx context.Context, upsert *UserSetting) (*UserSetting, error)
|
||||
ListUserSettings(ctx context.Context, find *FindUserSetting) ([]*UserSetting, error)
|
||||
GetUserSessionByID(ctx context.Context, sessionID string) (*UserSessionQueryResult, error)
|
||||
GetUserByPATHash(ctx context.Context, tokenHash string) (*PATQueryResult, error)
|
||||
|
||||
// IdentityProvider model related methods.
|
||||
CreateIdentityProvider(ctx context.Context, create *IdentityProvider) (*IdentityProvider, error)
|
||||
|
|
|
|||
|
|
@ -21,10 +21,17 @@ type FindUserSetting struct {
|
|||
Key storepb.UserSetting_Key
|
||||
}
|
||||
|
||||
// UserSessionQueryResult contains the result of querying a single session by ID.
|
||||
type UserSessionQueryResult struct {
|
||||
UserID int32
|
||||
Session *storepb.SessionsUserSetting_Session
|
||||
// RefreshTokenQueryResult contains the result of querying a refresh token.
|
||||
type RefreshTokenQueryResult struct {
|
||||
UserID int32
|
||||
RefreshToken *storepb.RefreshTokensUserSetting_RefreshToken
|
||||
}
|
||||
|
||||
// PATQueryResult contains the result of querying a PAT by hash.
|
||||
type PATQueryResult struct {
|
||||
UserID int32
|
||||
User *User
|
||||
PAT *storepb.PersonalAccessTokensUserSetting_PersonalAccessToken
|
||||
}
|
||||
|
||||
func (s *Store) UpsertUserSetting(ctx context.Context, upsert *storepb.UserSetting) (*storepb.UserSetting, error) {
|
||||
|
|
@ -95,164 +102,190 @@ func (s *Store) GetUserSetting(ctx context.Context, find *FindUserSetting) (*sto
|
|||
return userSetting, nil
|
||||
}
|
||||
|
||||
// GetUserAccessTokens returns the access tokens of the user.
|
||||
func (s *Store) GetUserAccessTokens(ctx context.Context, userID int32) ([]*storepb.AccessTokensUserSetting_AccessToken, error) {
|
||||
// GetUserByPATHash finds a user by PAT hash.
|
||||
func (s *Store) GetUserByPATHash(ctx context.Context, tokenHash string) (*PATQueryResult, error) {
|
||||
result, err := s.driver.GetUserByPATHash(ctx, tokenHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Fetch user info
|
||||
user, err := s.GetUser(ctx, &FindUser{ID: &result.UserID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user == nil {
|
||||
return nil, errors.New("user not found for PAT")
|
||||
}
|
||||
result.User = user
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetUserRefreshTokens returns the refresh tokens of the user.
|
||||
func (s *Store) GetUserRefreshTokens(ctx context.Context, userID int32) ([]*storepb.RefreshTokensUserSetting_RefreshToken, error) {
|
||||
userSetting, err := s.GetUserSetting(ctx, &FindUserSetting{
|
||||
UserID: &userID,
|
||||
Key: storepb.UserSetting_ACCESS_TOKENS,
|
||||
Key: storepb.UserSetting_REFRESH_TOKENS,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if userSetting == nil {
|
||||
return []*storepb.AccessTokensUserSetting_AccessToken{}, nil
|
||||
return []*storepb.RefreshTokensUserSetting_RefreshToken{}, nil
|
||||
}
|
||||
|
||||
accessTokensUserSetting := userSetting.GetAccessTokens()
|
||||
return accessTokensUserSetting.AccessTokens, nil
|
||||
return userSetting.GetRefreshTokens().RefreshTokens, nil
|
||||
}
|
||||
|
||||
// RemoveUserAccessToken remove the access token of the user.
|
||||
func (s *Store) RemoveUserAccessToken(ctx context.Context, userID int32, token string) error {
|
||||
oldAccessTokens, err := s.GetUserAccessTokens(ctx, userID)
|
||||
// AddUserRefreshToken adds a new refresh token for the user.
|
||||
func (s *Store) AddUserRefreshToken(ctx context.Context, userID int32, token *storepb.RefreshTokensUserSetting_RefreshToken) error {
|
||||
tokens, err := s.GetUserRefreshTokens(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newAccessTokens := make([]*storepb.AccessTokensUserSetting_AccessToken, 0, len(oldAccessTokens))
|
||||
for _, t := range oldAccessTokens {
|
||||
if token != t.AccessToken {
|
||||
newAccessTokens = append(newAccessTokens, t)
|
||||
tokens = append(tokens, token)
|
||||
|
||||
_, err = s.UpsertUserSetting(ctx, &storepb.UserSetting{
|
||||
UserId: userID,
|
||||
Key: storepb.UserSetting_REFRESH_TOKENS,
|
||||
Value: &storepb.UserSetting_RefreshTokens{
|
||||
RefreshTokens: &storepb.RefreshTokensUserSetting{
|
||||
RefreshTokens: tokens,
|
||||
},
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoveUserRefreshToken removes a refresh token from the user.
|
||||
func (s *Store) RemoveUserRefreshToken(ctx context.Context, userID int32, tokenID string) error {
|
||||
existingTokens, err := s.GetUserRefreshTokens(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newTokens := make([]*storepb.RefreshTokensUserSetting_RefreshToken, 0, len(existingTokens))
|
||||
for _, token := range existingTokens {
|
||||
if token.TokenId != tokenID {
|
||||
newTokens = append(newTokens, token)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = s.UpsertUserSetting(ctx, &storepb.UserSetting{
|
||||
UserId: userID,
|
||||
Key: storepb.UserSetting_ACCESS_TOKENS,
|
||||
Value: &storepb.UserSetting_AccessTokens{
|
||||
AccessTokens: &storepb.AccessTokensUserSetting{
|
||||
AccessTokens: newAccessTokens,
|
||||
Key: storepb.UserSetting_REFRESH_TOKENS,
|
||||
Value: &storepb.UserSetting_RefreshTokens{
|
||||
RefreshTokens: &storepb.RefreshTokensUserSetting{
|
||||
RefreshTokens: newTokens,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// GetUserSessions returns the sessions of the user.
|
||||
func (s *Store) GetUserSessions(ctx context.Context, userID int32) ([]*storepb.SessionsUserSetting_Session, error) {
|
||||
// GetUserRefreshTokenByID returns a specific refresh token.
|
||||
func (s *Store) GetUserRefreshTokenByID(ctx context.Context, userID int32, tokenID string) (*storepb.RefreshTokensUserSetting_RefreshToken, error) {
|
||||
tokens, err := s.GetUserRefreshTokens(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, token := range tokens {
|
||||
if token.TokenId == tokenID {
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetUserPersonalAccessTokens returns the PATs of the user.
|
||||
func (s *Store) GetUserPersonalAccessTokens(ctx context.Context, userID int32) ([]*storepb.PersonalAccessTokensUserSetting_PersonalAccessToken, error) {
|
||||
userSetting, err := s.GetUserSetting(ctx, &FindUserSetting{
|
||||
UserID: &userID,
|
||||
Key: storepb.UserSetting_SESSIONS,
|
||||
Key: storepb.UserSetting_PERSONAL_ACCESS_TOKENS,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if userSetting == nil {
|
||||
return []*storepb.SessionsUserSetting_Session{}, nil
|
||||
return []*storepb.PersonalAccessTokensUserSetting_PersonalAccessToken{}, nil
|
||||
}
|
||||
|
||||
sessionsUserSetting := userSetting.GetSessions()
|
||||
return sessionsUserSetting.Sessions, nil
|
||||
return userSetting.GetPersonalAccessTokens().Tokens, nil
|
||||
}
|
||||
|
||||
// RemoveUserSession removes the session of the user.
|
||||
func (s *Store) RemoveUserSession(ctx context.Context, userID int32, sessionID string) error {
|
||||
oldSessions, err := s.GetUserSessions(ctx, userID)
|
||||
// AddUserPersonalAccessToken adds a new PAT for the user.
|
||||
func (s *Store) AddUserPersonalAccessToken(ctx context.Context, userID int32, token *storepb.PersonalAccessTokensUserSetting_PersonalAccessToken) error {
|
||||
tokens, err := s.GetUserPersonalAccessTokens(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newSessions := make([]*storepb.SessionsUserSetting_Session, 0, len(oldSessions))
|
||||
for _, session := range oldSessions {
|
||||
if sessionID != session.SessionId {
|
||||
newSessions = append(newSessions, session)
|
||||
tokens = append(tokens, token)
|
||||
|
||||
_, err = s.UpsertUserSetting(ctx, &storepb.UserSetting{
|
||||
UserId: userID,
|
||||
Key: storepb.UserSetting_PERSONAL_ACCESS_TOKENS,
|
||||
Value: &storepb.UserSetting_PersonalAccessTokens{
|
||||
PersonalAccessTokens: &storepb.PersonalAccessTokensUserSetting{
|
||||
Tokens: tokens,
|
||||
},
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoveUserPersonalAccessToken removes a PAT from the user.
|
||||
func (s *Store) RemoveUserPersonalAccessToken(ctx context.Context, userID int32, tokenID string) error {
|
||||
existingTokens, err := s.GetUserPersonalAccessTokens(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newTokens := make([]*storepb.PersonalAccessTokensUserSetting_PersonalAccessToken, 0, len(existingTokens))
|
||||
for _, token := range existingTokens {
|
||||
if token.TokenId != tokenID {
|
||||
newTokens = append(newTokens, token)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = s.UpsertUserSetting(ctx, &storepb.UserSetting{
|
||||
UserId: userID,
|
||||
Key: storepb.UserSetting_SESSIONS,
|
||||
Value: &storepb.UserSetting_Sessions{
|
||||
Sessions: &storepb.SessionsUserSetting{
|
||||
Sessions: newSessions,
|
||||
Key: storepb.UserSetting_PERSONAL_ACCESS_TOKENS,
|
||||
Value: &storepb.UserSetting_PersonalAccessTokens{
|
||||
PersonalAccessTokens: &storepb.PersonalAccessTokensUserSetting{
|
||||
Tokens: newTokens,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// AddUserSession adds a new session for the user.
|
||||
func (s *Store) AddUserSession(ctx context.Context, userID int32, session *storepb.SessionsUserSetting_Session) error {
|
||||
existingSessions, err := s.GetUserSessions(ctx, userID)
|
||||
// UpdatePATLastUsed updates the last_used_at timestamp of a PAT.
|
||||
func (s *Store) UpdatePATLastUsed(ctx context.Context, userID int32, tokenID string, lastUsed *timestamppb.Timestamp) error {
|
||||
tokens, err := s.GetUserPersonalAccessTokens(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if session already exists, update if it does
|
||||
var updatedSessions []*storepb.SessionsUserSetting_Session
|
||||
sessionExists := false
|
||||
for _, existing := range existingSessions {
|
||||
if existing.SessionId == session.SessionId {
|
||||
updatedSessions = append(updatedSessions, session)
|
||||
sessionExists = true
|
||||
} else {
|
||||
updatedSessions = append(updatedSessions, existing)
|
||||
}
|
||||
}
|
||||
|
||||
// If session doesn't exist, add it
|
||||
if !sessionExists {
|
||||
updatedSessions = append(updatedSessions, session)
|
||||
}
|
||||
|
||||
_, err = s.UpsertUserSetting(ctx, &storepb.UserSetting{
|
||||
UserId: userID,
|
||||
Key: storepb.UserSetting_SESSIONS,
|
||||
Value: &storepb.UserSetting_Sessions{
|
||||
Sessions: &storepb.SessionsUserSetting{
|
||||
Sessions: updatedSessions,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateUserSessionLastAccessed updates the last accessed time of a session.
|
||||
func (s *Store) UpdateUserSessionLastAccessed(ctx context.Context, userID int32, sessionID string, lastAccessedTime *timestamppb.Timestamp) error {
|
||||
sessions, err := s.GetUserSessions(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, session := range sessions {
|
||||
if session.SessionId == sessionID {
|
||||
session.LastAccessedTime = lastAccessedTime
|
||||
for _, token := range tokens {
|
||||
if token.TokenId == tokenID {
|
||||
token.LastUsedAt = lastUsed
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
_, err = s.UpsertUserSetting(ctx, &storepb.UserSetting{
|
||||
UserId: userID,
|
||||
Key: storepb.UserSetting_SESSIONS,
|
||||
Value: &storepb.UserSetting_Sessions{
|
||||
Sessions: &storepb.SessionsUserSetting{
|
||||
Sessions: sessions,
|
||||
Key: storepb.UserSetting_PERSONAL_ACCESS_TOKENS,
|
||||
Value: &storepb.UserSetting_PersonalAccessTokens{
|
||||
PersonalAccessTokens: &storepb.PersonalAccessTokensUserSetting{
|
||||
Tokens: tokens,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// GetUserSessionByID returns the session details for the given session ID.
|
||||
// Uses database-specific JSON queries for efficient lookup without loading all sessions.
|
||||
func (s *Store) GetUserSessionByID(ctx context.Context, sessionID string) (*UserSessionQueryResult, error) {
|
||||
return s.driver.GetUserSessionByID(ctx, sessionID)
|
||||
}
|
||||
|
||||
// GetUserWebhooks returns the webhooks of the user.
|
||||
func (s *Store) GetUserWebhooks(ctx context.Context, userID int32) ([]*storepb.WebhooksUserSetting_Webhook, error) {
|
||||
userSetting, err := s.GetUserSetting(ctx, &FindUserSetting{
|
||||
|
|
@ -392,6 +425,18 @@ func convertUserSettingFromRaw(raw *UserSetting) (*storepb.UserSetting, error) {
|
|||
return nil, err
|
||||
}
|
||||
userSetting.Value = &storepb.UserSetting_General{General: generalUserSetting}
|
||||
case storepb.UserSetting_REFRESH_TOKENS:
|
||||
refreshTokensUserSetting := &storepb.RefreshTokensUserSetting{}
|
||||
if err := protojsonUnmarshaler.Unmarshal([]byte(raw.Value), refreshTokensUserSetting); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userSetting.Value = &storepb.UserSetting_RefreshTokens{RefreshTokens: refreshTokensUserSetting}
|
||||
case storepb.UserSetting_PERSONAL_ACCESS_TOKENS:
|
||||
patsUserSetting := &storepb.PersonalAccessTokensUserSetting{}
|
||||
if err := protojsonUnmarshaler.Unmarshal([]byte(raw.Value), patsUserSetting); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userSetting.Value = &storepb.UserSetting_PersonalAccessTokens{PersonalAccessTokens: patsUserSetting}
|
||||
case storepb.UserSetting_WEBHOOKS:
|
||||
webhooksUserSetting := &storepb.WebhooksUserSetting{}
|
||||
if err := protojsonUnmarshaler.Unmarshal([]byte(raw.Value), webhooksUserSetting); err != nil {
|
||||
|
|
@ -439,6 +484,20 @@ func convertUserSettingToRaw(userSetting *storepb.UserSetting) (*UserSetting, er
|
|||
return nil, err
|
||||
}
|
||||
raw.Value = string(value)
|
||||
case storepb.UserSetting_REFRESH_TOKENS:
|
||||
refreshTokensUserSetting := userSetting.GetRefreshTokens()
|
||||
value, err := protojson.Marshal(refreshTokensUserSetting)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw.Value = string(value)
|
||||
case storepb.UserSetting_PERSONAL_ACCESS_TOKENS:
|
||||
patsUserSetting := userSetting.GetPersonalAccessTokens()
|
||||
value, err := protojson.Marshal(patsUserSetting)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw.Value = string(value)
|
||||
case storepb.UserSetting_WEBHOOKS:
|
||||
webhooksUserSetting := userSetting.GetWebhooks()
|
||||
value, err := protojson.Marshal(webhooksUserSetting)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
// In-memory storage for access token (not persisted for security)
|
||||
let accessToken: string | null = null;
|
||||
let tokenExpiresAt: Date | null = null;
|
||||
|
||||
export const getAccessToken = (): string | null => accessToken;
|
||||
|
||||
export const setAccessToken = (token: string | null, expiresAt?: Date): void => {
|
||||
accessToken = token;
|
||||
tokenExpiresAt = expiresAt || null;
|
||||
};
|
||||
|
||||
export const isTokenExpired = (): boolean => {
|
||||
if (!tokenExpiresAt) return true;
|
||||
// Consider expired 30 seconds before actual expiry for safety
|
||||
return new Date() >= new Date(tokenExpiresAt.getTime() - 30000);
|
||||
};
|
||||
|
||||
export const clearAccessToken = (): void => {
|
||||
accessToken = null;
|
||||
tokenExpiresAt = null;
|
||||
};
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
import { timestampFromDate } from "@bufbuild/protobuf/wkt";
|
||||
import React, { useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
|
@ -9,13 +8,13 @@ import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|||
import { userServiceClient } from "@/connect";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import useLoading from "@/hooks/useLoading";
|
||||
import { UserAccessToken } from "@/types/proto/api/v1/user_service_pb";
|
||||
import { CreatePersonalAccessTokenResponse } from "@/types/proto/api/v1/user_service_pb";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onSuccess: (created: UserAccessToken) => void;
|
||||
onSuccess: (response: CreatePersonalAccessTokenResponse) => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
|
@ -28,18 +27,19 @@ function CreateAccessTokenDialog({ open, onOpenChange, onSuccess }: Props) {
|
|||
const currentUser = useCurrentUser();
|
||||
const [state, setState] = useState({
|
||||
description: "",
|
||||
expiration: 3600 * 8,
|
||||
expiration: 30, // Default: 30 days
|
||||
});
|
||||
const requestState = useLoading(false);
|
||||
|
||||
// Expiration options in days (0 = never expires)
|
||||
const expirationOptions = [
|
||||
{
|
||||
label: t("setting.access-token-section.create-dialog.duration-8h"),
|
||||
value: 3600 * 8,
|
||||
label: t("setting.access-token-section.create-dialog.duration-1m"),
|
||||
value: 30,
|
||||
},
|
||||
{
|
||||
label: t("setting.access-token-section.create-dialog.duration-1m"),
|
||||
value: 3600 * 24 * 30,
|
||||
label: "90 Days",
|
||||
value: 90,
|
||||
},
|
||||
{
|
||||
label: t("setting.access-token-section.create-dialog.duration-never"),
|
||||
|
|
@ -74,16 +74,14 @@ function CreateAccessTokenDialog({ open, onOpenChange, onSuccess }: Props) {
|
|||
|
||||
try {
|
||||
requestState.setLoading();
|
||||
const created = await userServiceClient.createUserAccessToken({
|
||||
const response = await userServiceClient.createPersonalAccessToken({
|
||||
parent: currentUser.name,
|
||||
accessToken: {
|
||||
description: state.description,
|
||||
expiresAt: state.expiration ? timestampFromDate(new Date(Date.now() + state.expiration * 1000)) : undefined,
|
||||
},
|
||||
description: state.description,
|
||||
expiresInDays: state.expiration,
|
||||
});
|
||||
|
||||
requestState.setFinish();
|
||||
onSuccess(created);
|
||||
onSuccess(response);
|
||||
onOpenChange(false);
|
||||
} catch (error: any) {
|
||||
toast.error(error.message);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import { timestampDate } from "@bufbuild/protobuf/wkt";
|
||||
import { ConnectError } from "@connectrpc/connect";
|
||||
import { LoaderIcon } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { setAccessToken } from "@/auth-state";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { authServiceClient } from "@/connect";
|
||||
|
|
@ -45,12 +47,16 @@ const PasswordSignInForm = observer(() => {
|
|||
|
||||
try {
|
||||
actionBtnLoadingState.setLoading();
|
||||
await authServiceClient.createSession({
|
||||
const response = await authServiceClient.signIn({
|
||||
credentials: {
|
||||
case: "passwordCredentials",
|
||||
value: { username, password },
|
||||
},
|
||||
});
|
||||
// Store access token from login response
|
||||
if (response.accessToken) {
|
||||
setAccessToken(response.accessToken, response.accessTokenExpiresAt ? timestampDate(response.accessTokenExpiresAt) : undefined);
|
||||
}
|
||||
await initialUserStore();
|
||||
navigateTo("/");
|
||||
} catch (error: any) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { timestampDate } from "@bufbuild/protobuf/wkt";
|
||||
import copy from "copy-to-clipboard";
|
||||
import { ClipboardIcon, PlusIcon, TrashIcon } from "lucide-react";
|
||||
import { PlusIcon, TrashIcon } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import ConfirmDialog from "@/components/ConfirmDialog";
|
||||
|
|
@ -8,66 +8,65 @@ import { Button } from "@/components/ui/button";
|
|||
import { userServiceClient } from "@/connect";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import { useDialog } from "@/hooks/useDialog";
|
||||
import { UserAccessToken } from "@/types/proto/api/v1/user_service_pb";
|
||||
import { CreatePersonalAccessTokenResponse, PersonalAccessToken } from "@/types/proto/api/v1/user_service_pb";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
import CreateAccessTokenDialog from "../CreateAccessTokenDialog";
|
||||
import SettingTable from "./SettingTable";
|
||||
|
||||
const listAccessTokens = async (parent: string) => {
|
||||
const { accessTokens } = await userServiceClient.listUserAccessTokens({ parent });
|
||||
return accessTokens.sort(
|
||||
const { personalAccessTokens } = await userServiceClient.listPersonalAccessTokens({ parent });
|
||||
return personalAccessTokens.sort(
|
||||
(a, b) =>
|
||||
((b.issuedAt ? timestampDate(b.issuedAt) : undefined)?.getTime() ?? 0) -
|
||||
((a.issuedAt ? timestampDate(a.issuedAt) : undefined)?.getTime() ?? 0),
|
||||
((b.createdAt ? timestampDate(b.createdAt) : undefined)?.getTime() ?? 0) -
|
||||
((a.createdAt ? timestampDate(a.createdAt) : undefined)?.getTime() ?? 0),
|
||||
);
|
||||
};
|
||||
|
||||
const AccessTokenSection = () => {
|
||||
const t = useTranslate();
|
||||
const currentUser = useCurrentUser();
|
||||
const [userAccessTokens, setUserAccessTokens] = useState<UserAccessToken[]>([]);
|
||||
const [personalAccessTokens, setPersonalAccessTokens] = useState<PersonalAccessToken[]>([]);
|
||||
const createTokenDialog = useDialog();
|
||||
const [deleteTarget, setDeleteTarget] = useState<UserAccessToken | undefined>(undefined);
|
||||
const [deleteTarget, setDeleteTarget] = useState<PersonalAccessToken | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
listAccessTokens(currentUser.name).then((accessTokens) => {
|
||||
setUserAccessTokens(accessTokens);
|
||||
listAccessTokens(currentUser.name).then((tokens) => {
|
||||
setPersonalAccessTokens(tokens);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleCreateAccessTokenDialogConfirm = async (created: UserAccessToken) => {
|
||||
const accessTokens = await listAccessTokens(currentUser.name);
|
||||
setUserAccessTokens(accessTokens);
|
||||
toast.success(t("setting.access-token-section.create-dialog.access-token-created", { description: created.description }));
|
||||
const handleCreateAccessTokenDialogConfirm = async (response: CreatePersonalAccessTokenResponse) => {
|
||||
const tokens = await listAccessTokens(currentUser.name);
|
||||
setPersonalAccessTokens(tokens);
|
||||
// Copy the token to clipboard - this is the only time it will be shown
|
||||
if (response.token) {
|
||||
copy(response.token);
|
||||
toast.success(t("setting.access-token-section.access-token-copied-to-clipboard"));
|
||||
}
|
||||
toast.success(
|
||||
t("setting.access-token-section.create-dialog.access-token-created", {
|
||||
description: response.personalAccessToken?.description ?? "",
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const handleCreateToken = () => {
|
||||
createTokenDialog.open();
|
||||
};
|
||||
|
||||
const copyAccessToken = (accessToken: string) => {
|
||||
copy(accessToken);
|
||||
toast.success(t("setting.access-token-section.access-token-copied-to-clipboard"));
|
||||
};
|
||||
|
||||
const handleDeleteAccessToken = async (userAccessToken: UserAccessToken) => {
|
||||
setDeleteTarget(userAccessToken);
|
||||
const handleDeleteAccessToken = async (token: PersonalAccessToken) => {
|
||||
setDeleteTarget(token);
|
||||
};
|
||||
|
||||
const confirmDeleteAccessToken = async () => {
|
||||
if (!deleteTarget) return;
|
||||
const { name: tokenName, description } = deleteTarget;
|
||||
await userServiceClient.deleteUserAccessToken({ name: tokenName });
|
||||
// Filter by stable resource name to avoid ambiguity with duplicate token strings
|
||||
setUserAccessTokens((prev) => prev.filter((token) => token.name !== tokenName));
|
||||
await userServiceClient.deletePersonalAccessToken({ name: tokenName });
|
||||
setPersonalAccessTokens((prev) => prev.filter((token) => token.name !== tokenName));
|
||||
setDeleteTarget(undefined);
|
||||
toast.success(t("setting.access-token-section.access-token-deleted", { description }));
|
||||
};
|
||||
|
||||
const getFormatedAccessToken = (accessToken: string) => {
|
||||
return `${accessToken.slice(0, 4)}****${accessToken.slice(-4)}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col gap-2">
|
||||
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-2">
|
||||
|
|
@ -83,32 +82,20 @@ const AccessTokenSection = () => {
|
|||
|
||||
<SettingTable
|
||||
columns={[
|
||||
{
|
||||
key: "accessToken",
|
||||
header: t("setting.access-token-section.token"),
|
||||
render: (_, token: UserAccessToken) => (
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="font-mono text-foreground">{getFormatedAccessToken(token.accessToken)}</span>
|
||||
<Button variant="ghost" size="sm" onClick={() => copyAccessToken(token.accessToken)}>
|
||||
<ClipboardIcon className="w-4 h-auto text-muted-foreground" />
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "description",
|
||||
header: t("common.description"),
|
||||
render: (_, token: UserAccessToken) => <span className="text-foreground">{token.description}</span>,
|
||||
render: (_, token: PersonalAccessToken) => <span className="text-foreground">{token.description}</span>,
|
||||
},
|
||||
{
|
||||
key: "issuedAt",
|
||||
key: "createdAt",
|
||||
header: t("setting.access-token-section.create-dialog.created-at"),
|
||||
render: (_, token: UserAccessToken) => (token.issuedAt ? timestampDate(token.issuedAt) : undefined)?.toLocaleString(),
|
||||
render: (_, token: PersonalAccessToken) => (token.createdAt ? timestampDate(token.createdAt) : undefined)?.toLocaleString(),
|
||||
},
|
||||
{
|
||||
key: "expiresAt",
|
||||
header: t("setting.access-token-section.create-dialog.expires-at"),
|
||||
render: (_, token: UserAccessToken) =>
|
||||
render: (_, token: PersonalAccessToken) =>
|
||||
(token.expiresAt ? timestampDate(token.expiresAt) : undefined)?.toLocaleString() ??
|
||||
t("setting.access-token-section.create-dialog.duration-never"),
|
||||
},
|
||||
|
|
@ -116,14 +103,14 @@ const AccessTokenSection = () => {
|
|||
key: "actions",
|
||||
header: "",
|
||||
className: "text-right",
|
||||
render: (_, token: UserAccessToken) => (
|
||||
render: (_, token: PersonalAccessToken) => (
|
||||
<Button variant="ghost" size="sm" onClick={() => handleDeleteAccessToken(token)}>
|
||||
<TrashIcon className="text-destructive w-4 h-auto" />
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
data={userAccessTokens}
|
||||
data={personalAccessTokens}
|
||||
emptyMessage="No access tokens found"
|
||||
getRowKey={(token) => token.name}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ import ConfirmDialog from "@/components/ConfirmDialog";
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { userServiceClient } from "@/connect";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import { UserSession } from "@/types/proto/api/v1/user_service_pb";
|
||||
import { Session } from "@/types/proto/api/v1/user_service_pb";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
import SettingTable from "./SettingTable";
|
||||
|
||||
const listUserSessions = async (parent: string) => {
|
||||
const { sessions } = await userServiceClient.listUserSessions({ parent });
|
||||
const listSessions = async (parent: string) => {
|
||||
const { sessions } = await userServiceClient.listSessions({ parent });
|
||||
return sessions.sort(
|
||||
(a, b) =>
|
||||
((b.lastAccessedTime ? timestampDate(b.lastAccessedTime) : undefined)?.getTime() ?? 0) -
|
||||
|
|
@ -22,23 +22,23 @@ const listUserSessions = async (parent: string) => {
|
|||
const UserSessionsSection = () => {
|
||||
const t = useTranslate();
|
||||
const currentUser = useCurrentUser();
|
||||
const [userSessions, setUserSessions] = useState<UserSession[]>([]);
|
||||
const [revokeTarget, setRevokeTarget] = useState<UserSession | undefined>(undefined);
|
||||
const [sessions, setSessions] = useState<Session[]>([]);
|
||||
const [revokeTarget, setRevokeTarget] = useState<Session | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
listUserSessions(currentUser.name).then((sessions) => {
|
||||
setUserSessions(sessions);
|
||||
listSessions(currentUser.name).then((sessions) => {
|
||||
setSessions(sessions);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleRevokeSession = async (userSession: UserSession) => {
|
||||
setRevokeTarget(userSession);
|
||||
const handleRevokeSession = async (session: Session) => {
|
||||
setRevokeTarget(session);
|
||||
};
|
||||
|
||||
const confirmRevokeSession = async () => {
|
||||
if (!revokeTarget) return;
|
||||
await userServiceClient.revokeUserSession({ name: revokeTarget.name });
|
||||
setUserSessions(userSessions.filter((session) => session.sessionId !== revokeTarget.sessionId));
|
||||
await userServiceClient.revokeSession({ name: revokeTarget.name });
|
||||
setSessions(sessions.filter((session) => session.sessionId !== revokeTarget.sessionId));
|
||||
toast.success(t("setting.user-sessions-section.session-revoked"));
|
||||
setRevokeTarget(undefined);
|
||||
};
|
||||
|
|
@ -59,7 +59,7 @@ const UserSessionsSection = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const formatDeviceInfo = (clientInfo: UserSession["clientInfo"]) => {
|
||||
const formatDeviceInfo = (clientInfo: Session["clientInfo"]) => {
|
||||
if (!clientInfo) return "Unknown Device";
|
||||
|
||||
const parts = [];
|
||||
|
|
@ -69,10 +69,10 @@ const UserSessionsSection = () => {
|
|||
return parts.length > 0 ? parts.join(" • ") : "Unknown Device";
|
||||
};
|
||||
|
||||
const isCurrentSession = (session: UserSession) => {
|
||||
const isCurrentSession = (session: Session) => {
|
||||
// A simple heuristic: the most recently accessed session is likely the current one
|
||||
if (userSessions.length === 0) return false;
|
||||
const mostRecent = userSessions[0];
|
||||
if (sessions.length === 0) return false;
|
||||
const mostRecent = sessions[0];
|
||||
return session.sessionId === mostRecent.sessionId;
|
||||
};
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ const UserSessionsSection = () => {
|
|||
{
|
||||
key: "device",
|
||||
header: t("setting.user-sessions-section.device"),
|
||||
render: (_, session: UserSession) => (
|
||||
render: (_, session: Session) => (
|
||||
<div className="flex items-center space-x-3">
|
||||
{getDeviceIcon(session.clientInfo?.deviceType || "")}
|
||||
<div className="flex flex-col">
|
||||
|
|
@ -109,7 +109,7 @@ const UserSessionsSection = () => {
|
|||
{
|
||||
key: "lastAccessedTime",
|
||||
header: t("setting.user-sessions-section.last-active"),
|
||||
render: (_, session: UserSession) => (
|
||||
render: (_, session: Session) => (
|
||||
<div className="flex items-center space-x-1">
|
||||
<ClockIcon className="w-4 h-4" />
|
||||
<span>{(session.lastAccessedTime ? timestampDate(session.lastAccessedTime) : undefined)?.toLocaleString()}</span>
|
||||
|
|
@ -120,7 +120,7 @@ const UserSessionsSection = () => {
|
|||
key: "actions",
|
||||
header: "",
|
||||
className: "text-right",
|
||||
render: (_, session: UserSession) => (
|
||||
render: (_, session: Session) => (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
|
|
@ -137,7 +137,7 @@ const UserSessionsSection = () => {
|
|||
),
|
||||
},
|
||||
]}
|
||||
data={userSessions}
|
||||
data={sessions}
|
||||
emptyMessage={t("setting.user-sessions-section.no-sessions")}
|
||||
getRowKey={(session) => session.sessionId}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ const UserMenu = observer((props: Props) => {
|
|||
};
|
||||
|
||||
const handleSignOut = async () => {
|
||||
await authServiceClient.deleteSession({});
|
||||
await authServiceClient.signOut({});
|
||||
|
||||
// Clear user-specific localStorage items (e.g., drafts)
|
||||
// Preserve app-wide settings like theme
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { createClient } from "@connectrpc/connect";
|
||||
import { createClient, Interceptor } from "@connectrpc/connect";
|
||||
import { createConnectTransport } from "@connectrpc/connect-web";
|
||||
import { getAccessToken, setAccessToken } from "./auth-state";
|
||||
import { ActivityService } from "./types/proto/api/v1/activity_service_pb";
|
||||
import { AttachmentService } from "./types/proto/api/v1/attachment_service_pb";
|
||||
import { AuthService } from "./types/proto/api/v1/auth_service_pb";
|
||||
|
|
@ -9,10 +10,71 @@ import { MemoService } from "./types/proto/api/v1/memo_service_pb";
|
|||
import { ShortcutService } from "./types/proto/api/v1/shortcut_service_pb";
|
||||
import { UserService } from "./types/proto/api/v1/user_service_pb";
|
||||
|
||||
let isRefreshing = false;
|
||||
let refreshPromise: Promise<void> | null = null;
|
||||
|
||||
// Auth interceptor that attaches access token and handles 401 errors by refreshing
|
||||
const authInterceptor: Interceptor = (next) => async (req) => {
|
||||
// Add access token to request if available
|
||||
const token = getAccessToken();
|
||||
if (token) {
|
||||
req.header.set("Authorization", `Bearer ${token}`);
|
||||
}
|
||||
|
||||
try {
|
||||
return await next(req);
|
||||
} catch (error: any) {
|
||||
// Handle unauthenticated error - try to refresh token
|
||||
if (error.code === "unauthenticated" && !req.header.get("X-Retry")) {
|
||||
// Prevent concurrent refresh attempts
|
||||
if (!isRefreshing) {
|
||||
isRefreshing = true;
|
||||
refreshPromise = refreshAccessToken();
|
||||
}
|
||||
|
||||
try {
|
||||
await refreshPromise;
|
||||
isRefreshing = false;
|
||||
refreshPromise = null;
|
||||
|
||||
// Retry with new token
|
||||
const newToken = getAccessToken();
|
||||
if (newToken) {
|
||||
req.header.set("Authorization", `Bearer ${newToken}`);
|
||||
req.header.set("X-Retry", "true");
|
||||
return await next(req);
|
||||
}
|
||||
} catch (refreshError) {
|
||||
isRefreshing = false;
|
||||
refreshPromise = null;
|
||||
// Refresh failed - redirect to login
|
||||
window.location.href = "/auth";
|
||||
throw refreshError;
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
async function refreshAccessToken(): Promise<void> {
|
||||
const response = await fetch("/api/v1/auth/refresh", {
|
||||
method: "POST",
|
||||
credentials: "include", // Include HttpOnly cookies with refresh token
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to refresh token");
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
setAccessToken(data.accessToken, new Date(data.expiresAt));
|
||||
}
|
||||
|
||||
const transport = createConnectTransport({
|
||||
baseUrl: window.location.origin,
|
||||
// Use binary protobuf format for better performance (smaller payloads, faster serialization)
|
||||
useBinaryFormat: true,
|
||||
interceptors: [authInterceptor],
|
||||
});
|
||||
|
||||
// Core service clients
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import { timestampDate } from "@bufbuild/protobuf/wkt";
|
||||
import { ConnectError } from "@connectrpc/connect";
|
||||
import { LoaderIcon } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
import { setAccessToken } from "@/auth-state";
|
||||
import { authServiceClient } from "@/connect";
|
||||
import { absolutifyLink } from "@/helpers/utils";
|
||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||
|
|
@ -71,7 +73,7 @@ const AuthCallback = observer(() => {
|
|||
|
||||
(async () => {
|
||||
try {
|
||||
await authServiceClient.createSession({
|
||||
const response = await authServiceClient.signIn({
|
||||
credentials: {
|
||||
case: "ssoCredentials",
|
||||
value: {
|
||||
|
|
@ -82,6 +84,10 @@ const AuthCallback = observer(() => {
|
|||
},
|
||||
},
|
||||
});
|
||||
// Store access token from login response
|
||||
if (response.accessToken) {
|
||||
setAccessToken(response.accessToken, response.accessTokenExpiresAt ? timestampDate(response.accessTokenExpiresAt) : undefined);
|
||||
}
|
||||
setState({
|
||||
loading: false,
|
||||
errorMessage: "",
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import { create } from "@bufbuild/protobuf";
|
||||
import { timestampDate } from "@bufbuild/protobuf/wkt";
|
||||
import { ConnectError } from "@connectrpc/connect";
|
||||
import { LoaderIcon } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { Link } from "react-router-dom";
|
||||
import { setAccessToken } from "@/auth-state";
|
||||
import AuthFooter from "@/components/AuthFooter";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
|
|
@ -56,12 +58,16 @@ const SignUp = observer(() => {
|
|||
role: User_Role.USER,
|
||||
});
|
||||
await userServiceClient.createUser({ user });
|
||||
await authServiceClient.createSession({
|
||||
const response = await authServiceClient.signIn({
|
||||
credentials: {
|
||||
case: "passwordCredentials",
|
||||
value: { username, password },
|
||||
},
|
||||
});
|
||||
// Store access token from login response
|
||||
if (response.accessToken) {
|
||||
setAccessToken(response.accessToken, response.accessTokenExpiresAt ? timestampDate(response.accessTokenExpiresAt) : undefined);
|
||||
}
|
||||
await initialUserStore();
|
||||
navigateTo("/");
|
||||
} catch (error: any) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { create } from "@bufbuild/protobuf";
|
|||
import { FieldMaskSchema } from "@bufbuild/protobuf/wkt";
|
||||
import { uniqueId } from "lodash-es";
|
||||
import { computed, makeAutoObservable } from "mobx";
|
||||
import { clearAccessToken, setAccessToken } from "@/auth-state";
|
||||
import { authServiceClient, shortcutServiceClient, userServiceClient } from "@/connect";
|
||||
import { Shortcut } from "@/types/proto/api/v1/shortcut_service_pb";
|
||||
import {
|
||||
|
|
@ -322,13 +323,21 @@ const userStore = (() => {
|
|||
// 1. Fetch current authenticated user session
|
||||
// 2. Set current user in store (required for subsequent calls)
|
||||
// 3. Fetch user settings (depends on currentUser being set)
|
||||
//
|
||||
// Auth flow:
|
||||
// - On first call, GetCurrentSession has no access token
|
||||
// - The interceptor will automatically call RefreshToken using the HttpOnly refresh cookie
|
||||
// - If refresh succeeds, GetCurrentSession is retried with the new access token
|
||||
// - If refresh fails (no cookie or expired), user needs to login
|
||||
export const initialUserStore = async () => {
|
||||
try {
|
||||
// Step 1: Authenticate and get current user
|
||||
const { user: currentUser } = await authServiceClient.getCurrentSession({});
|
||||
// The interceptor will handle token refresh if needed
|
||||
const { user: currentUser } = await authServiceClient.getCurrentUser({});
|
||||
|
||||
if (!currentUser) {
|
||||
// No authenticated user - clear state
|
||||
clearAccessToken();
|
||||
userStore.state.setPartial({
|
||||
currentUser: undefined,
|
||||
userGeneralSetting: undefined,
|
||||
|
|
@ -351,8 +360,44 @@ export const initialUserStore = async () => {
|
|||
// CRITICAL: This must happen after currentUser is set in step 2
|
||||
// The fetchUserSettings() and fetchUserStats() methods check state.currentUser internally
|
||||
await Promise.all([userStore.fetchUserSettings(), userStore.fetchUserStats()]);
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
// Auth failed (no refresh token, expired, or other error)
|
||||
// Clear state and let user login again
|
||||
console.error("Failed to initialize user store:", error);
|
||||
clearAccessToken();
|
||||
userStore.state.setPartial({
|
||||
currentUser: undefined,
|
||||
userGeneralSetting: undefined,
|
||||
userMapByName: {},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Logout function that clears tokens and state
|
||||
// This calls DeleteSession which:
|
||||
// 1. Revokes the refresh token in the database
|
||||
// 2. Clears both session and refresh token cookies
|
||||
// We then clear the in-memory access token and reset the store state
|
||||
export const logout = async () => {
|
||||
try {
|
||||
await authServiceClient.signOut({});
|
||||
} catch (error) {
|
||||
// Log error but continue with local cleanup
|
||||
console.error("Failed to delete session on server:", error);
|
||||
} finally {
|
||||
// Always clear local state, even if server call fails
|
||||
clearAccessToken();
|
||||
userStore.state.setPartial({
|
||||
currentUser: undefined,
|
||||
userGeneralSetting: undefined,
|
||||
userSessionsSetting: undefined,
|
||||
userAccessTokensSetting: undefined,
|
||||
userWebhooksSetting: undefined,
|
||||
shortcuts: [],
|
||||
notifications: [],
|
||||
userMapByName: {},
|
||||
userStatsByName: {},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// @generated by protoc-gen-es v2.10.1 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.2 with parameter "target=ts"
|
||||
// @generated from file api/v1/activity_service.proto (package memos.api.v1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// @generated by protoc-gen-es v2.10.1 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.2 with parameter "target=ts"
|
||||
// @generated from file api/v1/attachment_service.proto (package memos.api.v1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// @generated by protoc-gen-es v2.10.1 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.2 with parameter "target=ts"
|
||||
// @generated from file api/v1/auth_service.proto (package memos.api.v1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
|
@ -16,91 +16,83 @@ import type { Message } from "@bufbuild/protobuf";
|
|||
* Describes the file api/v1/auth_service.proto.
|
||||
*/
|
||||
export const file_api_v1_auth_service: GenFile = /*@__PURE__*/
|
||||
fileDesc("ChlhcGkvdjEvYXV0aF9zZXJ2aWNlLnByb3RvEgxtZW1vcy5hcGkudjEiGgoYR2V0Q3VycmVudFNlc3Npb25SZXF1ZXN0InMKGUdldEN1cnJlbnRTZXNzaW9uUmVzcG9uc2USIAoEdXNlchgBIAEoCzISLm1lbW9zLmFwaS52MS5Vc2VyEjQKEGxhc3RfYWNjZXNzZWRfYXQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wIoEDChRDcmVhdGVTZXNzaW9uUmVxdWVzdBJWChRwYXNzd29yZF9jcmVkZW50aWFscxgBIAEoCzI2Lm1lbW9zLmFwaS52MS5DcmVhdGVTZXNzaW9uUmVxdWVzdC5QYXNzd29yZENyZWRlbnRpYWxzSAASTAoPc3NvX2NyZWRlbnRpYWxzGAIgASgLMjEubWVtb3MuYXBpLnYxLkNyZWF0ZVNlc3Npb25SZXF1ZXN0LlNTT0NyZWRlbnRpYWxzSAAaQwoTUGFzc3dvcmRDcmVkZW50aWFscxIVCgh1c2VybmFtZRgBIAEoCUID4EECEhUKCHBhc3N3b3JkGAIgASgJQgPgQQIabwoOU1NPQ3JlZGVudGlhbHMSEwoGaWRwX2lkGAEgASgFQgPgQQISEQoEY29kZRgCIAEoCUID4EECEhkKDHJlZGlyZWN0X3VyaRgDIAEoCUID4EECEhoKDWNvZGVfdmVyaWZpZXIYBCABKAlCA+BBAUINCgtjcmVkZW50aWFscyJvChVDcmVhdGVTZXNzaW9uUmVzcG9uc2USIAoEdXNlchgBIAEoCzISLm1lbW9zLmFwaS52MS5Vc2VyEjQKEGxhc3RfYWNjZXNzZWRfYXQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wIhYKFERlbGV0ZVNlc3Npb25SZXF1ZXN0MosDCgtBdXRoU2VydmljZRKLAQoRR2V0Q3VycmVudFNlc3Npb24SJi5tZW1vcy5hcGkudjEuR2V0Q3VycmVudFNlc3Npb25SZXF1ZXN0GicubWVtb3MuYXBpLnYxLkdldEN1cnJlbnRTZXNzaW9uUmVzcG9uc2UiJYLT5JMCHxIdL2FwaS92MS9hdXRoL3Nlc3Npb25zL2N1cnJlbnQSegoNQ3JlYXRlU2Vzc2lvbhIiLm1lbW9zLmFwaS52MS5DcmVhdGVTZXNzaW9uUmVxdWVzdBojLm1lbW9zLmFwaS52MS5DcmVhdGVTZXNzaW9uUmVzcG9uc2UiIILT5JMCGjoBKiIVL2FwaS92MS9hdXRoL3Nlc3Npb25zEnIKDURlbGV0ZVNlc3Npb24SIi5tZW1vcy5hcGkudjEuRGVsZXRlU2Vzc2lvblJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiJYLT5JMCHyodL2FwaS92MS9hdXRoL3Nlc3Npb25zL2N1cnJlbnRCqAEKEGNvbS5tZW1vcy5hcGkudjFCEEF1dGhTZXJ2aWNlUHJvdG9QAVowZ2l0aHViLmNvbS91c2VtZW1vcy9tZW1vcy9wcm90by9nZW4vYXBpL3YxO2FwaXYxogIDTUFYqgIMTWVtb3MuQXBpLlYxygIMTWVtb3NcQXBpXFYx4gIYTWVtb3NcQXBpXFYxXEdQQk1ldGFkYXRh6gIOTWVtb3M6OkFwaTo6VjFiBnByb3RvMw", [file_api_v1_user_service, file_google_api_annotations, file_google_api_field_behavior, file_google_protobuf_empty, file_google_protobuf_timestamp]);
|
||||
fileDesc("ChlhcGkvdjEvYXV0aF9zZXJ2aWNlLnByb3RvEgxtZW1vcy5hcGkudjEiFwoVR2V0Q3VycmVudFVzZXJSZXF1ZXN0IjoKFkdldEN1cnJlbnRVc2VyUmVzcG9uc2USIAoEdXNlchgBIAEoCzISLm1lbW9zLmFwaS52MS5Vc2VyIuwCCg1TaWduSW5SZXF1ZXN0Ek8KFHBhc3N3b3JkX2NyZWRlbnRpYWxzGAEgASgLMi8ubWVtb3MuYXBpLnYxLlNpZ25JblJlcXVlc3QuUGFzc3dvcmRDcmVkZW50aWFsc0gAEkUKD3Nzb19jcmVkZW50aWFscxgCIAEoCzIqLm1lbW9zLmFwaS52MS5TaWduSW5SZXF1ZXN0LlNTT0NyZWRlbnRpYWxzSAAaQwoTUGFzc3dvcmRDcmVkZW50aWFscxIVCgh1c2VybmFtZRgBIAEoCUID4EECEhUKCHBhc3N3b3JkGAIgASgJQgPgQQIabwoOU1NPQ3JlZGVudGlhbHMSEwoGaWRwX2lkGAEgASgFQgPgQQISEQoEY29kZRgCIAEoCUID4EECEhkKDHJlZGlyZWN0X3VyaRgDIAEoCUID4EECEhoKDWNvZGVfdmVyaWZpZXIYBCABKAlCA+BBAUINCgtjcmVkZW50aWFscyKFAQoOU2lnbkluUmVzcG9uc2USIAoEdXNlchgBIAEoCzISLm1lbW9zLmFwaS52MS5Vc2VyEhQKDGFjY2Vzc190b2tlbhgCIAEoCRI7ChdhY2Nlc3NfdG9rZW5fZXhwaXJlc19hdBgDIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXAiEAoOU2lnbk91dFJlcXVlc3QiFQoTUmVmcmVzaFRva2VuUmVxdWVzdCJcChRSZWZyZXNoVG9rZW5SZXNwb25zZRIUCgxhY2Nlc3NfdG9rZW4YASABKAkSLgoKZXhwaXJlc19hdBgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXAyvwMKC0F1dGhTZXJ2aWNlEnQKDkdldEN1cnJlbnRVc2VyEiMubWVtb3MuYXBpLnYxLkdldEN1cnJlbnRVc2VyUmVxdWVzdBokLm1lbW9zLmFwaS52MS5HZXRDdXJyZW50VXNlclJlc3BvbnNlIheC0+STAhESDy9hcGkvdjEvYXV0aC9tZRJjCgZTaWduSW4SGy5tZW1vcy5hcGkudjEuU2lnbkluUmVxdWVzdBocLm1lbW9zLmFwaS52MS5TaWduSW5SZXNwb25zZSIegtPkkwIYOgEqIhMvYXBpL3YxL2F1dGgvc2lnbmluEl0KB1NpZ25PdXQSHC5tZW1vcy5hcGkudjEuU2lnbk91dFJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiHILT5JMCFiIUL2FwaS92MS9hdXRoL3NpZ25vdXQSdgoMUmVmcmVzaFRva2VuEiEubWVtb3MuYXBpLnYxLlJlZnJlc2hUb2tlblJlcXVlc3QaIi5tZW1vcy5hcGkudjEuUmVmcmVzaFRva2VuUmVzcG9uc2UiH4LT5JMCGToBKiIUL2FwaS92MS9hdXRoL3JlZnJlc2hCqAEKEGNvbS5tZW1vcy5hcGkudjFCEEF1dGhTZXJ2aWNlUHJvdG9QAVowZ2l0aHViLmNvbS91c2VtZW1vcy9tZW1vcy9wcm90by9nZW4vYXBpL3YxO2FwaXYxogIDTUFYqgIMTWVtb3MuQXBpLlYxygIMTWVtb3NcQXBpXFYx4gIYTWVtb3NcQXBpXFYxXEdQQk1ldGFkYXRh6gIOTWVtb3M6OkFwaTo6VjFiBnByb3RvMw", [file_api_v1_user_service, file_google_api_annotations, file_google_api_field_behavior, file_google_protobuf_empty, file_google_protobuf_timestamp]);
|
||||
|
||||
/**
|
||||
* @generated from message memos.api.v1.GetCurrentSessionRequest
|
||||
* @generated from message memos.api.v1.GetCurrentUserRequest
|
||||
*/
|
||||
export type GetCurrentSessionRequest = Message<"memos.api.v1.GetCurrentSessionRequest"> & {
|
||||
export type GetCurrentUserRequest = Message<"memos.api.v1.GetCurrentUserRequest"> & {
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message memos.api.v1.GetCurrentSessionRequest.
|
||||
* Use `create(GetCurrentSessionRequestSchema)` to create a new message.
|
||||
* Describes the message memos.api.v1.GetCurrentUserRequest.
|
||||
* Use `create(GetCurrentUserRequestSchema)` to create a new message.
|
||||
*/
|
||||
export const GetCurrentSessionRequestSchema: GenMessage<GetCurrentSessionRequest> = /*@__PURE__*/
|
||||
export const GetCurrentUserRequestSchema: GenMessage<GetCurrentUserRequest> = /*@__PURE__*/
|
||||
messageDesc(file_api_v1_auth_service, 0);
|
||||
|
||||
/**
|
||||
* @generated from message memos.api.v1.GetCurrentSessionResponse
|
||||
* @generated from message memos.api.v1.GetCurrentUserResponse
|
||||
*/
|
||||
export type GetCurrentSessionResponse = Message<"memos.api.v1.GetCurrentSessionResponse"> & {
|
||||
export type GetCurrentUserResponse = Message<"memos.api.v1.GetCurrentUserResponse"> & {
|
||||
/**
|
||||
* The authenticated user's information.
|
||||
*
|
||||
* @generated from field: memos.api.v1.User user = 1;
|
||||
*/
|
||||
user?: User;
|
||||
|
||||
/**
|
||||
* Last time the session was accessed.
|
||||
* Used for sliding expiration calculation (last_accessed_time + 2 weeks).
|
||||
*
|
||||
* @generated from field: google.protobuf.Timestamp last_accessed_at = 2;
|
||||
*/
|
||||
lastAccessedAt?: Timestamp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message memos.api.v1.GetCurrentSessionResponse.
|
||||
* Use `create(GetCurrentSessionResponseSchema)` to create a new message.
|
||||
* Describes the message memos.api.v1.GetCurrentUserResponse.
|
||||
* Use `create(GetCurrentUserResponseSchema)` to create a new message.
|
||||
*/
|
||||
export const GetCurrentSessionResponseSchema: GenMessage<GetCurrentSessionResponse> = /*@__PURE__*/
|
||||
export const GetCurrentUserResponseSchema: GenMessage<GetCurrentUserResponse> = /*@__PURE__*/
|
||||
messageDesc(file_api_v1_auth_service, 1);
|
||||
|
||||
/**
|
||||
* @generated from message memos.api.v1.CreateSessionRequest
|
||||
* @generated from message memos.api.v1.SignInRequest
|
||||
*/
|
||||
export type CreateSessionRequest = Message<"memos.api.v1.CreateSessionRequest"> & {
|
||||
export type SignInRequest = Message<"memos.api.v1.SignInRequest"> & {
|
||||
/**
|
||||
* Provide one authentication method (username/password or SSO).
|
||||
* Required field to specify the authentication method.
|
||||
* Authentication credentials. Provide one method.
|
||||
*
|
||||
* @generated from oneof memos.api.v1.CreateSessionRequest.credentials
|
||||
* @generated from oneof memos.api.v1.SignInRequest.credentials
|
||||
*/
|
||||
credentials: {
|
||||
/**
|
||||
* Username and password authentication method.
|
||||
* Username and password authentication.
|
||||
*
|
||||
* @generated from field: memos.api.v1.CreateSessionRequest.PasswordCredentials password_credentials = 1;
|
||||
* @generated from field: memos.api.v1.SignInRequest.PasswordCredentials password_credentials = 1;
|
||||
*/
|
||||
value: CreateSessionRequest_PasswordCredentials;
|
||||
value: SignInRequest_PasswordCredentials;
|
||||
case: "passwordCredentials";
|
||||
} | {
|
||||
/**
|
||||
* SSO provider authentication method.
|
||||
* SSO provider authentication.
|
||||
*
|
||||
* @generated from field: memos.api.v1.CreateSessionRequest.SSOCredentials sso_credentials = 2;
|
||||
* @generated from field: memos.api.v1.SignInRequest.SSOCredentials sso_credentials = 2;
|
||||
*/
|
||||
value: CreateSessionRequest_SSOCredentials;
|
||||
value: SignInRequest_SSOCredentials;
|
||||
case: "ssoCredentials";
|
||||
} | { case: undefined; value?: undefined };
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message memos.api.v1.CreateSessionRequest.
|
||||
* Use `create(CreateSessionRequestSchema)` to create a new message.
|
||||
* Describes the message memos.api.v1.SignInRequest.
|
||||
* Use `create(SignInRequestSchema)` to create a new message.
|
||||
*/
|
||||
export const CreateSessionRequestSchema: GenMessage<CreateSessionRequest> = /*@__PURE__*/
|
||||
export const SignInRequestSchema: GenMessage<SignInRequest> = /*@__PURE__*/
|
||||
messageDesc(file_api_v1_auth_service, 2);
|
||||
|
||||
/**
|
||||
* Nested message for password-based authentication credentials.
|
||||
*
|
||||
* @generated from message memos.api.v1.CreateSessionRequest.PasswordCredentials
|
||||
* @generated from message memos.api.v1.SignInRequest.PasswordCredentials
|
||||
*/
|
||||
export type CreateSessionRequest_PasswordCredentials = Message<"memos.api.v1.CreateSessionRequest.PasswordCredentials"> & {
|
||||
export type SignInRequest_PasswordCredentials = Message<"memos.api.v1.SignInRequest.PasswordCredentials"> & {
|
||||
/**
|
||||
* The username to sign in with.
|
||||
* Required field for password-based authentication.
|
||||
*
|
||||
* @generated from field: string username = 1;
|
||||
*/
|
||||
|
|
@ -108,7 +100,6 @@ export type CreateSessionRequest_PasswordCredentials = Message<"memos.api.v1.Cre
|
|||
|
||||
/**
|
||||
* The password to sign in with.
|
||||
* Required field for password-based authentication.
|
||||
*
|
||||
* @generated from field: string password = 2;
|
||||
*/
|
||||
|
|
@ -116,21 +107,20 @@ export type CreateSessionRequest_PasswordCredentials = Message<"memos.api.v1.Cre
|
|||
};
|
||||
|
||||
/**
|
||||
* Describes the message memos.api.v1.CreateSessionRequest.PasswordCredentials.
|
||||
* Use `create(CreateSessionRequest_PasswordCredentialsSchema)` to create a new message.
|
||||
* Describes the message memos.api.v1.SignInRequest.PasswordCredentials.
|
||||
* Use `create(SignInRequest_PasswordCredentialsSchema)` to create a new message.
|
||||
*/
|
||||
export const CreateSessionRequest_PasswordCredentialsSchema: GenMessage<CreateSessionRequest_PasswordCredentials> = /*@__PURE__*/
|
||||
export const SignInRequest_PasswordCredentialsSchema: GenMessage<SignInRequest_PasswordCredentials> = /*@__PURE__*/
|
||||
messageDesc(file_api_v1_auth_service, 2, 0);
|
||||
|
||||
/**
|
||||
* Nested message for SSO authentication credentials.
|
||||
*
|
||||
* @generated from message memos.api.v1.CreateSessionRequest.SSOCredentials
|
||||
* @generated from message memos.api.v1.SignInRequest.SSOCredentials
|
||||
*/
|
||||
export type CreateSessionRequest_SSOCredentials = Message<"memos.api.v1.CreateSessionRequest.SSOCredentials"> & {
|
||||
export type SignInRequest_SSOCredentials = Message<"memos.api.v1.SignInRequest.SSOCredentials"> & {
|
||||
/**
|
||||
* The ID of the SSO provider.
|
||||
* Required field to identify the SSO provider.
|
||||
*
|
||||
* @generated from field: int32 idp_id = 1;
|
||||
*/
|
||||
|
|
@ -138,7 +128,6 @@ export type CreateSessionRequest_SSOCredentials = Message<"memos.api.v1.CreateSe
|
|||
|
||||
/**
|
||||
* The authorization code from the SSO provider.
|
||||
* Required field for completing the SSO flow.
|
||||
*
|
||||
* @generated from field: string code = 2;
|
||||
*/
|
||||
|
|
@ -146,7 +135,6 @@ export type CreateSessionRequest_SSOCredentials = Message<"memos.api.v1.CreateSe
|
|||
|
||||
/**
|
||||
* The redirect URI used in the SSO flow.
|
||||
* Required field for security validation.
|
||||
*
|
||||
* @generated from field: string redirect_uri = 3;
|
||||
*/
|
||||
|
|
@ -154,7 +142,7 @@ export type CreateSessionRequest_SSOCredentials = Message<"memos.api.v1.CreateSe
|
|||
|
||||
/**
|
||||
* The PKCE code verifier for enhanced security (RFC 7636).
|
||||
* Optional field - if provided, enables PKCE flow protection against authorization code interception.
|
||||
* Optional - enables PKCE flow protection against authorization code interception.
|
||||
*
|
||||
* @generated from field: string code_verifier = 4;
|
||||
*/
|
||||
|
|
@ -162,89 +150,150 @@ export type CreateSessionRequest_SSOCredentials = Message<"memos.api.v1.CreateSe
|
|||
};
|
||||
|
||||
/**
|
||||
* Describes the message memos.api.v1.CreateSessionRequest.SSOCredentials.
|
||||
* Use `create(CreateSessionRequest_SSOCredentialsSchema)` to create a new message.
|
||||
* Describes the message memos.api.v1.SignInRequest.SSOCredentials.
|
||||
* Use `create(SignInRequest_SSOCredentialsSchema)` to create a new message.
|
||||
*/
|
||||
export const CreateSessionRequest_SSOCredentialsSchema: GenMessage<CreateSessionRequest_SSOCredentials> = /*@__PURE__*/
|
||||
export const SignInRequest_SSOCredentialsSchema: GenMessage<SignInRequest_SSOCredentials> = /*@__PURE__*/
|
||||
messageDesc(file_api_v1_auth_service, 2, 1);
|
||||
|
||||
/**
|
||||
* @generated from message memos.api.v1.CreateSessionResponse
|
||||
* @generated from message memos.api.v1.SignInResponse
|
||||
*/
|
||||
export type CreateSessionResponse = Message<"memos.api.v1.CreateSessionResponse"> & {
|
||||
export type SignInResponse = Message<"memos.api.v1.SignInResponse"> & {
|
||||
/**
|
||||
* The authenticated user information.
|
||||
* The authenticated user's information.
|
||||
*
|
||||
* @generated from field: memos.api.v1.User user = 1;
|
||||
*/
|
||||
user?: User;
|
||||
|
||||
/**
|
||||
* Last time the session was accessed.
|
||||
* Used for sliding expiration calculation (last_accessed_time + 2 weeks).
|
||||
* The short-lived access token for API requests.
|
||||
* Store in memory only, not in localStorage.
|
||||
*
|
||||
* @generated from field: google.protobuf.Timestamp last_accessed_at = 2;
|
||||
* @generated from field: string access_token = 2;
|
||||
*/
|
||||
lastAccessedAt?: Timestamp;
|
||||
accessToken: string;
|
||||
|
||||
/**
|
||||
* When the access token expires.
|
||||
* Client should call RefreshToken before this time.
|
||||
*
|
||||
* @generated from field: google.protobuf.Timestamp access_token_expires_at = 3;
|
||||
*/
|
||||
accessTokenExpiresAt?: Timestamp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message memos.api.v1.CreateSessionResponse.
|
||||
* Use `create(CreateSessionResponseSchema)` to create a new message.
|
||||
* Describes the message memos.api.v1.SignInResponse.
|
||||
* Use `create(SignInResponseSchema)` to create a new message.
|
||||
*/
|
||||
export const CreateSessionResponseSchema: GenMessage<CreateSessionResponse> = /*@__PURE__*/
|
||||
export const SignInResponseSchema: GenMessage<SignInResponse> = /*@__PURE__*/
|
||||
messageDesc(file_api_v1_auth_service, 3);
|
||||
|
||||
/**
|
||||
* @generated from message memos.api.v1.DeleteSessionRequest
|
||||
* @generated from message memos.api.v1.SignOutRequest
|
||||
*/
|
||||
export type DeleteSessionRequest = Message<"memos.api.v1.DeleteSessionRequest"> & {
|
||||
export type SignOutRequest = Message<"memos.api.v1.SignOutRequest"> & {
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message memos.api.v1.DeleteSessionRequest.
|
||||
* Use `create(DeleteSessionRequestSchema)` to create a new message.
|
||||
* Describes the message memos.api.v1.SignOutRequest.
|
||||
* Use `create(SignOutRequestSchema)` to create a new message.
|
||||
*/
|
||||
export const DeleteSessionRequestSchema: GenMessage<DeleteSessionRequest> = /*@__PURE__*/
|
||||
export const SignOutRequestSchema: GenMessage<SignOutRequest> = /*@__PURE__*/
|
||||
messageDesc(file_api_v1_auth_service, 4);
|
||||
|
||||
/**
|
||||
* @generated from message memos.api.v1.RefreshTokenRequest
|
||||
*/
|
||||
export type RefreshTokenRequest = Message<"memos.api.v1.RefreshTokenRequest"> & {
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message memos.api.v1.RefreshTokenRequest.
|
||||
* Use `create(RefreshTokenRequestSchema)` to create a new message.
|
||||
*/
|
||||
export const RefreshTokenRequestSchema: GenMessage<RefreshTokenRequest> = /*@__PURE__*/
|
||||
messageDesc(file_api_v1_auth_service, 5);
|
||||
|
||||
/**
|
||||
* @generated from message memos.api.v1.RefreshTokenResponse
|
||||
*/
|
||||
export type RefreshTokenResponse = Message<"memos.api.v1.RefreshTokenResponse"> & {
|
||||
/**
|
||||
* The new short-lived access token.
|
||||
*
|
||||
* @generated from field: string access_token = 1;
|
||||
*/
|
||||
accessToken: string;
|
||||
|
||||
/**
|
||||
* When the access token expires.
|
||||
*
|
||||
* @generated from field: google.protobuf.Timestamp expires_at = 2;
|
||||
*/
|
||||
expiresAt?: Timestamp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message memos.api.v1.RefreshTokenResponse.
|
||||
* Use `create(RefreshTokenResponseSchema)` to create a new message.
|
||||
*/
|
||||
export const RefreshTokenResponseSchema: GenMessage<RefreshTokenResponse> = /*@__PURE__*/
|
||||
messageDesc(file_api_v1_auth_service, 6);
|
||||
|
||||
/**
|
||||
* @generated from service memos.api.v1.AuthService
|
||||
*/
|
||||
export const AuthService: GenService<{
|
||||
/**
|
||||
* GetCurrentSession returns the current active session information.
|
||||
* This method is idempotent and safe, suitable for checking current session state.
|
||||
* GetCurrentUser returns the authenticated user's information.
|
||||
* Validates the access token and returns user details.
|
||||
* Similar to OIDC's /userinfo endpoint.
|
||||
*
|
||||
* @generated from rpc memos.api.v1.AuthService.GetCurrentSession
|
||||
* @generated from rpc memos.api.v1.AuthService.GetCurrentUser
|
||||
*/
|
||||
getCurrentSession: {
|
||||
getCurrentUser: {
|
||||
methodKind: "unary";
|
||||
input: typeof GetCurrentSessionRequestSchema;
|
||||
output: typeof GetCurrentSessionResponseSchema;
|
||||
input: typeof GetCurrentUserRequestSchema;
|
||||
output: typeof GetCurrentUserResponseSchema;
|
||||
},
|
||||
/**
|
||||
* CreateSession authenticates a user and creates a new session.
|
||||
* Returns the authenticated user information upon successful authentication.
|
||||
* SignIn authenticates a user with credentials and returns tokens.
|
||||
* On success, returns an access token and sets a refresh token cookie.
|
||||
* Supports password-based and SSO authentication methods.
|
||||
*
|
||||
* @generated from rpc memos.api.v1.AuthService.CreateSession
|
||||
* @generated from rpc memos.api.v1.AuthService.SignIn
|
||||
*/
|
||||
createSession: {
|
||||
signIn: {
|
||||
methodKind: "unary";
|
||||
input: typeof CreateSessionRequestSchema;
|
||||
output: typeof CreateSessionResponseSchema;
|
||||
input: typeof SignInRequestSchema;
|
||||
output: typeof SignInResponseSchema;
|
||||
},
|
||||
/**
|
||||
* DeleteSession terminates the current user session.
|
||||
* This is an idempotent operation that invalidates the user's authentication.
|
||||
* SignOut terminates the user's authentication.
|
||||
* Revokes the refresh token and clears the authentication cookie.
|
||||
*
|
||||
* @generated from rpc memos.api.v1.AuthService.DeleteSession
|
||||
* @generated from rpc memos.api.v1.AuthService.SignOut
|
||||
*/
|
||||
deleteSession: {
|
||||
signOut: {
|
||||
methodKind: "unary";
|
||||
input: typeof DeleteSessionRequestSchema;
|
||||
input: typeof SignOutRequestSchema;
|
||||
output: typeof EmptySchema;
|
||||
},
|
||||
/**
|
||||
* RefreshToken exchanges a valid refresh token for a new access token.
|
||||
* The refresh token is read from the HttpOnly cookie.
|
||||
* Returns a new short-lived access token.
|
||||
*
|
||||
* @generated from rpc memos.api.v1.AuthService.RefreshToken
|
||||
*/
|
||||
refreshToken: {
|
||||
methodKind: "unary";
|
||||
input: typeof RefreshTokenRequestSchema;
|
||||
output: typeof RefreshTokenResponseSchema;
|
||||
},
|
||||
}> = /*@__PURE__*/
|
||||
serviceDesc(file_api_v1_auth_service, 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// @generated by protoc-gen-es v2.10.1 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.2 with parameter "target=ts"
|
||||
// @generated from file api/v1/common.proto (package memos.api.v1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// @generated by protoc-gen-es v2.10.1 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.2 with parameter "target=ts"
|
||||
// @generated from file api/v1/idp_service.proto (package memos.api.v1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// @generated by protoc-gen-es v2.10.1 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.2 with parameter "target=ts"
|
||||
// @generated from file api/v1/instance_service.proto (package memos.api.v1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// @generated by protoc-gen-es v2.10.1 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.2 with parameter "target=ts"
|
||||
// @generated from file api/v1/memo_service.proto (package memos.api.v1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// @generated by protoc-gen-es v2.10.1 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.2 with parameter "target=ts"
|
||||
// @generated from file api/v1/shortcut_service.proto (package memos.api.v1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// @generated by protoc-gen-es v2.10.1 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.2 with parameter "target=ts"
|
||||
// @generated from file google/api/annotations.proto (package google.api, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// @generated by protoc-gen-es v2.10.1 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.2 with parameter "target=ts"
|
||||
// @generated from file google/api/client.proto (package google.api, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// @generated by protoc-gen-es v2.10.1 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.2 with parameter "target=ts"
|
||||
// @generated from file google/api/field_behavior.proto (package google.api, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// @generated by protoc-gen-es v2.10.1 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.2 with parameter "target=ts"
|
||||
// @generated from file google/api/http.proto (package google.api, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// @generated by protoc-gen-es v2.10.1 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.2 with parameter "target=ts"
|
||||
// @generated from file google/api/launch_stage.proto (package google.api, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// @generated by protoc-gen-es v2.10.1 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.2 with parameter "target=ts"
|
||||
// @generated from file google/api/resource.proto (package google.api, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue