From 4a0a0d9eaaaa3a1e9b5c5a34a6059eedfc87487a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 03:48:36 +0000 Subject: [PATCH 1/3] Split LoginByPassword into explicit RegisterByPassword and LoginByPassword Agent-Logs-Url: https://github.com/poly-workshop/identra/sessions/64e8656e-2b91-4cda-88bf-84437b094797 Co-authored-by: slhmy <31381093+slhmy@users.noreply.github.com> --- gen/go/identra/v1/identra_service.pb.go | 70 ++++--- gen/go/identra/v1/identra_service.pb.gw.go | 66 ++++++ gen/go/identra/v1/identra_service_grpc.pb.go | 38 ++++ gen/go/identra/v1/types.pb.go | 210 ++++++++++++++----- gen/openapi/identra.swagger.json | 51 +++++ internal/application/identra/service.go | 91 +++++--- proto/identra/v1/identra_service.proto | 6 + proto/identra/v1/types.proto | 8 + 8 files changed, 422 insertions(+), 118 deletions(-) diff --git a/gen/go/identra/v1/identra_service.pb.go b/gen/go/identra/v1/identra_service.pb.go index 46d5e5e..f039305 100644 --- a/gen/go/identra/v1/identra_service.pb.go +++ b/gen/go/identra/v1/identra_service.pb.go @@ -26,7 +26,8 @@ var File_identra_v1_identra_service_proto protoreflect.FileDescriptor const file_identra_v1_identra_service_proto_rawDesc = "" + "\n" + " identra/v1/identra_service.proto\x12\n" + - "identra.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x16identra/v1/types.proto2\xca\t\n" + + "identra.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x16identra/v1/types.proto2\xcf\n" + + "\n" + "\x0eIdentraService\x12b\n" + "\aGetJWKS\x12\x1a.identra.v1.GetJWKSRequest\x1a\x1b.identra.v1.GetJWKSResponse\"\x1e\x82\xd3\xe4\x93\x02\x18\x12\x16/.well-known/jwks.json\x12\x89\x01\n" + "\x18GetOAuthAuthorizationURL\x12+.identra.v1.GetOAuthAuthorizationURLRequest\x1a,.identra.v1.GetOAuthAuthorizationURLResponse\"\x12\x82\xd3\xe4\x93\x02\f\x12\n" + @@ -34,7 +35,8 @@ const file_identra_v1_identra_service_proto_rawDesc = "" + "\fLoginByOAuth\x12\x1f.identra.v1.LoginByOAuthRequest\x1a .identra.v1.LoginByOAuthResponse\"\x17\x82\xd3\xe4\x93\x02\x11:\x01*\"\f/oauth/login\x12r\n" + "\x0fBindUserByOAuth\x12\".identra.v1.BindUserByOAuthRequest\x1a#.identra.v1.BindUserByOAuthResponse\"\x16\x82\xd3\xe4\x93\x02\x10:\x01*\"\v/oauth/bind\x12{\n" + "\x12SendLoginEmailCode\x12%.identra.v1.SendLoginEmailCodeRequest\x1a&.identra.v1.SendLoginEmailCodeResponse\"\x16\x82\xd3\xe4\x93\x02\x10:\x01*\"\v/email/code\x12v\n" + - "\x10LoginByEmailCode\x12#.identra.v1.LoginByEmailCodeRequest\x1a$.identra.v1.LoginByEmailCodeResponse\"\x17\x82\xd3\xe4\x93\x02\x11:\x01*\"\f/email/login\x12v\n" + + "\x10LoginByEmailCode\x12#.identra.v1.LoginByEmailCodeRequest\x1a$.identra.v1.LoginByEmailCodeResponse\"\x17\x82\xd3\xe4\x93\x02\x11:\x01*\"\f/email/login\x12\x82\x01\n" + + "\x12RegisterByPassword\x12%.identra.v1.RegisterByPasswordRequest\x1a&.identra.v1.RegisterByPasswordResponse\"\x1d\x82\xd3\xe4\x93\x02\x17:\x01*\"\x12/password/register\x12v\n" + "\x0fLoginByPassword\x12\".identra.v1.LoginByPasswordRequest\x1a#.identra.v1.LoginByPasswordResponse\"\x1a\x82\xd3\xe4\x93\x02\x14:\x01*\"\x0f/password/login\x12l\n" + "\fRefreshToken\x12\x1f.identra.v1.RefreshTokenRequest\x1a .identra.v1.RefreshTokenResponse\"\x19\x82\xd3\xe4\x93\x02\x13:\x01*\"\x0e/token/refresh\x12}\n" + "\x12ListOAuthProviders\x12%.identra.v1.ListOAuthProvidersRequest\x1a&.identra.v1.ListOAuthProvidersResponse\"\x18\x82\xd3\xe4\x93\x02\x12\x12\x10/oauth/providers\x12\x8d\x01\n" + @@ -47,20 +49,22 @@ var file_identra_v1_identra_service_proto_goTypes = []any{ (*BindUserByOAuthRequest)(nil), // 3: identra.v1.BindUserByOAuthRequest (*SendLoginEmailCodeRequest)(nil), // 4: identra.v1.SendLoginEmailCodeRequest (*LoginByEmailCodeRequest)(nil), // 5: identra.v1.LoginByEmailCodeRequest - (*LoginByPasswordRequest)(nil), // 6: identra.v1.LoginByPasswordRequest - (*RefreshTokenRequest)(nil), // 7: identra.v1.RefreshTokenRequest - (*ListOAuthProvidersRequest)(nil), // 8: identra.v1.ListOAuthProvidersRequest - (*GetCurrentUserLoginInfoRequest)(nil), // 9: identra.v1.GetCurrentUserLoginInfoRequest - (*GetJWKSResponse)(nil), // 10: identra.v1.GetJWKSResponse - (*GetOAuthAuthorizationURLResponse)(nil), // 11: identra.v1.GetOAuthAuthorizationURLResponse - (*LoginByOAuthResponse)(nil), // 12: identra.v1.LoginByOAuthResponse - (*BindUserByOAuthResponse)(nil), // 13: identra.v1.BindUserByOAuthResponse - (*SendLoginEmailCodeResponse)(nil), // 14: identra.v1.SendLoginEmailCodeResponse - (*LoginByEmailCodeResponse)(nil), // 15: identra.v1.LoginByEmailCodeResponse - (*LoginByPasswordResponse)(nil), // 16: identra.v1.LoginByPasswordResponse - (*RefreshTokenResponse)(nil), // 17: identra.v1.RefreshTokenResponse - (*ListOAuthProvidersResponse)(nil), // 18: identra.v1.ListOAuthProvidersResponse - (*GetCurrentUserLoginInfoResponse)(nil), // 19: identra.v1.GetCurrentUserLoginInfoResponse + (*RegisterByPasswordRequest)(nil), // 6: identra.v1.RegisterByPasswordRequest + (*LoginByPasswordRequest)(nil), // 7: identra.v1.LoginByPasswordRequest + (*RefreshTokenRequest)(nil), // 8: identra.v1.RefreshTokenRequest + (*ListOAuthProvidersRequest)(nil), // 9: identra.v1.ListOAuthProvidersRequest + (*GetCurrentUserLoginInfoRequest)(nil), // 10: identra.v1.GetCurrentUserLoginInfoRequest + (*GetJWKSResponse)(nil), // 11: identra.v1.GetJWKSResponse + (*GetOAuthAuthorizationURLResponse)(nil), // 12: identra.v1.GetOAuthAuthorizationURLResponse + (*LoginByOAuthResponse)(nil), // 13: identra.v1.LoginByOAuthResponse + (*BindUserByOAuthResponse)(nil), // 14: identra.v1.BindUserByOAuthResponse + (*SendLoginEmailCodeResponse)(nil), // 15: identra.v1.SendLoginEmailCodeResponse + (*LoginByEmailCodeResponse)(nil), // 16: identra.v1.LoginByEmailCodeResponse + (*RegisterByPasswordResponse)(nil), // 17: identra.v1.RegisterByPasswordResponse + (*LoginByPasswordResponse)(nil), // 18: identra.v1.LoginByPasswordResponse + (*RefreshTokenResponse)(nil), // 19: identra.v1.RefreshTokenResponse + (*ListOAuthProvidersResponse)(nil), // 20: identra.v1.ListOAuthProvidersResponse + (*GetCurrentUserLoginInfoResponse)(nil), // 21: identra.v1.GetCurrentUserLoginInfoResponse } var file_identra_v1_identra_service_proto_depIdxs = []int32{ 0, // 0: identra.v1.IdentraService.GetJWKS:input_type -> identra.v1.GetJWKSRequest @@ -69,22 +73,24 @@ var file_identra_v1_identra_service_proto_depIdxs = []int32{ 3, // 3: identra.v1.IdentraService.BindUserByOAuth:input_type -> identra.v1.BindUserByOAuthRequest 4, // 4: identra.v1.IdentraService.SendLoginEmailCode:input_type -> identra.v1.SendLoginEmailCodeRequest 5, // 5: identra.v1.IdentraService.LoginByEmailCode:input_type -> identra.v1.LoginByEmailCodeRequest - 6, // 6: identra.v1.IdentraService.LoginByPassword:input_type -> identra.v1.LoginByPasswordRequest - 7, // 7: identra.v1.IdentraService.RefreshToken:input_type -> identra.v1.RefreshTokenRequest - 8, // 8: identra.v1.IdentraService.ListOAuthProviders:input_type -> identra.v1.ListOAuthProvidersRequest - 9, // 9: identra.v1.IdentraService.GetCurrentUserLoginInfo:input_type -> identra.v1.GetCurrentUserLoginInfoRequest - 10, // 10: identra.v1.IdentraService.GetJWKS:output_type -> identra.v1.GetJWKSResponse - 11, // 11: identra.v1.IdentraService.GetOAuthAuthorizationURL:output_type -> identra.v1.GetOAuthAuthorizationURLResponse - 12, // 12: identra.v1.IdentraService.LoginByOAuth:output_type -> identra.v1.LoginByOAuthResponse - 13, // 13: identra.v1.IdentraService.BindUserByOAuth:output_type -> identra.v1.BindUserByOAuthResponse - 14, // 14: identra.v1.IdentraService.SendLoginEmailCode:output_type -> identra.v1.SendLoginEmailCodeResponse - 15, // 15: identra.v1.IdentraService.LoginByEmailCode:output_type -> identra.v1.LoginByEmailCodeResponse - 16, // 16: identra.v1.IdentraService.LoginByPassword:output_type -> identra.v1.LoginByPasswordResponse - 17, // 17: identra.v1.IdentraService.RefreshToken:output_type -> identra.v1.RefreshTokenResponse - 18, // 18: identra.v1.IdentraService.ListOAuthProviders:output_type -> identra.v1.ListOAuthProvidersResponse - 19, // 19: identra.v1.IdentraService.GetCurrentUserLoginInfo:output_type -> identra.v1.GetCurrentUserLoginInfoResponse - 10, // [10:20] is the sub-list for method output_type - 0, // [0:10] is the sub-list for method input_type + 6, // 6: identra.v1.IdentraService.RegisterByPassword:input_type -> identra.v1.RegisterByPasswordRequest + 7, // 7: identra.v1.IdentraService.LoginByPassword:input_type -> identra.v1.LoginByPasswordRequest + 8, // 8: identra.v1.IdentraService.RefreshToken:input_type -> identra.v1.RefreshTokenRequest + 9, // 9: identra.v1.IdentraService.ListOAuthProviders:input_type -> identra.v1.ListOAuthProvidersRequest + 10, // 10: identra.v1.IdentraService.GetCurrentUserLoginInfo:input_type -> identra.v1.GetCurrentUserLoginInfoRequest + 11, // 11: identra.v1.IdentraService.GetJWKS:output_type -> identra.v1.GetJWKSResponse + 12, // 12: identra.v1.IdentraService.GetOAuthAuthorizationURL:output_type -> identra.v1.GetOAuthAuthorizationURLResponse + 13, // 13: identra.v1.IdentraService.LoginByOAuth:output_type -> identra.v1.LoginByOAuthResponse + 14, // 14: identra.v1.IdentraService.BindUserByOAuth:output_type -> identra.v1.BindUserByOAuthResponse + 15, // 15: identra.v1.IdentraService.SendLoginEmailCode:output_type -> identra.v1.SendLoginEmailCodeResponse + 16, // 16: identra.v1.IdentraService.LoginByEmailCode:output_type -> identra.v1.LoginByEmailCodeResponse + 17, // 17: identra.v1.IdentraService.RegisterByPassword:output_type -> identra.v1.RegisterByPasswordResponse + 18, // 18: identra.v1.IdentraService.LoginByPassword:output_type -> identra.v1.LoginByPasswordResponse + 19, // 19: identra.v1.IdentraService.RefreshToken:output_type -> identra.v1.RefreshTokenResponse + 20, // 20: identra.v1.IdentraService.ListOAuthProviders:output_type -> identra.v1.ListOAuthProvidersResponse + 21, // 21: identra.v1.IdentraService.GetCurrentUserLoginInfo:output_type -> identra.v1.GetCurrentUserLoginInfoResponse + 11, // [11:22] is the sub-list for method output_type + 0, // [0:11] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name diff --git a/gen/go/identra/v1/identra_service.pb.gw.go b/gen/go/identra/v1/identra_service.pb.gw.go index 4f396b3..c343031 100644 --- a/gen/go/identra/v1/identra_service.pb.gw.go +++ b/gen/go/identra/v1/identra_service.pb.gw.go @@ -199,6 +199,33 @@ func local_request_IdentraService_LoginByEmailCode_0(ctx context.Context, marsha return msg, metadata, err } +func request_IdentraService_RegisterByPassword_0(ctx context.Context, marshaler runtime.Marshaler, client IdentraServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq RegisterByPasswordRequest + 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.RegisterByPassword(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_IdentraService_RegisterByPassword_0(ctx context.Context, marshaler runtime.Marshaler, server IdentraServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq RegisterByPasswordRequest + 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.RegisterByPassword(ctx, &protoReq) + return msg, metadata, err +} + func request_IdentraService_LoginByPassword_0(ctx context.Context, marshaler runtime.Marshaler, client IdentraServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var ( protoReq LoginByPasswordRequest @@ -427,6 +454,26 @@ func RegisterIdentraServiceHandlerServer(ctx context.Context, mux *runtime.Serve } forward_IdentraService_LoginByEmailCode_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle(http.MethodPost, pattern_IdentraService_RegisterByPassword_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, "/identra.v1.IdentraService/RegisterByPassword", runtime.WithHTTPPathPattern("/password/register")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_IdentraService_RegisterByPassword_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_IdentraService_RegisterByPassword_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) mux.Handle(http.MethodPost, pattern_IdentraService_LoginByPassword_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -649,6 +696,23 @@ func RegisterIdentraServiceHandlerClient(ctx context.Context, mux *runtime.Serve } forward_IdentraService_LoginByEmailCode_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle(http.MethodPost, pattern_IdentraService_RegisterByPassword_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, "/identra.v1.IdentraService/RegisterByPassword", runtime.WithHTTPPathPattern("/password/register")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_IdentraService_RegisterByPassword_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_IdentraService_RegisterByPassword_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) mux.Handle(http.MethodPost, pattern_IdentraService_LoginByPassword_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -727,6 +791,7 @@ var ( pattern_IdentraService_BindUserByOAuth_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"oauth", "bind"}, "")) pattern_IdentraService_SendLoginEmailCode_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"email", "code"}, "")) pattern_IdentraService_LoginByEmailCode_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"email", "login"}, "")) + pattern_IdentraService_RegisterByPassword_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"password", "register"}, "")) pattern_IdentraService_LoginByPassword_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"password", "login"}, "")) pattern_IdentraService_RefreshToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"token", "refresh"}, "")) pattern_IdentraService_ListOAuthProviders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"oauth", "providers"}, "")) @@ -740,6 +805,7 @@ var ( forward_IdentraService_BindUserByOAuth_0 = runtime.ForwardResponseMessage forward_IdentraService_SendLoginEmailCode_0 = runtime.ForwardResponseMessage forward_IdentraService_LoginByEmailCode_0 = runtime.ForwardResponseMessage + forward_IdentraService_RegisterByPassword_0 = runtime.ForwardResponseMessage forward_IdentraService_LoginByPassword_0 = runtime.ForwardResponseMessage forward_IdentraService_RefreshToken_0 = runtime.ForwardResponseMessage forward_IdentraService_ListOAuthProviders_0 = runtime.ForwardResponseMessage diff --git a/gen/go/identra/v1/identra_service_grpc.pb.go b/gen/go/identra/v1/identra_service_grpc.pb.go index 5933889..926c445 100644 --- a/gen/go/identra/v1/identra_service_grpc.pb.go +++ b/gen/go/identra/v1/identra_service_grpc.pb.go @@ -25,6 +25,7 @@ const ( IdentraService_BindUserByOAuth_FullMethodName = "/identra.v1.IdentraService/BindUserByOAuth" IdentraService_SendLoginEmailCode_FullMethodName = "/identra.v1.IdentraService/SendLoginEmailCode" IdentraService_LoginByEmailCode_FullMethodName = "/identra.v1.IdentraService/LoginByEmailCode" + IdentraService_RegisterByPassword_FullMethodName = "/identra.v1.IdentraService/RegisterByPassword" IdentraService_LoginByPassword_FullMethodName = "/identra.v1.IdentraService/LoginByPassword" IdentraService_RefreshToken_FullMethodName = "/identra.v1.IdentraService/RefreshToken" IdentraService_ListOAuthProviders_FullMethodName = "/identra.v1.IdentraService/ListOAuthProviders" @@ -42,6 +43,7 @@ type IdentraServiceClient interface { BindUserByOAuth(ctx context.Context, in *BindUserByOAuthRequest, opts ...grpc.CallOption) (*BindUserByOAuthResponse, error) SendLoginEmailCode(ctx context.Context, in *SendLoginEmailCodeRequest, opts ...grpc.CallOption) (*SendLoginEmailCodeResponse, error) LoginByEmailCode(ctx context.Context, in *LoginByEmailCodeRequest, opts ...grpc.CallOption) (*LoginByEmailCodeResponse, error) + RegisterByPassword(ctx context.Context, in *RegisterByPasswordRequest, opts ...grpc.CallOption) (*RegisterByPasswordResponse, error) LoginByPassword(ctx context.Context, in *LoginByPasswordRequest, opts ...grpc.CallOption) (*LoginByPasswordResponse, error) RefreshToken(ctx context.Context, in *RefreshTokenRequest, opts ...grpc.CallOption) (*RefreshTokenResponse, error) // ListOAuthProviders returns all supported OAuth providers with their @@ -121,6 +123,16 @@ func (c *identraServiceClient) LoginByEmailCode(ctx context.Context, in *LoginBy return out, nil } +func (c *identraServiceClient) RegisterByPassword(ctx context.Context, in *RegisterByPasswordRequest, opts ...grpc.CallOption) (*RegisterByPasswordResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RegisterByPasswordResponse) + err := c.cc.Invoke(ctx, IdentraService_RegisterByPassword_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *identraServiceClient) LoginByPassword(ctx context.Context, in *LoginByPasswordRequest, opts ...grpc.CallOption) (*LoginByPasswordResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(LoginByPasswordResponse) @@ -172,6 +184,7 @@ type IdentraServiceServer interface { BindUserByOAuth(context.Context, *BindUserByOAuthRequest) (*BindUserByOAuthResponse, error) SendLoginEmailCode(context.Context, *SendLoginEmailCodeRequest) (*SendLoginEmailCodeResponse, error) LoginByEmailCode(context.Context, *LoginByEmailCodeRequest) (*LoginByEmailCodeResponse, error) + RegisterByPassword(context.Context, *RegisterByPasswordRequest) (*RegisterByPasswordResponse, error) LoginByPassword(context.Context, *LoginByPasswordRequest) (*LoginByPasswordResponse, error) RefreshToken(context.Context, *RefreshTokenRequest) (*RefreshTokenResponse, error) // ListOAuthProviders returns all supported OAuth providers with their @@ -209,6 +222,9 @@ func (UnimplementedIdentraServiceServer) SendLoginEmailCode(context.Context, *Se func (UnimplementedIdentraServiceServer) LoginByEmailCode(context.Context, *LoginByEmailCodeRequest) (*LoginByEmailCodeResponse, error) { return nil, status.Error(codes.Unimplemented, "method LoginByEmailCode not implemented") } +func (UnimplementedIdentraServiceServer) RegisterByPassword(context.Context, *RegisterByPasswordRequest) (*RegisterByPasswordResponse, error) { + return nil, status.Error(codes.Unimplemented, "method RegisterByPassword not implemented") +} func (UnimplementedIdentraServiceServer) LoginByPassword(context.Context, *LoginByPasswordRequest) (*LoginByPasswordResponse, error) { return nil, status.Error(codes.Unimplemented, "method LoginByPassword not implemented") } @@ -350,6 +366,24 @@ func _IdentraService_LoginByEmailCode_Handler(srv interface{}, ctx context.Conte return interceptor(ctx, in, info, handler) } +func _IdentraService_RegisterByPassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RegisterByPasswordRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IdentraServiceServer).RegisterByPassword(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: IdentraService_RegisterByPassword_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IdentraServiceServer).RegisterByPassword(ctx, req.(*RegisterByPasswordRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _IdentraService_LoginByPassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(LoginByPasswordRequest) if err := dec(in); err != nil { @@ -453,6 +487,10 @@ var IdentraService_ServiceDesc = grpc.ServiceDesc{ MethodName: "LoginByEmailCode", Handler: _IdentraService_LoginByEmailCode_Handler, }, + { + MethodName: "RegisterByPassword", + Handler: _IdentraService_RegisterByPassword_Handler, + }, { MethodName: "LoginByPassword", Handler: _IdentraService_LoginByPassword_Handler, diff --git a/gen/go/identra/v1/types.pb.go b/gen/go/identra/v1/types.pb.go index 3544828..b933c72 100644 --- a/gen/go/identra/v1/types.pb.go +++ b/gen/go/identra/v1/types.pb.go @@ -877,6 +877,102 @@ func (x *LoginByEmailCodeResponse) GetToken() *TokenPair { return nil } +type RegisterByPasswordRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterByPasswordRequest) Reset() { + *x = RegisterByPasswordRequest{} + mi := &file_identra_v1_types_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterByPasswordRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterByPasswordRequest) ProtoMessage() {} + +func (x *RegisterByPasswordRequest) ProtoReflect() protoreflect.Message { + mi := &file_identra_v1_types_proto_msgTypes[15] + 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 RegisterByPasswordRequest.ProtoReflect.Descriptor instead. +func (*RegisterByPasswordRequest) Descriptor() ([]byte, []int) { + return file_identra_v1_types_proto_rawDescGZIP(), []int{15} +} + +func (x *RegisterByPasswordRequest) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *RegisterByPasswordRequest) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +type RegisterByPasswordResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Token *TokenPair `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterByPasswordResponse) Reset() { + *x = RegisterByPasswordResponse{} + mi := &file_identra_v1_types_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterByPasswordResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterByPasswordResponse) ProtoMessage() {} + +func (x *RegisterByPasswordResponse) ProtoReflect() protoreflect.Message { + mi := &file_identra_v1_types_proto_msgTypes[16] + 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 RegisterByPasswordResponse.ProtoReflect.Descriptor instead. +func (*RegisterByPasswordResponse) Descriptor() ([]byte, []int) { + return file_identra_v1_types_proto_rawDescGZIP(), []int{16} +} + +func (x *RegisterByPasswordResponse) GetToken() *TokenPair { + if x != nil { + return x.Token + } + return nil +} + type LoginByPasswordRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` @@ -887,7 +983,7 @@ type LoginByPasswordRequest struct { func (x *LoginByPasswordRequest) Reset() { *x = LoginByPasswordRequest{} - mi := &file_identra_v1_types_proto_msgTypes[15] + mi := &file_identra_v1_types_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -899,7 +995,7 @@ func (x *LoginByPasswordRequest) String() string { func (*LoginByPasswordRequest) ProtoMessage() {} func (x *LoginByPasswordRequest) ProtoReflect() protoreflect.Message { - mi := &file_identra_v1_types_proto_msgTypes[15] + mi := &file_identra_v1_types_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -912,7 +1008,7 @@ func (x *LoginByPasswordRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use LoginByPasswordRequest.ProtoReflect.Descriptor instead. func (*LoginByPasswordRequest) Descriptor() ([]byte, []int) { - return file_identra_v1_types_proto_rawDescGZIP(), []int{15} + return file_identra_v1_types_proto_rawDescGZIP(), []int{17} } func (x *LoginByPasswordRequest) GetEmail() string { @@ -938,7 +1034,7 @@ type LoginByPasswordResponse struct { func (x *LoginByPasswordResponse) Reset() { *x = LoginByPasswordResponse{} - mi := &file_identra_v1_types_proto_msgTypes[16] + mi := &file_identra_v1_types_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -950,7 +1046,7 @@ func (x *LoginByPasswordResponse) String() string { func (*LoginByPasswordResponse) ProtoMessage() {} func (x *LoginByPasswordResponse) ProtoReflect() protoreflect.Message { - mi := &file_identra_v1_types_proto_msgTypes[16] + mi := &file_identra_v1_types_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -963,7 +1059,7 @@ func (x *LoginByPasswordResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use LoginByPasswordResponse.ProtoReflect.Descriptor instead. func (*LoginByPasswordResponse) Descriptor() ([]byte, []int) { - return file_identra_v1_types_proto_rawDescGZIP(), []int{16} + return file_identra_v1_types_proto_rawDescGZIP(), []int{18} } func (x *LoginByPasswordResponse) GetToken() *TokenPair { @@ -982,7 +1078,7 @@ type RefreshTokenRequest struct { func (x *RefreshTokenRequest) Reset() { *x = RefreshTokenRequest{} - mi := &file_identra_v1_types_proto_msgTypes[17] + mi := &file_identra_v1_types_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -994,7 +1090,7 @@ func (x *RefreshTokenRequest) String() string { func (*RefreshTokenRequest) ProtoMessage() {} func (x *RefreshTokenRequest) ProtoReflect() protoreflect.Message { - mi := &file_identra_v1_types_proto_msgTypes[17] + mi := &file_identra_v1_types_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1007,7 +1103,7 @@ func (x *RefreshTokenRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RefreshTokenRequest.ProtoReflect.Descriptor instead. func (*RefreshTokenRequest) Descriptor() ([]byte, []int) { - return file_identra_v1_types_proto_rawDescGZIP(), []int{17} + return file_identra_v1_types_proto_rawDescGZIP(), []int{19} } func (x *RefreshTokenRequest) GetRefreshToken() string { @@ -1026,7 +1122,7 @@ type RefreshTokenResponse struct { func (x *RefreshTokenResponse) Reset() { *x = RefreshTokenResponse{} - mi := &file_identra_v1_types_proto_msgTypes[18] + mi := &file_identra_v1_types_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1038,7 +1134,7 @@ func (x *RefreshTokenResponse) String() string { func (*RefreshTokenResponse) ProtoMessage() {} func (x *RefreshTokenResponse) ProtoReflect() protoreflect.Message { - mi := &file_identra_v1_types_proto_msgTypes[18] + mi := &file_identra_v1_types_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1051,7 +1147,7 @@ func (x *RefreshTokenResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RefreshTokenResponse.ProtoReflect.Descriptor instead. func (*RefreshTokenResponse) Descriptor() ([]byte, []int) { - return file_identra_v1_types_proto_rawDescGZIP(), []int{18} + return file_identra_v1_types_proto_rawDescGZIP(), []int{20} } func (x *RefreshTokenResponse) GetToken() *TokenPair { @@ -1074,7 +1170,7 @@ type OAuthConnection struct { func (x *OAuthConnection) Reset() { *x = OAuthConnection{} - mi := &file_identra_v1_types_proto_msgTypes[19] + mi := &file_identra_v1_types_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1086,7 +1182,7 @@ func (x *OAuthConnection) String() string { func (*OAuthConnection) ProtoMessage() {} func (x *OAuthConnection) ProtoReflect() protoreflect.Message { - mi := &file_identra_v1_types_proto_msgTypes[19] + mi := &file_identra_v1_types_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1099,7 +1195,7 @@ func (x *OAuthConnection) ProtoReflect() protoreflect.Message { // Deprecated: Use OAuthConnection.ProtoReflect.Descriptor instead. func (*OAuthConnection) Descriptor() ([]byte, []int) { - return file_identra_v1_types_proto_rawDescGZIP(), []int{19} + return file_identra_v1_types_proto_rawDescGZIP(), []int{21} } func (x *OAuthConnection) GetProvider() string { @@ -1124,7 +1220,7 @@ type ListOAuthProvidersRequest struct { func (x *ListOAuthProvidersRequest) Reset() { *x = ListOAuthProvidersRequest{} - mi := &file_identra_v1_types_proto_msgTypes[20] + mi := &file_identra_v1_types_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1136,7 +1232,7 @@ func (x *ListOAuthProvidersRequest) String() string { func (*ListOAuthProvidersRequest) ProtoMessage() {} func (x *ListOAuthProvidersRequest) ProtoReflect() protoreflect.Message { - mi := &file_identra_v1_types_proto_msgTypes[20] + mi := &file_identra_v1_types_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1149,7 +1245,7 @@ func (x *ListOAuthProvidersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListOAuthProvidersRequest.ProtoReflect.Descriptor instead. func (*ListOAuthProvidersRequest) Descriptor() ([]byte, []int) { - return file_identra_v1_types_proto_rawDescGZIP(), []int{20} + return file_identra_v1_types_proto_rawDescGZIP(), []int{22} } // OAuthProviderStatus describes whether a single OAuth provider is available. @@ -1168,7 +1264,7 @@ type OAuthProviderStatus struct { func (x *OAuthProviderStatus) Reset() { *x = OAuthProviderStatus{} - mi := &file_identra_v1_types_proto_msgTypes[21] + mi := &file_identra_v1_types_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1180,7 +1276,7 @@ func (x *OAuthProviderStatus) String() string { func (*OAuthProviderStatus) ProtoMessage() {} func (x *OAuthProviderStatus) ProtoReflect() protoreflect.Message { - mi := &file_identra_v1_types_proto_msgTypes[21] + mi := &file_identra_v1_types_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1193,7 +1289,7 @@ func (x *OAuthProviderStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use OAuthProviderStatus.ProtoReflect.Descriptor instead. func (*OAuthProviderStatus) Descriptor() ([]byte, []int) { - return file_identra_v1_types_proto_rawDescGZIP(), []int{21} + return file_identra_v1_types_proto_rawDescGZIP(), []int{23} } func (x *OAuthProviderStatus) GetName() string { @@ -1227,7 +1323,7 @@ type ListOAuthProvidersResponse struct { func (x *ListOAuthProvidersResponse) Reset() { *x = ListOAuthProvidersResponse{} - mi := &file_identra_v1_types_proto_msgTypes[22] + mi := &file_identra_v1_types_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1239,7 +1335,7 @@ func (x *ListOAuthProvidersResponse) String() string { func (*ListOAuthProvidersResponse) ProtoMessage() {} func (x *ListOAuthProvidersResponse) ProtoReflect() protoreflect.Message { - mi := &file_identra_v1_types_proto_msgTypes[22] + mi := &file_identra_v1_types_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1252,7 +1348,7 @@ func (x *ListOAuthProvidersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListOAuthProvidersResponse.ProtoReflect.Descriptor instead. func (*ListOAuthProvidersResponse) Descriptor() ([]byte, []int) { - return file_identra_v1_types_proto_rawDescGZIP(), []int{22} + return file_identra_v1_types_proto_rawDescGZIP(), []int{24} } func (x *ListOAuthProvidersResponse) GetProviders() []*OAuthProviderStatus { @@ -1272,7 +1368,7 @@ type GetCurrentUserLoginInfoRequest struct { func (x *GetCurrentUserLoginInfoRequest) Reset() { *x = GetCurrentUserLoginInfoRequest{} - mi := &file_identra_v1_types_proto_msgTypes[23] + mi := &file_identra_v1_types_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1284,7 +1380,7 @@ func (x *GetCurrentUserLoginInfoRequest) String() string { func (*GetCurrentUserLoginInfoRequest) ProtoMessage() {} func (x *GetCurrentUserLoginInfoRequest) ProtoReflect() protoreflect.Message { - mi := &file_identra_v1_types_proto_msgTypes[23] + mi := &file_identra_v1_types_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1297,7 +1393,7 @@ func (x *GetCurrentUserLoginInfoRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCurrentUserLoginInfoRequest.ProtoReflect.Descriptor instead. func (*GetCurrentUserLoginInfoRequest) Descriptor() ([]byte, []int) { - return file_identra_v1_types_proto_rawDescGZIP(), []int{23} + return file_identra_v1_types_proto_rawDescGZIP(), []int{25} } func (x *GetCurrentUserLoginInfoRequest) GetAccessToken() string { @@ -1324,7 +1420,7 @@ type GetCurrentUserLoginInfoResponse struct { func (x *GetCurrentUserLoginInfoResponse) Reset() { *x = GetCurrentUserLoginInfoResponse{} - mi := &file_identra_v1_types_proto_msgTypes[24] + mi := &file_identra_v1_types_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1336,7 +1432,7 @@ func (x *GetCurrentUserLoginInfoResponse) String() string { func (*GetCurrentUserLoginInfoResponse) ProtoMessage() {} func (x *GetCurrentUserLoginInfoResponse) ProtoReflect() protoreflect.Message { - mi := &file_identra_v1_types_proto_msgTypes[24] + mi := &file_identra_v1_types_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1349,7 +1445,7 @@ func (x *GetCurrentUserLoginInfoResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCurrentUserLoginInfoResponse.ProtoReflect.Descriptor instead. func (*GetCurrentUserLoginInfoResponse) Descriptor() ([]byte, []int) { - return file_identra_v1_types_proto_rawDescGZIP(), []int{24} + return file_identra_v1_types_proto_rawDescGZIP(), []int{26} } func (x *GetCurrentUserLoginInfoResponse) GetUserId() string { @@ -1454,6 +1550,11 @@ const file_identra_v1_types_proto_rawDesc = "" + "\x05email\x18\x01 \x01(\tR\x05email\x12\x12\n" + "\x04code\x18\x02 \x01(\tR\x04code\"G\n" + "\x18LoginByEmailCodeResponse\x12+\n" + + "\x05token\x18\x01 \x01(\v2\x15.identra.v1.TokenPairR\x05token\"M\n" + + "\x19RegisterByPasswordRequest\x12\x14\n" + + "\x05email\x18\x01 \x01(\tR\x05email\x12\x1a\n" + + "\bpassword\x18\x02 \x01(\tR\bpassword\"I\n" + + "\x1aRegisterByPasswordResponse\x12+\n" + "\x05token\x18\x01 \x01(\v2\x15.identra.v1.TokenPairR\x05token\"J\n" + "\x16LoginByPasswordRequest\x12\x14\n" + "\x05email\x18\x01 \x01(\tR\x05email\x12\x1a\n" + @@ -1498,7 +1599,7 @@ func file_identra_v1_types_proto_rawDescGZIP() []byte { return file_identra_v1_types_proto_rawDescData } -var file_identra_v1_types_proto_msgTypes = make([]protoimpl.MessageInfo, 25) +var file_identra_v1_types_proto_msgTypes = make([]protoimpl.MessageInfo, 27) var file_identra_v1_types_proto_goTypes = []any{ (*Token)(nil), // 0: identra.v1.Token (*TokenPair)(nil), // 1: identra.v1.TokenPair @@ -1515,16 +1616,18 @@ var file_identra_v1_types_proto_goTypes = []any{ (*SendLoginEmailCodeResponse)(nil), // 12: identra.v1.SendLoginEmailCodeResponse (*LoginByEmailCodeRequest)(nil), // 13: identra.v1.LoginByEmailCodeRequest (*LoginByEmailCodeResponse)(nil), // 14: identra.v1.LoginByEmailCodeResponse - (*LoginByPasswordRequest)(nil), // 15: identra.v1.LoginByPasswordRequest - (*LoginByPasswordResponse)(nil), // 16: identra.v1.LoginByPasswordResponse - (*RefreshTokenRequest)(nil), // 17: identra.v1.RefreshTokenRequest - (*RefreshTokenResponse)(nil), // 18: identra.v1.RefreshTokenResponse - (*OAuthConnection)(nil), // 19: identra.v1.OAuthConnection - (*ListOAuthProvidersRequest)(nil), // 20: identra.v1.ListOAuthProvidersRequest - (*OAuthProviderStatus)(nil), // 21: identra.v1.OAuthProviderStatus - (*ListOAuthProvidersResponse)(nil), // 22: identra.v1.ListOAuthProvidersResponse - (*GetCurrentUserLoginInfoRequest)(nil), // 23: identra.v1.GetCurrentUserLoginInfoRequest - (*GetCurrentUserLoginInfoResponse)(nil), // 24: identra.v1.GetCurrentUserLoginInfoResponse + (*RegisterByPasswordRequest)(nil), // 15: identra.v1.RegisterByPasswordRequest + (*RegisterByPasswordResponse)(nil), // 16: identra.v1.RegisterByPasswordResponse + (*LoginByPasswordRequest)(nil), // 17: identra.v1.LoginByPasswordRequest + (*LoginByPasswordResponse)(nil), // 18: identra.v1.LoginByPasswordResponse + (*RefreshTokenRequest)(nil), // 19: identra.v1.RefreshTokenRequest + (*RefreshTokenResponse)(nil), // 20: identra.v1.RefreshTokenResponse + (*OAuthConnection)(nil), // 21: identra.v1.OAuthConnection + (*ListOAuthProvidersRequest)(nil), // 22: identra.v1.ListOAuthProvidersRequest + (*OAuthProviderStatus)(nil), // 23: identra.v1.OAuthProviderStatus + (*ListOAuthProvidersResponse)(nil), // 24: identra.v1.ListOAuthProvidersResponse + (*GetCurrentUserLoginInfoRequest)(nil), // 25: identra.v1.GetCurrentUserLoginInfoRequest + (*GetCurrentUserLoginInfoResponse)(nil), // 26: identra.v1.GetCurrentUserLoginInfoResponse } var file_identra_v1_types_proto_depIdxs = []int32{ 0, // 0: identra.v1.TokenPair.access_token:type_name -> identra.v1.Token @@ -1533,15 +1636,16 @@ var file_identra_v1_types_proto_depIdxs = []int32{ 1, // 3: identra.v1.LoginByOAuthResponse.token:type_name -> identra.v1.TokenPair 1, // 4: identra.v1.BindUserByOAuthResponse.token:type_name -> identra.v1.TokenPair 1, // 5: identra.v1.LoginByEmailCodeResponse.token:type_name -> identra.v1.TokenPair - 1, // 6: identra.v1.LoginByPasswordResponse.token:type_name -> identra.v1.TokenPair - 1, // 7: identra.v1.RefreshTokenResponse.token:type_name -> identra.v1.TokenPair - 21, // 8: identra.v1.ListOAuthProvidersResponse.providers:type_name -> identra.v1.OAuthProviderStatus - 19, // 9: identra.v1.GetCurrentUserLoginInfoResponse.oauth_connections:type_name -> identra.v1.OAuthConnection - 10, // [10:10] is the sub-list for method output_type - 10, // [10:10] is the sub-list for method input_type - 10, // [10:10] is the sub-list for extension type_name - 10, // [10:10] is the sub-list for extension extendee - 0, // [0:10] is the sub-list for field type_name + 1, // 6: identra.v1.RegisterByPasswordResponse.token:type_name -> identra.v1.TokenPair + 1, // 7: identra.v1.LoginByPasswordResponse.token:type_name -> identra.v1.TokenPair + 1, // 8: identra.v1.RefreshTokenResponse.token:type_name -> identra.v1.TokenPair + 23, // 9: identra.v1.ListOAuthProvidersResponse.providers:type_name -> identra.v1.OAuthProviderStatus + 21, // 10: identra.v1.GetCurrentUserLoginInfoResponse.oauth_connections:type_name -> identra.v1.OAuthConnection + 11, // [11:11] is the sub-list for method output_type + 11, // [11:11] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name } func init() { file_identra_v1_types_proto_init() } @@ -1551,15 +1655,15 @@ func file_identra_v1_types_proto_init() { } file_identra_v1_types_proto_msgTypes[2].OneofWrappers = []any{} file_identra_v1_types_proto_msgTypes[5].OneofWrappers = []any{} - file_identra_v1_types_proto_msgTypes[21].OneofWrappers = []any{} - file_identra_v1_types_proto_msgTypes[24].OneofWrappers = []any{} + file_identra_v1_types_proto_msgTypes[23].OneofWrappers = []any{} + file_identra_v1_types_proto_msgTypes[26].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_identra_v1_types_proto_rawDesc), len(file_identra_v1_types_proto_rawDesc)), NumEnums: 0, - NumMessages: 25, + NumMessages: 27, NumExtensions: 0, NumServices: 0, }, diff --git a/gen/openapi/identra.swagger.json b/gen/openapi/identra.swagger.json index 3122379..215d8c7 100644 --- a/gen/openapi/identra.swagger.json +++ b/gen/openapi/identra.swagger.json @@ -291,6 +291,38 @@ ] } }, + "/password/register": { + "post": { + "operationId": "IdentraService_RegisterByPassword", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1RegisterByPasswordResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1RegisterByPasswordRequest" + } + } + ], + "tags": [ + "IdentraService" + ] + } + }, "/token/refresh": { "post": { "operationId": "IdentraService_RefreshToken", @@ -620,6 +652,25 @@ } } }, + "v1RegisterByPasswordRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "v1RegisterByPasswordResponse": { + "type": "object", + "properties": { + "token": { + "$ref": "#/definitions/v1TokenPair" + } + } + }, "v1SendLoginEmailCodeRequest": { "type": "object", "properties": { diff --git a/internal/application/identra/service.go b/internal/application/identra/service.go index d52e99c..8b849e0 100644 --- a/internal/application/identra/service.go +++ b/internal/application/identra/service.go @@ -532,6 +532,46 @@ func (s *Service) LoginByEmailCode( return &identra_v1_pb.LoginByEmailCodeResponse{Token: tokenPair}, nil } +func (s *Service) RegisterByPassword( + ctx context.Context, + req *identra_v1_pb.RegisterByPasswordRequest, +) (*identra_v1_pb.RegisterByPasswordResponse, error) { + email := strings.TrimSpace(req.GetEmail()) + password := req.GetPassword() + if email == "" || password == "" { + return nil, status.Error(codes.InvalidArgument, "email and password are required") + } + + _, err := s.userStore.GetByEmail(ctx, email) + switch { + case err == nil: + return nil, status.Error(codes.AlreadyExists, "user already exists") + case errors.Is(err, domain.ErrNotFound): + // expected — proceed to create + default: + return nil, status.Error(codes.Internal, "failed to fetch user") + } + + hash, hashErr := security.HashPassword(password) + if hashErr != nil { + slog.ErrorContext(ctx, "failed to hash password", "error", hashErr) + return nil, status.Error(codes.Internal, "failed to process password") + } + usr := &domain.UserModel{Email: email, HashedPassword: &hash} + if createErr := s.userStore.Create(ctx, usr); createErr != nil { + return nil, status.Error(codes.Internal, "failed to create user") + } + + s.recordLogin(ctx, usr) + tokenPair, err := security.NewTokenPair(usr.ID, s.tokenCfg) + if err != nil { + slog.ErrorContext(ctx, "failed to create token pair (register)", "error", err) + return nil, status.Error(codes.Internal, "failed to create token pair") + } + + return &identra_v1_pb.RegisterByPasswordResponse{Token: tokenPair}, nil +} + func (s *Service) LoginByPassword( ctx context.Context, req *identra_v1_pb.LoginByPasswordRequest, @@ -542,46 +582,31 @@ func (s *Service) LoginByPassword( return nil, status.Error(codes.InvalidArgument, "email and password are required") } - existing, err := s.userStore.GetByEmail(ctx, email) + usr, err := s.userStore.GetByEmail(ctx, email) switch { case err == nil: - if existing.HashedPassword == nil { - hash, hashErr := security.HashPassword(password) - if hashErr != nil { - slog.ErrorContext(ctx, "failed to hash password", "error", hashErr) - return nil, status.Error(codes.Internal, "failed to process password") - } - existing.HashedPassword = &hash - if updateErr := s.userStore.Update(ctx, existing); updateErr != nil { - return nil, status.Error(codes.Internal, "failed to persist password") - } - } else { - valid, verifyErr := security.VerifyPassword(password, *existing.HashedPassword) - if verifyErr != nil { - slog.ErrorContext(ctx, "password verification failed", "error", verifyErr) - return nil, status.Error(codes.Internal, "failed to verify password") - } - if !valid { - return nil, status.Error(codes.Unauthenticated, "invalid credentials") - } - } - + // user found — verify password below case errors.Is(err, domain.ErrNotFound): - hash, hashErr := security.HashPassword(password) - if hashErr != nil { - slog.ErrorContext(ctx, "failed to hash password", "error", hashErr) - return nil, status.Error(codes.Internal, "failed to process password") - } - existing = &domain.UserModel{Email: email, HashedPassword: &hash} - if createErr := s.userStore.Create(ctx, existing); createErr != nil { - return nil, status.Error(codes.Internal, "failed to create user") - } + return nil, status.Error(codes.NotFound, "user not found") default: return nil, status.Error(codes.Internal, "failed to fetch user") } - s.recordLogin(ctx, existing) - tokenPair, err := security.NewTokenPair(existing.ID, s.tokenCfg) + if usr.HashedPassword == nil { + return nil, status.Error(codes.FailedPrecondition, "password login not set up for this account") + } + + valid, verifyErr := security.VerifyPassword(password, *usr.HashedPassword) + if verifyErr != nil { + slog.ErrorContext(ctx, "password verification failed", "error", verifyErr) + return nil, status.Error(codes.Internal, "failed to verify password") + } + if !valid { + return nil, status.Error(codes.Unauthenticated, "invalid credentials") + } + + s.recordLogin(ctx, usr) + tokenPair, err := security.NewTokenPair(usr.ID, s.tokenCfg) if err != nil { slog.ErrorContext(ctx, "failed to create token pair", "error", err) return nil, status.Error(codes.Internal, "failed to create token pair") diff --git a/proto/identra/v1/identra_service.proto b/proto/identra/v1/identra_service.proto index 4409e19..6fcab86 100644 --- a/proto/identra/v1/identra_service.proto +++ b/proto/identra/v1/identra_service.proto @@ -39,6 +39,12 @@ service IdentraService { body: "*" }; } + rpc RegisterByPassword(RegisterByPasswordRequest) returns (RegisterByPasswordResponse) { + option (google.api.http) = { + post: "/password/register" + body: "*" + }; + } rpc LoginByPassword(LoginByPasswordRequest) returns (LoginByPasswordResponse) { option (google.api.http) = { post: "/password/login" diff --git a/proto/identra/v1/types.proto b/proto/identra/v1/types.proto index e6bad7a..3f5e7e1 100644 --- a/proto/identra/v1/types.proto +++ b/proto/identra/v1/types.proto @@ -105,6 +105,14 @@ message LoginByEmailCodeResponse { TokenPair token = 1; } +message RegisterByPasswordRequest { + string email = 1; + string password = 2; +} +message RegisterByPasswordResponse { + TokenPair token = 1; +} + message LoginByPasswordRequest { string email = 1; string password = 2; From 188d79b8928c7809430ec450620b62df05ea19a7 Mon Sep 17 00:00:00 2001 From: slhmy Date: Wed, 15 Apr 2026 12:47:43 +0800 Subject: [PATCH 2/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- internal/application/identra/service.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/application/identra/service.go b/internal/application/identra/service.go index 8b849e0..fbd10b9 100644 --- a/internal/application/identra/service.go +++ b/internal/application/identra/service.go @@ -559,6 +559,10 @@ func (s *Service) RegisterByPassword( } usr := &domain.UserModel{Email: email, HashedPassword: &hash} if createErr := s.userStore.Create(ctx, usr); createErr != nil { + if _, getErr := s.userStore.GetByEmail(ctx, email); getErr == nil { + return nil, status.Error(codes.AlreadyExists, "user already exists") + } + slog.ErrorContext(ctx, "failed to create user", "error", createErr, "email", email) return nil, status.Error(codes.Internal, "failed to create user") } From 2e9e749c9462729dd9ebf1c3616d0294a7c9910d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 04:55:16 +0000 Subject: [PATCH 3/3] Handle duplicate-key errors properly and add unit tests for password registration/login Agent-Logs-Url: https://github.com/poly-workshop/identra/sessions/96f57d20-bfe5-46ba-9991-86c33cd07e7a Co-authored-by: slhmy <31381093+slhmy@users.noreply.github.com> --- internal/application/identra/service.go | 2 +- .../identra/service_ensure_oauth_user_test.go | 6 +- .../identra/service_password_test.go | 222 ++++++++++++++++++ internal/domain/user.go | 3 + .../persistence/gorm_user_store.go | 5 +- .../persistence/mongo_user_store.go | 3 + 6 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 internal/application/identra/service_password_test.go diff --git a/internal/application/identra/service.go b/internal/application/identra/service.go index fbd10b9..2ff6a24 100644 --- a/internal/application/identra/service.go +++ b/internal/application/identra/service.go @@ -559,7 +559,7 @@ func (s *Service) RegisterByPassword( } usr := &domain.UserModel{Email: email, HashedPassword: &hash} if createErr := s.userStore.Create(ctx, usr); createErr != nil { - if _, getErr := s.userStore.GetByEmail(ctx, email); getErr == nil { + if errors.Is(createErr, domain.ErrAlreadyExists) { return nil, status.Error(codes.AlreadyExists, "user already exists") } slog.ErrorContext(ctx, "failed to create user", "error", createErr, "email", email) diff --git a/internal/application/identra/service_ensure_oauth_user_test.go b/internal/application/identra/service_ensure_oauth_user_test.go index be50d2f..5181387 100644 --- a/internal/application/identra/service_ensure_oauth_user_test.go +++ b/internal/application/identra/service_ensure_oauth_user_test.go @@ -9,7 +9,8 @@ import ( // mockUserStore is a simple in-memory user store for testing. type mockUserStore struct { - users map[string]*domain.UserModel + users map[string]*domain.UserModel + forceCreateErr error // when set, Create returns this error instead of storing the user } func newMockUserStore() *mockUserStore { @@ -19,6 +20,9 @@ func newMockUserStore() *mockUserStore { } func (m *mockUserStore) Create(ctx context.Context, user *domain.UserModel) error { + if m.forceCreateErr != nil { + return m.forceCreateErr + } if user.ID == "" { user.ID = "test-user-id" } diff --git a/internal/application/identra/service_password_test.go b/internal/application/identra/service_password_test.go new file mode 100644 index 0000000..3ad8c8e --- /dev/null +++ b/internal/application/identra/service_password_test.go @@ -0,0 +1,222 @@ +package identra + +import ( + "context" + "testing" + "time" + + identra_v1_pb "github.com/poly-workshop/identra/gen/go/identra/v1" + "github.com/poly-workshop/identra/internal/domain" + "github.com/poly-workshop/identra/internal/infrastructure/security" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// newTestTokenConfig generates a throwaway RSA key pair for unit tests. +func newTestTokenConfig(t *testing.T) security.TokenConfig { + t.Helper() + km := &security.KeyManager{} + if err := km.GenerateKeyPair(); err != nil { + t.Fatalf("failed to generate key pair for tests: %v", err) + } + return security.TokenConfig{ + PrivateKey: km.GetPrivateKey(), + PublicKey: km.GetPublicKey(), + KeyID: km.GetKeyID(), + Issuer: "test", + AccessTokenExpiration: 15 * time.Minute, + RefreshTokenExpiration: 7 * 24 * time.Hour, + } +} + +// requireCode asserts the gRPC status code of an error. +func requireCode(t *testing.T, err error, want codes.Code) { + t.Helper() + if err == nil { + t.Fatalf("expected error with code %s, got nil", want) + } + st, ok := status.FromError(err) + if !ok { + t.Fatalf("expected gRPC status error, got %T: %v", err, err) + } + if st.Code() != want { + t.Errorf("expected status code %s, got %s: %s", want, st.Code(), st.Message()) + } +} + +// ---- RegisterByPassword tests ---- + +func TestRegisterByPassword_MissingFields(t *testing.T) { + svc := &Service{userStore: newMockUserStore()} + + cases := []struct { + name string + email string + pwd string + }{ + {"empty email", "", "secret"}, + {"empty password", "user@example.com", ""}, + {"both empty", "", ""}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + _, err := svc.RegisterByPassword(context.Background(), &identra_v1_pb.RegisterByPasswordRequest{ + Email: tc.email, + Password: tc.pwd, + }) + requireCode(t, err, codes.InvalidArgument) + }) + } +} + +func TestRegisterByPassword_Success(t *testing.T) { + svc := &Service{ + userStore: newMockUserStore(), + tokenCfg: newTestTokenConfig(t), + } + + resp, err := svc.RegisterByPassword(context.Background(), &identra_v1_pb.RegisterByPasswordRequest{ + Email: "new@example.com", + Password: "s3cr3t", + }) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + if resp.Token == nil || resp.Token.AccessToken == nil || resp.Token.AccessToken.Token == "" { + t.Error("expected a non-empty access token in the response") + } +} + +func TestRegisterByPassword_ExistingEmail(t *testing.T) { + store := newMockUserStore() + // Pre-populate a user with the same email. + _ = store.Create(context.Background(), &domain.UserModel{ + ID: "existing-id", + Email: "taken@example.com", + }) + + svc := &Service{userStore: store} + + _, err := svc.RegisterByPassword(context.Background(), &identra_v1_pb.RegisterByPasswordRequest{ + Email: "taken@example.com", + Password: "s3cr3t", + }) + requireCode(t, err, codes.AlreadyExists) +} + +func TestRegisterByPassword_DuplicateRace(t *testing.T) { + // Simulate a race where GetByEmail returns ErrNotFound (pre-check passes) + // but Create fails with domain.ErrAlreadyExists (another goroutine just + // created the same user). + store := newMockUserStore() + store.forceCreateErr = domain.ErrAlreadyExists + + svc := &Service{userStore: store} + + _, err := svc.RegisterByPassword(context.Background(), &identra_v1_pb.RegisterByPasswordRequest{ + Email: "race@example.com", + Password: "s3cr3t", + }) + requireCode(t, err, codes.AlreadyExists) +} + +// ---- LoginByPassword tests ---- + +func TestLoginByPassword_MissingFields(t *testing.T) { + svc := &Service{userStore: newMockUserStore()} + + cases := []struct { + name string + email string + pwd string + }{ + {"empty email", "", "secret"}, + {"empty password", "user@example.com", ""}, + {"both empty", "", ""}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + _, err := svc.LoginByPassword(context.Background(), &identra_v1_pb.LoginByPasswordRequest{ + Email: tc.email, + Password: tc.pwd, + }) + requireCode(t, err, codes.InvalidArgument) + }) + } +} + +func TestLoginByPassword_NotFound(t *testing.T) { + svc := &Service{userStore: newMockUserStore()} + + _, err := svc.LoginByPassword(context.Background(), &identra_v1_pb.LoginByPasswordRequest{ + Email: "nobody@example.com", + Password: "s3cr3t", + }) + requireCode(t, err, codes.NotFound) +} + +func TestLoginByPassword_NoPassword(t *testing.T) { + store := newMockUserStore() + _ = store.Create(context.Background(), &domain.UserModel{ + ID: "uid1", + Email: "oauth@example.com", + HashedPassword: nil, // OAuth-only account + }) + svc := &Service{userStore: store} + + _, err := svc.LoginByPassword(context.Background(), &identra_v1_pb.LoginByPasswordRequest{ + Email: "oauth@example.com", + Password: "s3cr3t", + }) + requireCode(t, err, codes.FailedPrecondition) +} + +func TestLoginByPassword_WrongPassword(t *testing.T) { + hash, err := security.HashPassword("correct-password") + if err != nil { + t.Fatalf("failed to hash password: %v", err) + } + + store := newMockUserStore() + _ = store.Create(context.Background(), &domain.UserModel{ + ID: "uid2", + Email: "user@example.com", + HashedPassword: &hash, + }) + svc := &Service{userStore: store} + + _, loginErr := svc.LoginByPassword(context.Background(), &identra_v1_pb.LoginByPasswordRequest{ + Email: "user@example.com", + Password: "wrong-password", + }) + requireCode(t, loginErr, codes.Unauthenticated) +} + +func TestLoginByPassword_Success(t *testing.T) { + hash, err := security.HashPassword("correct-password") + if err != nil { + t.Fatalf("failed to hash password: %v", err) + } + + store := newMockUserStore() + _ = store.Create(context.Background(), &domain.UserModel{ + ID: "uid3", + Email: "user@example.com", + HashedPassword: &hash, + }) + svc := &Service{ + userStore: store, + tokenCfg: newTestTokenConfig(t), + } + + resp, loginErr := svc.LoginByPassword(context.Background(), &identra_v1_pb.LoginByPasswordRequest{ + Email: "user@example.com", + Password: "correct-password", + }) + if loginErr != nil { + t.Fatalf("expected no error, got %v", loginErr) + } + if resp.Token == nil || resp.Token.AccessToken == nil || resp.Token.AccessToken.Token == "" { + t.Error("expected a non-empty access token in the response") + } +} diff --git a/internal/domain/user.go b/internal/domain/user.go index 0a281ed..436de87 100644 --- a/internal/domain/user.go +++ b/internal/domain/user.go @@ -12,6 +12,9 @@ import ( // ErrNotFound is returned when a resource is not found. var ErrNotFound = errors.New("resource not found") +// ErrAlreadyExists is returned when a resource with the same unique key already exists. +var ErrAlreadyExists = errors.New("resource already exists") + // UserModel represents a user entity in the system. type UserModel struct { ID string `gorm:"type:varchar(36);primaryKey" bson:"_id,omitempty" json:"id"` diff --git a/internal/infrastructure/persistence/gorm_user_store.go b/internal/infrastructure/persistence/gorm_user_store.go index 5e0bebb..e34e0c1 100644 --- a/internal/infrastructure/persistence/gorm_user_store.go +++ b/internal/infrastructure/persistence/gorm_user_store.go @@ -26,6 +26,9 @@ func wrapGormError(err error) error { if errors.Is(err, gorm.ErrRecordNotFound) { return domain.ErrNotFound } + if errors.Is(err, gorm.ErrDuplicatedKey) { + return domain.ErrAlreadyExists + } return err } @@ -33,7 +36,7 @@ func (r *gormUserStore) Create(ctx context.Context, user *domain.UserModel) erro err := r.db.WithContext(ctx).Create(user).Error if err != nil { slog.ErrorContext(ctx, "failed to create user", "error", err, "email", user.Email) - return err + return wrapGormError(err) } slog.InfoContext( ctx, diff --git a/internal/infrastructure/persistence/mongo_user_store.go b/internal/infrastructure/persistence/mongo_user_store.go index dc0d7a0..be90ef5 100644 --- a/internal/infrastructure/persistence/mongo_user_store.go +++ b/internal/infrastructure/persistence/mongo_user_store.go @@ -71,6 +71,9 @@ func (r *mongoUserStore) Create(ctx context.Context, user *domain.UserModel) err if _, err := r.coll.InsertOne(ctx, user); err != nil { slog.ErrorContext(ctx, "failed to create user (mongo)", "error", err, "email", user.Email) + if mongo.IsDuplicateKeyError(err) { + return domain.ErrAlreadyExists + } return err } slog.InfoContext(ctx, "user created successfully (mongo)", "user_id", user.ID, "email", user.Email)