From dd84e73c0ec7eb4c09dfde37c30eea9b6e5da7fa Mon Sep 17 00:00:00 2001 From: Tyler Cloke Date: Mon, 6 Aug 2018 12:04:56 -0700 Subject: [PATCH] Add VerifyPassword to API It takes in an email and plain text password to verify. If it fails to find a password stored for email, it returns not_found. If it finds the password hash stored but that hash doesn't match the password passed via the API, it returns verified = false, else it returns verified = true. Co-authored-by: Alban Seurat --- api/api.pb.go | 199 ++++++++++++++++++++++++--------- api/api.proto | 12 ++ examples/grpc-client/README.md | 3 +- examples/grpc-client/client.go | 33 ++++++ server/api.go | 31 +++++ server/api_test.go | 53 ++++++++- 6 files changed, 274 insertions(+), 57 deletions(-) diff --git a/api/api.pb.go b/api/api.pb.go index 9395f484..56574c09 100644 --- a/api/api.pb.go +++ b/api/api.pb.go @@ -31,6 +31,8 @@ It has these top-level messages: ListRefreshResp RevokeRefreshReq RevokeRefreshResp + VerifyPasswordReq + VerifyPasswordResp */ package api @@ -607,6 +609,54 @@ func (m *RevokeRefreshResp) GetNotFound() bool { return false } +type VerifyPasswordReq struct { + Email string `protobuf:"bytes,1,opt,name=email" json:"email,omitempty"` + Password string `protobuf:"bytes,2,opt,name=password" json:"password,omitempty"` +} + +func (m *VerifyPasswordReq) Reset() { *m = VerifyPasswordReq{} } +func (m *VerifyPasswordReq) String() string { return proto.CompactTextString(m) } +func (*VerifyPasswordReq) ProtoMessage() {} +func (*VerifyPasswordReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} } + +func (m *VerifyPasswordReq) GetEmail() string { + if m != nil { + return m.Email + } + return "" +} + +func (m *VerifyPasswordReq) GetPassword() string { + if m != nil { + return m.Password + } + return "" +} + +type VerifyPasswordResp struct { + Verified bool `protobuf:"varint,1,opt,name=verified" json:"verified,omitempty"` + NotFound bool `protobuf:"varint,2,opt,name=not_found,json=notFound" json:"not_found,omitempty"` +} + +func (m *VerifyPasswordResp) Reset() { *m = VerifyPasswordResp{} } +func (m *VerifyPasswordResp) String() string { return proto.CompactTextString(m) } +func (*VerifyPasswordResp) ProtoMessage() {} +func (*VerifyPasswordResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} } + +func (m *VerifyPasswordResp) GetVerified() bool { + if m != nil { + return m.Verified + } + return false +} + +func (m *VerifyPasswordResp) GetNotFound() bool { + if m != nil { + return m.NotFound + } + return false +} + func init() { proto.RegisterType((*Client)(nil), "api.Client") proto.RegisterType((*CreateClientReq)(nil), "api.CreateClientReq") @@ -631,6 +681,8 @@ func init() { proto.RegisterType((*ListRefreshResp)(nil), "api.ListRefreshResp") proto.RegisterType((*RevokeRefreshReq)(nil), "api.RevokeRefreshReq") proto.RegisterType((*RevokeRefreshResp)(nil), "api.RevokeRefreshResp") + proto.RegisterType((*VerifyPasswordReq)(nil), "api.VerifyPasswordReq") + proto.RegisterType((*VerifyPasswordResp)(nil), "api.VerifyPasswordResp") } // Reference imports to suppress errors if they are not otherwise used. @@ -666,6 +718,8 @@ type DexClient interface { // // Note that each user-client pair can have only one refresh token at a time. RevokeRefresh(ctx context.Context, in *RevokeRefreshReq, opts ...grpc.CallOption) (*RevokeRefreshResp, error) + // VerifyPassword returns whether a password matches a hash for a specific email or not. + VerifyPassword(ctx context.Context, in *VerifyPasswordReq, opts ...grpc.CallOption) (*VerifyPasswordResp, error) } type dexClient struct { @@ -766,6 +820,15 @@ func (c *dexClient) RevokeRefresh(ctx context.Context, in *RevokeRefreshReq, opt return out, nil } +func (c *dexClient) VerifyPassword(ctx context.Context, in *VerifyPasswordReq, opts ...grpc.CallOption) (*VerifyPasswordResp, error) { + out := new(VerifyPasswordResp) + err := grpc.Invoke(ctx, "/api.Dex/VerifyPassword", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // Server API for Dex service type DexServer interface { @@ -791,6 +854,8 @@ type DexServer interface { // // Note that each user-client pair can have only one refresh token at a time. RevokeRefresh(context.Context, *RevokeRefreshReq) (*RevokeRefreshResp, error) + // VerifyPassword returns whether a password matches a hash for a specific email or not. + VerifyPassword(context.Context, *VerifyPasswordReq) (*VerifyPasswordResp, error) } func RegisterDexServer(s *grpc.Server, srv DexServer) { @@ -977,6 +1042,24 @@ func _Dex_RevokeRefresh_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } +func _Dex_VerifyPassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(VerifyPasswordReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DexServer).VerifyPassword(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Dex/VerifyPassword", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DexServer).VerifyPassword(ctx, req.(*VerifyPasswordReq)) + } + return interceptor(ctx, in, info, handler) +} + var _Dex_serviceDesc = grpc.ServiceDesc{ ServiceName: "api.Dex", HandlerType: (*DexServer)(nil), @@ -1021,6 +1104,10 @@ var _Dex_serviceDesc = grpc.ServiceDesc{ MethodName: "RevokeRefresh", Handler: _Dex_RevokeRefresh_Handler, }, + { + MethodName: "VerifyPassword", + Handler: _Dex_VerifyPassword_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "api/api.proto", @@ -1029,58 +1116,62 @@ var _Dex_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("api/api.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 848 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x56, 0xdb, 0x4e, 0xf3, 0x46, - 0x10, 0x4e, 0x62, 0x92, 0x38, 0x93, 0xf3, 0x36, 0x3f, 0x84, 0xa0, 0x4a, 0xb0, 0xa8, 0x12, 0xa8, - 0x52, 0x28, 0x54, 0x6a, 0xa5, 0xa2, 0xd2, 0x03, 0xb4, 0x05, 0xa9, 0xaa, 0x90, 0xd5, 0xf4, 0xb2, - 0x96, 0x89, 0x07, 0x58, 0x61, 0x6c, 0x77, 0x77, 0x43, 0x68, 0x2f, 0xfb, 0x18, 0x7d, 0x9b, 0xbe, - 0xd9, 0xaf, 0x5d, 0x6f, 0x82, 0x0f, 0x81, 0x70, 0xe7, 0xf9, 0x76, 0xe6, 0x9b, 0xf3, 0xc8, 0xd0, - 0xf6, 0x62, 0x76, 0xe4, 0xc5, 0x6c, 0x1c, 0xf3, 0x48, 0x46, 0xc4, 0xf2, 0x62, 0x46, 0xff, 0x2f, - 0x43, 0xed, 0x3c, 0x60, 0x18, 0x4a, 0xd2, 0x81, 0x0a, 0xf3, 0x87, 0xe5, 0xdd, 0xf2, 0x41, 0xc3, - 0xa9, 0x30, 0x9f, 0x6c, 0x42, 0x4d, 0xe0, 0x94, 0xa3, 0x1c, 0x56, 0x34, 0x66, 0x24, 0xb2, 0x0f, - 0x6d, 0x8e, 0x3e, 0xe3, 0x38, 0x95, 0xee, 0x8c, 0x33, 0x31, 0xb4, 0x76, 0xad, 0x83, 0x86, 0xd3, - 0x5a, 0x80, 0x13, 0xce, 0x84, 0x52, 0x92, 0x7c, 0x26, 0x24, 0xfa, 0x6e, 0x8c, 0xc8, 0xc5, 0x70, - 0x23, 0x51, 0x32, 0xe0, 0xb5, 0xc2, 0x94, 0x87, 0x78, 0x76, 0x13, 0xb0, 0xe9, 0xb0, 0xba, 0x5b, - 0x3e, 0xb0, 0x1d, 0x23, 0x11, 0x02, 0x1b, 0xa1, 0xf7, 0x88, 0xc3, 0x9a, 0xf6, 0xab, 0xbf, 0xc9, - 0x36, 0xd8, 0x41, 0x74, 0x17, 0xb9, 0x33, 0x1e, 0x0c, 0xeb, 0x1a, 0xaf, 0x2b, 0x79, 0xc2, 0x03, - 0xfa, 0x15, 0x74, 0xcf, 0x39, 0x7a, 0x12, 0x93, 0x44, 0x1c, 0xfc, 0x8b, 0xec, 0x43, 0x6d, 0xaa, - 0x05, 0x9d, 0x4f, 0xf3, 0xa4, 0x39, 0x56, 0x79, 0x9b, 0x77, 0xf3, 0x44, 0xff, 0x84, 0x5e, 0xd6, - 0x4e, 0xc4, 0xe4, 0x33, 0xe8, 0x78, 0x01, 0x47, 0xcf, 0xff, 0xdb, 0xc5, 0x67, 0x26, 0xa4, 0xd0, - 0x04, 0xb6, 0xd3, 0x36, 0xe8, 0x4f, 0x1a, 0x4c, 0xf1, 0x57, 0x5e, 0xe7, 0xdf, 0x83, 0xee, 0x05, - 0x06, 0x98, 0x8e, 0x2b, 0x57, 0x63, 0x7a, 0x04, 0xbd, 0xac, 0x8a, 0x88, 0xc9, 0x0e, 0x34, 0xc2, - 0x48, 0xba, 0xb7, 0xd1, 0x2c, 0xf4, 0x8d, 0x77, 0x3b, 0x8c, 0xe4, 0xcf, 0x4a, 0xa6, 0xff, 0x95, - 0xa1, 0x3b, 0x89, 0x7d, 0xef, 0x0d, 0xd2, 0x62, 0x83, 0x2a, 0xef, 0x69, 0x90, 0xb5, 0xa2, 0x41, - 0x8b, 0x46, 0x6c, 0xbc, 0xd2, 0x88, 0x6a, 0xb6, 0x11, 0x47, 0xd0, 0xcb, 0xc6, 0xb6, 0x2e, 0x1b, - 0x06, 0xf6, 0xb5, 0x27, 0xc4, 0x3c, 0xe2, 0x3e, 0x19, 0x40, 0x15, 0x1f, 0x3d, 0x16, 0x98, 0x44, - 0x12, 0x41, 0x45, 0x70, 0xef, 0x89, 0x7b, 0x5d, 0xe6, 0x96, 0xa3, 0xbf, 0xc9, 0x08, 0xec, 0x99, - 0x40, 0xae, 0x23, 0xb3, 0xb4, 0xf2, 0x52, 0x26, 0x5b, 0x50, 0x57, 0xdf, 0x2e, 0xf3, 0x4d, 0xd0, - 0x35, 0x25, 0x5e, 0xf9, 0xf4, 0x0c, 0xfa, 0x49, 0xb3, 0x17, 0x0e, 0x55, 0xe5, 0x0e, 0xc1, 0x8e, - 0x8d, 0x68, 0x06, 0xa5, 0xad, 0x1b, 0xb9, 0xd4, 0x59, 0x3e, 0xd3, 0x53, 0x20, 0x79, 0xfb, 0x77, - 0x8f, 0x0b, 0xbd, 0x83, 0x7e, 0x52, 0x98, 0xb4, 0xf3, 0xd5, 0x09, 0x6f, 0x83, 0x1d, 0xe2, 0xdc, - 0x4d, 0x25, 0x5d, 0x0f, 0x71, 0x7e, 0xa9, 0xf2, 0xde, 0x83, 0x96, 0x7a, 0xca, 0xe5, 0xde, 0x0c, - 0x71, 0x3e, 0x31, 0x10, 0x3d, 0x06, 0x92, 0x77, 0xb4, 0xae, 0x07, 0x87, 0xd0, 0x4f, 0x46, 0x70, - 0x6d, 0x6c, 0x8a, 0x3d, 0xaf, 0xba, 0x8e, 0xbd, 0x0f, 0xdd, 0x5f, 0x99, 0x90, 0x29, 0x6e, 0xfa, - 0x1d, 0xf4, 0xb2, 0x90, 0x88, 0xc9, 0xe7, 0xd0, 0x58, 0x54, 0x5a, 0x95, 0xd0, 0x2a, 0x76, 0xe2, - 0xe5, 0x9d, 0xb6, 0x00, 0xfe, 0x40, 0x2e, 0x58, 0x14, 0x2a, 0xba, 0xaf, 0xa1, 0xb9, 0x94, 0x44, - 0x9c, 0x5c, 0x2d, 0xfe, 0x84, 0xdc, 0x84, 0x6e, 0x24, 0xd2, 0x03, 0x75, 0xef, 0x74, 0x49, 0xab, - 0x8e, 0x3e, 0x7d, 0xff, 0x40, 0xd7, 0xc1, 0x5b, 0x8e, 0xe2, 0xfe, 0xf7, 0xe8, 0x01, 0x43, 0x07, - 0x6f, 0x0b, 0x9b, 0xb4, 0x03, 0x8d, 0x64, 0x97, 0xd5, 0x3c, 0x25, 0x57, 0xd0, 0x4e, 0x80, 0x2b, - 0x9f, 0x7c, 0x0a, 0x30, 0xd5, 0x13, 0xe1, 0xbb, 0x9e, 0xd4, 0xab, 0x60, 0x39, 0x0d, 0x83, 0xfc, - 0x20, 0x95, 0x6d, 0xe0, 0x09, 0xa9, 0xda, 0xe5, 0xeb, 0x4b, 0x66, 0x39, 0xb6, 0x02, 0x26, 0x02, - 0x55, 0xd1, 0x3b, 0xaa, 0x06, 0xc6, 0xbf, 0xaa, 0x78, 0x6a, 0x70, 0xcb, 0x99, 0xc1, 0xfd, 0x2d, - 0xa9, 0xe0, 0x52, 0x55, 0xc4, 0xe4, 0x14, 0x3a, 0x3c, 0x11, 0x5d, 0xa9, 0x42, 0x5f, 0x94, 0x6c, - 0xa0, 0x4b, 0x96, 0x4b, 0xca, 0x69, 0xf3, 0x14, 0x20, 0xe8, 0x25, 0xf4, 0x1c, 0x7c, 0x8a, 0x1e, - 0xf0, 0x1d, 0xce, 0xdf, 0x2c, 0x00, 0xfd, 0x02, 0xfa, 0x39, 0xa6, 0x35, 0xd3, 0x70, 0xf2, 0x6f, - 0x15, 0xac, 0x0b, 0x7c, 0x26, 0xdf, 0x42, 0x2b, 0x7d, 0x79, 0x49, 0x12, 0x78, 0xee, 0x88, 0x8f, - 0x3e, 0xac, 0x40, 0x45, 0x4c, 0x4b, 0xca, 0x3c, 0x7d, 0x67, 0x8c, 0x79, 0xee, 0x2c, 0x1a, 0xf3, - 0xfc, 0x41, 0x4a, 0xcc, 0xd3, 0x47, 0xd7, 0x98, 0xe7, 0x4e, 0xb5, 0x31, 0xcf, 0x5f, 0x67, 0x5a, - 0x22, 0xe7, 0xd0, 0xc9, 0x5e, 0x02, 0xb2, 0x99, 0x0a, 0x34, 0x35, 0xe9, 0xa3, 0xad, 0x95, 0xf8, - 0x82, 0x24, 0xbb, 0xa8, 0x86, 0xa4, 0x70, 0x26, 0x0c, 0x49, 0x71, 0xab, 0x13, 0x92, 0xec, 0x3e, - 0x1a, 0x92, 0xc2, 0x3e, 0x1b, 0x92, 0xe2, 0xf2, 0xd2, 0x12, 0x39, 0x83, 0x76, 0x7a, 0x1d, 0x85, - 0x29, 0x47, 0x6e, 0x6b, 0x4d, 0x39, 0xf2, 0x8b, 0x4b, 0x4b, 0xe4, 0x18, 0xe0, 0x17, 0x94, 0x66, - 0x05, 0x49, 0x57, 0xab, 0xbd, 0xac, 0xe7, 0xa8, 0x97, 0x05, 0xb4, 0xc9, 0x37, 0xd0, 0x4c, 0x8d, - 0x34, 0xf9, 0x64, 0x49, 0xfd, 0x32, 0x92, 0xa3, 0x41, 0x11, 0xd4, 0xb6, 0xdf, 0x43, 0x3b, 0x33, - 0x74, 0xe4, 0x83, 0x19, 0xfa, 0xec, 0x48, 0x8f, 0x36, 0x57, 0xc1, 0x8a, 0xe1, 0xc7, 0x01, 0x90, - 0x69, 0xf4, 0x38, 0x9e, 0x46, 0x1c, 0x23, 0x31, 0xf6, 0xf1, 0x59, 0x69, 0xde, 0xd4, 0xf4, 0x4f, - 0xd1, 0x97, 0x1f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xd1, 0x00, 0x08, 0xae, 0x25, 0x09, 0x00, 0x00, + // 905 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x56, 0xeb, 0x6e, 0xdb, 0x36, + 0x14, 0xb6, 0xad, 0xd8, 0x96, 0x8f, 0xef, 0x9c, 0x9b, 0xba, 0x2e, 0x06, 0xa4, 0x2c, 0x06, 0xa4, + 0x18, 0xe0, 0xac, 0x1d, 0xb0, 0x01, 0x2b, 0xd6, 0x5d, 0xd2, 0x6e, 0x2d, 0xb0, 0x0d, 0x85, 0x30, + 0xe7, 0xe7, 0x04, 0xc5, 0x3a, 0x4e, 0x88, 0x28, 0x92, 0x46, 0xd2, 0x71, 0xb2, 0x47, 0xd9, 0xdb, + 0xec, 0xd7, 0x5e, 0xab, 0x20, 0x45, 0x29, 0xba, 0x38, 0x71, 0xfe, 0xf9, 0x7c, 0xe2, 0xb9, 0x7d, + 0x87, 0xe7, 0xa3, 0xa1, 0xef, 0xc5, 0xec, 0xc8, 0x8b, 0xd9, 0x3c, 0xe6, 0x91, 0x8c, 0x88, 0xe5, + 0xc5, 0x8c, 0xfe, 0x57, 0x87, 0xd6, 0x71, 0xc0, 0x30, 0x94, 0x64, 0x00, 0x0d, 0xe6, 0x4f, 0xeb, + 0x07, 0xf5, 0xc3, 0x8e, 0xd3, 0x60, 0x3e, 0xd9, 0x87, 0x96, 0xc0, 0x25, 0x47, 0x39, 0x6d, 0x68, + 0xcc, 0x58, 0xe4, 0x39, 0xf4, 0x39, 0xfa, 0x8c, 0xe3, 0x52, 0xba, 0x6b, 0xce, 0xc4, 0xd4, 0x3a, + 0xb0, 0x0e, 0x3b, 0x4e, 0x2f, 0x05, 0x17, 0x9c, 0x09, 0x75, 0x48, 0xf2, 0xb5, 0x90, 0xe8, 0xbb, + 0x31, 0x22, 0x17, 0xd3, 0xbd, 0xe4, 0x90, 0x01, 0x3f, 0x2a, 0x4c, 0x65, 0x88, 0xd7, 0xa7, 0x01, + 0x5b, 0x4e, 0x9b, 0x07, 0xf5, 0x43, 0xdb, 0x31, 0x16, 0x21, 0xb0, 0x17, 0x7a, 0x97, 0x38, 0x6d, + 0xe9, 0xbc, 0xfa, 0x37, 0x79, 0x02, 0x76, 0x10, 0x9d, 0x45, 0xee, 0x9a, 0x07, 0xd3, 0xb6, 0xc6, + 0xdb, 0xca, 0x5e, 0xf0, 0x80, 0x7e, 0x03, 0xc3, 0x63, 0x8e, 0x9e, 0xc4, 0xa4, 0x11, 0x07, 0xff, + 0x26, 0xcf, 0xa1, 0xb5, 0xd4, 0x86, 0xee, 0xa7, 0xfb, 0xaa, 0x3b, 0x57, 0x7d, 0x9b, 0xef, 0xe6, + 0x13, 0xfd, 0x0b, 0x46, 0x45, 0x3f, 0x11, 0x93, 0x2f, 0x60, 0xe0, 0x05, 0x1c, 0x3d, 0xff, 0xc6, + 0xc5, 0x6b, 0x26, 0xa4, 0xd0, 0x01, 0x6c, 0xa7, 0x6f, 0xd0, 0x77, 0x1a, 0xcc, 0xc5, 0x6f, 0xdc, + 0x1d, 0xff, 0x19, 0x0c, 0xdf, 0x62, 0x80, 0xf9, 0xba, 0x4a, 0x1c, 0xd3, 0x23, 0x18, 0x15, 0x8f, + 0x88, 0x98, 0x3c, 0x85, 0x4e, 0x18, 0x49, 0x77, 0x15, 0xad, 0x43, 0xdf, 0x64, 0xb7, 0xc3, 0x48, + 0xfe, 0xa2, 0x6c, 0xfa, 0x6f, 0x1d, 0x86, 0x8b, 0xd8, 0xf7, 0xee, 0x09, 0x5a, 0x1d, 0x50, 0xe3, + 0x21, 0x03, 0xb2, 0xb6, 0x0c, 0x28, 0x1d, 0xc4, 0xde, 0x1d, 0x83, 0x68, 0x16, 0x07, 0x71, 0x04, + 0xa3, 0x62, 0x6d, 0xbb, 0xba, 0x61, 0x60, 0x7f, 0xf4, 0x84, 0xd8, 0x44, 0xdc, 0x27, 0x13, 0x68, + 0xe2, 0xa5, 0xc7, 0x02, 0xd3, 0x48, 0x62, 0xa8, 0x0a, 0xce, 0x3d, 0x71, 0xae, 0x69, 0xee, 0x39, + 0xfa, 0x37, 0x99, 0x81, 0xbd, 0x16, 0xc8, 0x75, 0x65, 0x96, 0x3e, 0x9c, 0xd9, 0xe4, 0x31, 0xb4, + 0xd5, 0x6f, 0x97, 0xf9, 0xa6, 0xe8, 0x96, 0x32, 0x3f, 0xf8, 0xf4, 0x0d, 0x8c, 0x93, 0x61, 0xa7, + 0x09, 0x15, 0x73, 0x2f, 0xc0, 0x8e, 0x8d, 0x69, 0x2e, 0x4a, 0x5f, 0x0f, 0x32, 0x3b, 0x93, 0x7d, + 0xa6, 0xaf, 0x81, 0x94, 0xfd, 0x1f, 0x7c, 0x5d, 0xe8, 0x19, 0x8c, 0x13, 0x62, 0xf2, 0xc9, 0xb7, + 0x37, 0xfc, 0x04, 0xec, 0x10, 0x37, 0x6e, 0xae, 0xe9, 0x76, 0x88, 0x9b, 0xf7, 0xaa, 0xef, 0x67, + 0xd0, 0x53, 0x9f, 0x4a, 0xbd, 0x77, 0x43, 0xdc, 0x2c, 0x0c, 0x44, 0x5f, 0x02, 0x29, 0x27, 0xda, + 0x35, 0x83, 0x17, 0x30, 0x4e, 0xae, 0xe0, 0xce, 0xda, 0x54, 0xf4, 0xf2, 0xd1, 0x5d, 0xd1, 0xc7, + 0x30, 0xfc, 0x8d, 0x09, 0x99, 0x8b, 0x4d, 0x7f, 0x80, 0x51, 0x11, 0x12, 0x31, 0xf9, 0x12, 0x3a, + 0x29, 0xd3, 0x8a, 0x42, 0xab, 0x3a, 0x89, 0xdb, 0xef, 0xb4, 0x07, 0x70, 0x82, 0x5c, 0xb0, 0x28, + 0x54, 0xe1, 0xbe, 0x85, 0x6e, 0x66, 0x89, 0x38, 0x51, 0x2d, 0x7e, 0x85, 0xdc, 0x94, 0x6e, 0x2c, + 0x32, 0x02, 0xa5, 0x77, 0x9a, 0xd2, 0xa6, 0xa3, 0xa5, 0xef, 0x1f, 0x18, 0x3a, 0xb8, 0xe2, 0x28, + 0xce, 0xff, 0x8c, 0x2e, 0x30, 0x74, 0x70, 0x55, 0xd9, 0xa4, 0xa7, 0xd0, 0x49, 0x76, 0x59, 0xdd, + 0xa7, 0x44, 0x05, 0xed, 0x04, 0xf8, 0xe0, 0x93, 0xcf, 0x01, 0x96, 0xfa, 0x46, 0xf8, 0xae, 0x27, + 0xf5, 0x2a, 0x58, 0x4e, 0xc7, 0x20, 0x3f, 0x49, 0xe5, 0x1b, 0x78, 0x42, 0xaa, 0x71, 0xf9, 0x5a, + 0xc9, 0x2c, 0xc7, 0x56, 0xc0, 0x42, 0xa0, 0x22, 0x7d, 0xa0, 0x38, 0x30, 0xf9, 0x15, 0xe3, 0xb9, + 0x8b, 0x5b, 0x2f, 0x5c, 0xdc, 0x3f, 0x12, 0x06, 0xb3, 0xa3, 0x22, 0x26, 0xaf, 0x61, 0xc0, 0x13, + 0xd3, 0x95, 0xaa, 0xf4, 0x94, 0xb2, 0x89, 0xa6, 0xac, 0xd4, 0x94, 0xd3, 0xe7, 0x39, 0x40, 0xd0, + 0xf7, 0x30, 0x72, 0xf0, 0x2a, 0xba, 0xc0, 0x07, 0x24, 0xbf, 0x97, 0x00, 0xfa, 0x15, 0x8c, 0x4b, + 0x91, 0x76, 0xdd, 0x86, 0x77, 0x30, 0x3e, 0x41, 0xce, 0x56, 0x37, 0xbb, 0xf7, 0x60, 0x96, 0x5b, + 0x4d, 0x93, 0x38, 0xdb, 0xc5, 0xdf, 0x81, 0x94, 0xc3, 0x88, 0x58, 0x79, 0x5c, 0x29, 0x94, 0x61, + 0x96, 0x38, 0xb5, 0x8b, 0x55, 0x35, 0x8a, 0x55, 0xbd, 0xfa, 0xbf, 0x09, 0xd6, 0x5b, 0xbc, 0x26, + 0xdf, 0x43, 0x2f, 0xff, 0x1e, 0x90, 0x84, 0xce, 0xd2, 0xd3, 0x32, 0x7b, 0xb4, 0x05, 0x15, 0x31, + 0xad, 0x29, 0xf7, 0xbc, 0xfa, 0x19, 0xf7, 0x92, 0x58, 0x1b, 0xf7, 0xb2, 0x4c, 0x26, 0xee, 0xf9, + 0xa7, 0xc0, 0xb8, 0x97, 0x1e, 0x10, 0xe3, 0x5e, 0x7e, 0x33, 0x68, 0x8d, 0x1c, 0xc3, 0xa0, 0xa8, + 0x4f, 0x64, 0x3f, 0x57, 0x68, 0x8e, 0xef, 0xd9, 0xe3, 0xad, 0x78, 0x1a, 0xa4, 0x28, 0x1f, 0x26, + 0x48, 0x45, 0xbc, 0x4c, 0x90, 0xaa, 0xd6, 0x24, 0x41, 0x8a, 0x2a, 0x61, 0x82, 0x54, 0x54, 0xc6, + 0x04, 0xa9, 0x4a, 0x0a, 0xad, 0x91, 0x37, 0xd0, 0xcf, 0x8b, 0x84, 0x30, 0x74, 0x94, 0xb4, 0xc4, + 0xd0, 0x51, 0x96, 0x13, 0x5a, 0x23, 0x2f, 0x01, 0x7e, 0x45, 0x69, 0x84, 0x81, 0x0c, 0xf5, 0xb1, + 0x5b, 0xd1, 0x98, 0x8d, 0x8a, 0x80, 0x76, 0xf9, 0x0e, 0xba, 0xb9, 0x45, 0x23, 0x9f, 0x65, 0xa1, + 0x6f, 0x17, 0x65, 0x36, 0xa9, 0x82, 0xda, 0xf7, 0x47, 0xe8, 0x17, 0x56, 0x81, 0x3c, 0x32, 0xab, + 0x58, 0x5c, 0xb4, 0xd9, 0xfe, 0x36, 0x38, 0x65, 0xad, 0x78, 0xa7, 0x0d, 0x6b, 0x95, 0x7d, 0x31, + 0xac, 0x55, 0x17, 0x80, 0xd6, 0x7e, 0x9e, 0x00, 0x59, 0x46, 0x97, 0xf3, 0x65, 0xc4, 0x31, 0x12, + 0x73, 0x1f, 0xaf, 0xd5, 0xd1, 0xd3, 0x96, 0xfe, 0xbf, 0xf7, 0xf5, 0xa7, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x49, 0x46, 0x0e, 0xa3, 0x00, 0x0a, 0x00, 0x00, } diff --git a/api/api.proto b/api/api.proto index 7b91e822..5d9ce1a1 100644 --- a/api/api.proto +++ b/api/api.proto @@ -148,6 +148,16 @@ message RevokeRefreshResp { bool not_found = 1; } +message VerifyPasswordReq { + string email = 1; + string password = 2; +} + +message VerifyPasswordResp { + bool verified = 1; + bool not_found = 2; +} + // Dex represents the dex gRPC service. service Dex { // CreateClient creates a client. @@ -172,4 +182,6 @@ service Dex { // // Note that each user-client pair can have only one refresh token at a time. rpc RevokeRefresh(RevokeRefreshReq) returns (RevokeRefreshResp) {}; + // VerifyPassword returns whether a password matches a hash for a specific email or not. + rpc VerifyPassword(VerifyPasswordReq) returns (VerifyPasswordResp) {}; } diff --git a/examples/grpc-client/README.md b/examples/grpc-client/README.md index bd39219b..460a5d1c 100644 --- a/examples/grpc-client/README.md +++ b/examples/grpc-client/README.md @@ -48,7 +48,8 @@ Finally run the Dex client providing the CA certificate, client certificate and Running the gRPC client will cause the following API calls to be made to the server 1. CreatePassword 2. ListPasswords -3. DeletePassword +3. VerifyPassword +4. DeletePassword ## Cleaning up diff --git a/examples/grpc-client/client.go b/examples/grpc-client/client.go index ba976f5e..e4cd526d 100644 --- a/examples/grpc-client/client.go +++ b/examples/grpc-client/client.go @@ -76,6 +76,39 @@ func createPassword(cli api.DexClient) error { log.Printf("%+v", pass) } + // Verifying correct and incorrect passwords + log.Print("Verifying Password:\n") + verifyReq := &api.VerifyPasswordReq{ + Email: "test@example.com", + Password: "test1", + } + verifyResp, err := cli.VerifyPassword(context.TODO(), verifyReq) + if err != nil { + return fmt.Errorf("failed to run VerifyPassword for correct password: %v", err) + } + if !verifyResp.Verified { + return fmt.Errorf("failed to verify correct password: %v", verifyResp) + } + log.Printf("properly verified correct password: %t\n", verifyResp.Verified) + + badVerifyReq := &api.VerifyPasswordReq{ + Email: "test@example.com", + Password: "wrong_password", + } + badVerifyResp, err := cli.VerifyPassword(context.TODO(), badVerifyReq) + if err != nil { + return fmt.Errorf("failed to run VerifyPassword for incorrect password: %v", err) + } + if badVerifyResp.Verified { + return fmt.Errorf("verify returned true for incorrect password: %v", badVerifyResp) + } + log.Printf("properly failed to verify incorrect password: %t\n", badVerifyResp.Verified) + + log.Print("Listing Passwords:\n") + for _, pass := range resp.Passwords { + log.Printf("%+v", pass) + } + deleteReq := &api.DeletePasswordReq{ Email: p.Email, } diff --git a/server/api.go b/server/api.go index f900cd1c..850ec493 100644 --- a/server/api.go +++ b/server/api.go @@ -254,6 +254,37 @@ func (d dexAPI) ListPasswords(ctx context.Context, req *api.ListPasswordReq) (*a } +func (d dexAPI) VerifyPassword(ctx context.Context, req *api.VerifyPasswordReq) (*api.VerifyPasswordResp, error) { + if req.Email == "" { + return nil, errors.New("no email supplied") + } + + if req.Password == "" { + return nil, errors.New("no password to verify supplied") + } + + password, err := d.s.GetPassword(req.Email) + if err != nil { + if err == storage.ErrNotFound { + return &api.VerifyPasswordResp{ + NotFound: true, + }, nil + } + d.logger.Errorf("api: there was an error retrieving the password: %v", err) + return nil, fmt.Errorf("verify password: %v", err) + } + + if err := bcrypt.CompareHashAndPassword(password.Hash, []byte(req.Password)); err != nil { + d.logger.Info("api: password check failed : %v", err) + return &api.VerifyPasswordResp{ + Verified: false, + }, nil + } + return &api.VerifyPasswordResp{ + Verified: true, + }, nil +} + func (d dexAPI) ListRefresh(ctx context.Context, req *api.ListRefreshReq) (*api.ListRefreshResp, error) { id := new(internal.IDTokenSubject) if err := internal.Unmarshal(req.UserId, id); err != nil { diff --git a/server/api_test.go b/server/api_test.go index 568748bf..80a22486 100644 --- a/server/api_test.go +++ b/server/api_test.go @@ -69,8 +69,9 @@ func TestPassword(t *testing.T) { defer client.Close() ctx := context.Background() + email := "test@example.com" p := api.Password{ - Email: "test@example.com", + Email: email, // bcrypt hash of the value "test1" with cost 10 Hash: []byte("$2a$10$XVMN/Fid.Ks4CXgzo8fpR.iU1khOMsP5g9xQeXuBm1wXjRX8pjUtO"), Username: "test", @@ -93,8 +94,56 @@ func TestPassword(t *testing.T) { t.Fatalf("Created password %s twice", createReq.Password.Email) } + // Attempt to verify valid password and email + goodVerifyReq := &api.VerifyPasswordReq{ + Email: email, + Password: "test1", + } + goodVerifyResp, err := client.VerifyPassword(ctx, goodVerifyReq) + if err != nil { + t.Fatalf("Unable to run verify password we expected to be valid for correct email: %v", err) + } + if !goodVerifyResp.Verified { + t.Fatalf("verify password failed for password expected to be valid for correct email. expected %t, found %t", true, goodVerifyResp.Verified) + } + if goodVerifyResp.NotFound { + t.Fatalf("verify password failed to return not found response. expected %t, found %t", false, goodVerifyResp.NotFound) + } + + // Check not found response for valid password with wrong email + badEmailVerifyReq := &api.VerifyPasswordReq{ + Email: "somewrongaddress@email.com", + Password: "test1", + } + badEmailVerifyResp, err := client.VerifyPassword(ctx, badEmailVerifyReq) + if err != nil { + t.Fatalf("Unable to run verify password for incorrect email: %v", err) + } + if badEmailVerifyResp.Verified { + t.Fatalf("verify password passed for password expected to be not found. expected %t, found %t", false, badEmailVerifyResp.Verified) + } + if !badEmailVerifyResp.NotFound { + t.Fatalf("expected not found response for verify password with bad email. expected %t, found %t", true, badEmailVerifyResp.NotFound) + } + + // Check that wrong password fails + badPassVerifyReq := &api.VerifyPasswordReq{ + Email: email, + Password: "wrong_password", + } + badPassVerifyResp, err := client.VerifyPassword(ctx, badPassVerifyReq) + if err != nil { + t.Fatalf("Unable to run verify password for password we expected to be invalid: %v", err) + } + if badPassVerifyResp.Verified { + t.Fatalf("verify password passed for password we expected to fail. expected %t, found %t", false, badPassVerifyResp.Verified) + } + if badPassVerifyResp.NotFound { + t.Fatalf("did not expect expected not found response for verify password with bad email. expected %t, found %t", false, badPassVerifyResp.NotFound) + } + updateReq := api.UpdatePasswordReq{ - Email: "test@example.com", + Email: email, NewUsername: "test1", }