From 776aa9dd53b6e08ef00e420d42582c27dfd8236b Mon Sep 17 00:00:00 2001 From: krishnadurai Date: Fri, 13 Dec 2019 16:33:21 -0800 Subject: [PATCH 01/15] Option to add staticPasswords from environment variables Signed-off-by: justin-slowik --- cmd/dex/config.go | 6 +++++- cmd/dex/config_test.go | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/cmd/dex/config.go b/cmd/dex/config.go index 3d07f2ff62..2d71a936ea 100644 --- a/cmd/dex/config.go +++ b/cmd/dex/config.go @@ -103,7 +103,11 @@ func (p *password) UnmarshalJSON(b []byte) error { data.Hash = os.Getenv(data.HashFromEnv) } if len(data.Hash) == 0 { - return fmt.Errorf("no password hash provided") + if len(data.HashFromEnv) > 0 { + data.Hash = os.Getenv(data.HashFromEnv) + } else { + return fmt.Errorf("no password hash provided") + } } // If this value is a valid bcrypt, use it. diff --git a/cmd/dex/config_test.go b/cmd/dex/config_test.go index 12c7b21884..f17f62b41a 100644 --- a/cmd/dex/config_test.go +++ b/cmd/dex/config_test.go @@ -4,6 +4,8 @@ import ( "os" "testing" + "github.com/dexidp/dex/server" + "github.com/ghodss/yaml" "github.com/kylelemons/godebug/pretty" From 6698f1f80a7ffc2832589a138e71499bbd21dd1d Mon Sep 17 00:00:00 2001 From: krishnadurai Date: Tue, 7 Jan 2020 11:48:35 -0800 Subject: [PATCH 02/15] Corrects imports after merge Signed-off-by: justin-slowik --- cmd/dex/config_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/dex/config_test.go b/cmd/dex/config_test.go index f17f62b41a..12c7b21884 100644 --- a/cmd/dex/config_test.go +++ b/cmd/dex/config_test.go @@ -4,8 +4,6 @@ import ( "os" "testing" - "github.com/dexidp/dex/server" - "github.com/ghodss/yaml" "github.com/kylelemons/godebug/pretty" From bad2a06960cb8dcedf77c628796968489bbfb2c5 Mon Sep 17 00:00:00 2001 From: Mark Sagi-Kazar Date: Tue, 30 Jun 2020 17:36:41 +0200 Subject: [PATCH 03/15] Revendor dependencies Signed-off-by: justin-slowik --- vendor/github.com/dexidp/dex/api/api.pb.go | 1758 ++++++++++++++++++++ vendor/github.com/dexidp/dex/api/api.proto | 187 +++ vendor/github.com/dexidp/dex/api/go.mod | 8 + vendor/github.com/dexidp/dex/api/go.sum | 49 + 4 files changed, 2002 insertions(+) create mode 100644 vendor/github.com/dexidp/dex/api/api.pb.go create mode 100644 vendor/github.com/dexidp/dex/api/api.proto create mode 100644 vendor/github.com/dexidp/dex/api/go.mod create mode 100644 vendor/github.com/dexidp/dex/api/go.sum diff --git a/vendor/github.com/dexidp/dex/api/api.pb.go b/vendor/github.com/dexidp/dex/api/api.pb.go new file mode 100644 index 0000000000..5bac4e9514 --- /dev/null +++ b/vendor/github.com/dexidp/dex/api/api.pb.go @@ -0,0 +1,1758 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: api/api.proto + +package api + +import ( + context "context" + fmt "fmt" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +// Client represents an OAuth2 client. +type Client struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Secret string `protobuf:"bytes,2,opt,name=secret,proto3" json:"secret,omitempty"` + RedirectUris []string `protobuf:"bytes,3,rep,name=redirect_uris,json=redirectUris,proto3" json:"redirect_uris,omitempty"` + TrustedPeers []string `protobuf:"bytes,4,rep,name=trusted_peers,json=trustedPeers,proto3" json:"trusted_peers,omitempty"` + Public bool `protobuf:"varint,5,opt,name=public,proto3" json:"public,omitempty"` + Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"` + LogoUrl string `protobuf:"bytes,7,opt,name=logo_url,json=logoUrl,proto3" json:"logo_url,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Client) Reset() { *m = Client{} } +func (m *Client) String() string { return proto.CompactTextString(m) } +func (*Client) ProtoMessage() {} +func (*Client) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{0} +} + +func (m *Client) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Client.Unmarshal(m, b) +} +func (m *Client) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Client.Marshal(b, m, deterministic) +} +func (m *Client) XXX_Merge(src proto.Message) { + xxx_messageInfo_Client.Merge(m, src) +} +func (m *Client) XXX_Size() int { + return xxx_messageInfo_Client.Size(m) +} +func (m *Client) XXX_DiscardUnknown() { + xxx_messageInfo_Client.DiscardUnknown(m) +} + +var xxx_messageInfo_Client proto.InternalMessageInfo + +func (m *Client) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *Client) GetSecret() string { + if m != nil { + return m.Secret + } + return "" +} + +func (m *Client) GetRedirectUris() []string { + if m != nil { + return m.RedirectUris + } + return nil +} + +func (m *Client) GetTrustedPeers() []string { + if m != nil { + return m.TrustedPeers + } + return nil +} + +func (m *Client) GetPublic() bool { + if m != nil { + return m.Public + } + return false +} + +func (m *Client) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Client) GetLogoUrl() string { + if m != nil { + return m.LogoUrl + } + return "" +} + +// CreateClientReq is a request to make a client. +type CreateClientReq struct { + Client *Client `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateClientReq) Reset() { *m = CreateClientReq{} } +func (m *CreateClientReq) String() string { return proto.CompactTextString(m) } +func (*CreateClientReq) ProtoMessage() {} +func (*CreateClientReq) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{1} +} + +func (m *CreateClientReq) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CreateClientReq.Unmarshal(m, b) +} +func (m *CreateClientReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CreateClientReq.Marshal(b, m, deterministic) +} +func (m *CreateClientReq) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateClientReq.Merge(m, src) +} +func (m *CreateClientReq) XXX_Size() int { + return xxx_messageInfo_CreateClientReq.Size(m) +} +func (m *CreateClientReq) XXX_DiscardUnknown() { + xxx_messageInfo_CreateClientReq.DiscardUnknown(m) +} + +var xxx_messageInfo_CreateClientReq proto.InternalMessageInfo + +func (m *CreateClientReq) GetClient() *Client { + if m != nil { + return m.Client + } + return nil +} + +// CreateClientResp returns the response from creating a client. +type CreateClientResp struct { + AlreadyExists bool `protobuf:"varint,1,opt,name=already_exists,json=alreadyExists,proto3" json:"already_exists,omitempty"` + Client *Client `protobuf:"bytes,2,opt,name=client,proto3" json:"client,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateClientResp) Reset() { *m = CreateClientResp{} } +func (m *CreateClientResp) String() string { return proto.CompactTextString(m) } +func (*CreateClientResp) ProtoMessage() {} +func (*CreateClientResp) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{2} +} + +func (m *CreateClientResp) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CreateClientResp.Unmarshal(m, b) +} +func (m *CreateClientResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CreateClientResp.Marshal(b, m, deterministic) +} +func (m *CreateClientResp) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateClientResp.Merge(m, src) +} +func (m *CreateClientResp) XXX_Size() int { + return xxx_messageInfo_CreateClientResp.Size(m) +} +func (m *CreateClientResp) XXX_DiscardUnknown() { + xxx_messageInfo_CreateClientResp.DiscardUnknown(m) +} + +var xxx_messageInfo_CreateClientResp proto.InternalMessageInfo + +func (m *CreateClientResp) GetAlreadyExists() bool { + if m != nil { + return m.AlreadyExists + } + return false +} + +func (m *CreateClientResp) GetClient() *Client { + if m != nil { + return m.Client + } + return nil +} + +// DeleteClientReq is a request to delete a client. +type DeleteClientReq struct { + // The ID of the client. + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteClientReq) Reset() { *m = DeleteClientReq{} } +func (m *DeleteClientReq) String() string { return proto.CompactTextString(m) } +func (*DeleteClientReq) ProtoMessage() {} +func (*DeleteClientReq) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{3} +} + +func (m *DeleteClientReq) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DeleteClientReq.Unmarshal(m, b) +} +func (m *DeleteClientReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DeleteClientReq.Marshal(b, m, deterministic) +} +func (m *DeleteClientReq) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteClientReq.Merge(m, src) +} +func (m *DeleteClientReq) XXX_Size() int { + return xxx_messageInfo_DeleteClientReq.Size(m) +} +func (m *DeleteClientReq) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteClientReq.DiscardUnknown(m) +} + +var xxx_messageInfo_DeleteClientReq proto.InternalMessageInfo + +func (m *DeleteClientReq) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +// DeleteClientResp determines if the client is deleted successfully. +type DeleteClientResp struct { + NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteClientResp) Reset() { *m = DeleteClientResp{} } +func (m *DeleteClientResp) String() string { return proto.CompactTextString(m) } +func (*DeleteClientResp) ProtoMessage() {} +func (*DeleteClientResp) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{4} +} + +func (m *DeleteClientResp) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DeleteClientResp.Unmarshal(m, b) +} +func (m *DeleteClientResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DeleteClientResp.Marshal(b, m, deterministic) +} +func (m *DeleteClientResp) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteClientResp.Merge(m, src) +} +func (m *DeleteClientResp) XXX_Size() int { + return xxx_messageInfo_DeleteClientResp.Size(m) +} +func (m *DeleteClientResp) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteClientResp.DiscardUnknown(m) +} + +var xxx_messageInfo_DeleteClientResp proto.InternalMessageInfo + +func (m *DeleteClientResp) GetNotFound() bool { + if m != nil { + return m.NotFound + } + return false +} + +// UpdateClientReq is a request to update an exisitng client. +type UpdateClientReq struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + RedirectUris []string `protobuf:"bytes,2,rep,name=redirect_uris,json=redirectUris,proto3" json:"redirect_uris,omitempty"` + TrustedPeers []string `protobuf:"bytes,3,rep,name=trusted_peers,json=trustedPeers,proto3" json:"trusted_peers,omitempty"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` + LogoUrl string `protobuf:"bytes,5,opt,name=logo_url,json=logoUrl,proto3" json:"logo_url,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdateClientReq) Reset() { *m = UpdateClientReq{} } +func (m *UpdateClientReq) String() string { return proto.CompactTextString(m) } +func (*UpdateClientReq) ProtoMessage() {} +func (*UpdateClientReq) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{5} +} + +func (m *UpdateClientReq) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_UpdateClientReq.Unmarshal(m, b) +} +func (m *UpdateClientReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_UpdateClientReq.Marshal(b, m, deterministic) +} +func (m *UpdateClientReq) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateClientReq.Merge(m, src) +} +func (m *UpdateClientReq) XXX_Size() int { + return xxx_messageInfo_UpdateClientReq.Size(m) +} +func (m *UpdateClientReq) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateClientReq.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdateClientReq proto.InternalMessageInfo + +func (m *UpdateClientReq) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *UpdateClientReq) GetRedirectUris() []string { + if m != nil { + return m.RedirectUris + } + return nil +} + +func (m *UpdateClientReq) GetTrustedPeers() []string { + if m != nil { + return m.TrustedPeers + } + return nil +} + +func (m *UpdateClientReq) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *UpdateClientReq) GetLogoUrl() string { + if m != nil { + return m.LogoUrl + } + return "" +} + +// UpdateClientResp returns the reponse form updating a client. +type UpdateClientResp struct { + NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdateClientResp) Reset() { *m = UpdateClientResp{} } +func (m *UpdateClientResp) String() string { return proto.CompactTextString(m) } +func (*UpdateClientResp) ProtoMessage() {} +func (*UpdateClientResp) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{6} +} + +func (m *UpdateClientResp) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_UpdateClientResp.Unmarshal(m, b) +} +func (m *UpdateClientResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_UpdateClientResp.Marshal(b, m, deterministic) +} +func (m *UpdateClientResp) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateClientResp.Merge(m, src) +} +func (m *UpdateClientResp) XXX_Size() int { + return xxx_messageInfo_UpdateClientResp.Size(m) +} +func (m *UpdateClientResp) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateClientResp.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdateClientResp proto.InternalMessageInfo + +func (m *UpdateClientResp) GetNotFound() bool { + if m != nil { + return m.NotFound + } + return false +} + +// Password is an email for password mapping managed by the storage. +type Password struct { + Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` + // Currently we do not accept plain text passwords. Could be an option in the future. + Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"` + Username string `protobuf:"bytes,3,opt,name=username,proto3" json:"username,omitempty"` + UserId string `protobuf:"bytes,4,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Password) Reset() { *m = Password{} } +func (m *Password) String() string { return proto.CompactTextString(m) } +func (*Password) ProtoMessage() {} +func (*Password) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{7} +} + +func (m *Password) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Password.Unmarshal(m, b) +} +func (m *Password) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Password.Marshal(b, m, deterministic) +} +func (m *Password) XXX_Merge(src proto.Message) { + xxx_messageInfo_Password.Merge(m, src) +} +func (m *Password) XXX_Size() int { + return xxx_messageInfo_Password.Size(m) +} +func (m *Password) XXX_DiscardUnknown() { + xxx_messageInfo_Password.DiscardUnknown(m) +} + +var xxx_messageInfo_Password proto.InternalMessageInfo + +func (m *Password) GetEmail() string { + if m != nil { + return m.Email + } + return "" +} + +func (m *Password) GetHash() []byte { + if m != nil { + return m.Hash + } + return nil +} + +func (m *Password) GetUsername() string { + if m != nil { + return m.Username + } + return "" +} + +func (m *Password) GetUserId() string { + if m != nil { + return m.UserId + } + return "" +} + +// CreatePasswordReq is a request to make a password. +type CreatePasswordReq struct { + Password *Password `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreatePasswordReq) Reset() { *m = CreatePasswordReq{} } +func (m *CreatePasswordReq) String() string { return proto.CompactTextString(m) } +func (*CreatePasswordReq) ProtoMessage() {} +func (*CreatePasswordReq) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{8} +} + +func (m *CreatePasswordReq) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CreatePasswordReq.Unmarshal(m, b) +} +func (m *CreatePasswordReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CreatePasswordReq.Marshal(b, m, deterministic) +} +func (m *CreatePasswordReq) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreatePasswordReq.Merge(m, src) +} +func (m *CreatePasswordReq) XXX_Size() int { + return xxx_messageInfo_CreatePasswordReq.Size(m) +} +func (m *CreatePasswordReq) XXX_DiscardUnknown() { + xxx_messageInfo_CreatePasswordReq.DiscardUnknown(m) +} + +var xxx_messageInfo_CreatePasswordReq proto.InternalMessageInfo + +func (m *CreatePasswordReq) GetPassword() *Password { + if m != nil { + return m.Password + } + return nil +} + +// CreatePasswordResp returns the response from creating a password. +type CreatePasswordResp struct { + AlreadyExists bool `protobuf:"varint,1,opt,name=already_exists,json=alreadyExists,proto3" json:"already_exists,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreatePasswordResp) Reset() { *m = CreatePasswordResp{} } +func (m *CreatePasswordResp) String() string { return proto.CompactTextString(m) } +func (*CreatePasswordResp) ProtoMessage() {} +func (*CreatePasswordResp) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{9} +} + +func (m *CreatePasswordResp) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CreatePasswordResp.Unmarshal(m, b) +} +func (m *CreatePasswordResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CreatePasswordResp.Marshal(b, m, deterministic) +} +func (m *CreatePasswordResp) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreatePasswordResp.Merge(m, src) +} +func (m *CreatePasswordResp) XXX_Size() int { + return xxx_messageInfo_CreatePasswordResp.Size(m) +} +func (m *CreatePasswordResp) XXX_DiscardUnknown() { + xxx_messageInfo_CreatePasswordResp.DiscardUnknown(m) +} + +var xxx_messageInfo_CreatePasswordResp proto.InternalMessageInfo + +func (m *CreatePasswordResp) GetAlreadyExists() bool { + if m != nil { + return m.AlreadyExists + } + return false +} + +// UpdatePasswordReq is a request to modify an existing password. +type UpdatePasswordReq struct { + // The email used to lookup the password. This field cannot be modified + Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` + NewHash []byte `protobuf:"bytes,2,opt,name=new_hash,json=newHash,proto3" json:"new_hash,omitempty"` + NewUsername string `protobuf:"bytes,3,opt,name=new_username,json=newUsername,proto3" json:"new_username,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdatePasswordReq) Reset() { *m = UpdatePasswordReq{} } +func (m *UpdatePasswordReq) String() string { return proto.CompactTextString(m) } +func (*UpdatePasswordReq) ProtoMessage() {} +func (*UpdatePasswordReq) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{10} +} + +func (m *UpdatePasswordReq) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_UpdatePasswordReq.Unmarshal(m, b) +} +func (m *UpdatePasswordReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_UpdatePasswordReq.Marshal(b, m, deterministic) +} +func (m *UpdatePasswordReq) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdatePasswordReq.Merge(m, src) +} +func (m *UpdatePasswordReq) XXX_Size() int { + return xxx_messageInfo_UpdatePasswordReq.Size(m) +} +func (m *UpdatePasswordReq) XXX_DiscardUnknown() { + xxx_messageInfo_UpdatePasswordReq.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdatePasswordReq proto.InternalMessageInfo + +func (m *UpdatePasswordReq) GetEmail() string { + if m != nil { + return m.Email + } + return "" +} + +func (m *UpdatePasswordReq) GetNewHash() []byte { + if m != nil { + return m.NewHash + } + return nil +} + +func (m *UpdatePasswordReq) GetNewUsername() string { + if m != nil { + return m.NewUsername + } + return "" +} + +// UpdatePasswordResp returns the response from modifying an existing password. +type UpdatePasswordResp struct { + NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdatePasswordResp) Reset() { *m = UpdatePasswordResp{} } +func (m *UpdatePasswordResp) String() string { return proto.CompactTextString(m) } +func (*UpdatePasswordResp) ProtoMessage() {} +func (*UpdatePasswordResp) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{11} +} + +func (m *UpdatePasswordResp) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_UpdatePasswordResp.Unmarshal(m, b) +} +func (m *UpdatePasswordResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_UpdatePasswordResp.Marshal(b, m, deterministic) +} +func (m *UpdatePasswordResp) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdatePasswordResp.Merge(m, src) +} +func (m *UpdatePasswordResp) XXX_Size() int { + return xxx_messageInfo_UpdatePasswordResp.Size(m) +} +func (m *UpdatePasswordResp) XXX_DiscardUnknown() { + xxx_messageInfo_UpdatePasswordResp.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdatePasswordResp proto.InternalMessageInfo + +func (m *UpdatePasswordResp) GetNotFound() bool { + if m != nil { + return m.NotFound + } + return false +} + +// DeletePasswordReq is a request to delete a password. +type DeletePasswordReq struct { + Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeletePasswordReq) Reset() { *m = DeletePasswordReq{} } +func (m *DeletePasswordReq) String() string { return proto.CompactTextString(m) } +func (*DeletePasswordReq) ProtoMessage() {} +func (*DeletePasswordReq) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{12} +} + +func (m *DeletePasswordReq) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DeletePasswordReq.Unmarshal(m, b) +} +func (m *DeletePasswordReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DeletePasswordReq.Marshal(b, m, deterministic) +} +func (m *DeletePasswordReq) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeletePasswordReq.Merge(m, src) +} +func (m *DeletePasswordReq) XXX_Size() int { + return xxx_messageInfo_DeletePasswordReq.Size(m) +} +func (m *DeletePasswordReq) XXX_DiscardUnknown() { + xxx_messageInfo_DeletePasswordReq.DiscardUnknown(m) +} + +var xxx_messageInfo_DeletePasswordReq proto.InternalMessageInfo + +func (m *DeletePasswordReq) GetEmail() string { + if m != nil { + return m.Email + } + return "" +} + +// DeletePasswordResp returns the response from deleting a password. +type DeletePasswordResp struct { + NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeletePasswordResp) Reset() { *m = DeletePasswordResp{} } +func (m *DeletePasswordResp) String() string { return proto.CompactTextString(m) } +func (*DeletePasswordResp) ProtoMessage() {} +func (*DeletePasswordResp) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{13} +} + +func (m *DeletePasswordResp) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DeletePasswordResp.Unmarshal(m, b) +} +func (m *DeletePasswordResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DeletePasswordResp.Marshal(b, m, deterministic) +} +func (m *DeletePasswordResp) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeletePasswordResp.Merge(m, src) +} +func (m *DeletePasswordResp) XXX_Size() int { + return xxx_messageInfo_DeletePasswordResp.Size(m) +} +func (m *DeletePasswordResp) XXX_DiscardUnknown() { + xxx_messageInfo_DeletePasswordResp.DiscardUnknown(m) +} + +var xxx_messageInfo_DeletePasswordResp proto.InternalMessageInfo + +func (m *DeletePasswordResp) GetNotFound() bool { + if m != nil { + return m.NotFound + } + return false +} + +// ListPasswordReq is a request to enumerate passwords. +type ListPasswordReq struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListPasswordReq) Reset() { *m = ListPasswordReq{} } +func (m *ListPasswordReq) String() string { return proto.CompactTextString(m) } +func (*ListPasswordReq) ProtoMessage() {} +func (*ListPasswordReq) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{14} +} + +func (m *ListPasswordReq) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListPasswordReq.Unmarshal(m, b) +} +func (m *ListPasswordReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListPasswordReq.Marshal(b, m, deterministic) +} +func (m *ListPasswordReq) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListPasswordReq.Merge(m, src) +} +func (m *ListPasswordReq) XXX_Size() int { + return xxx_messageInfo_ListPasswordReq.Size(m) +} +func (m *ListPasswordReq) XXX_DiscardUnknown() { + xxx_messageInfo_ListPasswordReq.DiscardUnknown(m) +} + +var xxx_messageInfo_ListPasswordReq proto.InternalMessageInfo + +// ListPasswordResp returns a list of passwords. +type ListPasswordResp struct { + Passwords []*Password `protobuf:"bytes,1,rep,name=passwords,proto3" json:"passwords,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListPasswordResp) Reset() { *m = ListPasswordResp{} } +func (m *ListPasswordResp) String() string { return proto.CompactTextString(m) } +func (*ListPasswordResp) ProtoMessage() {} +func (*ListPasswordResp) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{15} +} + +func (m *ListPasswordResp) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListPasswordResp.Unmarshal(m, b) +} +func (m *ListPasswordResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListPasswordResp.Marshal(b, m, deterministic) +} +func (m *ListPasswordResp) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListPasswordResp.Merge(m, src) +} +func (m *ListPasswordResp) XXX_Size() int { + return xxx_messageInfo_ListPasswordResp.Size(m) +} +func (m *ListPasswordResp) XXX_DiscardUnknown() { + xxx_messageInfo_ListPasswordResp.DiscardUnknown(m) +} + +var xxx_messageInfo_ListPasswordResp proto.InternalMessageInfo + +func (m *ListPasswordResp) GetPasswords() []*Password { + if m != nil { + return m.Passwords + } + return nil +} + +// VersionReq is a request to fetch version info. +type VersionReq struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *VersionReq) Reset() { *m = VersionReq{} } +func (m *VersionReq) String() string { return proto.CompactTextString(m) } +func (*VersionReq) ProtoMessage() {} +func (*VersionReq) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{16} +} + +func (m *VersionReq) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_VersionReq.Unmarshal(m, b) +} +func (m *VersionReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_VersionReq.Marshal(b, m, deterministic) +} +func (m *VersionReq) XXX_Merge(src proto.Message) { + xxx_messageInfo_VersionReq.Merge(m, src) +} +func (m *VersionReq) XXX_Size() int { + return xxx_messageInfo_VersionReq.Size(m) +} +func (m *VersionReq) XXX_DiscardUnknown() { + xxx_messageInfo_VersionReq.DiscardUnknown(m) +} + +var xxx_messageInfo_VersionReq proto.InternalMessageInfo + +// VersionResp holds the version info of components. +type VersionResp struct { + // Semantic version of the server. + Server string `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"` + // Numeric version of the API. It increases everytime a new call is added to the API. + // Clients should use this info to determine if the server supports specific features. + Api int32 `protobuf:"varint,2,opt,name=api,proto3" json:"api,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *VersionResp) Reset() { *m = VersionResp{} } +func (m *VersionResp) String() string { return proto.CompactTextString(m) } +func (*VersionResp) ProtoMessage() {} +func (*VersionResp) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{17} +} + +func (m *VersionResp) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_VersionResp.Unmarshal(m, b) +} +func (m *VersionResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_VersionResp.Marshal(b, m, deterministic) +} +func (m *VersionResp) XXX_Merge(src proto.Message) { + xxx_messageInfo_VersionResp.Merge(m, src) +} +func (m *VersionResp) XXX_Size() int { + return xxx_messageInfo_VersionResp.Size(m) +} +func (m *VersionResp) XXX_DiscardUnknown() { + xxx_messageInfo_VersionResp.DiscardUnknown(m) +} + +var xxx_messageInfo_VersionResp proto.InternalMessageInfo + +func (m *VersionResp) GetServer() string { + if m != nil { + return m.Server + } + return "" +} + +func (m *VersionResp) GetApi() int32 { + if m != nil { + return m.Api + } + return 0 +} + +// RefreshTokenRef contains the metadata for a refresh token that is managed by the storage. +type RefreshTokenRef struct { + // ID of the refresh token. + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` + CreatedAt int64 `protobuf:"varint,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + LastUsed int64 `protobuf:"varint,6,opt,name=last_used,json=lastUsed,proto3" json:"last_used,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RefreshTokenRef) Reset() { *m = RefreshTokenRef{} } +func (m *RefreshTokenRef) String() string { return proto.CompactTextString(m) } +func (*RefreshTokenRef) ProtoMessage() {} +func (*RefreshTokenRef) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{18} +} + +func (m *RefreshTokenRef) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RefreshTokenRef.Unmarshal(m, b) +} +func (m *RefreshTokenRef) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RefreshTokenRef.Marshal(b, m, deterministic) +} +func (m *RefreshTokenRef) XXX_Merge(src proto.Message) { + xxx_messageInfo_RefreshTokenRef.Merge(m, src) +} +func (m *RefreshTokenRef) XXX_Size() int { + return xxx_messageInfo_RefreshTokenRef.Size(m) +} +func (m *RefreshTokenRef) XXX_DiscardUnknown() { + xxx_messageInfo_RefreshTokenRef.DiscardUnknown(m) +} + +var xxx_messageInfo_RefreshTokenRef proto.InternalMessageInfo + +func (m *RefreshTokenRef) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *RefreshTokenRef) GetClientId() string { + if m != nil { + return m.ClientId + } + return "" +} + +func (m *RefreshTokenRef) GetCreatedAt() int64 { + if m != nil { + return m.CreatedAt + } + return 0 +} + +func (m *RefreshTokenRef) GetLastUsed() int64 { + if m != nil { + return m.LastUsed + } + return 0 +} + +// ListRefreshReq is a request to enumerate the refresh tokens of a user. +type ListRefreshReq struct { + // The "sub" claim returned in the ID Token. + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListRefreshReq) Reset() { *m = ListRefreshReq{} } +func (m *ListRefreshReq) String() string { return proto.CompactTextString(m) } +func (*ListRefreshReq) ProtoMessage() {} +func (*ListRefreshReq) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{19} +} + +func (m *ListRefreshReq) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListRefreshReq.Unmarshal(m, b) +} +func (m *ListRefreshReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListRefreshReq.Marshal(b, m, deterministic) +} +func (m *ListRefreshReq) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListRefreshReq.Merge(m, src) +} +func (m *ListRefreshReq) XXX_Size() int { + return xxx_messageInfo_ListRefreshReq.Size(m) +} +func (m *ListRefreshReq) XXX_DiscardUnknown() { + xxx_messageInfo_ListRefreshReq.DiscardUnknown(m) +} + +var xxx_messageInfo_ListRefreshReq proto.InternalMessageInfo + +func (m *ListRefreshReq) GetUserId() string { + if m != nil { + return m.UserId + } + return "" +} + +// ListRefreshResp returns a list of refresh tokens for a user. +type ListRefreshResp struct { + RefreshTokens []*RefreshTokenRef `protobuf:"bytes,1,rep,name=refresh_tokens,json=refreshTokens,proto3" json:"refresh_tokens,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListRefreshResp) Reset() { *m = ListRefreshResp{} } +func (m *ListRefreshResp) String() string { return proto.CompactTextString(m) } +func (*ListRefreshResp) ProtoMessage() {} +func (*ListRefreshResp) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{20} +} + +func (m *ListRefreshResp) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListRefreshResp.Unmarshal(m, b) +} +func (m *ListRefreshResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListRefreshResp.Marshal(b, m, deterministic) +} +func (m *ListRefreshResp) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListRefreshResp.Merge(m, src) +} +func (m *ListRefreshResp) XXX_Size() int { + return xxx_messageInfo_ListRefreshResp.Size(m) +} +func (m *ListRefreshResp) XXX_DiscardUnknown() { + xxx_messageInfo_ListRefreshResp.DiscardUnknown(m) +} + +var xxx_messageInfo_ListRefreshResp proto.InternalMessageInfo + +func (m *ListRefreshResp) GetRefreshTokens() []*RefreshTokenRef { + if m != nil { + return m.RefreshTokens + } + return nil +} + +// RevokeRefreshReq is a request to revoke the refresh token of the user-client pair. +type RevokeRefreshReq struct { + // The "sub" claim returned in the ID Token. + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RevokeRefreshReq) Reset() { *m = RevokeRefreshReq{} } +func (m *RevokeRefreshReq) String() string { return proto.CompactTextString(m) } +func (*RevokeRefreshReq) ProtoMessage() {} +func (*RevokeRefreshReq) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{21} +} + +func (m *RevokeRefreshReq) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RevokeRefreshReq.Unmarshal(m, b) +} +func (m *RevokeRefreshReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RevokeRefreshReq.Marshal(b, m, deterministic) +} +func (m *RevokeRefreshReq) XXX_Merge(src proto.Message) { + xxx_messageInfo_RevokeRefreshReq.Merge(m, src) +} +func (m *RevokeRefreshReq) XXX_Size() int { + return xxx_messageInfo_RevokeRefreshReq.Size(m) +} +func (m *RevokeRefreshReq) XXX_DiscardUnknown() { + xxx_messageInfo_RevokeRefreshReq.DiscardUnknown(m) +} + +var xxx_messageInfo_RevokeRefreshReq proto.InternalMessageInfo + +func (m *RevokeRefreshReq) GetUserId() string { + if m != nil { + return m.UserId + } + return "" +} + +func (m *RevokeRefreshReq) GetClientId() string { + if m != nil { + return m.ClientId + } + return "" +} + +// RevokeRefreshResp determines if the refresh token is revoked successfully. +type RevokeRefreshResp struct { + // Set to true is refresh token was not found and token could not be revoked. + NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RevokeRefreshResp) Reset() { *m = RevokeRefreshResp{} } +func (m *RevokeRefreshResp) String() string { return proto.CompactTextString(m) } +func (*RevokeRefreshResp) ProtoMessage() {} +func (*RevokeRefreshResp) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{22} +} + +func (m *RevokeRefreshResp) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RevokeRefreshResp.Unmarshal(m, b) +} +func (m *RevokeRefreshResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RevokeRefreshResp.Marshal(b, m, deterministic) +} +func (m *RevokeRefreshResp) XXX_Merge(src proto.Message) { + xxx_messageInfo_RevokeRefreshResp.Merge(m, src) +} +func (m *RevokeRefreshResp) XXX_Size() int { + return xxx_messageInfo_RevokeRefreshResp.Size(m) +} +func (m *RevokeRefreshResp) XXX_DiscardUnknown() { + xxx_messageInfo_RevokeRefreshResp.DiscardUnknown(m) +} + +var xxx_messageInfo_RevokeRefreshResp proto.InternalMessageInfo + +func (m *RevokeRefreshResp) GetNotFound() bool { + if m != nil { + return m.NotFound + } + return false +} + +type VerifyPasswordReq struct { + Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *VerifyPasswordReq) Reset() { *m = VerifyPasswordReq{} } +func (m *VerifyPasswordReq) String() string { return proto.CompactTextString(m) } +func (*VerifyPasswordReq) ProtoMessage() {} +func (*VerifyPasswordReq) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{23} +} + +func (m *VerifyPasswordReq) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_VerifyPasswordReq.Unmarshal(m, b) +} +func (m *VerifyPasswordReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_VerifyPasswordReq.Marshal(b, m, deterministic) +} +func (m *VerifyPasswordReq) XXX_Merge(src proto.Message) { + xxx_messageInfo_VerifyPasswordReq.Merge(m, src) +} +func (m *VerifyPasswordReq) XXX_Size() int { + return xxx_messageInfo_VerifyPasswordReq.Size(m) +} +func (m *VerifyPasswordReq) XXX_DiscardUnknown() { + xxx_messageInfo_VerifyPasswordReq.DiscardUnknown(m) +} + +var xxx_messageInfo_VerifyPasswordReq proto.InternalMessageInfo + +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,proto3" json:"verified,omitempty"` + NotFound bool `protobuf:"varint,2,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *VerifyPasswordResp) Reset() { *m = VerifyPasswordResp{} } +func (m *VerifyPasswordResp) String() string { return proto.CompactTextString(m) } +func (*VerifyPasswordResp) ProtoMessage() {} +func (*VerifyPasswordResp) Descriptor() ([]byte, []int) { + return fileDescriptor_1b40cafcd4234784, []int{24} +} + +func (m *VerifyPasswordResp) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_VerifyPasswordResp.Unmarshal(m, b) +} +func (m *VerifyPasswordResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_VerifyPasswordResp.Marshal(b, m, deterministic) +} +func (m *VerifyPasswordResp) XXX_Merge(src proto.Message) { + xxx_messageInfo_VerifyPasswordResp.Merge(m, src) +} +func (m *VerifyPasswordResp) XXX_Size() int { + return xxx_messageInfo_VerifyPasswordResp.Size(m) +} +func (m *VerifyPasswordResp) XXX_DiscardUnknown() { + xxx_messageInfo_VerifyPasswordResp.DiscardUnknown(m) +} + +var xxx_messageInfo_VerifyPasswordResp proto.InternalMessageInfo + +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") + proto.RegisterType((*CreateClientResp)(nil), "api.CreateClientResp") + proto.RegisterType((*DeleteClientReq)(nil), "api.DeleteClientReq") + proto.RegisterType((*DeleteClientResp)(nil), "api.DeleteClientResp") + proto.RegisterType((*UpdateClientReq)(nil), "api.UpdateClientReq") + proto.RegisterType((*UpdateClientResp)(nil), "api.UpdateClientResp") + proto.RegisterType((*Password)(nil), "api.Password") + proto.RegisterType((*CreatePasswordReq)(nil), "api.CreatePasswordReq") + proto.RegisterType((*CreatePasswordResp)(nil), "api.CreatePasswordResp") + proto.RegisterType((*UpdatePasswordReq)(nil), "api.UpdatePasswordReq") + proto.RegisterType((*UpdatePasswordResp)(nil), "api.UpdatePasswordResp") + proto.RegisterType((*DeletePasswordReq)(nil), "api.DeletePasswordReq") + proto.RegisterType((*DeletePasswordResp)(nil), "api.DeletePasswordResp") + proto.RegisterType((*ListPasswordReq)(nil), "api.ListPasswordReq") + proto.RegisterType((*ListPasswordResp)(nil), "api.ListPasswordResp") + proto.RegisterType((*VersionReq)(nil), "api.VersionReq") + proto.RegisterType((*VersionResp)(nil), "api.VersionResp") + proto.RegisterType((*RefreshTokenRef)(nil), "api.RefreshTokenRef") + proto.RegisterType((*ListRefreshReq)(nil), "api.ListRefreshReq") + 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") +} + +func init() { proto.RegisterFile("api/api.proto", fileDescriptor_1b40cafcd4234784) } + +var fileDescriptor_1b40cafcd4234784 = []byte{ + // 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, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// DexClient is the client API for Dex service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type DexClient interface { + // CreateClient creates a client. + CreateClient(ctx context.Context, in *CreateClientReq, opts ...grpc.CallOption) (*CreateClientResp, error) + // UpdateClient updates an existing client + UpdateClient(ctx context.Context, in *UpdateClientReq, opts ...grpc.CallOption) (*UpdateClientResp, error) + // DeleteClient deletes the provided client. + DeleteClient(ctx context.Context, in *DeleteClientReq, opts ...grpc.CallOption) (*DeleteClientResp, error) + // CreatePassword creates a password. + CreatePassword(ctx context.Context, in *CreatePasswordReq, opts ...grpc.CallOption) (*CreatePasswordResp, error) + // UpdatePassword modifies existing password. + UpdatePassword(ctx context.Context, in *UpdatePasswordReq, opts ...grpc.CallOption) (*UpdatePasswordResp, error) + // DeletePassword deletes the password. + DeletePassword(ctx context.Context, in *DeletePasswordReq, opts ...grpc.CallOption) (*DeletePasswordResp, error) + // ListPassword lists all password entries. + ListPasswords(ctx context.Context, in *ListPasswordReq, opts ...grpc.CallOption) (*ListPasswordResp, error) + // GetVersion returns version information of the server. + GetVersion(ctx context.Context, in *VersionReq, opts ...grpc.CallOption) (*VersionResp, error) + // ListRefresh lists all the refresh token entries for a particular user. + ListRefresh(ctx context.Context, in *ListRefreshReq, opts ...grpc.CallOption) (*ListRefreshResp, error) + // RevokeRefresh revokes the refresh token for the provided user-client pair. + // + // 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 { + cc *grpc.ClientConn +} + +func NewDexClient(cc *grpc.ClientConn) DexClient { + return &dexClient{cc} +} + +func (c *dexClient) CreateClient(ctx context.Context, in *CreateClientReq, opts ...grpc.CallOption) (*CreateClientResp, error) { + out := new(CreateClientResp) + err := c.cc.Invoke(ctx, "/api.Dex/CreateClient", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dexClient) UpdateClient(ctx context.Context, in *UpdateClientReq, opts ...grpc.CallOption) (*UpdateClientResp, error) { + out := new(UpdateClientResp) + err := c.cc.Invoke(ctx, "/api.Dex/UpdateClient", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dexClient) DeleteClient(ctx context.Context, in *DeleteClientReq, opts ...grpc.CallOption) (*DeleteClientResp, error) { + out := new(DeleteClientResp) + err := c.cc.Invoke(ctx, "/api.Dex/DeleteClient", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dexClient) CreatePassword(ctx context.Context, in *CreatePasswordReq, opts ...grpc.CallOption) (*CreatePasswordResp, error) { + out := new(CreatePasswordResp) + err := c.cc.Invoke(ctx, "/api.Dex/CreatePassword", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dexClient) UpdatePassword(ctx context.Context, in *UpdatePasswordReq, opts ...grpc.CallOption) (*UpdatePasswordResp, error) { + out := new(UpdatePasswordResp) + err := c.cc.Invoke(ctx, "/api.Dex/UpdatePassword", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dexClient) DeletePassword(ctx context.Context, in *DeletePasswordReq, opts ...grpc.CallOption) (*DeletePasswordResp, error) { + out := new(DeletePasswordResp) + err := c.cc.Invoke(ctx, "/api.Dex/DeletePassword", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dexClient) ListPasswords(ctx context.Context, in *ListPasswordReq, opts ...grpc.CallOption) (*ListPasswordResp, error) { + out := new(ListPasswordResp) + err := c.cc.Invoke(ctx, "/api.Dex/ListPasswords", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dexClient) GetVersion(ctx context.Context, in *VersionReq, opts ...grpc.CallOption) (*VersionResp, error) { + out := new(VersionResp) + err := c.cc.Invoke(ctx, "/api.Dex/GetVersion", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dexClient) ListRefresh(ctx context.Context, in *ListRefreshReq, opts ...grpc.CallOption) (*ListRefreshResp, error) { + out := new(ListRefreshResp) + err := c.cc.Invoke(ctx, "/api.Dex/ListRefresh", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dexClient) RevokeRefresh(ctx context.Context, in *RevokeRefreshReq, opts ...grpc.CallOption) (*RevokeRefreshResp, error) { + out := new(RevokeRefreshResp) + err := c.cc.Invoke(ctx, "/api.Dex/RevokeRefresh", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dexClient) VerifyPassword(ctx context.Context, in *VerifyPasswordReq, opts ...grpc.CallOption) (*VerifyPasswordResp, error) { + out := new(VerifyPasswordResp) + err := c.cc.Invoke(ctx, "/api.Dex/VerifyPassword", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// DexServer is the server API for Dex service. +type DexServer interface { + // CreateClient creates a client. + CreateClient(context.Context, *CreateClientReq) (*CreateClientResp, error) + // UpdateClient updates an existing client + UpdateClient(context.Context, *UpdateClientReq) (*UpdateClientResp, error) + // DeleteClient deletes the provided client. + DeleteClient(context.Context, *DeleteClientReq) (*DeleteClientResp, error) + // CreatePassword creates a password. + CreatePassword(context.Context, *CreatePasswordReq) (*CreatePasswordResp, error) + // UpdatePassword modifies existing password. + UpdatePassword(context.Context, *UpdatePasswordReq) (*UpdatePasswordResp, error) + // DeletePassword deletes the password. + DeletePassword(context.Context, *DeletePasswordReq) (*DeletePasswordResp, error) + // ListPassword lists all password entries. + ListPasswords(context.Context, *ListPasswordReq) (*ListPasswordResp, error) + // GetVersion returns version information of the server. + GetVersion(context.Context, *VersionReq) (*VersionResp, error) + // ListRefresh lists all the refresh token entries for a particular user. + ListRefresh(context.Context, *ListRefreshReq) (*ListRefreshResp, error) + // RevokeRefresh revokes the refresh token for the provided user-client pair. + // + // 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) +} + +// UnimplementedDexServer can be embedded to have forward compatible implementations. +type UnimplementedDexServer struct { +} + +func (*UnimplementedDexServer) CreateClient(ctx context.Context, req *CreateClientReq) (*CreateClientResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateClient not implemented") +} +func (*UnimplementedDexServer) UpdateClient(ctx context.Context, req *UpdateClientReq) (*UpdateClientResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateClient not implemented") +} +func (*UnimplementedDexServer) DeleteClient(ctx context.Context, req *DeleteClientReq) (*DeleteClientResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteClient not implemented") +} +func (*UnimplementedDexServer) CreatePassword(ctx context.Context, req *CreatePasswordReq) (*CreatePasswordResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreatePassword not implemented") +} +func (*UnimplementedDexServer) UpdatePassword(ctx context.Context, req *UpdatePasswordReq) (*UpdatePasswordResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdatePassword not implemented") +} +func (*UnimplementedDexServer) DeletePassword(ctx context.Context, req *DeletePasswordReq) (*DeletePasswordResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeletePassword not implemented") +} +func (*UnimplementedDexServer) ListPasswords(ctx context.Context, req *ListPasswordReq) (*ListPasswordResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListPasswords not implemented") +} +func (*UnimplementedDexServer) GetVersion(ctx context.Context, req *VersionReq) (*VersionResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetVersion not implemented") +} +func (*UnimplementedDexServer) ListRefresh(ctx context.Context, req *ListRefreshReq) (*ListRefreshResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListRefresh not implemented") +} +func (*UnimplementedDexServer) RevokeRefresh(ctx context.Context, req *RevokeRefreshReq) (*RevokeRefreshResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method RevokeRefresh not implemented") +} +func (*UnimplementedDexServer) VerifyPassword(ctx context.Context, req *VerifyPasswordReq) (*VerifyPasswordResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method VerifyPassword not implemented") +} + +func RegisterDexServer(s *grpc.Server, srv DexServer) { + s.RegisterService(&_Dex_serviceDesc, srv) +} + +func _Dex_CreateClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateClientReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DexServer).CreateClient(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Dex/CreateClient", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DexServer).CreateClient(ctx, req.(*CreateClientReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Dex_UpdateClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateClientReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DexServer).UpdateClient(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Dex/UpdateClient", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DexServer).UpdateClient(ctx, req.(*UpdateClientReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Dex_DeleteClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteClientReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DexServer).DeleteClient(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Dex/DeleteClient", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DexServer).DeleteClient(ctx, req.(*DeleteClientReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Dex_CreatePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreatePasswordReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DexServer).CreatePassword(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Dex/CreatePassword", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DexServer).CreatePassword(ctx, req.(*CreatePasswordReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Dex_UpdatePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdatePasswordReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DexServer).UpdatePassword(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Dex/UpdatePassword", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DexServer).UpdatePassword(ctx, req.(*UpdatePasswordReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Dex_DeletePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeletePasswordReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DexServer).DeletePassword(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Dex/DeletePassword", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DexServer).DeletePassword(ctx, req.(*DeletePasswordReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Dex_ListPasswords_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListPasswordReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DexServer).ListPasswords(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Dex/ListPasswords", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DexServer).ListPasswords(ctx, req.(*ListPasswordReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Dex_GetVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(VersionReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DexServer).GetVersion(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Dex/GetVersion", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DexServer).GetVersion(ctx, req.(*VersionReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Dex_ListRefresh_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListRefreshReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DexServer).ListRefresh(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Dex/ListRefresh", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DexServer).ListRefresh(ctx, req.(*ListRefreshReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Dex_RevokeRefresh_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RevokeRefreshReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DexServer).RevokeRefresh(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Dex/RevokeRefresh", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DexServer).RevokeRefresh(ctx, req.(*RevokeRefreshReq)) + } + 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), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateClient", + Handler: _Dex_CreateClient_Handler, + }, + { + MethodName: "UpdateClient", + Handler: _Dex_UpdateClient_Handler, + }, + { + MethodName: "DeleteClient", + Handler: _Dex_DeleteClient_Handler, + }, + { + MethodName: "CreatePassword", + Handler: _Dex_CreatePassword_Handler, + }, + { + MethodName: "UpdatePassword", + Handler: _Dex_UpdatePassword_Handler, + }, + { + MethodName: "DeletePassword", + Handler: _Dex_DeletePassword_Handler, + }, + { + MethodName: "ListPasswords", + Handler: _Dex_ListPasswords_Handler, + }, + { + MethodName: "GetVersion", + Handler: _Dex_GetVersion_Handler, + }, + { + MethodName: "ListRefresh", + Handler: _Dex_ListRefresh_Handler, + }, + { + MethodName: "RevokeRefresh", + Handler: _Dex_RevokeRefresh_Handler, + }, + { + MethodName: "VerifyPassword", + Handler: _Dex_VerifyPassword_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "api/api.proto", +} diff --git a/vendor/github.com/dexidp/dex/api/api.proto b/vendor/github.com/dexidp/dex/api/api.proto new file mode 100644 index 0000000000..5d9ce1a1e3 --- /dev/null +++ b/vendor/github.com/dexidp/dex/api/api.proto @@ -0,0 +1,187 @@ +syntax = "proto3"; +option java_package = "com.coreos.dex.api"; + +package api; + +// Client represents an OAuth2 client. +message Client { + string id = 1; + string secret = 2; + repeated string redirect_uris = 3; + repeated string trusted_peers = 4; + bool public = 5; + string name = 6; + string logo_url = 7; +} + +// CreateClientReq is a request to make a client. +message CreateClientReq { + Client client = 1; +} + +// CreateClientResp returns the response from creating a client. +message CreateClientResp { + bool already_exists = 1; + Client client = 2; +} + +// DeleteClientReq is a request to delete a client. +message DeleteClientReq { + // The ID of the client. + string id = 1; +} + +// DeleteClientResp determines if the client is deleted successfully. +message DeleteClientResp { + bool not_found = 1; +} + +// UpdateClientReq is a request to update an exisitng client. +message UpdateClientReq { + string id = 1; + repeated string redirect_uris = 2; + repeated string trusted_peers = 3; + string name = 4; + string logo_url = 5; +} + +// UpdateClientResp returns the reponse form updating a client. +message UpdateClientResp { + bool not_found = 1; +} + +// TODO(ericchiang): expand this. + +// Password is an email for password mapping managed by the storage. +message Password { + string email = 1; + + // Currently we do not accept plain text passwords. Could be an option in the future. + bytes hash = 2; + string username = 3; + string user_id = 4; +} + +// CreatePasswordReq is a request to make a password. +message CreatePasswordReq { + Password password = 1; +} + +// CreatePasswordResp returns the response from creating a password. +message CreatePasswordResp { + bool already_exists = 1; +} + +// UpdatePasswordReq is a request to modify an existing password. +message UpdatePasswordReq { + // The email used to lookup the password. This field cannot be modified + string email = 1; + bytes new_hash = 2; + string new_username = 3; +} + +// UpdatePasswordResp returns the response from modifying an existing password. +message UpdatePasswordResp { + bool not_found = 1; +} + +// DeletePasswordReq is a request to delete a password. +message DeletePasswordReq { + string email = 1; +} + +// DeletePasswordResp returns the response from deleting a password. +message DeletePasswordResp { + bool not_found = 1; +} + +// ListPasswordReq is a request to enumerate passwords. +message ListPasswordReq {} + +// ListPasswordResp returns a list of passwords. +message ListPasswordResp { + repeated Password passwords = 1; +} + +// VersionReq is a request to fetch version info. +message VersionReq {} + +// VersionResp holds the version info of components. +message VersionResp { + // Semantic version of the server. + string server = 1; + // Numeric version of the API. It increases everytime a new call is added to the API. + // Clients should use this info to determine if the server supports specific features. + int32 api = 2; +} + +// RefreshTokenRef contains the metadata for a refresh token that is managed by the storage. +message RefreshTokenRef { + // ID of the refresh token. + string id = 1; + string client_id = 2; + int64 created_at = 5; + int64 last_used = 6; +} + +// ListRefreshReq is a request to enumerate the refresh tokens of a user. +message ListRefreshReq { + // The "sub" claim returned in the ID Token. + string user_id = 1; +} + +// ListRefreshResp returns a list of refresh tokens for a user. +message ListRefreshResp { + repeated RefreshTokenRef refresh_tokens = 1; +} + +// RevokeRefreshReq is a request to revoke the refresh token of the user-client pair. +message RevokeRefreshReq { + // The "sub" claim returned in the ID Token. + string user_id = 1; + string client_id = 2; +} + +// RevokeRefreshResp determines if the refresh token is revoked successfully. +message RevokeRefreshResp { + // Set to true is refresh token was not found and token could not be revoked. + 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. + rpc CreateClient(CreateClientReq) returns (CreateClientResp) {}; + // UpdateClient updates an existing client + rpc UpdateClient(UpdateClientReq) returns (UpdateClientResp) {}; + // DeleteClient deletes the provided client. + rpc DeleteClient(DeleteClientReq) returns (DeleteClientResp) {}; + // CreatePassword creates a password. + rpc CreatePassword(CreatePasswordReq) returns (CreatePasswordResp) {}; + // UpdatePassword modifies existing password. + rpc UpdatePassword(UpdatePasswordReq) returns (UpdatePasswordResp) {}; + // DeletePassword deletes the password. + rpc DeletePassword(DeletePasswordReq) returns (DeletePasswordResp) {}; + // ListPassword lists all password entries. + rpc ListPasswords(ListPasswordReq) returns (ListPasswordResp) {}; + // GetVersion returns version information of the server. + rpc GetVersion(VersionReq) returns (VersionResp) {}; + // ListRefresh lists all the refresh token entries for a particular user. + rpc ListRefresh(ListRefreshReq) returns (ListRefreshResp) {}; + // RevokeRefresh revokes the refresh token for the provided user-client pair. + // + // 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/vendor/github.com/dexidp/dex/api/go.mod b/vendor/github.com/dexidp/dex/api/go.mod new file mode 100644 index 0000000000..285e093075 --- /dev/null +++ b/vendor/github.com/dexidp/dex/api/go.mod @@ -0,0 +1,8 @@ +module github.com/dexidp/dex/api + +go 1.14 + +require ( + github.com/golang/protobuf v1.3.2 + google.golang.org/grpc v1.26.0 +) diff --git a/vendor/github.com/dexidp/dex/api/go.sum b/vendor/github.com/dexidp/dex/api/go.sum new file mode 100644 index 0000000000..fdc9375d01 --- /dev/null +++ b/vendor/github.com/dexidp/dex/api/go.sum @@ -0,0 +1,49 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From 11fc8568cbcd8b55e8a650c9e5dda153d1b0e21a Mon Sep 17 00:00:00 2001 From: Mark Sagi-Kazar Date: Tue, 30 Jun 2020 18:51:19 +0200 Subject: [PATCH 04/15] Remove vendor folder Signed-off-by: justin-slowik --- vendor/github.com/dexidp/dex/api/api.pb.go | 1758 -------------------- vendor/github.com/dexidp/dex/api/api.proto | 187 --- vendor/github.com/dexidp/dex/api/go.mod | 8 - vendor/github.com/dexidp/dex/api/go.sum | 49 - 4 files changed, 2002 deletions(-) delete mode 100644 vendor/github.com/dexidp/dex/api/api.pb.go delete mode 100644 vendor/github.com/dexidp/dex/api/api.proto delete mode 100644 vendor/github.com/dexidp/dex/api/go.mod delete mode 100644 vendor/github.com/dexidp/dex/api/go.sum diff --git a/vendor/github.com/dexidp/dex/api/api.pb.go b/vendor/github.com/dexidp/dex/api/api.pb.go deleted file mode 100644 index 5bac4e9514..0000000000 --- a/vendor/github.com/dexidp/dex/api/api.pb.go +++ /dev/null @@ -1,1758 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: api/api.proto - -package api - -import ( - context "context" - fmt "fmt" - proto "github.com/golang/protobuf/proto" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" - math "math" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -// Client represents an OAuth2 client. -type Client struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Secret string `protobuf:"bytes,2,opt,name=secret,proto3" json:"secret,omitempty"` - RedirectUris []string `protobuf:"bytes,3,rep,name=redirect_uris,json=redirectUris,proto3" json:"redirect_uris,omitempty"` - TrustedPeers []string `protobuf:"bytes,4,rep,name=trusted_peers,json=trustedPeers,proto3" json:"trusted_peers,omitempty"` - Public bool `protobuf:"varint,5,opt,name=public,proto3" json:"public,omitempty"` - Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"` - LogoUrl string `protobuf:"bytes,7,opt,name=logo_url,json=logoUrl,proto3" json:"logo_url,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Client) Reset() { *m = Client{} } -func (m *Client) String() string { return proto.CompactTextString(m) } -func (*Client) ProtoMessage() {} -func (*Client) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{0} -} - -func (m *Client) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Client.Unmarshal(m, b) -} -func (m *Client) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Client.Marshal(b, m, deterministic) -} -func (m *Client) XXX_Merge(src proto.Message) { - xxx_messageInfo_Client.Merge(m, src) -} -func (m *Client) XXX_Size() int { - return xxx_messageInfo_Client.Size(m) -} -func (m *Client) XXX_DiscardUnknown() { - xxx_messageInfo_Client.DiscardUnknown(m) -} - -var xxx_messageInfo_Client proto.InternalMessageInfo - -func (m *Client) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -func (m *Client) GetSecret() string { - if m != nil { - return m.Secret - } - return "" -} - -func (m *Client) GetRedirectUris() []string { - if m != nil { - return m.RedirectUris - } - return nil -} - -func (m *Client) GetTrustedPeers() []string { - if m != nil { - return m.TrustedPeers - } - return nil -} - -func (m *Client) GetPublic() bool { - if m != nil { - return m.Public - } - return false -} - -func (m *Client) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -func (m *Client) GetLogoUrl() string { - if m != nil { - return m.LogoUrl - } - return "" -} - -// CreateClientReq is a request to make a client. -type CreateClientReq struct { - Client *Client `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *CreateClientReq) Reset() { *m = CreateClientReq{} } -func (m *CreateClientReq) String() string { return proto.CompactTextString(m) } -func (*CreateClientReq) ProtoMessage() {} -func (*CreateClientReq) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{1} -} - -func (m *CreateClientReq) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_CreateClientReq.Unmarshal(m, b) -} -func (m *CreateClientReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_CreateClientReq.Marshal(b, m, deterministic) -} -func (m *CreateClientReq) XXX_Merge(src proto.Message) { - xxx_messageInfo_CreateClientReq.Merge(m, src) -} -func (m *CreateClientReq) XXX_Size() int { - return xxx_messageInfo_CreateClientReq.Size(m) -} -func (m *CreateClientReq) XXX_DiscardUnknown() { - xxx_messageInfo_CreateClientReq.DiscardUnknown(m) -} - -var xxx_messageInfo_CreateClientReq proto.InternalMessageInfo - -func (m *CreateClientReq) GetClient() *Client { - if m != nil { - return m.Client - } - return nil -} - -// CreateClientResp returns the response from creating a client. -type CreateClientResp struct { - AlreadyExists bool `protobuf:"varint,1,opt,name=already_exists,json=alreadyExists,proto3" json:"already_exists,omitempty"` - Client *Client `protobuf:"bytes,2,opt,name=client,proto3" json:"client,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *CreateClientResp) Reset() { *m = CreateClientResp{} } -func (m *CreateClientResp) String() string { return proto.CompactTextString(m) } -func (*CreateClientResp) ProtoMessage() {} -func (*CreateClientResp) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{2} -} - -func (m *CreateClientResp) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_CreateClientResp.Unmarshal(m, b) -} -func (m *CreateClientResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_CreateClientResp.Marshal(b, m, deterministic) -} -func (m *CreateClientResp) XXX_Merge(src proto.Message) { - xxx_messageInfo_CreateClientResp.Merge(m, src) -} -func (m *CreateClientResp) XXX_Size() int { - return xxx_messageInfo_CreateClientResp.Size(m) -} -func (m *CreateClientResp) XXX_DiscardUnknown() { - xxx_messageInfo_CreateClientResp.DiscardUnknown(m) -} - -var xxx_messageInfo_CreateClientResp proto.InternalMessageInfo - -func (m *CreateClientResp) GetAlreadyExists() bool { - if m != nil { - return m.AlreadyExists - } - return false -} - -func (m *CreateClientResp) GetClient() *Client { - if m != nil { - return m.Client - } - return nil -} - -// DeleteClientReq is a request to delete a client. -type DeleteClientReq struct { - // The ID of the client. - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *DeleteClientReq) Reset() { *m = DeleteClientReq{} } -func (m *DeleteClientReq) String() string { return proto.CompactTextString(m) } -func (*DeleteClientReq) ProtoMessage() {} -func (*DeleteClientReq) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{3} -} - -func (m *DeleteClientReq) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_DeleteClientReq.Unmarshal(m, b) -} -func (m *DeleteClientReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_DeleteClientReq.Marshal(b, m, deterministic) -} -func (m *DeleteClientReq) XXX_Merge(src proto.Message) { - xxx_messageInfo_DeleteClientReq.Merge(m, src) -} -func (m *DeleteClientReq) XXX_Size() int { - return xxx_messageInfo_DeleteClientReq.Size(m) -} -func (m *DeleteClientReq) XXX_DiscardUnknown() { - xxx_messageInfo_DeleteClientReq.DiscardUnknown(m) -} - -var xxx_messageInfo_DeleteClientReq proto.InternalMessageInfo - -func (m *DeleteClientReq) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -// DeleteClientResp determines if the client is deleted successfully. -type DeleteClientResp struct { - NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *DeleteClientResp) Reset() { *m = DeleteClientResp{} } -func (m *DeleteClientResp) String() string { return proto.CompactTextString(m) } -func (*DeleteClientResp) ProtoMessage() {} -func (*DeleteClientResp) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{4} -} - -func (m *DeleteClientResp) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_DeleteClientResp.Unmarshal(m, b) -} -func (m *DeleteClientResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_DeleteClientResp.Marshal(b, m, deterministic) -} -func (m *DeleteClientResp) XXX_Merge(src proto.Message) { - xxx_messageInfo_DeleteClientResp.Merge(m, src) -} -func (m *DeleteClientResp) XXX_Size() int { - return xxx_messageInfo_DeleteClientResp.Size(m) -} -func (m *DeleteClientResp) XXX_DiscardUnknown() { - xxx_messageInfo_DeleteClientResp.DiscardUnknown(m) -} - -var xxx_messageInfo_DeleteClientResp proto.InternalMessageInfo - -func (m *DeleteClientResp) GetNotFound() bool { - if m != nil { - return m.NotFound - } - return false -} - -// UpdateClientReq is a request to update an exisitng client. -type UpdateClientReq struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - RedirectUris []string `protobuf:"bytes,2,rep,name=redirect_uris,json=redirectUris,proto3" json:"redirect_uris,omitempty"` - TrustedPeers []string `protobuf:"bytes,3,rep,name=trusted_peers,json=trustedPeers,proto3" json:"trusted_peers,omitempty"` - Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` - LogoUrl string `protobuf:"bytes,5,opt,name=logo_url,json=logoUrl,proto3" json:"logo_url,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpdateClientReq) Reset() { *m = UpdateClientReq{} } -func (m *UpdateClientReq) String() string { return proto.CompactTextString(m) } -func (*UpdateClientReq) ProtoMessage() {} -func (*UpdateClientReq) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{5} -} - -func (m *UpdateClientReq) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UpdateClientReq.Unmarshal(m, b) -} -func (m *UpdateClientReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UpdateClientReq.Marshal(b, m, deterministic) -} -func (m *UpdateClientReq) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpdateClientReq.Merge(m, src) -} -func (m *UpdateClientReq) XXX_Size() int { - return xxx_messageInfo_UpdateClientReq.Size(m) -} -func (m *UpdateClientReq) XXX_DiscardUnknown() { - xxx_messageInfo_UpdateClientReq.DiscardUnknown(m) -} - -var xxx_messageInfo_UpdateClientReq proto.InternalMessageInfo - -func (m *UpdateClientReq) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -func (m *UpdateClientReq) GetRedirectUris() []string { - if m != nil { - return m.RedirectUris - } - return nil -} - -func (m *UpdateClientReq) GetTrustedPeers() []string { - if m != nil { - return m.TrustedPeers - } - return nil -} - -func (m *UpdateClientReq) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -func (m *UpdateClientReq) GetLogoUrl() string { - if m != nil { - return m.LogoUrl - } - return "" -} - -// UpdateClientResp returns the reponse form updating a client. -type UpdateClientResp struct { - NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpdateClientResp) Reset() { *m = UpdateClientResp{} } -func (m *UpdateClientResp) String() string { return proto.CompactTextString(m) } -func (*UpdateClientResp) ProtoMessage() {} -func (*UpdateClientResp) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{6} -} - -func (m *UpdateClientResp) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UpdateClientResp.Unmarshal(m, b) -} -func (m *UpdateClientResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UpdateClientResp.Marshal(b, m, deterministic) -} -func (m *UpdateClientResp) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpdateClientResp.Merge(m, src) -} -func (m *UpdateClientResp) XXX_Size() int { - return xxx_messageInfo_UpdateClientResp.Size(m) -} -func (m *UpdateClientResp) XXX_DiscardUnknown() { - xxx_messageInfo_UpdateClientResp.DiscardUnknown(m) -} - -var xxx_messageInfo_UpdateClientResp proto.InternalMessageInfo - -func (m *UpdateClientResp) GetNotFound() bool { - if m != nil { - return m.NotFound - } - return false -} - -// Password is an email for password mapping managed by the storage. -type Password struct { - Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` - // Currently we do not accept plain text passwords. Could be an option in the future. - Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"` - Username string `protobuf:"bytes,3,opt,name=username,proto3" json:"username,omitempty"` - UserId string `protobuf:"bytes,4,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Password) Reset() { *m = Password{} } -func (m *Password) String() string { return proto.CompactTextString(m) } -func (*Password) ProtoMessage() {} -func (*Password) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{7} -} - -func (m *Password) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Password.Unmarshal(m, b) -} -func (m *Password) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Password.Marshal(b, m, deterministic) -} -func (m *Password) XXX_Merge(src proto.Message) { - xxx_messageInfo_Password.Merge(m, src) -} -func (m *Password) XXX_Size() int { - return xxx_messageInfo_Password.Size(m) -} -func (m *Password) XXX_DiscardUnknown() { - xxx_messageInfo_Password.DiscardUnknown(m) -} - -var xxx_messageInfo_Password proto.InternalMessageInfo - -func (m *Password) GetEmail() string { - if m != nil { - return m.Email - } - return "" -} - -func (m *Password) GetHash() []byte { - if m != nil { - return m.Hash - } - return nil -} - -func (m *Password) GetUsername() string { - if m != nil { - return m.Username - } - return "" -} - -func (m *Password) GetUserId() string { - if m != nil { - return m.UserId - } - return "" -} - -// CreatePasswordReq is a request to make a password. -type CreatePasswordReq struct { - Password *Password `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *CreatePasswordReq) Reset() { *m = CreatePasswordReq{} } -func (m *CreatePasswordReq) String() string { return proto.CompactTextString(m) } -func (*CreatePasswordReq) ProtoMessage() {} -func (*CreatePasswordReq) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{8} -} - -func (m *CreatePasswordReq) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_CreatePasswordReq.Unmarshal(m, b) -} -func (m *CreatePasswordReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_CreatePasswordReq.Marshal(b, m, deterministic) -} -func (m *CreatePasswordReq) XXX_Merge(src proto.Message) { - xxx_messageInfo_CreatePasswordReq.Merge(m, src) -} -func (m *CreatePasswordReq) XXX_Size() int { - return xxx_messageInfo_CreatePasswordReq.Size(m) -} -func (m *CreatePasswordReq) XXX_DiscardUnknown() { - xxx_messageInfo_CreatePasswordReq.DiscardUnknown(m) -} - -var xxx_messageInfo_CreatePasswordReq proto.InternalMessageInfo - -func (m *CreatePasswordReq) GetPassword() *Password { - if m != nil { - return m.Password - } - return nil -} - -// CreatePasswordResp returns the response from creating a password. -type CreatePasswordResp struct { - AlreadyExists bool `protobuf:"varint,1,opt,name=already_exists,json=alreadyExists,proto3" json:"already_exists,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *CreatePasswordResp) Reset() { *m = CreatePasswordResp{} } -func (m *CreatePasswordResp) String() string { return proto.CompactTextString(m) } -func (*CreatePasswordResp) ProtoMessage() {} -func (*CreatePasswordResp) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{9} -} - -func (m *CreatePasswordResp) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_CreatePasswordResp.Unmarshal(m, b) -} -func (m *CreatePasswordResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_CreatePasswordResp.Marshal(b, m, deterministic) -} -func (m *CreatePasswordResp) XXX_Merge(src proto.Message) { - xxx_messageInfo_CreatePasswordResp.Merge(m, src) -} -func (m *CreatePasswordResp) XXX_Size() int { - return xxx_messageInfo_CreatePasswordResp.Size(m) -} -func (m *CreatePasswordResp) XXX_DiscardUnknown() { - xxx_messageInfo_CreatePasswordResp.DiscardUnknown(m) -} - -var xxx_messageInfo_CreatePasswordResp proto.InternalMessageInfo - -func (m *CreatePasswordResp) GetAlreadyExists() bool { - if m != nil { - return m.AlreadyExists - } - return false -} - -// UpdatePasswordReq is a request to modify an existing password. -type UpdatePasswordReq struct { - // The email used to lookup the password. This field cannot be modified - Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` - NewHash []byte `protobuf:"bytes,2,opt,name=new_hash,json=newHash,proto3" json:"new_hash,omitempty"` - NewUsername string `protobuf:"bytes,3,opt,name=new_username,json=newUsername,proto3" json:"new_username,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpdatePasswordReq) Reset() { *m = UpdatePasswordReq{} } -func (m *UpdatePasswordReq) String() string { return proto.CompactTextString(m) } -func (*UpdatePasswordReq) ProtoMessage() {} -func (*UpdatePasswordReq) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{10} -} - -func (m *UpdatePasswordReq) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UpdatePasswordReq.Unmarshal(m, b) -} -func (m *UpdatePasswordReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UpdatePasswordReq.Marshal(b, m, deterministic) -} -func (m *UpdatePasswordReq) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpdatePasswordReq.Merge(m, src) -} -func (m *UpdatePasswordReq) XXX_Size() int { - return xxx_messageInfo_UpdatePasswordReq.Size(m) -} -func (m *UpdatePasswordReq) XXX_DiscardUnknown() { - xxx_messageInfo_UpdatePasswordReq.DiscardUnknown(m) -} - -var xxx_messageInfo_UpdatePasswordReq proto.InternalMessageInfo - -func (m *UpdatePasswordReq) GetEmail() string { - if m != nil { - return m.Email - } - return "" -} - -func (m *UpdatePasswordReq) GetNewHash() []byte { - if m != nil { - return m.NewHash - } - return nil -} - -func (m *UpdatePasswordReq) GetNewUsername() string { - if m != nil { - return m.NewUsername - } - return "" -} - -// UpdatePasswordResp returns the response from modifying an existing password. -type UpdatePasswordResp struct { - NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpdatePasswordResp) Reset() { *m = UpdatePasswordResp{} } -func (m *UpdatePasswordResp) String() string { return proto.CompactTextString(m) } -func (*UpdatePasswordResp) ProtoMessage() {} -func (*UpdatePasswordResp) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{11} -} - -func (m *UpdatePasswordResp) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UpdatePasswordResp.Unmarshal(m, b) -} -func (m *UpdatePasswordResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UpdatePasswordResp.Marshal(b, m, deterministic) -} -func (m *UpdatePasswordResp) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpdatePasswordResp.Merge(m, src) -} -func (m *UpdatePasswordResp) XXX_Size() int { - return xxx_messageInfo_UpdatePasswordResp.Size(m) -} -func (m *UpdatePasswordResp) XXX_DiscardUnknown() { - xxx_messageInfo_UpdatePasswordResp.DiscardUnknown(m) -} - -var xxx_messageInfo_UpdatePasswordResp proto.InternalMessageInfo - -func (m *UpdatePasswordResp) GetNotFound() bool { - if m != nil { - return m.NotFound - } - return false -} - -// DeletePasswordReq is a request to delete a password. -type DeletePasswordReq struct { - Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *DeletePasswordReq) Reset() { *m = DeletePasswordReq{} } -func (m *DeletePasswordReq) String() string { return proto.CompactTextString(m) } -func (*DeletePasswordReq) ProtoMessage() {} -func (*DeletePasswordReq) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{12} -} - -func (m *DeletePasswordReq) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_DeletePasswordReq.Unmarshal(m, b) -} -func (m *DeletePasswordReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_DeletePasswordReq.Marshal(b, m, deterministic) -} -func (m *DeletePasswordReq) XXX_Merge(src proto.Message) { - xxx_messageInfo_DeletePasswordReq.Merge(m, src) -} -func (m *DeletePasswordReq) XXX_Size() int { - return xxx_messageInfo_DeletePasswordReq.Size(m) -} -func (m *DeletePasswordReq) XXX_DiscardUnknown() { - xxx_messageInfo_DeletePasswordReq.DiscardUnknown(m) -} - -var xxx_messageInfo_DeletePasswordReq proto.InternalMessageInfo - -func (m *DeletePasswordReq) GetEmail() string { - if m != nil { - return m.Email - } - return "" -} - -// DeletePasswordResp returns the response from deleting a password. -type DeletePasswordResp struct { - NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *DeletePasswordResp) Reset() { *m = DeletePasswordResp{} } -func (m *DeletePasswordResp) String() string { return proto.CompactTextString(m) } -func (*DeletePasswordResp) ProtoMessage() {} -func (*DeletePasswordResp) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{13} -} - -func (m *DeletePasswordResp) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_DeletePasswordResp.Unmarshal(m, b) -} -func (m *DeletePasswordResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_DeletePasswordResp.Marshal(b, m, deterministic) -} -func (m *DeletePasswordResp) XXX_Merge(src proto.Message) { - xxx_messageInfo_DeletePasswordResp.Merge(m, src) -} -func (m *DeletePasswordResp) XXX_Size() int { - return xxx_messageInfo_DeletePasswordResp.Size(m) -} -func (m *DeletePasswordResp) XXX_DiscardUnknown() { - xxx_messageInfo_DeletePasswordResp.DiscardUnknown(m) -} - -var xxx_messageInfo_DeletePasswordResp proto.InternalMessageInfo - -func (m *DeletePasswordResp) GetNotFound() bool { - if m != nil { - return m.NotFound - } - return false -} - -// ListPasswordReq is a request to enumerate passwords. -type ListPasswordReq struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ListPasswordReq) Reset() { *m = ListPasswordReq{} } -func (m *ListPasswordReq) String() string { return proto.CompactTextString(m) } -func (*ListPasswordReq) ProtoMessage() {} -func (*ListPasswordReq) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{14} -} - -func (m *ListPasswordReq) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ListPasswordReq.Unmarshal(m, b) -} -func (m *ListPasswordReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ListPasswordReq.Marshal(b, m, deterministic) -} -func (m *ListPasswordReq) XXX_Merge(src proto.Message) { - xxx_messageInfo_ListPasswordReq.Merge(m, src) -} -func (m *ListPasswordReq) XXX_Size() int { - return xxx_messageInfo_ListPasswordReq.Size(m) -} -func (m *ListPasswordReq) XXX_DiscardUnknown() { - xxx_messageInfo_ListPasswordReq.DiscardUnknown(m) -} - -var xxx_messageInfo_ListPasswordReq proto.InternalMessageInfo - -// ListPasswordResp returns a list of passwords. -type ListPasswordResp struct { - Passwords []*Password `protobuf:"bytes,1,rep,name=passwords,proto3" json:"passwords,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ListPasswordResp) Reset() { *m = ListPasswordResp{} } -func (m *ListPasswordResp) String() string { return proto.CompactTextString(m) } -func (*ListPasswordResp) ProtoMessage() {} -func (*ListPasswordResp) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{15} -} - -func (m *ListPasswordResp) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ListPasswordResp.Unmarshal(m, b) -} -func (m *ListPasswordResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ListPasswordResp.Marshal(b, m, deterministic) -} -func (m *ListPasswordResp) XXX_Merge(src proto.Message) { - xxx_messageInfo_ListPasswordResp.Merge(m, src) -} -func (m *ListPasswordResp) XXX_Size() int { - return xxx_messageInfo_ListPasswordResp.Size(m) -} -func (m *ListPasswordResp) XXX_DiscardUnknown() { - xxx_messageInfo_ListPasswordResp.DiscardUnknown(m) -} - -var xxx_messageInfo_ListPasswordResp proto.InternalMessageInfo - -func (m *ListPasswordResp) GetPasswords() []*Password { - if m != nil { - return m.Passwords - } - return nil -} - -// VersionReq is a request to fetch version info. -type VersionReq struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *VersionReq) Reset() { *m = VersionReq{} } -func (m *VersionReq) String() string { return proto.CompactTextString(m) } -func (*VersionReq) ProtoMessage() {} -func (*VersionReq) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{16} -} - -func (m *VersionReq) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_VersionReq.Unmarshal(m, b) -} -func (m *VersionReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_VersionReq.Marshal(b, m, deterministic) -} -func (m *VersionReq) XXX_Merge(src proto.Message) { - xxx_messageInfo_VersionReq.Merge(m, src) -} -func (m *VersionReq) XXX_Size() int { - return xxx_messageInfo_VersionReq.Size(m) -} -func (m *VersionReq) XXX_DiscardUnknown() { - xxx_messageInfo_VersionReq.DiscardUnknown(m) -} - -var xxx_messageInfo_VersionReq proto.InternalMessageInfo - -// VersionResp holds the version info of components. -type VersionResp struct { - // Semantic version of the server. - Server string `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"` - // Numeric version of the API. It increases everytime a new call is added to the API. - // Clients should use this info to determine if the server supports specific features. - Api int32 `protobuf:"varint,2,opt,name=api,proto3" json:"api,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *VersionResp) Reset() { *m = VersionResp{} } -func (m *VersionResp) String() string { return proto.CompactTextString(m) } -func (*VersionResp) ProtoMessage() {} -func (*VersionResp) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{17} -} - -func (m *VersionResp) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_VersionResp.Unmarshal(m, b) -} -func (m *VersionResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_VersionResp.Marshal(b, m, deterministic) -} -func (m *VersionResp) XXX_Merge(src proto.Message) { - xxx_messageInfo_VersionResp.Merge(m, src) -} -func (m *VersionResp) XXX_Size() int { - return xxx_messageInfo_VersionResp.Size(m) -} -func (m *VersionResp) XXX_DiscardUnknown() { - xxx_messageInfo_VersionResp.DiscardUnknown(m) -} - -var xxx_messageInfo_VersionResp proto.InternalMessageInfo - -func (m *VersionResp) GetServer() string { - if m != nil { - return m.Server - } - return "" -} - -func (m *VersionResp) GetApi() int32 { - if m != nil { - return m.Api - } - return 0 -} - -// RefreshTokenRef contains the metadata for a refresh token that is managed by the storage. -type RefreshTokenRef struct { - // ID of the refresh token. - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` - CreatedAt int64 `protobuf:"varint,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - LastUsed int64 `protobuf:"varint,6,opt,name=last_used,json=lastUsed,proto3" json:"last_used,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *RefreshTokenRef) Reset() { *m = RefreshTokenRef{} } -func (m *RefreshTokenRef) String() string { return proto.CompactTextString(m) } -func (*RefreshTokenRef) ProtoMessage() {} -func (*RefreshTokenRef) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{18} -} - -func (m *RefreshTokenRef) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_RefreshTokenRef.Unmarshal(m, b) -} -func (m *RefreshTokenRef) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_RefreshTokenRef.Marshal(b, m, deterministic) -} -func (m *RefreshTokenRef) XXX_Merge(src proto.Message) { - xxx_messageInfo_RefreshTokenRef.Merge(m, src) -} -func (m *RefreshTokenRef) XXX_Size() int { - return xxx_messageInfo_RefreshTokenRef.Size(m) -} -func (m *RefreshTokenRef) XXX_DiscardUnknown() { - xxx_messageInfo_RefreshTokenRef.DiscardUnknown(m) -} - -var xxx_messageInfo_RefreshTokenRef proto.InternalMessageInfo - -func (m *RefreshTokenRef) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -func (m *RefreshTokenRef) GetClientId() string { - if m != nil { - return m.ClientId - } - return "" -} - -func (m *RefreshTokenRef) GetCreatedAt() int64 { - if m != nil { - return m.CreatedAt - } - return 0 -} - -func (m *RefreshTokenRef) GetLastUsed() int64 { - if m != nil { - return m.LastUsed - } - return 0 -} - -// ListRefreshReq is a request to enumerate the refresh tokens of a user. -type ListRefreshReq struct { - // The "sub" claim returned in the ID Token. - UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ListRefreshReq) Reset() { *m = ListRefreshReq{} } -func (m *ListRefreshReq) String() string { return proto.CompactTextString(m) } -func (*ListRefreshReq) ProtoMessage() {} -func (*ListRefreshReq) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{19} -} - -func (m *ListRefreshReq) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ListRefreshReq.Unmarshal(m, b) -} -func (m *ListRefreshReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ListRefreshReq.Marshal(b, m, deterministic) -} -func (m *ListRefreshReq) XXX_Merge(src proto.Message) { - xxx_messageInfo_ListRefreshReq.Merge(m, src) -} -func (m *ListRefreshReq) XXX_Size() int { - return xxx_messageInfo_ListRefreshReq.Size(m) -} -func (m *ListRefreshReq) XXX_DiscardUnknown() { - xxx_messageInfo_ListRefreshReq.DiscardUnknown(m) -} - -var xxx_messageInfo_ListRefreshReq proto.InternalMessageInfo - -func (m *ListRefreshReq) GetUserId() string { - if m != nil { - return m.UserId - } - return "" -} - -// ListRefreshResp returns a list of refresh tokens for a user. -type ListRefreshResp struct { - RefreshTokens []*RefreshTokenRef `protobuf:"bytes,1,rep,name=refresh_tokens,json=refreshTokens,proto3" json:"refresh_tokens,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ListRefreshResp) Reset() { *m = ListRefreshResp{} } -func (m *ListRefreshResp) String() string { return proto.CompactTextString(m) } -func (*ListRefreshResp) ProtoMessage() {} -func (*ListRefreshResp) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{20} -} - -func (m *ListRefreshResp) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ListRefreshResp.Unmarshal(m, b) -} -func (m *ListRefreshResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ListRefreshResp.Marshal(b, m, deterministic) -} -func (m *ListRefreshResp) XXX_Merge(src proto.Message) { - xxx_messageInfo_ListRefreshResp.Merge(m, src) -} -func (m *ListRefreshResp) XXX_Size() int { - return xxx_messageInfo_ListRefreshResp.Size(m) -} -func (m *ListRefreshResp) XXX_DiscardUnknown() { - xxx_messageInfo_ListRefreshResp.DiscardUnknown(m) -} - -var xxx_messageInfo_ListRefreshResp proto.InternalMessageInfo - -func (m *ListRefreshResp) GetRefreshTokens() []*RefreshTokenRef { - if m != nil { - return m.RefreshTokens - } - return nil -} - -// RevokeRefreshReq is a request to revoke the refresh token of the user-client pair. -type RevokeRefreshReq struct { - // The "sub" claim returned in the ID Token. - UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` - ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *RevokeRefreshReq) Reset() { *m = RevokeRefreshReq{} } -func (m *RevokeRefreshReq) String() string { return proto.CompactTextString(m) } -func (*RevokeRefreshReq) ProtoMessage() {} -func (*RevokeRefreshReq) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{21} -} - -func (m *RevokeRefreshReq) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_RevokeRefreshReq.Unmarshal(m, b) -} -func (m *RevokeRefreshReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_RevokeRefreshReq.Marshal(b, m, deterministic) -} -func (m *RevokeRefreshReq) XXX_Merge(src proto.Message) { - xxx_messageInfo_RevokeRefreshReq.Merge(m, src) -} -func (m *RevokeRefreshReq) XXX_Size() int { - return xxx_messageInfo_RevokeRefreshReq.Size(m) -} -func (m *RevokeRefreshReq) XXX_DiscardUnknown() { - xxx_messageInfo_RevokeRefreshReq.DiscardUnknown(m) -} - -var xxx_messageInfo_RevokeRefreshReq proto.InternalMessageInfo - -func (m *RevokeRefreshReq) GetUserId() string { - if m != nil { - return m.UserId - } - return "" -} - -func (m *RevokeRefreshReq) GetClientId() string { - if m != nil { - return m.ClientId - } - return "" -} - -// RevokeRefreshResp determines if the refresh token is revoked successfully. -type RevokeRefreshResp struct { - // Set to true is refresh token was not found and token could not be revoked. - NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *RevokeRefreshResp) Reset() { *m = RevokeRefreshResp{} } -func (m *RevokeRefreshResp) String() string { return proto.CompactTextString(m) } -func (*RevokeRefreshResp) ProtoMessage() {} -func (*RevokeRefreshResp) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{22} -} - -func (m *RevokeRefreshResp) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_RevokeRefreshResp.Unmarshal(m, b) -} -func (m *RevokeRefreshResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_RevokeRefreshResp.Marshal(b, m, deterministic) -} -func (m *RevokeRefreshResp) XXX_Merge(src proto.Message) { - xxx_messageInfo_RevokeRefreshResp.Merge(m, src) -} -func (m *RevokeRefreshResp) XXX_Size() int { - return xxx_messageInfo_RevokeRefreshResp.Size(m) -} -func (m *RevokeRefreshResp) XXX_DiscardUnknown() { - xxx_messageInfo_RevokeRefreshResp.DiscardUnknown(m) -} - -var xxx_messageInfo_RevokeRefreshResp proto.InternalMessageInfo - -func (m *RevokeRefreshResp) GetNotFound() bool { - if m != nil { - return m.NotFound - } - return false -} - -type VerifyPasswordReq struct { - Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` - Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *VerifyPasswordReq) Reset() { *m = VerifyPasswordReq{} } -func (m *VerifyPasswordReq) String() string { return proto.CompactTextString(m) } -func (*VerifyPasswordReq) ProtoMessage() {} -func (*VerifyPasswordReq) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{23} -} - -func (m *VerifyPasswordReq) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_VerifyPasswordReq.Unmarshal(m, b) -} -func (m *VerifyPasswordReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_VerifyPasswordReq.Marshal(b, m, deterministic) -} -func (m *VerifyPasswordReq) XXX_Merge(src proto.Message) { - xxx_messageInfo_VerifyPasswordReq.Merge(m, src) -} -func (m *VerifyPasswordReq) XXX_Size() int { - return xxx_messageInfo_VerifyPasswordReq.Size(m) -} -func (m *VerifyPasswordReq) XXX_DiscardUnknown() { - xxx_messageInfo_VerifyPasswordReq.DiscardUnknown(m) -} - -var xxx_messageInfo_VerifyPasswordReq proto.InternalMessageInfo - -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,proto3" json:"verified,omitempty"` - NotFound bool `protobuf:"varint,2,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *VerifyPasswordResp) Reset() { *m = VerifyPasswordResp{} } -func (m *VerifyPasswordResp) String() string { return proto.CompactTextString(m) } -func (*VerifyPasswordResp) ProtoMessage() {} -func (*VerifyPasswordResp) Descriptor() ([]byte, []int) { - return fileDescriptor_1b40cafcd4234784, []int{24} -} - -func (m *VerifyPasswordResp) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_VerifyPasswordResp.Unmarshal(m, b) -} -func (m *VerifyPasswordResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_VerifyPasswordResp.Marshal(b, m, deterministic) -} -func (m *VerifyPasswordResp) XXX_Merge(src proto.Message) { - xxx_messageInfo_VerifyPasswordResp.Merge(m, src) -} -func (m *VerifyPasswordResp) XXX_Size() int { - return xxx_messageInfo_VerifyPasswordResp.Size(m) -} -func (m *VerifyPasswordResp) XXX_DiscardUnknown() { - xxx_messageInfo_VerifyPasswordResp.DiscardUnknown(m) -} - -var xxx_messageInfo_VerifyPasswordResp proto.InternalMessageInfo - -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") - proto.RegisterType((*CreateClientResp)(nil), "api.CreateClientResp") - proto.RegisterType((*DeleteClientReq)(nil), "api.DeleteClientReq") - proto.RegisterType((*DeleteClientResp)(nil), "api.DeleteClientResp") - proto.RegisterType((*UpdateClientReq)(nil), "api.UpdateClientReq") - proto.RegisterType((*UpdateClientResp)(nil), "api.UpdateClientResp") - proto.RegisterType((*Password)(nil), "api.Password") - proto.RegisterType((*CreatePasswordReq)(nil), "api.CreatePasswordReq") - proto.RegisterType((*CreatePasswordResp)(nil), "api.CreatePasswordResp") - proto.RegisterType((*UpdatePasswordReq)(nil), "api.UpdatePasswordReq") - proto.RegisterType((*UpdatePasswordResp)(nil), "api.UpdatePasswordResp") - proto.RegisterType((*DeletePasswordReq)(nil), "api.DeletePasswordReq") - proto.RegisterType((*DeletePasswordResp)(nil), "api.DeletePasswordResp") - proto.RegisterType((*ListPasswordReq)(nil), "api.ListPasswordReq") - proto.RegisterType((*ListPasswordResp)(nil), "api.ListPasswordResp") - proto.RegisterType((*VersionReq)(nil), "api.VersionReq") - proto.RegisterType((*VersionResp)(nil), "api.VersionResp") - proto.RegisterType((*RefreshTokenRef)(nil), "api.RefreshTokenRef") - proto.RegisterType((*ListRefreshReq)(nil), "api.ListRefreshReq") - 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") -} - -func init() { proto.RegisterFile("api/api.proto", fileDescriptor_1b40cafcd4234784) } - -var fileDescriptor_1b40cafcd4234784 = []byte{ - // 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, -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// DexClient is the client API for Dex service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type DexClient interface { - // CreateClient creates a client. - CreateClient(ctx context.Context, in *CreateClientReq, opts ...grpc.CallOption) (*CreateClientResp, error) - // UpdateClient updates an existing client - UpdateClient(ctx context.Context, in *UpdateClientReq, opts ...grpc.CallOption) (*UpdateClientResp, error) - // DeleteClient deletes the provided client. - DeleteClient(ctx context.Context, in *DeleteClientReq, opts ...grpc.CallOption) (*DeleteClientResp, error) - // CreatePassword creates a password. - CreatePassword(ctx context.Context, in *CreatePasswordReq, opts ...grpc.CallOption) (*CreatePasswordResp, error) - // UpdatePassword modifies existing password. - UpdatePassword(ctx context.Context, in *UpdatePasswordReq, opts ...grpc.CallOption) (*UpdatePasswordResp, error) - // DeletePassword deletes the password. - DeletePassword(ctx context.Context, in *DeletePasswordReq, opts ...grpc.CallOption) (*DeletePasswordResp, error) - // ListPassword lists all password entries. - ListPasswords(ctx context.Context, in *ListPasswordReq, opts ...grpc.CallOption) (*ListPasswordResp, error) - // GetVersion returns version information of the server. - GetVersion(ctx context.Context, in *VersionReq, opts ...grpc.CallOption) (*VersionResp, error) - // ListRefresh lists all the refresh token entries for a particular user. - ListRefresh(ctx context.Context, in *ListRefreshReq, opts ...grpc.CallOption) (*ListRefreshResp, error) - // RevokeRefresh revokes the refresh token for the provided user-client pair. - // - // 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 { - cc *grpc.ClientConn -} - -func NewDexClient(cc *grpc.ClientConn) DexClient { - return &dexClient{cc} -} - -func (c *dexClient) CreateClient(ctx context.Context, in *CreateClientReq, opts ...grpc.CallOption) (*CreateClientResp, error) { - out := new(CreateClientResp) - err := c.cc.Invoke(ctx, "/api.Dex/CreateClient", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dexClient) UpdateClient(ctx context.Context, in *UpdateClientReq, opts ...grpc.CallOption) (*UpdateClientResp, error) { - out := new(UpdateClientResp) - err := c.cc.Invoke(ctx, "/api.Dex/UpdateClient", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dexClient) DeleteClient(ctx context.Context, in *DeleteClientReq, opts ...grpc.CallOption) (*DeleteClientResp, error) { - out := new(DeleteClientResp) - err := c.cc.Invoke(ctx, "/api.Dex/DeleteClient", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dexClient) CreatePassword(ctx context.Context, in *CreatePasswordReq, opts ...grpc.CallOption) (*CreatePasswordResp, error) { - out := new(CreatePasswordResp) - err := c.cc.Invoke(ctx, "/api.Dex/CreatePassword", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dexClient) UpdatePassword(ctx context.Context, in *UpdatePasswordReq, opts ...grpc.CallOption) (*UpdatePasswordResp, error) { - out := new(UpdatePasswordResp) - err := c.cc.Invoke(ctx, "/api.Dex/UpdatePassword", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dexClient) DeletePassword(ctx context.Context, in *DeletePasswordReq, opts ...grpc.CallOption) (*DeletePasswordResp, error) { - out := new(DeletePasswordResp) - err := c.cc.Invoke(ctx, "/api.Dex/DeletePassword", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dexClient) ListPasswords(ctx context.Context, in *ListPasswordReq, opts ...grpc.CallOption) (*ListPasswordResp, error) { - out := new(ListPasswordResp) - err := c.cc.Invoke(ctx, "/api.Dex/ListPasswords", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dexClient) GetVersion(ctx context.Context, in *VersionReq, opts ...grpc.CallOption) (*VersionResp, error) { - out := new(VersionResp) - err := c.cc.Invoke(ctx, "/api.Dex/GetVersion", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dexClient) ListRefresh(ctx context.Context, in *ListRefreshReq, opts ...grpc.CallOption) (*ListRefreshResp, error) { - out := new(ListRefreshResp) - err := c.cc.Invoke(ctx, "/api.Dex/ListRefresh", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dexClient) RevokeRefresh(ctx context.Context, in *RevokeRefreshReq, opts ...grpc.CallOption) (*RevokeRefreshResp, error) { - out := new(RevokeRefreshResp) - err := c.cc.Invoke(ctx, "/api.Dex/RevokeRefresh", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dexClient) VerifyPassword(ctx context.Context, in *VerifyPasswordReq, opts ...grpc.CallOption) (*VerifyPasswordResp, error) { - out := new(VerifyPasswordResp) - err := c.cc.Invoke(ctx, "/api.Dex/VerifyPassword", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// DexServer is the server API for Dex service. -type DexServer interface { - // CreateClient creates a client. - CreateClient(context.Context, *CreateClientReq) (*CreateClientResp, error) - // UpdateClient updates an existing client - UpdateClient(context.Context, *UpdateClientReq) (*UpdateClientResp, error) - // DeleteClient deletes the provided client. - DeleteClient(context.Context, *DeleteClientReq) (*DeleteClientResp, error) - // CreatePassword creates a password. - CreatePassword(context.Context, *CreatePasswordReq) (*CreatePasswordResp, error) - // UpdatePassword modifies existing password. - UpdatePassword(context.Context, *UpdatePasswordReq) (*UpdatePasswordResp, error) - // DeletePassword deletes the password. - DeletePassword(context.Context, *DeletePasswordReq) (*DeletePasswordResp, error) - // ListPassword lists all password entries. - ListPasswords(context.Context, *ListPasswordReq) (*ListPasswordResp, error) - // GetVersion returns version information of the server. - GetVersion(context.Context, *VersionReq) (*VersionResp, error) - // ListRefresh lists all the refresh token entries for a particular user. - ListRefresh(context.Context, *ListRefreshReq) (*ListRefreshResp, error) - // RevokeRefresh revokes the refresh token for the provided user-client pair. - // - // 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) -} - -// UnimplementedDexServer can be embedded to have forward compatible implementations. -type UnimplementedDexServer struct { -} - -func (*UnimplementedDexServer) CreateClient(ctx context.Context, req *CreateClientReq) (*CreateClientResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateClient not implemented") -} -func (*UnimplementedDexServer) UpdateClient(ctx context.Context, req *UpdateClientReq) (*UpdateClientResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method UpdateClient not implemented") -} -func (*UnimplementedDexServer) DeleteClient(ctx context.Context, req *DeleteClientReq) (*DeleteClientResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method DeleteClient not implemented") -} -func (*UnimplementedDexServer) CreatePassword(ctx context.Context, req *CreatePasswordReq) (*CreatePasswordResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreatePassword not implemented") -} -func (*UnimplementedDexServer) UpdatePassword(ctx context.Context, req *UpdatePasswordReq) (*UpdatePasswordResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method UpdatePassword not implemented") -} -func (*UnimplementedDexServer) DeletePassword(ctx context.Context, req *DeletePasswordReq) (*DeletePasswordResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method DeletePassword not implemented") -} -func (*UnimplementedDexServer) ListPasswords(ctx context.Context, req *ListPasswordReq) (*ListPasswordResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListPasswords not implemented") -} -func (*UnimplementedDexServer) GetVersion(ctx context.Context, req *VersionReq) (*VersionResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetVersion not implemented") -} -func (*UnimplementedDexServer) ListRefresh(ctx context.Context, req *ListRefreshReq) (*ListRefreshResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListRefresh not implemented") -} -func (*UnimplementedDexServer) RevokeRefresh(ctx context.Context, req *RevokeRefreshReq) (*RevokeRefreshResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method RevokeRefresh not implemented") -} -func (*UnimplementedDexServer) VerifyPassword(ctx context.Context, req *VerifyPasswordReq) (*VerifyPasswordResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method VerifyPassword not implemented") -} - -func RegisterDexServer(s *grpc.Server, srv DexServer) { - s.RegisterService(&_Dex_serviceDesc, srv) -} - -func _Dex_CreateClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateClientReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DexServer).CreateClient(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/api.Dex/CreateClient", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DexServer).CreateClient(ctx, req.(*CreateClientReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _Dex_UpdateClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateClientReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DexServer).UpdateClient(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/api.Dex/UpdateClient", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DexServer).UpdateClient(ctx, req.(*UpdateClientReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _Dex_DeleteClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteClientReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DexServer).DeleteClient(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/api.Dex/DeleteClient", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DexServer).DeleteClient(ctx, req.(*DeleteClientReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _Dex_CreatePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreatePasswordReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DexServer).CreatePassword(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/api.Dex/CreatePassword", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DexServer).CreatePassword(ctx, req.(*CreatePasswordReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _Dex_UpdatePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdatePasswordReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DexServer).UpdatePassword(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/api.Dex/UpdatePassword", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DexServer).UpdatePassword(ctx, req.(*UpdatePasswordReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _Dex_DeletePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeletePasswordReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DexServer).DeletePassword(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/api.Dex/DeletePassword", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DexServer).DeletePassword(ctx, req.(*DeletePasswordReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _Dex_ListPasswords_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListPasswordReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DexServer).ListPasswords(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/api.Dex/ListPasswords", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DexServer).ListPasswords(ctx, req.(*ListPasswordReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _Dex_GetVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(VersionReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DexServer).GetVersion(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/api.Dex/GetVersion", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DexServer).GetVersion(ctx, req.(*VersionReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _Dex_ListRefresh_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListRefreshReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DexServer).ListRefresh(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/api.Dex/ListRefresh", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DexServer).ListRefresh(ctx, req.(*ListRefreshReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _Dex_RevokeRefresh_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RevokeRefreshReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DexServer).RevokeRefresh(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/api.Dex/RevokeRefresh", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DexServer).RevokeRefresh(ctx, req.(*RevokeRefreshReq)) - } - 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), - Methods: []grpc.MethodDesc{ - { - MethodName: "CreateClient", - Handler: _Dex_CreateClient_Handler, - }, - { - MethodName: "UpdateClient", - Handler: _Dex_UpdateClient_Handler, - }, - { - MethodName: "DeleteClient", - Handler: _Dex_DeleteClient_Handler, - }, - { - MethodName: "CreatePassword", - Handler: _Dex_CreatePassword_Handler, - }, - { - MethodName: "UpdatePassword", - Handler: _Dex_UpdatePassword_Handler, - }, - { - MethodName: "DeletePassword", - Handler: _Dex_DeletePassword_Handler, - }, - { - MethodName: "ListPasswords", - Handler: _Dex_ListPasswords_Handler, - }, - { - MethodName: "GetVersion", - Handler: _Dex_GetVersion_Handler, - }, - { - MethodName: "ListRefresh", - Handler: _Dex_ListRefresh_Handler, - }, - { - MethodName: "RevokeRefresh", - Handler: _Dex_RevokeRefresh_Handler, - }, - { - MethodName: "VerifyPassword", - Handler: _Dex_VerifyPassword_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "api/api.proto", -} diff --git a/vendor/github.com/dexidp/dex/api/api.proto b/vendor/github.com/dexidp/dex/api/api.proto deleted file mode 100644 index 5d9ce1a1e3..0000000000 --- a/vendor/github.com/dexidp/dex/api/api.proto +++ /dev/null @@ -1,187 +0,0 @@ -syntax = "proto3"; -option java_package = "com.coreos.dex.api"; - -package api; - -// Client represents an OAuth2 client. -message Client { - string id = 1; - string secret = 2; - repeated string redirect_uris = 3; - repeated string trusted_peers = 4; - bool public = 5; - string name = 6; - string logo_url = 7; -} - -// CreateClientReq is a request to make a client. -message CreateClientReq { - Client client = 1; -} - -// CreateClientResp returns the response from creating a client. -message CreateClientResp { - bool already_exists = 1; - Client client = 2; -} - -// DeleteClientReq is a request to delete a client. -message DeleteClientReq { - // The ID of the client. - string id = 1; -} - -// DeleteClientResp determines if the client is deleted successfully. -message DeleteClientResp { - bool not_found = 1; -} - -// UpdateClientReq is a request to update an exisitng client. -message UpdateClientReq { - string id = 1; - repeated string redirect_uris = 2; - repeated string trusted_peers = 3; - string name = 4; - string logo_url = 5; -} - -// UpdateClientResp returns the reponse form updating a client. -message UpdateClientResp { - bool not_found = 1; -} - -// TODO(ericchiang): expand this. - -// Password is an email for password mapping managed by the storage. -message Password { - string email = 1; - - // Currently we do not accept plain text passwords. Could be an option in the future. - bytes hash = 2; - string username = 3; - string user_id = 4; -} - -// CreatePasswordReq is a request to make a password. -message CreatePasswordReq { - Password password = 1; -} - -// CreatePasswordResp returns the response from creating a password. -message CreatePasswordResp { - bool already_exists = 1; -} - -// UpdatePasswordReq is a request to modify an existing password. -message UpdatePasswordReq { - // The email used to lookup the password. This field cannot be modified - string email = 1; - bytes new_hash = 2; - string new_username = 3; -} - -// UpdatePasswordResp returns the response from modifying an existing password. -message UpdatePasswordResp { - bool not_found = 1; -} - -// DeletePasswordReq is a request to delete a password. -message DeletePasswordReq { - string email = 1; -} - -// DeletePasswordResp returns the response from deleting a password. -message DeletePasswordResp { - bool not_found = 1; -} - -// ListPasswordReq is a request to enumerate passwords. -message ListPasswordReq {} - -// ListPasswordResp returns a list of passwords. -message ListPasswordResp { - repeated Password passwords = 1; -} - -// VersionReq is a request to fetch version info. -message VersionReq {} - -// VersionResp holds the version info of components. -message VersionResp { - // Semantic version of the server. - string server = 1; - // Numeric version of the API. It increases everytime a new call is added to the API. - // Clients should use this info to determine if the server supports specific features. - int32 api = 2; -} - -// RefreshTokenRef contains the metadata for a refresh token that is managed by the storage. -message RefreshTokenRef { - // ID of the refresh token. - string id = 1; - string client_id = 2; - int64 created_at = 5; - int64 last_used = 6; -} - -// ListRefreshReq is a request to enumerate the refresh tokens of a user. -message ListRefreshReq { - // The "sub" claim returned in the ID Token. - string user_id = 1; -} - -// ListRefreshResp returns a list of refresh tokens for a user. -message ListRefreshResp { - repeated RefreshTokenRef refresh_tokens = 1; -} - -// RevokeRefreshReq is a request to revoke the refresh token of the user-client pair. -message RevokeRefreshReq { - // The "sub" claim returned in the ID Token. - string user_id = 1; - string client_id = 2; -} - -// RevokeRefreshResp determines if the refresh token is revoked successfully. -message RevokeRefreshResp { - // Set to true is refresh token was not found and token could not be revoked. - 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. - rpc CreateClient(CreateClientReq) returns (CreateClientResp) {}; - // UpdateClient updates an existing client - rpc UpdateClient(UpdateClientReq) returns (UpdateClientResp) {}; - // DeleteClient deletes the provided client. - rpc DeleteClient(DeleteClientReq) returns (DeleteClientResp) {}; - // CreatePassword creates a password. - rpc CreatePassword(CreatePasswordReq) returns (CreatePasswordResp) {}; - // UpdatePassword modifies existing password. - rpc UpdatePassword(UpdatePasswordReq) returns (UpdatePasswordResp) {}; - // DeletePassword deletes the password. - rpc DeletePassword(DeletePasswordReq) returns (DeletePasswordResp) {}; - // ListPassword lists all password entries. - rpc ListPasswords(ListPasswordReq) returns (ListPasswordResp) {}; - // GetVersion returns version information of the server. - rpc GetVersion(VersionReq) returns (VersionResp) {}; - // ListRefresh lists all the refresh token entries for a particular user. - rpc ListRefresh(ListRefreshReq) returns (ListRefreshResp) {}; - // RevokeRefresh revokes the refresh token for the provided user-client pair. - // - // 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/vendor/github.com/dexidp/dex/api/go.mod b/vendor/github.com/dexidp/dex/api/go.mod deleted file mode 100644 index 285e093075..0000000000 --- a/vendor/github.com/dexidp/dex/api/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/dexidp/dex/api - -go 1.14 - -require ( - github.com/golang/protobuf v1.3.2 - google.golang.org/grpc v1.26.0 -) diff --git a/vendor/github.com/dexidp/dex/api/go.sum b/vendor/github.com/dexidp/dex/api/go.sum deleted file mode 100644 index fdc9375d01..0000000000 --- a/vendor/github.com/dexidp/dex/api/go.sum +++ /dev/null @@ -1,49 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From 6d343e059bb21dac8b1ffff99691b43d6e26629d Mon Sep 17 00:00:00 2001 From: Justin Slowik Date: Thu, 16 Jan 2020 10:55:07 -0500 Subject: [PATCH 05/15] Generates/Stores the device request and returns the device and user codes. Signed-off-by: justin-slowik --- scripts/manifests/crds/devicerequests.yaml | 12 +++ scripts/manifests/crds/devicetokens.yaml | 12 +++ server/handlers.go | 115 +++++++++++++++++++- server/server.go | 4 +- storage/conformance/conformance.go | 119 +++++++++++++++++++++ storage/etcd/etcd.go | 74 +++++++++++++ storage/etcd/types.go | 38 +++++++ storage/kubernetes/storage.go | 46 ++++++++ storage/kubernetes/storage_test.go | 2 + storage/kubernetes/types.go | 104 ++++++++++++++++++ storage/memory/memory.go | 38 +++++++ storage/sql/crud.go | 55 ++++++++++ storage/sql/migrate.go | 19 ++++ storage/storage.go | 60 ++++++++++- 14 files changed, 690 insertions(+), 8 deletions(-) create mode 100644 scripts/manifests/crds/devicerequests.yaml create mode 100644 scripts/manifests/crds/devicetokens.yaml diff --git a/scripts/manifests/crds/devicerequests.yaml b/scripts/manifests/crds/devicerequests.yaml new file mode 100644 index 0000000000..9b5b420067 --- /dev/null +++ b/scripts/manifests/crds/devicerequests.yaml @@ -0,0 +1,12 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: devicerequests.dex.coreos.com +spec: + group: dex.coreos.com + names: + kind: DeviceRequest + listKind: DeviceRequestList + plural: devicerequests + singular: devicerequest + version: v1 diff --git a/scripts/manifests/crds/devicetokens.yaml b/scripts/manifests/crds/devicetokens.yaml new file mode 100644 index 0000000000..b6ce78dc28 --- /dev/null +++ b/scripts/manifests/crds/devicetokens.yaml @@ -0,0 +1,12 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: devicetokens.dex.coreos.com +spec: + group: dex.coreos.com + names: + kind: DeviceToken + listKind: DeviceTokenList + plural: devicetokens + singular: devicetoken + version: v1 diff --git a/server/handlers.go b/server/handlers.go index 5512d87fb7..5756f65246 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "net/http" "net/url" "path" @@ -15,12 +16,11 @@ import ( "time" oidc "github.com/coreos/go-oidc" - "github.com/gorilla/mux" - jose "gopkg.in/square/go-jose.v2" - "github.com/dexidp/dex/connector" "github.com/dexidp/dex/server/internal" "github.com/dexidp/dex/storage" + "github.com/gorilla/mux" + jose "gopkg.in/square/go-jose.v2" ) // newHealthChecker returns the healthz handler. The handler runs until the @@ -1415,3 +1415,112 @@ func usernamePrompt(conn connector.PasswordConnector) string { } return "Username" } + +type deviceCodeResponse struct { + //The unique device code for device authentication + DeviceCode string `json:"device_code"` + //The code the user will exchange via a browser and log in + UserCode string `json:"user_code"` + //The url to verify the user code. + VerificationURI string `json:"verification_uri"` + //The lifetime of the device code + ExpireTime int `json:"expires_in"` + //How often the device is allowed to poll to verify that the user login occurred + PollInterval int `json:"interval"` +} + +func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) { + //TODO replace with configurable values + expireIntervalSeconds := 300 + requestsPerMinute := 5 + + switch r.Method { + case http.MethodPost: + err := r.ParseForm() + if err != nil { + message := "Could not parse Device Request body" + s.logger.Errorf("%s : %v", message, err) + respondWithError(w, message, err) + return + } + + //Get the client id and scopes from the post + clientID := r.Form.Get("client_id") + scopes := r.Form["scope"] + + s.logger.Infof("Received device request for client %v with scopes %v", clientID, scopes) + + //Make device code + deviceCode := storage.NewDeviceCode() + + //make user code + userCode := storage.NewUserCode() + + //make a pkce verification code + pkceCode := storage.NewID() + + //Generate the expire time + expireTime := time.Now().Add(time.Second * time.Duration(expireIntervalSeconds)) + + //Store the Device Request + deviceReq := storage.DeviceRequest{ + UserCode: userCode, + DeviceCode: deviceCode, + ClientID: clientID, + Scopes: scopes, + PkceVerifier: pkceCode, + Expiry: expireTime, + } + + if err := s.storage.CreateDeviceRequest(deviceReq); err != nil { + message := fmt.Sprintf("Failed to store device request %v", err) + s.logger.Errorf(message) + respondWithError(w, message, err) + return + } + + //Store the device token + deviceToken := storage.DeviceToken{ + DeviceCode: deviceCode, + Status: "pending", + Token: "", + Expiry: expireTime, + } + + if err := s.storage.CreateDeviceToken(deviceToken); err != nil { + message := fmt.Sprintf("Failed to store device token %v", err) + s.logger.Errorf(message) + respondWithError(w, message, err) + return + } + + code := deviceCodeResponse{ + DeviceCode: deviceCode, + UserCode: userCode, + VerificationURI: path.Join(s.issuerURL.String(), "/device"), + ExpireTime: expireIntervalSeconds, + PollInterval: requestsPerMinute, + } + + enc := json.NewEncoder(w) + enc.SetIndent("", " ") + enc.Encode(code) + + default: + s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.") + } +} + +func respondWithError(w io.Writer, errorMessage string, err error) { + resp := struct { + Error string `json:"error"` + ErrorMessage string `json:"message"` + }{ + Error: err.Error(), + ErrorMessage: errorMessage, + } + + enc := json.NewEncoder(w) + enc.SetIndent("", " ") + enc.Encode(resp) +} diff --git a/server/server.go b/server/server.go index a0a075fbfc..95f5359ba9 100644 --- a/server/server.go +++ b/server/server.go @@ -302,6 +302,7 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy) handleWithCORS("/userinfo", s.handleUserInfo) handleFunc("/auth", s.handleAuthorization) handleFunc("/auth/{connector}", s.handleConnectorLogin) + handleFunc("/device/code", s.handleDeviceCode) r.HandleFunc(path.Join(issuerURL.Path, "/callback"), func(w http.ResponseWriter, r *http.Request) { // Strip the X-Remote-* headers to prevent security issues on // misconfigured authproxy connector setups. @@ -450,7 +451,8 @@ func (s *Server) startGarbageCollection(ctx context.Context, frequency time.Dura if r, err := s.storage.GarbageCollect(now()); err != nil { s.logger.Errorf("garbage collection failed: %v", err) } else if r.AuthRequests > 0 || r.AuthCodes > 0 { - s.logger.Infof("garbage collection run, delete auth requests=%d, auth codes=%d", r.AuthRequests, r.AuthCodes) + s.logger.Infof("garbage collection run, delete auth requests=%d, auth codes=%d, device requests =%d, device tokens=%d", + r.AuthRequests, r.AuthCodes, r.DeviceRequests, r.DeviceTokens) } } } diff --git a/storage/conformance/conformance.go b/storage/conformance/conformance.go index 1ac51fc859..c1bd318f24 100644 --- a/storage/conformance/conformance.go +++ b/storage/conformance/conformance.go @@ -49,6 +49,8 @@ func RunTests(t *testing.T, newStorage func() storage.Storage) { {"ConnectorCRUD", testConnectorCRUD}, {"GarbageCollection", testGC}, {"TimezoneSupport", testTimezones}, + {"DeviceRequestCRUD", testDeviceRequestCRUD}, + {"DeviceTokenCRUD", testDeviceTokenCRUD}, }) } @@ -834,6 +836,82 @@ func testGC(t *testing.T, s storage.Storage) { } else if err != storage.ErrNotFound { t.Errorf("expected storage.ErrNotFound, got %v", err) } + + d := storage.DeviceRequest{ + UserCode: storage.NewUserCode(), + DeviceCode: storage.NewID(), + ClientID: "client1", + Scopes: []string{"openid", "email"}, + PkceVerifier: storage.NewID(), + Expiry: expiry, + } + + if err := s.CreateDeviceRequest(d); err != nil { + t.Fatalf("failed creating device request: %v", err) + } + + for _, tz := range []*time.Location{time.UTC, est, pst} { + result, err := s.GarbageCollect(expiry.Add(-time.Hour).In(tz)) + if err != nil { + t.Errorf("garbage collection failed: %v", err) + } else { + if result.DeviceRequests != 0 { + t.Errorf("expected no device garbage collection results, got %#v", result) + } + } + //if _, err := s.GetDeviceRequest(d.UserCode); err != nil { + // t.Errorf("expected to be able to get auth request after GC: %v", err) + //} + } + if r, err := s.GarbageCollect(expiry.Add(time.Hour)); err != nil { + t.Errorf("garbage collection failed: %v", err) + } else if r.DeviceRequests != 1 { + t.Errorf("expected to garbage collect 1 device request, got %d", r.DeviceRequests) + } + + //TODO add this code back once Getters are written for device requests + //if _, err := s.GetDeviceRequest(d.UserCode); err == nil { + // t.Errorf("expected device request to be GC'd") + //} else if err != storage.ErrNotFound { + // t.Errorf("expected storage.ErrNotFound, got %v", err) + //} + + dt := storage.DeviceToken{ + DeviceCode: storage.NewID(), + Status: "pending", + Token: "foo", + Expiry: expiry, + } + + if err := s.CreateDeviceToken(dt); err != nil { + t.Fatalf("failed creating device token: %v", err) + } + + for _, tz := range []*time.Location{time.UTC, est, pst} { + result, err := s.GarbageCollect(expiry.Add(-time.Hour).In(tz)) + if err != nil { + t.Errorf("garbage collection failed: %v", err) + } else { + if result.DeviceTokens != 0 { + t.Errorf("expected no device token garbage collection results, got %#v", result) + } + } + //if _, err := s.GetDeviceRequest(d.UserCode); err != nil { + // t.Errorf("expected to be able to get auth request after GC: %v", err) + //} + } + if r, err := s.GarbageCollect(expiry.Add(time.Hour)); err != nil { + t.Errorf("garbage collection failed: %v", err) + } else if r.DeviceTokens != 1 { + t.Errorf("expected to garbage collect 1 device token, got %d", r.DeviceTokens) + } + + //TODO add this code back once Getters are written for device tokens + //if _, err := s.GetDeviceRequest(d.UserCode); err == nil { + // t.Errorf("expected device request to be GC'd") + //} else if err != storage.ErrNotFound { + // t.Errorf("expected storage.ErrNotFound, got %v", err) + //} } // testTimezones tests that backends either fully support timezones or @@ -881,3 +959,44 @@ func testTimezones(t *testing.T, s storage.Storage) { t.Fatalf("expected expiry %v got %v", wantTime, gotTime) } } + +func testDeviceRequestCRUD(t *testing.T, s storage.Storage) { + d1 := storage.DeviceRequest{ + UserCode: storage.NewUserCode(), + DeviceCode: storage.NewID(), + ClientID: "client1", + Scopes: []string{"openid", "email"}, + PkceVerifier: storage.NewID(), + Expiry: neverExpire, + } + + if err := s.CreateDeviceRequest(d1); err != nil { + t.Fatalf("failed creating device request: %v", err) + } + + // Attempt to create same DeviceRequest twice. + err := s.CreateDeviceRequest(d1) + mustBeErrAlreadyExists(t, "device request", err) + + //No manual deletes for device requests, will be handled by garbage collection routines + //see testGC +} + +func testDeviceTokenCRUD(t *testing.T, s storage.Storage) { + d1 := storage.DeviceToken{ + DeviceCode: storage.NewID(), + Status: "pending", + Token: storage.NewID(), + Expiry: neverExpire, + } + + if err := s.CreateDeviceToken(d1); err != nil { + t.Fatalf("failed creating device token: %v", err) + } + + // Attempt to create same DeviceRequest twice. + err := s.CreateDeviceToken(d1) + mustBeErrAlreadyExists(t, "device token", err) + + //TODO Add update / delete tests as functionality is put into main code +} diff --git a/storage/etcd/etcd.go b/storage/etcd/etcd.go index e26ce760c9..27e337a4d6 100644 --- a/storage/etcd/etcd.go +++ b/storage/etcd/etcd.go @@ -22,6 +22,8 @@ const ( offlineSessionPrefix = "offline_session/" connectorPrefix = "connector/" keysName = "openid-connect-keys" + deviceRequestPrefix = "device_req/" + deviceTokenPrefix = "device_token/" // defaultStorageTimeout will be applied to all storage's operations. defaultStorageTimeout = 5 * time.Second @@ -72,6 +74,36 @@ func (c *conn) GarbageCollect(now time.Time) (result storage.GCResult, err error result.AuthCodes++ } } + + deviceRequests, err := c.listDeviceRequests(ctx) + if err != nil { + return result, err + } + + for _, deviceRequest := range deviceRequests { + if now.After(deviceRequest.Expiry) { + if err := c.deleteKey(ctx, keyID(deviceRequestPrefix, deviceRequest.UserCode)); err != nil { + c.logger.Errorf("failed to delete device request %v", err) + delErr = fmt.Errorf("failed to delete device request: %v", err) + } + result.DeviceRequests++ + } + } + + deviceTokens, err := c.listDeviceTokens(ctx) + if err != nil { + return result, err + } + + for _, deviceToken := range deviceTokens { + if now.After(deviceToken.Expiry) { + if err := c.deleteKey(ctx, keyID(deviceTokenPrefix, deviceToken.DeviceCode)); err != nil { + c.logger.Errorf("failed to delete device token %v", err) + delErr = fmt.Errorf("failed to delete device token: %v", err) + } + result.DeviceTokens++ + } + } return result, delErr } @@ -531,3 +563,45 @@ func keyEmail(prefix, email string) string { return prefix + strings.ToLower(ema func keySession(prefix, userID, connID string) string { return prefix + strings.ToLower(userID+"|"+connID) } + +func (c *conn) CreateDeviceRequest(d storage.DeviceRequest) error { + ctx, cancel := context.WithTimeout(context.Background(), defaultStorageTimeout) + defer cancel() + return c.txnCreate(ctx, keyID(deviceRequestPrefix, d.UserCode), fromStorageDeviceRequest(d)) +} + +func (c *conn) listDeviceRequests(ctx context.Context) (requests []DeviceRequest, err error) { + res, err := c.db.Get(ctx, deviceRequestPrefix, clientv3.WithPrefix()) + if err != nil { + return requests, err + } + for _, v := range res.Kvs { + var r DeviceRequest + if err = json.Unmarshal(v.Value, &r); err != nil { + return requests, err + } + requests = append(requests, r) + } + return requests, nil +} + +func (c *conn) CreateDeviceToken(t storage.DeviceToken) error { + ctx, cancel := context.WithTimeout(context.Background(), defaultStorageTimeout) + defer cancel() + return c.txnCreate(ctx, keyID(deviceRequestPrefix, t.DeviceCode), fromStorageDeviceToken(t)) +} + +func (c *conn) listDeviceTokens(ctx context.Context) (deviceTokens []DeviceToken, err error) { + res, err := c.db.Get(ctx, deviceTokenPrefix, clientv3.WithPrefix()) + if err != nil { + return deviceTokens, err + } + for _, v := range res.Kvs { + var dt DeviceToken + if err = json.Unmarshal(v.Value, &dt); err != nil { + return deviceTokens, err + } + deviceTokens = append(deviceTokens, dt) + } + return deviceTokens, nil +} diff --git a/storage/etcd/types.go b/storage/etcd/types.go index a16eae8e94..ab7bce4ce9 100644 --- a/storage/etcd/types.go +++ b/storage/etcd/types.go @@ -216,3 +216,41 @@ func toStorageOfflineSessions(o OfflineSessions) storage.OfflineSessions { } return s } + +// DeviceRequest is a mirrored struct from storage with JSON struct tags +type DeviceRequest struct { + UserCode string `json:"user_code"` + DeviceCode string `json:"device_code"` + ClientID string `json:"client_id"` + Scopes []string `json:"scopes"` + PkceVerifier string `json:"pkce_verifier"` + Expiry time.Time `json:"expiry"` +} + +func fromStorageDeviceRequest(d storage.DeviceRequest) DeviceRequest { + return DeviceRequest{ + UserCode: d.UserCode, + DeviceCode: d.DeviceCode, + ClientID: d.ClientID, + Scopes: d.Scopes, + PkceVerifier: d.PkceVerifier, + Expiry: d.Expiry, + } +} + +// DeviceToken is a mirrored struct from storage with JSON struct tags +type DeviceToken struct { + DeviceCode string `json:"device_code"` + Status string `json:"status"` + Token string `json:"token"` + Expiry time.Time `json:"expiry"` +} + +func fromStorageDeviceToken(t storage.DeviceToken) DeviceToken { + return DeviceToken{ + DeviceCode: t.DeviceCode, + Status: t.Status, + Token: t.Token, + Expiry: t.Expiry, + } +} diff --git a/storage/kubernetes/storage.go b/storage/kubernetes/storage.go index 4bdf3dd6ec..e87b9c01de 100644 --- a/storage/kubernetes/storage.go +++ b/storage/kubernetes/storage.go @@ -21,6 +21,8 @@ const ( kindPassword = "Password" kindOfflineSessions = "OfflineSessions" kindConnector = "Connector" + kindDeviceRequest = "DeviceRequest" + kindDeviceToken = "DeviceToken" ) const ( @@ -32,6 +34,8 @@ const ( resourcePassword = "passwords" resourceOfflineSessions = "offlinesessionses" // Again attempts to pluralize. resourceConnector = "connectors" + resourceDeviceRequest = "devicerequests" + resourceDeviceToken = "devicetokens" ) // Config values for the Kubernetes storage type. @@ -593,5 +597,47 @@ func (cli *client) GarbageCollect(now time.Time) (result storage.GCResult, err e result.AuthCodes++ } } + + var deviceRequests DeviceRequestList + if err := cli.list(resourceDeviceRequest, &deviceRequests); err != nil { + return result, fmt.Errorf("failed to list device requests: %v", err) + } + + for _, deviceRequest := range deviceRequests.DeviceRequests { + if now.After(deviceRequest.Expiry) { + if err := cli.delete(resourceDeviceRequest, deviceRequest.ObjectMeta.Name); err != nil { + cli.logger.Errorf("failed to delete device request: %v", err) + delErr = fmt.Errorf("failed to delete device request: %v", err) + } + result.DeviceRequests++ + } + } + + var deviceTokens DeviceTokenList + if err := cli.list(resourceDeviceToken, &deviceTokens); err != nil { + return result, fmt.Errorf("failed to list device tokens: %v", err) + } + + for _, deviceToken := range deviceTokens.DeviceTokens { + if now.After(deviceToken.Expiry) { + if err := cli.delete(resourceDeviceToken, deviceToken.ObjectMeta.Name); err != nil { + cli.logger.Errorf("failed to delete device token: %v", err) + delErr = fmt.Errorf("failed to delete device token: %v", err) + } + result.DeviceTokens++ + } + } + + if delErr != nil { + return result, delErr + } return result, delErr } + +func (cli *client) CreateDeviceRequest(d storage.DeviceRequest) error { + return cli.post(resourceDeviceRequest, cli.fromStorageDeviceRequest(d)) +} + +func (cli *client) CreateDeviceToken(t storage.DeviceToken) error { + return cli.post(resourceDeviceToken, cli.fromStorageDeviceToken(t)) +} diff --git a/storage/kubernetes/storage_test.go b/storage/kubernetes/storage_test.go index ea471427cd..2c9deeb261 100644 --- a/storage/kubernetes/storage_test.go +++ b/storage/kubernetes/storage_test.go @@ -85,6 +85,8 @@ func (s *StorageTestSuite) TestStorage() { for _, resource := range []string{ resourceAuthCode, resourceAuthRequest, + resourceDeviceRequest, + resourceDeviceToken, resourceClient, resourceRefreshToken, resourceKeys, diff --git a/storage/kubernetes/types.go b/storage/kubernetes/types.go index 0fbb2907cb..5a61b92e15 100644 --- a/storage/kubernetes/types.go +++ b/storage/kubernetes/types.go @@ -143,6 +143,36 @@ var customResourceDefinitions = []k8sapi.CustomResourceDefinition{ }, }, }, + { + ObjectMeta: k8sapi.ObjectMeta{ + Name: "devicerequests.dex.coreos.com", + }, + TypeMeta: crdMeta, + Spec: k8sapi.CustomResourceDefinitionSpec{ + Group: apiGroup, + Version: "v1", + Names: k8sapi.CustomResourceDefinitionNames{ + Plural: "devicerequests", + Singular: "devicerequest", + Kind: "DeviceRequest", + }, + }, + }, + { + ObjectMeta: k8sapi.ObjectMeta{ + Name: "devicetokens.dex.coreos.com", + }, + TypeMeta: crdMeta, + Spec: k8sapi.CustomResourceDefinitionSpec{ + Group: apiGroup, + Version: "v1", + Names: k8sapi.CustomResourceDefinitionNames{ + Plural: "devicetokens", + Singular: "devicetoken", + Kind: "DeviceToken", + }, + }, + }, } // There will only ever be a single keys resource. Maintain this by setting a @@ -635,3 +665,77 @@ type ConnectorList struct { k8sapi.ListMeta `json:"metadata,omitempty"` Connectors []Connector `json:"items"` } + +// DeviceRequest is a mirrored struct from storage with JSON struct tags and +// Kubernetes type metadata. +type DeviceRequest struct { + k8sapi.TypeMeta `json:",inline"` + k8sapi.ObjectMeta `json:"metadata,omitempty"` + + DeviceCode string `json:"device_code,omitempty"` + CLientID string `json:"client_id,omitempty"` + Scopes []string `json:"scopes,omitempty"` + PkceVerifier string `json:"pkce_verifier,omitempty"` + Expiry time.Time `json:"expiry"` +} + +// AuthRequestList is a list of AuthRequests. +type DeviceRequestList struct { + k8sapi.TypeMeta `json:",inline"` + k8sapi.ListMeta `json:"metadata,omitempty"` + DeviceRequests []DeviceRequest `json:"items"` +} + +func (cli *client) fromStorageDeviceRequest(a storage.DeviceRequest) DeviceRequest { + req := DeviceRequest{ + TypeMeta: k8sapi.TypeMeta{ + Kind: kindDeviceRequest, + APIVersion: cli.apiVersion, + }, + ObjectMeta: k8sapi.ObjectMeta{ + Name: strings.ToLower(a.UserCode), + Namespace: cli.namespace, + }, + DeviceCode: a.DeviceCode, + CLientID: a.ClientID, + Scopes: a.Scopes, + PkceVerifier: a.PkceVerifier, + Expiry: a.Expiry, + } + return req +} + +// DeviceToken is a mirrored struct from storage with JSON struct tags and +// Kubernetes type metadata. +type DeviceToken struct { + k8sapi.TypeMeta `json:",inline"` + k8sapi.ObjectMeta `json:"metadata,omitempty"` + + Status string `json:"status,omitempty"` + Token string `json:"token,omitempty"` + Expiry time.Time `json:"expiry"` +} + +// DeviceTokenList is a list of DeviceTokens. +type DeviceTokenList struct { + k8sapi.TypeMeta `json:",inline"` + k8sapi.ListMeta `json:"metadata,omitempty"` + DeviceTokens []DeviceToken `json:"items"` +} + +func (cli *client) fromStorageDeviceToken(t storage.DeviceToken) DeviceToken { + req := DeviceToken{ + TypeMeta: k8sapi.TypeMeta{ + Kind: kindDeviceToken, + APIVersion: cli.apiVersion, + }, + ObjectMeta: k8sapi.ObjectMeta{ + Name: t.DeviceCode, + Namespace: cli.namespace, + }, + Status: t.Status, + Token: t.Token, + Expiry: t.Expiry, + } + return req +} diff --git a/storage/memory/memory.go b/storage/memory/memory.go index 681d204ea8..29d4af27b2 100644 --- a/storage/memory/memory.go +++ b/storage/memory/memory.go @@ -20,6 +20,8 @@ func New(logger log.Logger) storage.Storage { passwords: make(map[string]storage.Password), offlineSessions: make(map[offlineSessionID]storage.OfflineSessions), connectors: make(map[string]storage.Connector), + deviceRequests: make(map[string]storage.DeviceRequest), + deviceTokens: make(map[string]storage.DeviceToken), logger: logger, } } @@ -46,6 +48,8 @@ type memStorage struct { passwords map[string]storage.Password offlineSessions map[offlineSessionID]storage.OfflineSessions connectors map[string]storage.Connector + deviceRequests map[string]storage.DeviceRequest + deviceTokens map[string]storage.DeviceToken keys storage.Keys @@ -79,6 +83,18 @@ func (s *memStorage) GarbageCollect(now time.Time) (result storage.GCResult, err result.AuthRequests++ } } + for id, a := range s.deviceRequests { + if now.After(a.Expiry) { + delete(s.deviceRequests, id) + result.DeviceRequests++ + } + } + for id, a := range s.deviceTokens { + if now.After(a.Expiry) { + delete(s.deviceTokens, id) + result.DeviceTokens++ + } + } }) return result, nil } @@ -465,3 +481,25 @@ func (s *memStorage) UpdateConnector(id string, updater func(c storage.Connector }) return } + +func (s *memStorage) CreateDeviceRequest(d storage.DeviceRequest) (err error) { + s.tx(func() { + if _, ok := s.deviceRequests[d.UserCode]; ok { + err = storage.ErrAlreadyExists + } else { + s.deviceRequests[d.UserCode] = d + } + }) + return +} + +func (s *memStorage) CreateDeviceToken(t storage.DeviceToken) (err error) { + s.tx(func() { + if _, ok := s.deviceTokens[t.DeviceCode]; ok { + err = storage.ErrAlreadyExists + } else { + s.deviceTokens[t.DeviceCode] = t + } + }) + return +} diff --git a/storage/sql/crud.go b/storage/sql/crud.go index e87dc56aa5..989d2db000 100644 --- a/storage/sql/crud.go +++ b/storage/sql/crud.go @@ -100,6 +100,23 @@ func (c *conn) GarbageCollect(now time.Time) (result storage.GCResult, err error if n, err := r.RowsAffected(); err == nil { result.AuthCodes = n } + + r, err = c.Exec(`delete from device_request where expiry < $1`, now) + if err != nil { + return result, fmt.Errorf("gc device_request: %v", err) + } + if n, err := r.RowsAffected(); err == nil { + result.DeviceRequests = n + } + + r, err = c.Exec(`delete from device_token where expiry < $1`, now) + if err != nil { + return result, fmt.Errorf("gc device_token: %v", err) + } + if n, err := r.RowsAffected(); err == nil { + result.DeviceTokens = n + } + return } @@ -867,3 +884,41 @@ func (c *conn) delete(table, field, id string) error { } return nil } + +func (c *conn) CreateDeviceRequest(d storage.DeviceRequest) error { + _, err := c.Exec(` + insert into device_request ( + user_code, device_code, client_id, scopes, pkce_verifier, expiry + ) + values ( + $1, $2, $3, $4, $5, $6 + );`, + d.UserCode, d.DeviceCode, d.ClientID, encoder(d.Scopes), d.PkceVerifier, d.Expiry, + ) + if err != nil { + if c.alreadyExistsCheck(err) { + return storage.ErrAlreadyExists + } + return fmt.Errorf("insert device request: %v", err) + } + return nil +} + +func (c *conn) CreateDeviceToken(t storage.DeviceToken) error { + _, err := c.Exec(` + insert into device_token ( + device_code, status, token, expiry + ) + values ( + $1, $2, $3, $4 + );`, + t.DeviceCode, t.Status, t.Token, t.Expiry, + ) + if err != nil { + if c.alreadyExistsCheck(err) { + return storage.ErrAlreadyExists + } + return fmt.Errorf("insert device token: %v", err) + } + return nil +} diff --git a/storage/sql/migrate.go b/storage/sql/migrate.go index dc727535d4..96cd6c0aa6 100644 --- a/storage/sql/migrate.go +++ b/storage/sql/migrate.go @@ -229,4 +229,23 @@ var migrations = []migration{ }, flavor: &flavorMySQL, }, + { + stmts: []string{` + create table device_request ( + user_code text not null primary key, + device_code text not null, + client_id text not null, + scopes bytea not null, -- JSON array of strings + pkce_verifier text not null, + expiry timestamptz not null + );`, + ` + create table device_token ( + device_code text not null primary key, + status text not null, + token text, + expiry timestamptz not null + );`, + }, + }, } diff --git a/storage/storage.go b/storage/storage.go index 5bbb2b3f1b..7078ccf5a7 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -5,6 +5,7 @@ import ( "encoding/base32" "errors" "io" + mrand "math/rand" "strings" "time" @@ -24,9 +25,18 @@ var ( // TODO(ericchiang): refactor ID creation onto the storage. var encoding = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567") +// NewDeviceCode returns a 32 char alphanumeric cryptographically secure string +func NewDeviceCode() string { + return newSecureID(32) +} + // NewID returns a random string which can be used as an ID for objects. func NewID() string { - buff := make([]byte, 16) // 128 bit random ID. + return newSecureID(16) +} + +func newSecureID(len int) string { + buff := make([]byte, len) // 128 bit random ID. if _, err := io.ReadFull(rand.Reader, buff); err != nil { panic(err) } @@ -36,8 +46,10 @@ func NewID() string { // GCResult returns the number of objects deleted by garbage collection. type GCResult struct { - AuthRequests int64 - AuthCodes int64 + AuthRequests int64 + AuthCodes int64 + DeviceRequests int64 + DeviceTokens int64 } // Storage is the storage interface used by the server. Implementations are @@ -54,6 +66,8 @@ type Storage interface { CreatePassword(p Password) error CreateOfflineSessions(s OfflineSessions) error CreateConnector(c Connector) error + CreateDeviceRequest(d DeviceRequest) error + CreateDeviceToken(d DeviceToken) error // TODO(ericchiang): return (T, bool, error) so we can indicate not found // requests that way instead of using ErrNotFound. @@ -102,7 +116,7 @@ type Storage interface { UpdateOfflineSessions(userID string, connID string, updater func(s OfflineSessions) (OfflineSessions, error)) error UpdateConnector(id string, updater func(c Connector) (Connector, error)) error - // GarbageCollect deletes all expired AuthCodes and AuthRequests. + // GarbageCollect deletes all expired AuthCodes,AuthRequests, DeviceRequests, and DeviceTokens. GarbageCollect(now time.Time) (GCResult, error) } @@ -342,3 +356,41 @@ type Keys struct { // For caching purposes, implementations MUST NOT update keys before this time. NextRotation time.Time } + +func NewUserCode() string { + mrand.Seed(time.Now().UnixNano()) + return randomString(4) + "-" + randomString(4) +} + +func randomString(n int) string { + var letter = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ") + b := make([]rune, n) + for i := range b { + b[i] = letter[mrand.Intn(len(letter))] + } + return string(b) +} + +//DeviceRequest represents an OIDC device authorization request. It holds the state of a device request until the user +//authenticates using their user code or the expiry time passes. +type DeviceRequest struct { + //The code the user will enter in a browser + UserCode string + //The unique device code for device authentication + DeviceCode string + //The client ID the code is for + ClientID string + //The scopes the device requests + Scopes []string + //PKCE Verification + PkceVerifier string + //The expire time + Expiry time.Time +} + +type DeviceToken struct { + DeviceCode string + Status string + Token string + Expiry time.Time +} From 0d1a0e4129cb065dbc708f02ac54feaa05bae192 Mon Sep 17 00:00:00 2001 From: Justin Slowik <54368740+justin-slowik@users.noreply.github.com> Date: Mon, 27 Jan 2020 10:35:37 -0500 Subject: [PATCH 06/15] Device token api endpoint (#1) * Added /device/token handler with associated business logic and storage tests. * Use crypto rand for user code Signed-off-by: justin-slowik --- server/handlers.go | 82 +++++++++++++++++++++--------- server/oauth2.go | 6 +++ server/server.go | 1 + storage/conformance/conformance.go | 32 +++++++----- storage/etcd/etcd.go | 7 +++ storage/kubernetes/storage.go | 8 +++ storage/kubernetes/types.go | 9 ++++ storage/memory/memory.go | 11 ++++ storage/sql/crud.go | 22 ++++++++ storage/storage.go | 30 +++++++---- 10 files changed, 161 insertions(+), 47 deletions(-) diff --git a/server/handlers.go b/server/handlers.go index 5756f65246..b059b98fb4 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -5,7 +5,6 @@ import ( "encoding/json" "errors" "fmt" - "io" "net/http" "net/url" "path" @@ -1438,9 +1437,8 @@ func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) { case http.MethodPost: err := r.ParseForm() if err != nil { - message := "Could not parse Device Request body" - s.logger.Errorf("%s : %v", message, err) - respondWithError(w, message, err) + s.logger.Errorf("Could not parse Device Request body: %v", err) + s.tokenErrHelper(w, errInvalidRequest, "", http.StatusNotFound) return } @@ -1454,7 +1452,11 @@ func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) { deviceCode := storage.NewDeviceCode() //make user code - userCode := storage.NewUserCode() + userCode, err := storage.NewUserCode() + if err != nil { + s.logger.Errorf("Error generating user code: %v", err) + s.tokenErrHelper(w, errInvalidRequest, "", http.StatusInternalServerError) + } //make a pkce verification code pkceCode := storage.NewID() @@ -1473,24 +1475,21 @@ func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) { } if err := s.storage.CreateDeviceRequest(deviceReq); err != nil { - message := fmt.Sprintf("Failed to store device request %v", err) - s.logger.Errorf(message) - respondWithError(w, message, err) + s.logger.Errorf("Failed to store device request; %v", err) + s.tokenErrHelper(w, errInvalidRequest, "", http.StatusInternalServerError) return } //Store the device token deviceToken := storage.DeviceToken{ DeviceCode: deviceCode, - Status: "pending", - Token: "", + Status: deviceTokenPending, Expiry: expireTime, } if err := s.storage.CreateDeviceToken(deviceToken); err != nil { - message := fmt.Sprintf("Failed to store device token %v", err) - s.logger.Errorf(message) - respondWithError(w, message, err) + s.logger.Errorf("Failed to store device token %v", err) + s.tokenErrHelper(w, errInvalidRequest, "", http.StatusInternalServerError) return } @@ -1507,20 +1506,53 @@ func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) { enc.Encode(code) default: - s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.") + s.renderError(r, w, http.StatusBadRequest, "Invalid device code request type") + s.tokenErrHelper(w, errInvalidRequest, "", http.StatusBadRequest) } } -func respondWithError(w io.Writer, errorMessage string, err error) { - resp := struct { - Error string `json:"error"` - ErrorMessage string `json:"message"` - }{ - Error: err.Error(), - ErrorMessage: errorMessage, - } +func (s *Server) handleDeviceToken(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + switch r.Method { + case http.MethodPost: + err := r.ParseForm() + if err != nil { + message := "Could not parse Device Token Request body" + s.logger.Warnf("%s : %v", message, err) + s.tokenErrHelper(w, errInvalidRequest, "", http.StatusBadRequest) + return + } + + deviceCode := r.Form.Get("device_code") + if deviceCode == "" { + message := "No device code received" + s.tokenErrHelper(w, errInvalidRequest, message, http.StatusBadRequest) + return + } + + grantType := r.PostFormValue("grant_type") + if grantType != grantTypeDeviceCode { + s.tokenErrHelper(w, errInvalidGrant, "", http.StatusBadRequest) + return + } + + //Grab the device token from the db + deviceToken, err := s.storage.GetDeviceToken(deviceCode) + if err != nil || s.now().After(deviceToken.Expiry) { + if err != storage.ErrNotFound { + s.logger.Errorf("failed to get device code: %v", err) + } + s.tokenErrHelper(w, errInvalidRequest, "Invalid or expired device code.", http.StatusBadRequest) + return + } - enc := json.NewEncoder(w) - enc.SetIndent("", " ") - enc.Encode(resp) + switch deviceToken.Status { + case deviceTokenPending: + s.tokenErrHelper(w, deviceTokenPending, "", http.StatusUnauthorized) + case deviceTokenComplete: + w.Write([]byte(deviceToken.Token)) + } + default: + s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.") + } } diff --git a/server/oauth2.go b/server/oauth2.go index 05dd25d256..ddeffc3fa5 100644 --- a/server/oauth2.go +++ b/server/oauth2.go @@ -122,6 +122,7 @@ const ( grantTypeAuthorizationCode = "authorization_code" grantTypeRefreshToken = "refresh_token" grantTypePassword = "password" + grantTypeDeviceCode = "device_code" ) const ( @@ -130,6 +131,11 @@ const ( responseTypeIDToken = "id_token" // ID Token in url fragment ) +const ( + deviceTokenPending = "authorization_pending" + deviceTokenComplete = "complete" +) + func parseScopes(scopes []string) connector.Scopes { var s connector.Scopes for _, scope := range scopes { diff --git a/server/server.go b/server/server.go index 95f5359ba9..b86dac04e9 100644 --- a/server/server.go +++ b/server/server.go @@ -303,6 +303,7 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy) handleFunc("/auth", s.handleAuthorization) handleFunc("/auth/{connector}", s.handleConnectorLogin) handleFunc("/device/code", s.handleDeviceCode) + handleFunc("/device/token", s.handleDeviceToken) r.HandleFunc(path.Join(issuerURL.Path, "/callback"), func(w http.ResponseWriter, r *http.Request) { // Strip the X-Remote-* headers to prevent security issues on // misconfigured authproxy connector setups. diff --git a/storage/conformance/conformance.go b/storage/conformance/conformance.go index c1bd318f24..944d8a7831 100644 --- a/storage/conformance/conformance.go +++ b/storage/conformance/conformance.go @@ -837,8 +837,13 @@ func testGC(t *testing.T, s storage.Storage) { t.Errorf("expected storage.ErrNotFound, got %v", err) } + userCode, err := storage.NewUserCode() + if err != nil { + t.Errorf("Unexpected Error: %v", err) + } + d := storage.DeviceRequest{ - UserCode: storage.NewUserCode(), + UserCode: userCode, DeviceCode: storage.NewID(), ClientID: "client1", Scopes: []string{"openid", "email"}, @@ -896,9 +901,9 @@ func testGC(t *testing.T, s storage.Storage) { t.Errorf("expected no device token garbage collection results, got %#v", result) } } - //if _, err := s.GetDeviceRequest(d.UserCode); err != nil { - // t.Errorf("expected to be able to get auth request after GC: %v", err) - //} + if _, err := s.GetDeviceToken(dt.DeviceCode); err != nil { + t.Errorf("expected to be able to get device token after GC: %v", err) + } } if r, err := s.GarbageCollect(expiry.Add(time.Hour)); err != nil { t.Errorf("garbage collection failed: %v", err) @@ -906,12 +911,11 @@ func testGC(t *testing.T, s storage.Storage) { t.Errorf("expected to garbage collect 1 device token, got %d", r.DeviceTokens) } - //TODO add this code back once Getters are written for device tokens - //if _, err := s.GetDeviceRequest(d.UserCode); err == nil { - // t.Errorf("expected device request to be GC'd") - //} else if err != storage.ErrNotFound { - // t.Errorf("expected storage.ErrNotFound, got %v", err) - //} + if _, err := s.GetDeviceToken(dt.DeviceCode); err == nil { + t.Errorf("expected device token to be GC'd") + } else if err != storage.ErrNotFound { + t.Errorf("expected storage.ErrNotFound, got %v", err) + } } // testTimezones tests that backends either fully support timezones or @@ -961,8 +965,12 @@ func testTimezones(t *testing.T, s storage.Storage) { } func testDeviceRequestCRUD(t *testing.T, s storage.Storage) { + userCode, err := storage.NewUserCode() + if err != nil { + panic(err) + } d1 := storage.DeviceRequest{ - UserCode: storage.NewUserCode(), + UserCode: userCode, DeviceCode: storage.NewID(), ClientID: "client1", Scopes: []string{"openid", "email"}, @@ -975,7 +983,7 @@ func testDeviceRequestCRUD(t *testing.T, s storage.Storage) { } // Attempt to create same DeviceRequest twice. - err := s.CreateDeviceRequest(d1) + err = s.CreateDeviceRequest(d1) mustBeErrAlreadyExists(t, "device request", err) //No manual deletes for device requests, will be handled by garbage collection routines diff --git a/storage/etcd/etcd.go b/storage/etcd/etcd.go index 27e337a4d6..bbb86651fd 100644 --- a/storage/etcd/etcd.go +++ b/storage/etcd/etcd.go @@ -591,6 +591,13 @@ func (c *conn) CreateDeviceToken(t storage.DeviceToken) error { return c.txnCreate(ctx, keyID(deviceRequestPrefix, t.DeviceCode), fromStorageDeviceToken(t)) } +func (c *conn) GetDeviceToken(deviceCode string) (t storage.DeviceToken, err error) { + ctx, cancel := context.WithTimeout(context.Background(), defaultStorageTimeout) + defer cancel() + err = c.getKey(ctx, keyID(deviceTokenPrefix, deviceCode), &t) + return t, err +} + func (c *conn) listDeviceTokens(ctx context.Context) (deviceTokens []DeviceToken, err error) { res, err := c.db.Get(ctx, deviceTokenPrefix, clientv3.WithPrefix()) if err != nil { diff --git a/storage/kubernetes/storage.go b/storage/kubernetes/storage.go index e87b9c01de..20f9daac71 100644 --- a/storage/kubernetes/storage.go +++ b/storage/kubernetes/storage.go @@ -641,3 +641,11 @@ func (cli *client) CreateDeviceRequest(d storage.DeviceRequest) error { func (cli *client) CreateDeviceToken(t storage.DeviceToken) error { return cli.post(resourceDeviceToken, cli.fromStorageDeviceToken(t)) } + +func (cli *client) GetDeviceToken(deviceCode string) (storage.DeviceToken, error) { + var token DeviceToken + if err := cli.get(resourceDeviceToken, deviceCode, &token); err != nil { + return storage.DeviceToken{}, err + } + return toStorageDeviceToken(token), nil +} diff --git a/storage/kubernetes/types.go b/storage/kubernetes/types.go index 5a61b92e15..66fe5780ed 100644 --- a/storage/kubernetes/types.go +++ b/storage/kubernetes/types.go @@ -739,3 +739,12 @@ func (cli *client) fromStorageDeviceToken(t storage.DeviceToken) DeviceToken { } return req } + +func toStorageDeviceToken(t DeviceToken) storage.DeviceToken { + return storage.DeviceToken{ + DeviceCode: t.ObjectMeta.Name, + Status: t.Status, + Token: t.Token, + Expiry: t.Expiry, + } +} diff --git a/storage/memory/memory.go b/storage/memory/memory.go index 29d4af27b2..32cfd415ed 100644 --- a/storage/memory/memory.go +++ b/storage/memory/memory.go @@ -503,3 +503,14 @@ func (s *memStorage) CreateDeviceToken(t storage.DeviceToken) (err error) { }) return } + +func (s *memStorage) GetDeviceToken(deviceCode string) (t storage.DeviceToken, err error) { + s.tx(func() { + var ok bool + if t, ok = s.deviceTokens[deviceCode]; !ok { + err = storage.ErrNotFound + return + } + }) + return +} diff --git a/storage/sql/crud.go b/storage/sql/crud.go index 989d2db000..c52e67cff4 100644 --- a/storage/sql/crud.go +++ b/storage/sql/crud.go @@ -922,3 +922,25 @@ func (c *conn) CreateDeviceToken(t storage.DeviceToken) error { } return nil } + +func (c *conn) GetDeviceToken(deviceCode string) (storage.DeviceToken, error) { + return getDeviceToken(c, deviceCode) +} + +func getDeviceToken(q querier, deviceCode string) (a storage.DeviceToken, err error) { + err = q.QueryRow(` + select + status, token, expiry + from device_token where device_code = $1; + `, deviceCode).Scan( + &a.Status, &a.Token, &a.Expiry, + ) + if err != nil { + if err == sql.ErrNoRows { + return a, storage.ErrNotFound + } + return a, fmt.Errorf("select device token: %v", err) + } + a.DeviceCode = deviceCode + return a, nil +} diff --git a/storage/storage.go b/storage/storage.go index 7078ccf5a7..88ab71cdeb 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -5,7 +5,7 @@ import ( "encoding/base32" "errors" "io" - mrand "math/rand" + "math/big" "strings" "time" @@ -25,6 +25,9 @@ var ( // TODO(ericchiang): refactor ID creation onto the storage. var encoding = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567") +//Valid characters for user codes +const validUserCharacters = "BCDFGHJKLMNPQRSTVWXZ" + // NewDeviceCode returns a 32 char alphanumeric cryptographically secure string func NewDeviceCode() string { return newSecureID(32) @@ -79,6 +82,7 @@ type Storage interface { GetPassword(email string) (Password, error) GetOfflineSessions(userID string, connID string) (OfflineSessions, error) GetConnector(id string) (Connector, error) + GetDeviceToken(deviceCode string) (DeviceToken, error) ListClients() ([]Client, error) ListRefreshTokens() ([]RefreshToken, error) @@ -357,18 +361,24 @@ type Keys struct { NextRotation time.Time } -func NewUserCode() string { - mrand.Seed(time.Now().UnixNano()) - return randomString(4) + "-" + randomString(4) +// NewUserCode returns a randomized 8 character user code for the device flow. +// No vowels are included to prevent accidental generation of words +func NewUserCode() (string, error) { + code, err := randomString(8) + if err != nil { + return "", err + } + return code[:4] + "-" + code[4:], nil } -func randomString(n int) string { - var letter = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ") - b := make([]rune, n) - for i := range b { - b[i] = letter[mrand.Intn(len(letter))] +func randomString(n int) (string, error) { + v := big.NewInt(int64(len(validUserCharacters))) + bytes := make([]byte, n) + for i := 0; i < n; i++ { + c, _ := rand.Int(rand.Reader, v) + bytes[i] = validUserCharacters[c.Int64()] } - return string(b) + return string(bytes), nil } //DeviceRequest represents an OIDC device authorization request. It holds the state of a device request until the user From 9bbdc721d532ea7928ca63c38e7768e13e97413a Mon Sep 17 00:00:00 2001 From: Justin Slowik <54368740+justin-slowik@users.noreply.github.com> Date: Tue, 28 Jan 2020 14:14:30 -0500 Subject: [PATCH 07/15] Device flow token code exchange (#2) * Added /device/token handler with associated business logic and storage tests. Perform user code exchange, flag the device code as complete. Moved device handler code into its own file for cleanliness. Cleanup * Removed PKCE code * Rate limiting for /device/token endpoint based on ietf standards * Configurable Device expiry Signed-off-by: justin-slowik --- cmd/dex/config.go | 3 + cmd/dex/config_test.go | 8 +- cmd/dex/serve.go | 9 +- examples/config-dev.yaml | 1 + server/deviceHandlers.go | 359 +++++++++++++++++++++++++++++ server/handlers.go | 234 +++++-------------- server/oauth2.go | 4 +- server/server.go | 18 +- server/templates.go | 55 +++-- storage/conformance/conformance.go | 58 +++-- storage/etcd/etcd.go | 25 ++ storage/etcd/types.go | 53 +++-- storage/kubernetes/storage.go | 29 +++ storage/kubernetes/types.go | 54 +++-- storage/memory/memory.go | 25 ++ storage/sql/crud.go | 66 +++++- storage/sql/migrate.go | 5 +- storage/storage.go | 14 +- web/templates/device.html | 23 ++ web/templates/device_success.html | 8 + 20 files changed, 777 insertions(+), 274 deletions(-) create mode 100644 server/deviceHandlers.go create mode 100644 web/templates/device.html create mode 100644 web/templates/device_success.html diff --git a/cmd/dex/config.go b/cmd/dex/config.go index 2d71a936ea..cc8a727308 100644 --- a/cmd/dex/config.go +++ b/cmd/dex/config.go @@ -283,6 +283,9 @@ type Expiry struct { // AuthRequests defines the duration of time for which the AuthRequests will be valid. AuthRequests string `json:"authRequests"` + + // DeviceRequests defines the duration of time for which the DeviceRequests will be valid. + DeviceRequests string `json:"deviceRequests"` } // Logger holds configuration required to customize logging for dex. diff --git a/cmd/dex/config_test.go b/cmd/dex/config_test.go index 12c7b21884..bced93b5d9 100644 --- a/cmd/dex/config_test.go +++ b/cmd/dex/config_test.go @@ -119,6 +119,7 @@ expiry: signingKeys: "7h" idTokens: "25h" authRequests: "25h" + deviceRequests: "10m" logger: level: "debug" @@ -197,9 +198,10 @@ logger: }, }, Expiry: Expiry{ - SigningKeys: "7h", - IDTokens: "25h", - AuthRequests: "25h", + SigningKeys: "7h", + IDTokens: "25h", + AuthRequests: "25h", + DeviceRequests: "10m", }, Logger: Logger{ Level: "debug", diff --git a/cmd/dex/serve.go b/cmd/dex/serve.go index d0e8f9ac5c..ca74059366 100644 --- a/cmd/dex/serve.go +++ b/cmd/dex/serve.go @@ -269,7 +269,14 @@ func serve(cmd *cobra.Command, args []string) error { logger.Infof("config auth requests valid for: %v", authRequests) serverConfig.AuthRequestsValidFor = authRequests } - + if c.Expiry.DeviceRequests != "" { + deviceRequests, err := time.ParseDuration(c.Expiry.DeviceRequests) + if err != nil { + return fmt.Errorf("invalid config value %q for device request expiry: %v", c.Expiry.AuthRequests, err) + } + logger.Infof("config device requests valid for: %v", deviceRequests) + serverConfig.DeviceRequestsValidFor = deviceRequests + } serv, err := server.NewServer(context.Background(), serverConfig) if err != nil { return fmt.Errorf("failed to initialize server: %v", err) diff --git a/examples/config-dev.yaml b/examples/config-dev.yaml index f7b011baec..9cd81fa62e 100644 --- a/examples/config-dev.yaml +++ b/examples/config-dev.yaml @@ -64,6 +64,7 @@ telemetry: # Uncomment this block to enable configuration for the expiration time durations. # expiry: +# deviceRequests: "5m" # signingKeys: "6h" # idTokens: "24h" diff --git a/server/deviceHandlers.go b/server/deviceHandlers.go new file mode 100644 index 0000000000..552554088f --- /dev/null +++ b/server/deviceHandlers.go @@ -0,0 +1,359 @@ +package server + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "net/url" + "path" + "strconv" + "strings" + "time" + + "github.com/dexidp/dex/storage" +) + +type deviceCodeResponse struct { + //The unique device code for device authentication + DeviceCode string `json:"device_code"` + //The code the user will exchange via a browser and log in + UserCode string `json:"user_code"` + //The url to verify the user code. + VerificationURI string `json:"verification_uri"` + //The verification uri with the user code appended for pre-filling form + VerificationURIComplete string `json:"verification_uri_complete"` + //The lifetime of the device code + ExpireTime int `json:"expires_in"` + //How often the device is allowed to poll to verify that the user login occurred + PollInterval int `json:"interval"` +} + +func (s *Server) getDeviceAuthURI() string { + return path.Join(s.issuerURL.Path, "/device/auth/verify_code") +} + +func (s *Server) handleDeviceExchange(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + userCode := r.URL.Query().Get("user_code") + invalidAttempt, err := strconv.ParseBool(r.URL.Query().Get("invalid")) + if err != nil { + invalidAttempt = false + } + if err := s.templates.device(r, w, s.getDeviceAuthURI(), userCode, invalidAttempt); err != nil { + s.logger.Errorf("Server template error: %v", err) + } + default: + s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.") + } +} + +func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) { + pollIntervalSeconds := 5 + + switch r.Method { + case http.MethodPost: + err := r.ParseForm() + if err != nil { + s.logger.Errorf("Could not parse Device Request body: %v", err) + s.tokenErrHelper(w, errInvalidRequest, "", http.StatusNotFound) + return + } + + //Get the client id and scopes from the post + clientID := r.Form.Get("client_id") + scopes := r.Form["scope"] + + s.logger.Infof("Received device request for client %v with scopes %v", clientID, scopes) + + //Make device code + deviceCode := storage.NewDeviceCode() + + //make user code + userCode, err := storage.NewUserCode() + if err != nil { + s.logger.Errorf("Error generating user code: %v", err) + s.tokenErrHelper(w, errInvalidRequest, "", http.StatusInternalServerError) + } + + //Generate the expire time + expireTime := time.Now().Add(s.deviceRequestsValidFor) + + //Store the Device Request + deviceReq := storage.DeviceRequest{ + UserCode: userCode, + DeviceCode: deviceCode, + ClientID: clientID, + Scopes: scopes, + Expiry: expireTime, + } + + if err := s.storage.CreateDeviceRequest(deviceReq); err != nil { + s.logger.Errorf("Failed to store device request; %v", err) + s.tokenErrHelper(w, errInvalidRequest, "", http.StatusInternalServerError) + return + } + + //Store the device token + deviceToken := storage.DeviceToken{ + DeviceCode: deviceCode, + Status: deviceTokenPending, + Expiry: expireTime, + LastRequestTime: time.Now(), + PollIntervalSeconds: 5, + } + + if err := s.storage.CreateDeviceToken(deviceToken); err != nil { + s.logger.Errorf("Failed to store device token %v", err) + s.tokenErrHelper(w, errInvalidRequest, "", http.StatusInternalServerError) + return + } + + u, err := url.Parse(s.issuerURL.String()) + if err != nil { + s.logger.Errorf("Could not parse issuer URL %v", err) + s.renderError(r, w, http.StatusInternalServerError, "") + return + } + u.Path = path.Join(u.Path, "device") + vURI := u.String() + + q := u.Query() + q.Set("user_code", userCode) + u.RawQuery = q.Encode() + vURIComplete := u.String() + + code := deviceCodeResponse{ + DeviceCode: deviceCode, + UserCode: userCode, + VerificationURI: vURI, + VerificationURIComplete: vURIComplete, + ExpireTime: int(s.deviceRequestsValidFor.Seconds()), + PollInterval: pollIntervalSeconds, + } + + enc := json.NewEncoder(w) + enc.SetIndent("", " ") + enc.Encode(code) + + default: + s.renderError(r, w, http.StatusBadRequest, "Invalid device code request type") + s.tokenErrHelper(w, errInvalidRequest, "", http.StatusBadRequest) + } +} + +func (s *Server) handleDeviceToken(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + switch r.Method { + case http.MethodPost: + err := r.ParseForm() + if err != nil { + s.logger.Warnf("Could not parse Device Token Request body: %v", err) + s.tokenErrHelper(w, errInvalidRequest, "", http.StatusBadRequest) + return + } + + deviceCode := r.Form.Get("device_code") + if deviceCode == "" { + s.tokenErrHelper(w, errInvalidRequest, "No device code received", http.StatusBadRequest) + return + } + + grantType := r.PostFormValue("grant_type") + if grantType != grantTypeDeviceCode { + s.tokenErrHelper(w, errInvalidGrant, "", http.StatusBadRequest) + return + } + + now := s.now() + + //Grab the device token + deviceToken, err := s.storage.GetDeviceToken(deviceCode) + if err != nil || now.After(deviceToken.Expiry) { + if err != storage.ErrNotFound { + s.logger.Errorf("failed to get device code: %v", err) + } + s.tokenErrHelper(w, errInvalidRequest, "Invalid or expired device code.", http.StatusBadRequest) + return + } + + //Rate Limiting check + pollInterval := deviceToken.PollIntervalSeconds + minRequestTime := deviceToken.LastRequestTime.Add(time.Second * time.Duration(pollInterval)) + if now.Before(minRequestTime) { + s.tokenErrHelper(w, deviceTokenSlowDown, "", http.StatusBadRequest) + //Continually increase the poll interval until the user waits the proper time + pollInterval += 5 + } else { + pollInterval = 5 + } + + switch deviceToken.Status { + case deviceTokenPending: + updater := func(old storage.DeviceToken) (storage.DeviceToken, error) { + old.PollIntervalSeconds = pollInterval + old.LastRequestTime = now + return old, nil + } + // Update device token last request time in storage + if err := s.storage.UpdateDeviceToken(deviceCode, updater); err != nil { + s.logger.Errorf("failed to update device token: %v", err) + s.renderError(r, w, http.StatusInternalServerError, "") + return + } + s.tokenErrHelper(w, deviceTokenPending, "", http.StatusUnauthorized) + case deviceTokenComplete: + w.Write([]byte(deviceToken.Token)) + } + default: + s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.") + } +} + +func (s *Server) handleDeviceCallback(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + userCode := r.FormValue("state") + code := r.FormValue("code") + + if userCode == "" || code == "" { + s.renderError(r, w, http.StatusBadRequest, "Request was missing parameters") + return + } + + // Authorization redirect callback from OAuth2 auth flow. + if errMsg := r.FormValue("error"); errMsg != "" { + http.Error(w, errMsg+": "+r.FormValue("error_description"), http.StatusBadRequest) + return + } + + authCode, err := s.storage.GetAuthCode(code) + if err != nil || s.now().After(authCode.Expiry) { + if err != storage.ErrNotFound { + s.logger.Errorf("failed to get auth code: %v", err) + } + s.renderError(r, w, http.StatusBadRequest, "Invalid or expired auth code.") + return + } + + //Grab the device request from storage + deviceReq, err := s.storage.GetDeviceRequest(userCode) + if err != nil || s.now().After(deviceReq.Expiry) { + if err != storage.ErrNotFound { + s.logger.Errorf("failed to get device code: %v", err) + } + s.renderError(r, w, http.StatusInternalServerError, "Invalid or expired device code.") + return + } + + reqClient, err := s.storage.GetClient(deviceReq.ClientID) + if err != nil { + s.logger.Errorf("Failed to get reqClient %q: %v", deviceReq.ClientID, err) + s.renderError(r, w, http.StatusInternalServerError, "Failed to retrieve device client.") + return + } + + resp, err := s.exchangeAuthCode(w, authCode, reqClient) + if err != nil { + s.logger.Errorf("Could not exchange auth code for client %q: %v", deviceReq.ClientID, err) + s.renderError(r, w, http.StatusInternalServerError, "Failed to exchange auth code.") + return + } + + //Grab the device request from storage + old, err := s.storage.GetDeviceToken(deviceReq.DeviceCode) + if err != nil || s.now().After(old.Expiry) { + if err != storage.ErrNotFound { + s.logger.Errorf("failed to get device token: %v", err) + } + s.renderError(r, w, http.StatusInternalServerError, "Invalid or expired device code.") + return + } + + updater := func(old storage.DeviceToken) (storage.DeviceToken, error) { + if old.Status == deviceTokenComplete { + return old, errors.New("device token already complete") + } + respStr, err := json.MarshalIndent(resp, "", " ") + if err != nil { + s.logger.Errorf("failed to marshal device token response: %v", err) + s.renderError(r, w, http.StatusInternalServerError, "") + return old, err + } + + old.Token = string(respStr) + old.Status = deviceTokenComplete + return old, nil + } + + // Update refresh token in the storage, store the token and mark as complete + if err := s.storage.UpdateDeviceToken(deviceReq.DeviceCode, updater); err != nil { + s.logger.Errorf("failed to update device token: %v", err) + s.renderError(r, w, http.StatusInternalServerError, "") + return + } + + if err := s.templates.deviceSuccess(r, w, reqClient.Name); err != nil { + s.logger.Errorf("Server template error: %v", err) + } + + default: + http.Error(w, fmt.Sprintf("method not implemented: %s", r.Method), http.StatusBadRequest) + return + } +} + +func (s *Server) verifyUserCode(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodPost: + err := r.ParseForm() + if err != nil { + message := "Could not parse user code verification Request body" + s.logger.Warnf("%s : %v", message, err) + s.tokenErrHelper(w, errInvalidRequest, message, http.StatusBadRequest) + return + } + + userCode := r.Form.Get("user_code") + if userCode == "" { + s.renderError(r, w, http.StatusBadRequest, "No user code received") + return + } + + userCode = strings.ToUpper(userCode) + + //Find the user code in the available requests + deviceRequest, err := s.storage.GetDeviceRequest(userCode) + if err != nil || s.now().After(deviceRequest.Expiry) { + if err != storage.ErrNotFound { + s.logger.Errorf("failed to get device request: %v", err) + s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError) + } + if err := s.templates.device(r, w, s.getDeviceAuthURI(), userCode, true); err != nil { + s.logger.Errorf("Server template error: %v", err) + } + return + } + + //Redirect to Dex Auth Endpoint + authURL := path.Join(s.issuerURL.Path, "/auth") + u, err := url.Parse(authURL) + if err != nil { + s.renderError(r, w, http.StatusInternalServerError, "Invalid auth URI.") + return + } + q := u.Query() + q.Set("client_id", deviceRequest.ClientID) + q.Set("state", deviceRequest.UserCode) + q.Set("response_type", "code") + q.Set("redirect_uri", path.Join(s.issuerURL.Path, "/device/callback")) + q.Set("scope", strings.Join(deviceRequest.Scopes, " ")) + u.RawQuery = q.Encode() + + http.Redirect(w, r, u.String(), http.StatusFound) + + default: + s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.") + } +} diff --git a/server/handlers.go b/server/handlers.go index b059b98fb4..32a81b9868 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -147,30 +147,34 @@ func (s *Server) handlePublicKeys(w http.ResponseWriter, r *http.Request) { } type discovery struct { - Issuer string `json:"issuer"` - Auth string `json:"authorization_endpoint"` - Token string `json:"token_endpoint"` - Keys string `json:"jwks_uri"` - UserInfo string `json:"userinfo_endpoint"` - ResponseTypes []string `json:"response_types_supported"` - Subjects []string `json:"subject_types_supported"` - IDTokenAlgs []string `json:"id_token_signing_alg_values_supported"` - Scopes []string `json:"scopes_supported"` - AuthMethods []string `json:"token_endpoint_auth_methods_supported"` - Claims []string `json:"claims_supported"` + Issuer string `json:"issuer"` + Auth string `json:"authorization_endpoint"` + Token string `json:"token_endpoint"` + Keys string `json:"jwks_uri"` + UserInfo string `json:"userinfo_endpoint"` + DeviceEndpoint string `json:"device_authorization_endpoint"` + GrantTypes []string `json:"grant_types_supported"'` + ResponseTypes []string `json:"response_types_supported"` + Subjects []string `json:"subject_types_supported"` + IDTokenAlgs []string `json:"id_token_signing_alg_values_supported"` + Scopes []string `json:"scopes_supported"` + AuthMethods []string `json:"token_endpoint_auth_methods_supported"` + Claims []string `json:"claims_supported"` } func (s *Server) discoveryHandler() (http.HandlerFunc, error) { d := discovery{ - Issuer: s.issuerURL.String(), - Auth: s.absURL("/auth"), - Token: s.absURL("/token"), - Keys: s.absURL("/keys"), - UserInfo: s.absURL("/userinfo"), - Subjects: []string{"public"}, - IDTokenAlgs: []string{string(jose.RS256)}, - Scopes: []string{"openid", "email", "groups", "profile", "offline_access"}, - AuthMethods: []string{"client_secret_basic"}, + Issuer: s.issuerURL.String(), + Auth: s.absURL("/auth"), + Token: s.absURL("/token"), + Keys: s.absURL("/keys"), + UserInfo: s.absURL("/userinfo"), + DeviceEndpoint: s.absURL("/device/code"), + Subjects: []string{"public"}, + GrantTypes: []string{grantTypeAuthorizationCode, grantTypeRefreshToken, grantTypeDeviceCode}, + IDTokenAlgs: []string{string(jose.RS256)}, + Scopes: []string{"openid", "email", "groups", "profile", "offline_access"}, + AuthMethods: []string{"client_secret_basic"}, Claims: []string{ "aud", "email", "email_verified", "exp", "iat", "iss", "locale", "name", "sub", @@ -783,24 +787,33 @@ func (s *Server) handleAuthCode(w http.ResponseWriter, r *http.Request, client s return } + tokenResponse, err := s.exchangeAuthCode(w, authCode, client) + if err != nil { + s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError) + return + } + s.writeAccessToken(w, tokenResponse) +} + +func (s *Server) exchangeAuthCode(w http.ResponseWriter, authCode storage.AuthCode, client storage.Client) (*accessTokenReponse, error) { accessToken, err := s.newAccessToken(client.ID, authCode.Claims, authCode.Scopes, authCode.Nonce, authCode.ConnectorID) if err != nil { s.logger.Errorf("failed to create new access token: %v", err) s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError) - return + return nil, err } idToken, expiry, err := s.newIDToken(client.ID, authCode.Claims, authCode.Scopes, authCode.Nonce, accessToken, authCode.ConnectorID) if err != nil { s.logger.Errorf("failed to create ID token: %v", err) s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError) - return + return nil, err } - if err := s.storage.DeleteAuthCode(code); err != nil { + if err := s.storage.DeleteAuthCode(authCode.ID); err != nil { s.logger.Errorf("failed to delete auth code: %v", err) s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError) - return + return nil, err } reqRefresh := func() bool { @@ -847,13 +860,13 @@ func (s *Server) handleAuthCode(w http.ResponseWriter, r *http.Request, client s if refreshToken, err = internal.Marshal(token); err != nil { s.logger.Errorf("failed to marshal refresh token: %v", err) s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError) - return + return nil, err } if err := s.storage.CreateRefresh(refresh); err != nil { s.logger.Errorf("failed to create refresh token: %v", err) s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError) - return + return nil, err } // deleteToken determines if we need to delete the newly created refresh token @@ -884,7 +897,7 @@ func (s *Server) handleAuthCode(w http.ResponseWriter, r *http.Request, client s s.logger.Errorf("failed to get offline session: %v", err) s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError) deleteToken = true - return + return nil, err } offlineSessions := storage.OfflineSessions{ UserID: refresh.Claims.UserID, @@ -899,7 +912,7 @@ func (s *Server) handleAuthCode(w http.ResponseWriter, r *http.Request, client s s.logger.Errorf("failed to create offline session: %v", err) s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError) deleteToken = true - return + return nil, err } } else { if oldTokenRef, ok := session.Refresh[tokenRef.ClientID]; ok { @@ -908,7 +921,7 @@ func (s *Server) handleAuthCode(w http.ResponseWriter, r *http.Request, client s s.logger.Errorf("failed to delete refresh token: %v", err) s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError) deleteToken = true - return + return nil, err } } @@ -920,11 +933,11 @@ func (s *Server) handleAuthCode(w http.ResponseWriter, r *http.Request, client s s.logger.Errorf("failed to update offline session: %v", err) s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError) deleteToken = true - return + return nil, err } } } - s.writeAccessToken(w, idToken, accessToken, refreshToken, expiry) + return s.toAccessTokenResponse(idToken, accessToken, refreshToken, expiry), nil } // handle a refresh token request https://tools.ietf.org/html/rfc6749#section-6 @@ -1120,7 +1133,8 @@ func (s *Server) handleRefreshToken(w http.ResponseWriter, r *http.Request, clie return } - s.writeAccessToken(w, idToken, accessToken, rawNewToken, expiry) + resp := s.toAccessTokenResponse(idToken, accessToken, rawNewToken, expiry) + s.writeAccessToken(w, resp) } func (s *Server) handleUserInfo(w http.ResponseWriter, r *http.Request) { @@ -1378,12 +1392,26 @@ func (s *Server) writeAccessToken(w http.ResponseWriter, idToken, accessToken, r RefreshToken string `json:"refresh_token,omitempty"` IDToken string `json:"id_token"` }{ + +type accessTokenReponse struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + ExpiresIn int `json:"expires_in"` + RefreshToken string `json:"refresh_token,omitempty"` + IDToken string `json:"id_token"` +} + +func (s *Server) toAccessTokenResponse(idToken, accessToken, refreshToken string, expiry time.Time) *accessTokenReponse { + return &accessTokenReponse{ accessToken, "bearer", int(expiry.Sub(s.now()).Seconds()), refreshToken, idToken, } +} + +func (s *Server) writeAccessToken(w http.ResponseWriter, resp *accessTokenReponse) { data, err := json.Marshal(resp) if err != nil { s.logger.Errorf("failed to marshal access token response: %v", err) @@ -1414,145 +1442,3 @@ func usernamePrompt(conn connector.PasswordConnector) string { } return "Username" } - -type deviceCodeResponse struct { - //The unique device code for device authentication - DeviceCode string `json:"device_code"` - //The code the user will exchange via a browser and log in - UserCode string `json:"user_code"` - //The url to verify the user code. - VerificationURI string `json:"verification_uri"` - //The lifetime of the device code - ExpireTime int `json:"expires_in"` - //How often the device is allowed to poll to verify that the user login occurred - PollInterval int `json:"interval"` -} - -func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) { - //TODO replace with configurable values - expireIntervalSeconds := 300 - requestsPerMinute := 5 - - switch r.Method { - case http.MethodPost: - err := r.ParseForm() - if err != nil { - s.logger.Errorf("Could not parse Device Request body: %v", err) - s.tokenErrHelper(w, errInvalidRequest, "", http.StatusNotFound) - return - } - - //Get the client id and scopes from the post - clientID := r.Form.Get("client_id") - scopes := r.Form["scope"] - - s.logger.Infof("Received device request for client %v with scopes %v", clientID, scopes) - - //Make device code - deviceCode := storage.NewDeviceCode() - - //make user code - userCode, err := storage.NewUserCode() - if err != nil { - s.logger.Errorf("Error generating user code: %v", err) - s.tokenErrHelper(w, errInvalidRequest, "", http.StatusInternalServerError) - } - - //make a pkce verification code - pkceCode := storage.NewID() - - //Generate the expire time - expireTime := time.Now().Add(time.Second * time.Duration(expireIntervalSeconds)) - - //Store the Device Request - deviceReq := storage.DeviceRequest{ - UserCode: userCode, - DeviceCode: deviceCode, - ClientID: clientID, - Scopes: scopes, - PkceVerifier: pkceCode, - Expiry: expireTime, - } - - if err := s.storage.CreateDeviceRequest(deviceReq); err != nil { - s.logger.Errorf("Failed to store device request; %v", err) - s.tokenErrHelper(w, errInvalidRequest, "", http.StatusInternalServerError) - return - } - - //Store the device token - deviceToken := storage.DeviceToken{ - DeviceCode: deviceCode, - Status: deviceTokenPending, - Expiry: expireTime, - } - - if err := s.storage.CreateDeviceToken(deviceToken); err != nil { - s.logger.Errorf("Failed to store device token %v", err) - s.tokenErrHelper(w, errInvalidRequest, "", http.StatusInternalServerError) - return - } - - code := deviceCodeResponse{ - DeviceCode: deviceCode, - UserCode: userCode, - VerificationURI: path.Join(s.issuerURL.String(), "/device"), - ExpireTime: expireIntervalSeconds, - PollInterval: requestsPerMinute, - } - - enc := json.NewEncoder(w) - enc.SetIndent("", " ") - enc.Encode(code) - - default: - s.renderError(r, w, http.StatusBadRequest, "Invalid device code request type") - s.tokenErrHelper(w, errInvalidRequest, "", http.StatusBadRequest) - } -} - -func (s *Server) handleDeviceToken(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - switch r.Method { - case http.MethodPost: - err := r.ParseForm() - if err != nil { - message := "Could not parse Device Token Request body" - s.logger.Warnf("%s : %v", message, err) - s.tokenErrHelper(w, errInvalidRequest, "", http.StatusBadRequest) - return - } - - deviceCode := r.Form.Get("device_code") - if deviceCode == "" { - message := "No device code received" - s.tokenErrHelper(w, errInvalidRequest, message, http.StatusBadRequest) - return - } - - grantType := r.PostFormValue("grant_type") - if grantType != grantTypeDeviceCode { - s.tokenErrHelper(w, errInvalidGrant, "", http.StatusBadRequest) - return - } - - //Grab the device token from the db - deviceToken, err := s.storage.GetDeviceToken(deviceCode) - if err != nil || s.now().After(deviceToken.Expiry) { - if err != storage.ErrNotFound { - s.logger.Errorf("failed to get device code: %v", err) - } - s.tokenErrHelper(w, errInvalidRequest, "Invalid or expired device code.", http.StatusBadRequest) - return - } - - switch deviceToken.Status { - case deviceTokenPending: - s.tokenErrHelper(w, deviceTokenPending, "", http.StatusUnauthorized) - case deviceTokenComplete: - w.Write([]byte(deviceToken.Token)) - } - default: - s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.") - } -} diff --git a/server/oauth2.go b/server/oauth2.go index ddeffc3fa5..59e132bc45 100644 --- a/server/oauth2.go +++ b/server/oauth2.go @@ -122,7 +122,7 @@ const ( grantTypeAuthorizationCode = "authorization_code" grantTypeRefreshToken = "refresh_token" grantTypePassword = "password" - grantTypeDeviceCode = "device_code" + grantTypeDeviceCode = "urn:ietf:params:oauth:grant-type:device_code" ) const ( @@ -134,6 +134,8 @@ const ( const ( deviceTokenPending = "authorization_pending" deviceTokenComplete = "complete" + deviceTokenSlowDown = "slow_down" + deviceTokenExpired = "expired_token" ) func parseScopes(scopes []string) connector.Scopes { diff --git a/server/server.go b/server/server.go index b86dac04e9..90e963276d 100644 --- a/server/server.go +++ b/server/server.go @@ -75,12 +75,13 @@ type Config struct { // If enabled, the connectors selection page will always be shown even if there's only one AlwaysShowLoginScreen bool - RotateKeysAfter time.Duration // Defaults to 6 hours. - IDTokensValidFor time.Duration // Defaults to 24 hours - AuthRequestsValidFor time.Duration // Defaults to 24 hours + RotateKeysAfter time.Duration // Defaults to 6 hours. + IDTokensValidFor time.Duration // Defaults to 24 hours + AuthRequestsValidFor time.Duration // Defaults to 24 hours + DeviceRequestsValidFor time.Duration // Defaults to 5 minutes // If set, the server will use this connector to handle password grants PasswordConnector string - + GCFrequency time.Duration // Defaults to 5 minutes // If specified, the server will use this function for determining time. @@ -156,8 +157,9 @@ type Server struct { now func() time.Time - idTokensValidFor time.Duration - authRequestsValidFor time.Duration + idTokensValidFor time.Duration + authRequestsValidFor time.Duration + deviceRequestsValidFor time.Duration logger log.Logger } @@ -219,6 +221,7 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy) supportedResponseTypes: supported, idTokensValidFor: value(c.IDTokensValidFor, 24*time.Hour), authRequestsValidFor: value(c.AuthRequestsValidFor, 24*time.Hour), + deviceRequestsValidFor: value(c.DeviceRequestsValidFor, 5*time.Minute), skipApproval: c.SkipApprovalScreen, alwaysShowLogin: c.AlwaysShowLoginScreen, now: now, @@ -302,8 +305,11 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy) handleWithCORS("/userinfo", s.handleUserInfo) handleFunc("/auth", s.handleAuthorization) handleFunc("/auth/{connector}", s.handleConnectorLogin) + handleFunc("/device", s.handleDeviceExchange) + handleFunc("/device/auth/verify_code", s.verifyUserCode) handleFunc("/device/code", s.handleDeviceCode) handleFunc("/device/token", s.handleDeviceToken) + handleFunc("/device/callback", s.handleDeviceCallback) r.HandleFunc(path.Join(issuerURL.Path, "/callback"), func(w http.ResponseWriter, r *http.Request) { // Strip the X-Remote-* headers to prevent security issues on // misconfigured authproxy connector setups. diff --git a/server/templates.go b/server/templates.go index 4947a10249..934362b772 100644 --- a/server/templates.go +++ b/server/templates.go @@ -15,11 +15,13 @@ import ( ) const ( - tmplApproval = "approval.html" - tmplLogin = "login.html" - tmplPassword = "password.html" - tmplOOB = "oob.html" - tmplError = "error.html" + tmplApproval = "approval.html" + tmplLogin = "login.html" + tmplPassword = "password.html" + tmplOOB = "oob.html" + tmplError = "error.html" + tmplDevice = "device.html" + tmplDeviceSuccess = "device_success.html" ) var requiredTmpls = []string{ @@ -28,14 +30,17 @@ var requiredTmpls = []string{ tmplPassword, tmplOOB, tmplError, + tmplDevice, } type templates struct { - loginTmpl *template.Template - approvalTmpl *template.Template - passwordTmpl *template.Template - oobTmpl *template.Template - errorTmpl *template.Template + loginTmpl *template.Template + approvalTmpl *template.Template + passwordTmpl *template.Template + oobTmpl *template.Template + errorTmpl *template.Template + deviceTmpl *template.Template + deviceSuccessTmpl *template.Template } type webConfig struct { @@ -152,11 +157,13 @@ func loadTemplates(c webConfig, templatesDir string) (*templates, error) { return nil, fmt.Errorf("missing template(s): %s", missingTmpls) } return &templates{ - loginTmpl: tmpls.Lookup(tmplLogin), - approvalTmpl: tmpls.Lookup(tmplApproval), - passwordTmpl: tmpls.Lookup(tmplPassword), - oobTmpl: tmpls.Lookup(tmplOOB), - errorTmpl: tmpls.Lookup(tmplError), + loginTmpl: tmpls.Lookup(tmplLogin), + approvalTmpl: tmpls.Lookup(tmplApproval), + passwordTmpl: tmpls.Lookup(tmplPassword), + oobTmpl: tmpls.Lookup(tmplOOB), + errorTmpl: tmpls.Lookup(tmplError), + deviceTmpl: tmpls.Lookup(tmplDevice), + deviceSuccessTmpl: tmpls.Lookup(tmplDeviceSuccess), }, nil } @@ -242,6 +249,24 @@ func (n byName) Len() int { return len(n) } func (n byName) Less(i, j int) bool { return n[i].Name < n[j].Name } func (n byName) Swap(i, j int) { n[i], n[j] = n[j], n[i] } +func (t *templates) device(r *http.Request, w http.ResponseWriter, postURL string, userCode string, lastWasInvalid bool) error { + data := struct { + PostURL string + UserCode string + Invalid bool + ReqPath string + }{postURL, userCode, lastWasInvalid, r.URL.Path} + return renderTemplate(w, t.deviceTmpl, data) +} + +func (t *templates) deviceSuccess(r *http.Request, w http.ResponseWriter, clientName string) error { + data := struct { + ClientName string + ReqPath string + }{clientName, r.URL.Path} + return renderTemplate(w, t.deviceSuccessTmpl, data) +} + func (t *templates) login(r *http.Request, w http.ResponseWriter, connectors []connectorInfo, reqPath string) error { sort.Sort(byName(connectors)) data := struct { diff --git a/storage/conformance/conformance.go b/storage/conformance/conformance.go index 944d8a7831..6edc8350ab 100644 --- a/storage/conformance/conformance.go +++ b/storage/conformance/conformance.go @@ -843,12 +843,11 @@ func testGC(t *testing.T, s storage.Storage) { } d := storage.DeviceRequest{ - UserCode: userCode, - DeviceCode: storage.NewID(), - ClientID: "client1", - Scopes: []string{"openid", "email"}, - PkceVerifier: storage.NewID(), - Expiry: expiry, + UserCode: userCode, + DeviceCode: storage.NewID(), + ClientID: "client1", + Scopes: []string{"openid", "email"}, + Expiry: expiry, } if err := s.CreateDeviceRequest(d); err != nil { @@ -970,12 +969,11 @@ func testDeviceRequestCRUD(t *testing.T, s storage.Storage) { panic(err) } d1 := storage.DeviceRequest{ - UserCode: userCode, - DeviceCode: storage.NewID(), - ClientID: "client1", - Scopes: []string{"openid", "email"}, - PkceVerifier: storage.NewID(), - Expiry: neverExpire, + UserCode: userCode, + DeviceCode: storage.NewID(), + ClientID: "client1", + Scopes: []string{"openid", "email"}, + Expiry: neverExpire, } if err := s.CreateDeviceRequest(d1); err != nil { @@ -991,20 +989,44 @@ func testDeviceRequestCRUD(t *testing.T, s storage.Storage) { } func testDeviceTokenCRUD(t *testing.T, s storage.Storage) { + //Create a Token d1 := storage.DeviceToken{ - DeviceCode: storage.NewID(), - Status: "pending", - Token: storage.NewID(), - Expiry: neverExpire, + DeviceCode: storage.NewID(), + Status: "pending", + Token: storage.NewID(), + Expiry: neverExpire, + LastRequestTime: time.Now(), + PollIntervalSeconds: 0, } if err := s.CreateDeviceToken(d1); err != nil { t.Fatalf("failed creating device token: %v", err) } - // Attempt to create same DeviceRequest twice. + // Attempt to create same Device Token twice. err := s.CreateDeviceToken(d1) mustBeErrAlreadyExists(t, "device token", err) - //TODO Add update / delete tests as functionality is put into main code + //Update the device token, simulate a redemption + if err := s.UpdateDeviceToken(d1.DeviceCode, func(old storage.DeviceToken) (storage.DeviceToken, error) { + old.Token = "token data" + old.Status = "complete" + return old, nil + }); err != nil { + t.Fatalf("failed to update device token: %v", err) + } + + //Retrieve the device token + got, err := s.GetDeviceToken(d1.DeviceCode) + if err != nil { + t.Fatalf("failed to get device token: %v", err) + } + + //Validate expected result set + if got.Status != "complete" { + t.Fatalf("update failed, wanted token status=%#v got %#v", "complete", got.Status) + } + if got.Token != "token data" { + t.Fatalf("update failed, wanted token =%#v got %#v", "token data", got.Token) + } } diff --git a/storage/etcd/etcd.go b/storage/etcd/etcd.go index bbb86651fd..f41831cd41 100644 --- a/storage/etcd/etcd.go +++ b/storage/etcd/etcd.go @@ -570,6 +570,13 @@ func (c *conn) CreateDeviceRequest(d storage.DeviceRequest) error { return c.txnCreate(ctx, keyID(deviceRequestPrefix, d.UserCode), fromStorageDeviceRequest(d)) } +func (c *conn) GetDeviceRequest(userCode string) (r storage.DeviceRequest, err error) { + ctx, cancel := context.WithTimeout(context.Background(), defaultStorageTimeout) + defer cancel() + err = c.getKey(ctx, keyID(deviceRequestPrefix, userCode), &r) + return r, err +} + func (c *conn) listDeviceRequests(ctx context.Context) (requests []DeviceRequest, err error) { res, err := c.db.Get(ctx, deviceRequestPrefix, clientv3.WithPrefix()) if err != nil { @@ -612,3 +619,21 @@ func (c *conn) listDeviceTokens(ctx context.Context) (deviceTokens []DeviceToken } return deviceTokens, nil } + +func (c *conn) UpdateDeviceToken(deviceCode string, updater func(old storage.DeviceToken) (storage.DeviceToken, error)) error { + ctx, cancel := context.WithTimeout(context.Background(), defaultStorageTimeout) + defer cancel() + return c.txnUpdate(ctx, keyID(deviceTokenPrefix, deviceCode), func(currentValue []byte) ([]byte, error) { + var current DeviceToken + if len(currentValue) > 0 { + if err := json.Unmarshal(currentValue, ¤t); err != nil { + return nil, err + } + } + updated, err := updater(toStorageDeviceToken(current)) + if err != nil { + return nil, err + } + return json.Marshal(fromStorageDeviceToken(updated)) + }) +} diff --git a/storage/etcd/types.go b/storage/etcd/types.go index ab7bce4ce9..cc8045c90e 100644 --- a/storage/etcd/types.go +++ b/storage/etcd/types.go @@ -219,38 +219,51 @@ func toStorageOfflineSessions(o OfflineSessions) storage.OfflineSessions { // DeviceRequest is a mirrored struct from storage with JSON struct tags type DeviceRequest struct { - UserCode string `json:"user_code"` - DeviceCode string `json:"device_code"` - ClientID string `json:"client_id"` - Scopes []string `json:"scopes"` - PkceVerifier string `json:"pkce_verifier"` - Expiry time.Time `json:"expiry"` + UserCode string `json:"user_code"` + DeviceCode string `json:"device_code"` + ClientID string `json:"client_id"` + Scopes []string `json:"scopes"` + Expiry time.Time `json:"expiry"` } func fromStorageDeviceRequest(d storage.DeviceRequest) DeviceRequest { return DeviceRequest{ - UserCode: d.UserCode, - DeviceCode: d.DeviceCode, - ClientID: d.ClientID, - Scopes: d.Scopes, - PkceVerifier: d.PkceVerifier, - Expiry: d.Expiry, + UserCode: d.UserCode, + DeviceCode: d.DeviceCode, + ClientID: d.ClientID, + Scopes: d.Scopes, + Expiry: d.Expiry, } } // DeviceToken is a mirrored struct from storage with JSON struct tags type DeviceToken struct { - DeviceCode string `json:"device_code"` - Status string `json:"status"` - Token string `json:"token"` - Expiry time.Time `json:"expiry"` + DeviceCode string `json:"device_code"` + Status string `json:"status"` + Token string `json:"token"` + Expiry time.Time `json:"expiry"` + LastRequestTime time.Time `json:"last_request"` + PollIntervalSeconds int `json:"poll_interval"` } func fromStorageDeviceToken(t storage.DeviceToken) DeviceToken { return DeviceToken{ - DeviceCode: t.DeviceCode, - Status: t.Status, - Token: t.Token, - Expiry: t.Expiry, + DeviceCode: t.DeviceCode, + Status: t.Status, + Token: t.Token, + Expiry: t.Expiry, + LastRequestTime: t.LastRequestTime, + PollIntervalSeconds: t.PollIntervalSeconds, + } +} + +func toStorageDeviceToken(t DeviceToken) storage.DeviceToken { + return storage.DeviceToken{ + DeviceCode: t.DeviceCode, + Status: t.Status, + Token: t.Token, + Expiry: t.Expiry, + LastRequestTime: t.LastRequestTime, + PollIntervalSeconds: t.PollIntervalSeconds, } } diff --git a/storage/kubernetes/storage.go b/storage/kubernetes/storage.go index 20f9daac71..baf1d5671d 100644 --- a/storage/kubernetes/storage.go +++ b/storage/kubernetes/storage.go @@ -638,6 +638,14 @@ func (cli *client) CreateDeviceRequest(d storage.DeviceRequest) error { return cli.post(resourceDeviceRequest, cli.fromStorageDeviceRequest(d)) } +func (cli *client) GetDeviceRequest(userCode string) (storage.DeviceRequest, error) { + var req DeviceRequest + if err := cli.get(resourceDeviceRequest, strings.ToLower(userCode), &req); err != nil { + return storage.DeviceRequest{}, err + } + return toStorageDeviceRequest(req), nil +} + func (cli *client) CreateDeviceToken(t storage.DeviceToken) error { return cli.post(resourceDeviceToken, cli.fromStorageDeviceToken(t)) } @@ -649,3 +657,24 @@ func (cli *client) GetDeviceToken(deviceCode string) (storage.DeviceToken, error } return toStorageDeviceToken(token), nil } + +func (cli *client) getDeviceToken(deviceCode string) (t DeviceToken, err error) { + err = cli.get(resourceDeviceToken, deviceCode, &t) + return +} + +func (cli *client) UpdateDeviceToken(deviceCode string, updater func(old storage.DeviceToken) (storage.DeviceToken, error)) error { + r, err := cli.getDeviceToken(deviceCode) + if err != nil { + return err + } + updated, err := updater(toStorageDeviceToken(r)) + if err != nil { + return err + } + updated.DeviceCode = deviceCode + + newToken := cli.fromStorageDeviceToken(updated) + newToken.ObjectMeta = r.ObjectMeta + return cli.put(resourceDeviceToken, r.ObjectMeta.Name, newToken) +} diff --git a/storage/kubernetes/types.go b/storage/kubernetes/types.go index 66fe5780ed..61794ccf24 100644 --- a/storage/kubernetes/types.go +++ b/storage/kubernetes/types.go @@ -672,11 +672,10 @@ type DeviceRequest struct { k8sapi.TypeMeta `json:",inline"` k8sapi.ObjectMeta `json:"metadata,omitempty"` - DeviceCode string `json:"device_code,omitempty"` - CLientID string `json:"client_id,omitempty"` - Scopes []string `json:"scopes,omitempty"` - PkceVerifier string `json:"pkce_verifier,omitempty"` - Expiry time.Time `json:"expiry"` + DeviceCode string `json:"device_code,omitempty"` + CLientID string `json:"client_id,omitempty"` + Scopes []string `json:"scopes,omitempty"` + Expiry time.Time `json:"expiry"` } // AuthRequestList is a list of AuthRequests. @@ -696,24 +695,35 @@ func (cli *client) fromStorageDeviceRequest(a storage.DeviceRequest) DeviceReque Name: strings.ToLower(a.UserCode), Namespace: cli.namespace, }, - DeviceCode: a.DeviceCode, - CLientID: a.ClientID, - Scopes: a.Scopes, - PkceVerifier: a.PkceVerifier, - Expiry: a.Expiry, + DeviceCode: a.DeviceCode, + CLientID: a.ClientID, + Scopes: a.Scopes, + Expiry: a.Expiry, } return req } +func toStorageDeviceRequest(req DeviceRequest) storage.DeviceRequest { + return storage.DeviceRequest{ + UserCode: strings.ToUpper(req.ObjectMeta.Name), + DeviceCode: req.DeviceCode, + ClientID: req.CLientID, + Scopes: req.Scopes, + Expiry: req.Expiry, + } +} + // DeviceToken is a mirrored struct from storage with JSON struct tags and // Kubernetes type metadata. type DeviceToken struct { k8sapi.TypeMeta `json:",inline"` k8sapi.ObjectMeta `json:"metadata,omitempty"` - Status string `json:"status,omitempty"` - Token string `json:"token,omitempty"` - Expiry time.Time `json:"expiry"` + Status string `json:"status,omitempty"` + Token string `json:"token,omitempty"` + Expiry time.Time `json:"expiry"` + LastRequestTime time.Time `json:"last_request"` + PollIntervalSeconds int `json:"poll_interval"` } // DeviceTokenList is a list of DeviceTokens. @@ -733,18 +743,22 @@ func (cli *client) fromStorageDeviceToken(t storage.DeviceToken) DeviceToken { Name: t.DeviceCode, Namespace: cli.namespace, }, - Status: t.Status, - Token: t.Token, - Expiry: t.Expiry, + Status: t.Status, + Token: t.Token, + Expiry: t.Expiry, + LastRequestTime: t.LastRequestTime, + PollIntervalSeconds: t.PollIntervalSeconds, } return req } func toStorageDeviceToken(t DeviceToken) storage.DeviceToken { return storage.DeviceToken{ - DeviceCode: t.ObjectMeta.Name, - Status: t.Status, - Token: t.Token, - Expiry: t.Expiry, + DeviceCode: t.ObjectMeta.Name, + Status: t.Status, + Token: t.Token, + Expiry: t.Expiry, + LastRequestTime: t.LastRequestTime, + PollIntervalSeconds: t.PollIntervalSeconds, } } diff --git a/storage/memory/memory.go b/storage/memory/memory.go index 32cfd415ed..82264205e7 100644 --- a/storage/memory/memory.go +++ b/storage/memory/memory.go @@ -493,6 +493,17 @@ func (s *memStorage) CreateDeviceRequest(d storage.DeviceRequest) (err error) { return } +func (s *memStorage) GetDeviceRequest(userCode string) (req storage.DeviceRequest, err error) { + s.tx(func() { + var ok bool + if req, ok = s.deviceRequests[userCode]; !ok { + err = storage.ErrNotFound + return + } + }) + return +} + func (s *memStorage) CreateDeviceToken(t storage.DeviceToken) (err error) { s.tx(func() { if _, ok := s.deviceTokens[t.DeviceCode]; ok { @@ -514,3 +525,17 @@ func (s *memStorage) GetDeviceToken(deviceCode string) (t storage.DeviceToken, e }) return } + +func (s *memStorage) UpdateDeviceToken(deviceCode string, updater func(p storage.DeviceToken) (storage.DeviceToken, error)) (err error) { + s.tx(func() { + r, ok := s.deviceTokens[deviceCode] + if !ok { + err = storage.ErrNotFound + return + } + if r, err = updater(r); err == nil { + s.deviceTokens[deviceCode] = r + } + }) + return +} diff --git a/storage/sql/crud.go b/storage/sql/crud.go index c52e67cff4..a85c972b60 100644 --- a/storage/sql/crud.go +++ b/storage/sql/crud.go @@ -888,12 +888,12 @@ func (c *conn) delete(table, field, id string) error { func (c *conn) CreateDeviceRequest(d storage.DeviceRequest) error { _, err := c.Exec(` insert into device_request ( - user_code, device_code, client_id, scopes, pkce_verifier, expiry + user_code, device_code, client_id, scopes, expiry ) values ( - $1, $2, $3, $4, $5, $6 + $1, $2, $3, $4, $5 );`, - d.UserCode, d.DeviceCode, d.ClientID, encoder(d.Scopes), d.PkceVerifier, d.Expiry, + d.UserCode, d.DeviceCode, d.ClientID, encoder(d.Scopes), d.Expiry, ) if err != nil { if c.alreadyExistsCheck(err) { @@ -907,12 +907,12 @@ func (c *conn) CreateDeviceRequest(d storage.DeviceRequest) error { func (c *conn) CreateDeviceToken(t storage.DeviceToken) error { _, err := c.Exec(` insert into device_token ( - device_code, status, token, expiry + device_code, status, token, expiry, last_request, poll_interval ) values ( - $1, $2, $3, $4 + $1, $2, $3, $4, $5, $6 );`, - t.DeviceCode, t.Status, t.Token, t.Expiry, + t.DeviceCode, t.Status, t.Token, t.Expiry, t.LastRequestTime, t.PollIntervalSeconds, ) if err != nil { if c.alreadyExistsCheck(err) { @@ -923,6 +923,28 @@ func (c *conn) CreateDeviceToken(t storage.DeviceToken) error { return nil } +func (c *conn) GetDeviceRequest(userCode string) (storage.DeviceRequest, error) { + return getDeviceRequest(c, userCode) +} + +func getDeviceRequest(q querier, userCode string) (d storage.DeviceRequest, err error) { + err = q.QueryRow(` + select + device_code, client_id, scopes, expiry + from device_request where user_code = $1; + `, userCode).Scan( + &d.DeviceCode, &d.ClientID, decoder(&d.Scopes), &d.Expiry, + ) + if err != nil { + if err == sql.ErrNoRows { + return d, storage.ErrNotFound + } + return d, fmt.Errorf("select device token: %v", err) + } + d.UserCode = userCode + return d, nil +} + func (c *conn) GetDeviceToken(deviceCode string) (storage.DeviceToken, error) { return getDeviceToken(c, deviceCode) } @@ -930,10 +952,10 @@ func (c *conn) GetDeviceToken(deviceCode string) (storage.DeviceToken, error) { func getDeviceToken(q querier, deviceCode string) (a storage.DeviceToken, err error) { err = q.QueryRow(` select - status, token, expiry + status, token, expiry, last_request, poll_interval from device_token where device_code = $1; `, deviceCode).Scan( - &a.Status, &a.Token, &a.Expiry, + &a.Status, &a.Token, &a.Expiry, &a.LastRequestTime, &a.PollIntervalSeconds, ) if err != nil { if err == sql.ErrNoRows { @@ -944,3 +966,31 @@ func getDeviceToken(q querier, deviceCode string) (a storage.DeviceToken, err er a.DeviceCode = deviceCode return a, nil } + +func (c *conn) UpdateDeviceToken(deviceCode string, updater func(old storage.DeviceToken) (storage.DeviceToken, error)) error { + return c.ExecTx(func(tx *trans) error { + r, err := getDeviceToken(tx, deviceCode) + if err != nil { + return err + } + if r, err = updater(r); err != nil { + return err + } + _, err = tx.Exec(` + update device_token + set + status = $1, + token = $2, + last_request = $3, + poll_interval = $4 + where + device_code = $5 + `, + r.Status, r.Token, r.LastRequestTime, r.PollIntervalSeconds, r.DeviceCode, + ) + if err != nil { + return fmt.Errorf("update device token: %v", err) + } + return nil + }) +} diff --git a/storage/sql/migrate.go b/storage/sql/migrate.go index 96cd6c0aa6..e399d2b8e6 100644 --- a/storage/sql/migrate.go +++ b/storage/sql/migrate.go @@ -236,7 +236,6 @@ var migrations = []migration{ device_code text not null, client_id text not null, scopes bytea not null, -- JSON array of strings - pkce_verifier text not null, expiry timestamptz not null );`, ` @@ -244,7 +243,9 @@ var migrations = []migration{ device_code text not null primary key, status text not null, token text, - expiry timestamptz not null + expiry timestamptz not null, + last_request timestamptz not null, + poll_interval integer not null );`, }, }, diff --git a/storage/storage.go b/storage/storage.go index 88ab71cdeb..005e9190a3 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -82,6 +82,7 @@ type Storage interface { GetPassword(email string) (Password, error) GetOfflineSessions(userID string, connID string) (OfflineSessions, error) GetConnector(id string) (Connector, error) + GetDeviceRequest(userCode string) (DeviceRequest, error) GetDeviceToken(deviceCode string) (DeviceToken, error) ListClients() ([]Client, error) @@ -119,6 +120,7 @@ type Storage interface { UpdatePassword(email string, updater func(p Password) (Password, error)) error UpdateOfflineSessions(userID string, connID string, updater func(s OfflineSessions) (OfflineSessions, error)) error UpdateConnector(id string, updater func(c Connector) (Connector, error)) error + UpdateDeviceToken(deviceCode string, updater func(t DeviceToken) (DeviceToken, error)) error // GarbageCollect deletes all expired AuthCodes,AuthRequests, DeviceRequests, and DeviceTokens. GarbageCollect(now time.Time) (GCResult, error) @@ -392,15 +394,15 @@ type DeviceRequest struct { ClientID string //The scopes the device requests Scopes []string - //PKCE Verification - PkceVerifier string //The expire time Expiry time.Time } type DeviceToken struct { - DeviceCode string - Status string - Token string - Expiry time.Time + DeviceCode string + Status string + Token string + Expiry time.Time + LastRequestTime time.Time + PollIntervalSeconds int } diff --git a/web/templates/device.html b/web/templates/device.html new file mode 100644 index 0000000000..674cbdc32f --- /dev/null +++ b/web/templates/device.html @@ -0,0 +1,23 @@ +{{ template "header.html" . }} + +
+

Enter User Code

+
+
+ {{ if( .UserCode )}} + + {{ else }} + + {{ end }} +
+ + {{ if .Invalid }} +
+ Invalid or Expired User Code +
+ {{ end }} + +
+
+ +{{ template "footer.html" . }} diff --git a/web/templates/device_success.html b/web/templates/device_success.html new file mode 100644 index 0000000000..53b09ce58c --- /dev/null +++ b/web/templates/device_success.html @@ -0,0 +1,8 @@ +{{ template "header.html" . }} + +
+

Login Successful for {{ .ClientName }}

+

Return to your device to continue

+
+ +{{ template "footer.html" . }} From 9c699b10284b5ad9e610cef30ed778b30e637f8c Mon Sep 17 00:00:00 2001 From: Justin Slowik <54368740+justin-slowik@users.noreply.github.com> Date: Tue, 4 Feb 2020 10:07:18 -0500 Subject: [PATCH 08/15] Server integration test for Device Flow (#3) Extracted test cases from OAuth2Code flow tests to reuse in device flow deviceHandler unit tests to test specific device endpoints Include client secret as an optional parameter for standards compliance Signed-off-by: justin-slowik --- ...eviceHandlers.go => deviceflowhandlers.go} | 97 ++- server/deviceflowhandlers_test.go | 672 ++++++++++++++++++ server/handlers.go | 19 +- server/server.go | 2 +- server/server_test.go | 646 +++++++++++------ server/templates.go | 3 + storage/conformance/conformance.go | 49 +- storage/etcd/etcd.go | 2 +- storage/etcd/etcd_test.go | 2 + storage/etcd/types.go | 22 +- storage/kubernetes/types.go | 29 +- storage/sql/crud.go | 10 +- storage/sql/migrate.go | 1 + storage/storage.go | 2 + 14 files changed, 1222 insertions(+), 334 deletions(-) rename server/{deviceHandlers.go => deviceflowhandlers.go} (77%) create mode 100644 server/deviceflowhandlers_test.go diff --git a/server/deviceHandlers.go b/server/deviceflowhandlers.go similarity index 77% rename from server/deviceHandlers.go rename to server/deviceflowhandlers.go index 552554088f..39ead50376 100644 --- a/server/deviceHandlers.go +++ b/server/deviceflowhandlers.go @@ -29,7 +29,7 @@ type deviceCodeResponse struct { PollInterval int `json:"interval"` } -func (s *Server) getDeviceAuthURI() string { +func (s *Server) getDeviceVerificationURI() string { return path.Join(s.issuerURL.Path, "/device/auth/verify_code") } @@ -41,8 +41,9 @@ func (s *Server) handleDeviceExchange(w http.ResponseWriter, r *http.Request) { if err != nil { invalidAttempt = false } - if err := s.templates.device(r, w, s.getDeviceAuthURI(), userCode, invalidAttempt); err != nil { + if err := s.templates.device(r, w, s.getDeviceVerificationURI(), userCode, invalidAttempt); err != nil { s.logger.Errorf("Server template error: %v", err) + s.renderError(r, w, http.StatusNotFound, "Page not found") } default: s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.") @@ -63,7 +64,8 @@ func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) { //Get the client id and scopes from the post clientID := r.Form.Get("client_id") - scopes := r.Form["scope"] + clientSecret := r.Form.Get("client_secret") + scopes := strings.Fields(r.Form.Get("scope")) s.logger.Infof("Received device request for client %v with scopes %v", clientID, scopes) @@ -82,11 +84,12 @@ func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) { //Store the Device Request deviceReq := storage.DeviceRequest{ - UserCode: userCode, - DeviceCode: deviceCode, - ClientID: clientID, - Scopes: scopes, - Expiry: expireTime, + UserCode: userCode, + DeviceCode: deviceCode, + ClientID: clientID, + ClientSecret: clientSecret, + Scopes: scopes, + Expiry: expireTime, } if err := s.storage.CreateDeviceRequest(deviceReq); err != nil { @@ -100,8 +103,8 @@ func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) { DeviceCode: deviceCode, Status: deviceTokenPending, Expiry: expireTime, - LastRequestTime: time.Now(), - PollIntervalSeconds: 5, + LastRequestTime: s.now(), + PollIntervalSeconds: 0, } if err := s.storage.CreateDeviceToken(deviceToken); err != nil { @@ -113,7 +116,7 @@ func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) { u, err := url.Parse(s.issuerURL.String()) if err != nil { s.logger.Errorf("Could not parse issuer URL %v", err) - s.renderError(r, w, http.StatusInternalServerError, "") + s.tokenErrHelper(w, errInvalidRequest, "", http.StatusInternalServerError) return } u.Path = path.Join(u.Path, "device") @@ -134,6 +137,7 @@ func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) { } enc := json.NewEncoder(w) + enc.SetEscapeHTML(false) enc.SetIndent("", " ") enc.Encode(code) @@ -168,21 +172,25 @@ func (s *Server) handleDeviceToken(w http.ResponseWriter, r *http.Request) { now := s.now() - //Grab the device token + //Grab the device token, check validity deviceToken, err := s.storage.GetDeviceToken(deviceCode) - if err != nil || now.After(deviceToken.Expiry) { + if err != nil { if err != storage.ErrNotFound { s.logger.Errorf("failed to get device code: %v", err) } - s.tokenErrHelper(w, errInvalidRequest, "Invalid or expired device code.", http.StatusBadRequest) + s.tokenErrHelper(w, errInvalidRequest, "Invalid Device code.", http.StatusBadRequest) + return + } else if now.After(deviceToken.Expiry) { + s.tokenErrHelper(w, deviceTokenExpired, "", http.StatusBadRequest) return } //Rate Limiting check + slowDown := false pollInterval := deviceToken.PollIntervalSeconds minRequestTime := deviceToken.LastRequestTime.Add(time.Second * time.Duration(pollInterval)) if now.Before(minRequestTime) { - s.tokenErrHelper(w, deviceTokenSlowDown, "", http.StatusBadRequest) + slowDown = true //Continually increase the poll interval until the user waits the proper time pollInterval += 5 } else { @@ -202,7 +210,11 @@ func (s *Server) handleDeviceToken(w http.ResponseWriter, r *http.Request) { s.renderError(r, w, http.StatusInternalServerError, "") return } - s.tokenErrHelper(w, deviceTokenPending, "", http.StatusUnauthorized) + if slowDown { + s.tokenErrHelper(w, deviceTokenSlowDown, "", http.StatusBadRequest) + } else { + s.tokenErrHelper(w, deviceTokenPending, "", http.StatusUnauthorized) + } case deviceTokenComplete: w.Write([]byte(deviceToken.Token)) } @@ -230,44 +242,58 @@ func (s *Server) handleDeviceCallback(w http.ResponseWriter, r *http.Request) { authCode, err := s.storage.GetAuthCode(code) if err != nil || s.now().After(authCode.Expiry) { - if err != storage.ErrNotFound { + errCode := http.StatusBadRequest + if err != nil && err != storage.ErrNotFound { s.logger.Errorf("failed to get auth code: %v", err) + errCode = http.StatusInternalServerError } - s.renderError(r, w, http.StatusBadRequest, "Invalid or expired auth code.") + s.renderError(r, w, errCode, "Invalid or expired auth code.") return } //Grab the device request from storage deviceReq, err := s.storage.GetDeviceRequest(userCode) if err != nil || s.now().After(deviceReq.Expiry) { - if err != storage.ErrNotFound { + errCode := http.StatusBadRequest + if err != nil && err != storage.ErrNotFound { s.logger.Errorf("failed to get device code: %v", err) + errCode = http.StatusInternalServerError } - s.renderError(r, w, http.StatusInternalServerError, "Invalid or expired device code.") + s.renderError(r, w, errCode, "Invalid or expired user code.") return } - reqClient, err := s.storage.GetClient(deviceReq.ClientID) + client, err := s.storage.GetClient(deviceReq.ClientID) if err != nil { - s.logger.Errorf("Failed to get reqClient %q: %v", deviceReq.ClientID, err) - s.renderError(r, w, http.StatusInternalServerError, "Failed to retrieve device client.") + if err != storage.ErrNotFound { + s.logger.Errorf("failed to get client: %v", err) + s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError) + } else { + s.tokenErrHelper(w, errInvalidClient, "Invalid client credentials.", http.StatusUnauthorized) + } + return + } + if client.Secret != deviceReq.ClientSecret { + s.tokenErrHelper(w, errInvalidClient, "Invalid client credentials.", http.StatusUnauthorized) return } - resp, err := s.exchangeAuthCode(w, authCode, reqClient) + resp, err := s.exchangeAuthCode(w, authCode, client) if err != nil { s.logger.Errorf("Could not exchange auth code for client %q: %v", deviceReq.ClientID, err) s.renderError(r, w, http.StatusInternalServerError, "Failed to exchange auth code.") return } - //Grab the device request from storage + //Grab the device token from storage old, err := s.storage.GetDeviceToken(deviceReq.DeviceCode) if err != nil || s.now().After(old.Expiry) { - if err != storage.ErrNotFound { + errCode := http.StatusBadRequest + if err != nil && err != storage.ErrNotFound { s.logger.Errorf("failed to get device token: %v", err) + errCode = http.StatusInternalServerError } - s.renderError(r, w, http.StatusInternalServerError, "Invalid or expired device code.") + s.renderError(r, w, errCode, "Invalid or expired device code.") return } @@ -290,12 +316,13 @@ func (s *Server) handleDeviceCallback(w http.ResponseWriter, r *http.Request) { // Update refresh token in the storage, store the token and mark as complete if err := s.storage.UpdateDeviceToken(deviceReq.DeviceCode, updater); err != nil { s.logger.Errorf("failed to update device token: %v", err) - s.renderError(r, w, http.StatusInternalServerError, "") + s.renderError(r, w, http.StatusBadRequest, "") return } - if err := s.templates.deviceSuccess(r, w, reqClient.Name); err != nil { + if err := s.templates.deviceSuccess(r, w, client.Name); err != nil { s.logger.Errorf("Server template error: %v", err) + s.renderError(r, w, http.StatusNotFound, "Page not found") } default: @@ -309,9 +336,8 @@ func (s *Server) verifyUserCode(w http.ResponseWriter, r *http.Request) { case http.MethodPost: err := r.ParseForm() if err != nil { - message := "Could not parse user code verification Request body" - s.logger.Warnf("%s : %v", message, err) - s.tokenErrHelper(w, errInvalidRequest, message, http.StatusBadRequest) + s.logger.Warnf("Could not parse user code verification request body : %v", err) + s.renderError(r, w, http.StatusBadRequest, "") return } @@ -326,12 +352,12 @@ func (s *Server) verifyUserCode(w http.ResponseWriter, r *http.Request) { //Find the user code in the available requests deviceRequest, err := s.storage.GetDeviceRequest(userCode) if err != nil || s.now().After(deviceRequest.Expiry) { - if err != storage.ErrNotFound { + if err != nil && err != storage.ErrNotFound { s.logger.Errorf("failed to get device request: %v", err) - s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError) } - if err := s.templates.device(r, w, s.getDeviceAuthURI(), userCode, true); err != nil { + if err := s.templates.device(r, w, s.getDeviceVerificationURI(), userCode, true); err != nil { s.logger.Errorf("Server template error: %v", err) + s.renderError(r, w, http.StatusNotFound, "Page not found") } return } @@ -345,6 +371,7 @@ func (s *Server) verifyUserCode(w http.ResponseWriter, r *http.Request) { } q := u.Query() q.Set("client_id", deviceRequest.ClientID) + q.Set("client_secret", deviceRequest.ClientSecret) q.Set("state", deviceRequest.UserCode) q.Set("response_type", "code") q.Set("redirect_uri", path.Join(s.issuerURL.Path, "/device/callback")) diff --git a/server/deviceflowhandlers_test.go b/server/deviceflowhandlers_test.go new file mode 100644 index 0000000000..353069710f --- /dev/null +++ b/server/deviceflowhandlers_test.go @@ -0,0 +1,672 @@ +package server + +import ( + "bytes" + "context" + "encoding/json" + "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" + "path" + "strings" + "testing" + "time" + + "github.com/dexidp/dex/storage" +) + +func TestDeviceVerificationURI(t *testing.T) { + t0 := time.Now() + + now := func() time.Time { return t0 } + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + // Setup a dex server. + httpServer, s := newTestServer(ctx, t, func(c *Config) { + c.Issuer = c.Issuer + "/non-root-path" + c.Now = now + }) + defer httpServer.Close() + + u, err := url.Parse(s.issuerURL.String()) + if err != nil { + t.Errorf("Could not parse issuer URL %v", err) + } + u.Path = path.Join(u.Path, "/device/auth/verify_code") + + uri := s.getDeviceVerificationURI() + if uri != u.Path { + t.Errorf("Invalid verification URI. Expected %v got %v", u.Path, uri) + } +} + +func TestHandleDeviceCode(t *testing.T) { + t0 := time.Now() + + now := func() time.Time { return t0 } + + tests := []struct { + testName string + clientID string + scopes []string + expectedResponseCode int + expectedServerResponse string + }{ + { + testName: "New Valid Code", + clientID: "test", + scopes: []string{"openid", "profile", "email"}, + expectedResponseCode: http.StatusOK, + }, + } + for _, tc := range tests { + func() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Setup a dex server. + httpServer, s := newTestServer(ctx, t, func(c *Config) { + c.Issuer = c.Issuer + "/non-root-path" + c.Now = now + }) + defer httpServer.Close() + + u, err := url.Parse(s.issuerURL.String()) + if err != nil { + t.Errorf("Could not parse issuer URL %v", err) + } + u.Path = path.Join(u.Path, "device/code") + + data := url.Values{} + data.Set("client_id", tc.clientID) + for _, scope := range tc.scopes { + data.Add("scope", scope) + } + req, _ := http.NewRequest("POST", u.String(), bytes.NewBufferString(data.Encode())) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") + + rr := httptest.NewRecorder() + s.ServeHTTP(rr, req) + if rr.Code != tc.expectedResponseCode { + t.Errorf("Unexpected Response Type. Expected %v got %v", tc.expectedResponseCode, rr.Code) + } + + body, err := ioutil.ReadAll(rr.Body) + if err != nil { + t.Errorf("Could read token response %v", err) + } + if tc.expectedResponseCode == http.StatusOK { + var resp deviceCodeResponse + if err := json.Unmarshal(body, &resp); err != nil { + t.Errorf("Unexpected Device Code Response Format %v", string(body)) + } + } + if tc.expectedResponseCode == http.StatusBadRequest || tc.expectedResponseCode == http.StatusUnauthorized { + expectErrorResponse(tc.testName, body, tc.expectedServerResponse, t) + } + }() + } +} + +func TestDeviceCallback(t *testing.T) { + t0 := time.Now() + + now := func() time.Time { return t0 } + + type formValues struct { + state string + code string + error string + } + + // Base "Control" test values + baseFormValues := formValues{ + state: "XXXX-XXXX", + code: "somecode", + } + baseAuthCode := storage.AuthCode{ + ID: "somecode", + ClientID: "testclient", + RedirectURI: "/device/callback", + Nonce: "", + Scopes: []string{"openid", "profile", "email"}, + ConnectorID: "mock", + ConnectorData: nil, + Claims: storage.Claims{}, + Expiry: now().Add(5 * time.Minute), + } + baseDeviceRequest := storage.DeviceRequest{ + UserCode: "XXXX-XXXX", + DeviceCode: "devicecode", + ClientID: "testclient", + ClientSecret: "", + Scopes: []string{"openid", "profile", "email"}, + Expiry: now().Add(5 * time.Minute), + } + baseDeviceToken := storage.DeviceToken{ + DeviceCode: "devicecode", + Status: deviceTokenPending, + Token: "", + Expiry: now().Add(5 * time.Minute), + LastRequestTime: time.Time{}, + PollIntervalSeconds: 0, + } + + tests := []struct { + testName string + expectedResponseCode int + values formValues + testAuthCode storage.AuthCode + testDeviceRequest storage.DeviceRequest + testDeviceToken storage.DeviceToken + }{ + { + testName: "Missing State", + values: formValues{ + state: "", + code: "somecode", + error: "", + }, + expectedResponseCode: http.StatusBadRequest, + }, + { + testName: "Missing Code", + values: formValues{ + state: "XXXX-XXXX", + code: "", + error: "", + }, + expectedResponseCode: http.StatusBadRequest, + }, + { + testName: "Error During Authorization", + values: formValues{ + state: "XXXX-XXXX", + code: "somecode", + error: "Error Condition", + }, + expectedResponseCode: http.StatusBadRequest, + }, + { + testName: "Expired Auth Code", + values: baseFormValues, + testAuthCode: storage.AuthCode{ + ID: "somecode", + ClientID: "testclient", + RedirectURI: "/device/callback", + Nonce: "", + Scopes: []string{"openid", "profile", "email"}, + ConnectorID: "pic", + ConnectorData: nil, + Claims: storage.Claims{}, + Expiry: now().Add(-5 * time.Minute), + }, + expectedResponseCode: http.StatusBadRequest, + }, + { + testName: "Invalid Auth Code", + values: baseFormValues, + testAuthCode: storage.AuthCode{ + ID: "somecode", + ClientID: "testclient", + RedirectURI: "/device/callback", + Nonce: "", + Scopes: []string{"openid", "profile", "email"}, + ConnectorID: "pic", + ConnectorData: nil, + Claims: storage.Claims{}, + Expiry: now().Add(5 * time.Minute), + }, + expectedResponseCode: http.StatusBadRequest, + }, + { + testName: "Expired Device Request", + values: baseFormValues, + testAuthCode: baseAuthCode, + testDeviceRequest: storage.DeviceRequest{ + UserCode: "XXXX-XXXX", + DeviceCode: "devicecode", + ClientID: "testclient", + Scopes: []string{"openid", "profile", "email"}, + Expiry: now().Add(-5 * time.Minute), + }, + expectedResponseCode: http.StatusBadRequest, + }, + { + testName: "Non-Existent User Code", + values: baseFormValues, + testAuthCode: baseAuthCode, + testDeviceRequest: storage.DeviceRequest{ + UserCode: "ZZZZ-ZZZZ", + DeviceCode: "devicecode", + Scopes: []string{"openid", "profile", "email"}, + Expiry: now().Add(5 * time.Minute), + }, + expectedResponseCode: http.StatusBadRequest, + }, + { + testName: "Bad Device Request Client", + values: baseFormValues, + testAuthCode: baseAuthCode, + testDeviceRequest: storage.DeviceRequest{ + UserCode: "XXXX-XXXX", + DeviceCode: "devicecode", + Scopes: []string{"openid", "profile", "email"}, + Expiry: now().Add(5 * time.Minute), + }, + expectedResponseCode: http.StatusUnauthorized, + }, + { + testName: "Bad Device Request Secret", + values: baseFormValues, + testAuthCode: baseAuthCode, + testDeviceRequest: storage.DeviceRequest{ + UserCode: "XXXX-XXXX", + DeviceCode: "devicecode", + ClientSecret: "foobar", + Scopes: []string{"openid", "profile", "email"}, + Expiry: now().Add(5 * time.Minute), + }, + expectedResponseCode: http.StatusUnauthorized, + }, + { + testName: "Expired Device Token", + values: baseFormValues, + testAuthCode: baseAuthCode, + testDeviceRequest: baseDeviceRequest, + testDeviceToken: storage.DeviceToken{ + DeviceCode: "devicecode", + Status: deviceTokenPending, + Token: "", + Expiry: now().Add(-5 * time.Minute), + LastRequestTime: time.Time{}, + PollIntervalSeconds: 0, + }, + expectedResponseCode: http.StatusBadRequest, + }, + { + testName: "Device Code Already Redeemed", + values: baseFormValues, + testAuthCode: baseAuthCode, + testDeviceRequest: baseDeviceRequest, + testDeviceToken: storage.DeviceToken{ + DeviceCode: "devicecode", + Status: deviceTokenComplete, + Token: "", + Expiry: now().Add(5 * time.Minute), + LastRequestTime: time.Time{}, + PollIntervalSeconds: 0, + }, + expectedResponseCode: http.StatusBadRequest, + }, + { + testName: "Successful Exchange", + values: baseFormValues, + testAuthCode: baseAuthCode, + testDeviceRequest: baseDeviceRequest, + testDeviceToken: baseDeviceToken, + expectedResponseCode: http.StatusOK, + }, + } + for _, tc := range tests { + func() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Setup a dex server. + httpServer, s := newTestServer(ctx, t, func(c *Config) { + //c.Issuer = c.Issuer + "/non-root-path" + c.Now = now + }) + defer httpServer.Close() + + if err := s.storage.CreateAuthCode(tc.testAuthCode); err != nil { + t.Errorf("failed to create auth code: %v", err) + } + + if err := s.storage.CreateDeviceRequest(tc.testDeviceRequest); err != nil { + t.Errorf("failed to create device request: %v", err) + } + + if err := s.storage.CreateDeviceToken(tc.testDeviceToken); err != nil { + t.Errorf("failed to create device token: %v", err) + } + + client := storage.Client{ + ID: "testclient", + Secret: "", + RedirectURIs: []string{"/device/callback"}, + } + if err := s.storage.CreateClient(client); err != nil { + t.Fatalf("failed to create client: %v", err) + } + + u, err := url.Parse(s.issuerURL.String()) + if err != nil { + t.Errorf("Could not parse issuer URL %v", err) + } + u.Path = path.Join(u.Path, "device/callback") + q := u.Query() + q.Set("state", tc.values.state) + q.Set("code", tc.values.code) + q.Set("error", tc.values.error) + u.RawQuery = q.Encode() + req, _ := http.NewRequest("GET", u.String(), nil) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") + + rr := httptest.NewRecorder() + s.ServeHTTP(rr, req) + if rr.Code != tc.expectedResponseCode { + t.Errorf("%s: Unexpected Response Type. Expected %v got %v", tc.testName, tc.expectedResponseCode, rr.Code) + } + }() + } +} + +func TestDeviceTokenResponse(t *testing.T) { + t0 := time.Now() + + now := func() time.Time { return t0 } + + baseDeviceRequest := storage.DeviceRequest{ + UserCode: "ABCD-WXYZ", + DeviceCode: "foo", + ClientID: "testclient", + Scopes: []string{"openid", "profile", "offline_access"}, + Expiry: now().Add(5 * time.Minute), + } + + tests := []struct { + testName string + testDeviceRequest storage.DeviceRequest + testDeviceToken storage.DeviceToken + testGrantType string + testDeviceCode string + expectedServerResponse string + expectedResponseCode int + }{ + { + testName: "Valid but pending token", + testDeviceRequest: baseDeviceRequest, + testDeviceToken: storage.DeviceToken{ + DeviceCode: "f00bar", + Status: deviceTokenPending, + Token: "", + Expiry: now().Add(5 * time.Minute), + LastRequestTime: time.Time{}, + PollIntervalSeconds: 0, + }, + testDeviceCode: "f00bar", + expectedServerResponse: deviceTokenPending, + expectedResponseCode: http.StatusUnauthorized, + }, + { + testName: "Invalid Grant Type", + testDeviceRequest: baseDeviceRequest, + testDeviceToken: storage.DeviceToken{ + DeviceCode: "f00bar", + Status: deviceTokenPending, + Token: "", + Expiry: now().Add(5 * time.Minute), + LastRequestTime: time.Time{}, + PollIntervalSeconds: 0, + }, + testDeviceCode: "f00bar", + testGrantType: grantTypeAuthorizationCode, + expectedServerResponse: errInvalidGrant, + expectedResponseCode: http.StatusBadRequest, + }, + { + testName: "Test Slow Down State", + testDeviceRequest: baseDeviceRequest, + testDeviceToken: storage.DeviceToken{ + DeviceCode: "f00bar", + Status: deviceTokenPending, + Token: "", + Expiry: now().Add(5 * time.Minute), + LastRequestTime: now(), + PollIntervalSeconds: 10, + }, + testDeviceCode: "f00bar", + expectedServerResponse: deviceTokenSlowDown, + expectedResponseCode: http.StatusBadRequest, + }, + { + testName: "Test Expired Device Token", + testDeviceRequest: baseDeviceRequest, + testDeviceToken: storage.DeviceToken{ + DeviceCode: "f00bar", + Status: deviceTokenPending, + Token: "", + Expiry: now().Add(-5 * time.Minute), + LastRequestTime: time.Time{}, + PollIntervalSeconds: 0, + }, + testDeviceCode: "f00bar", + expectedServerResponse: deviceTokenExpired, + expectedResponseCode: http.StatusBadRequest, + }, + { + testName: "Test Non-existent Device Code", + testDeviceRequest: baseDeviceRequest, + testDeviceToken: storage.DeviceToken{ + DeviceCode: "foo", + Status: deviceTokenPending, + Token: "", + Expiry: now().Add(-5 * time.Minute), + LastRequestTime: time.Time{}, + PollIntervalSeconds: 0, + }, + testDeviceCode: "bar", + expectedServerResponse: errInvalidRequest, + expectedResponseCode: http.StatusBadRequest, + }, + { + testName: "Empty Device Code in Request", + testDeviceRequest: baseDeviceRequest, + testDeviceToken: storage.DeviceToken{ + DeviceCode: "bar", + Status: deviceTokenPending, + Token: "", + Expiry: now().Add(-5 * time.Minute), + LastRequestTime: time.Time{}, + PollIntervalSeconds: 0, + }, + testDeviceCode: "", + expectedServerResponse: errInvalidRequest, + expectedResponseCode: http.StatusBadRequest, + }, + { + testName: "Claim validated token from Device Code", + testDeviceRequest: baseDeviceRequest, + testDeviceToken: storage.DeviceToken{ + DeviceCode: "foo", + Status: deviceTokenComplete, + Token: "{\"access_token\": \"foobar\"}", + Expiry: now().Add(5 * time.Minute), + LastRequestTime: time.Time{}, + PollIntervalSeconds: 0, + }, + testDeviceCode: "foo", + expectedServerResponse: "{\"access_token\": \"foobar\"}", + expectedResponseCode: http.StatusOK, + }, + } + for _, tc := range tests { + func() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Setup a dex server. + httpServer, s := newTestServer(ctx, t, func(c *Config) { + c.Issuer = c.Issuer + "/non-root-path" + c.Now = now + }) + defer httpServer.Close() + + if err := s.storage.CreateDeviceRequest(tc.testDeviceRequest); err != nil { + t.Errorf("Failed to store device token %v", err) + } + + if err := s.storage.CreateDeviceToken(tc.testDeviceToken); err != nil { + t.Errorf("Failed to store device token %v", err) + } + + u, err := url.Parse(s.issuerURL.String()) + if err != nil { + t.Errorf("Could not parse issuer URL %v", err) + } + u.Path = path.Join(u.Path, "device/token") + + data := url.Values{} + grantType := grantTypeDeviceCode + if tc.testGrantType != "" { + grantType = tc.testGrantType + } + data.Set("grant_type", grantType) + data.Set("device_code", tc.testDeviceCode) + req, _ := http.NewRequest("POST", u.String(), bytes.NewBufferString(data.Encode())) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") + + rr := httptest.NewRecorder() + s.ServeHTTP(rr, req) + if rr.Code != tc.expectedResponseCode { + t.Errorf("Unexpected Response Type. Expected %v got %v", tc.expectedResponseCode, rr.Code) + } + + body, err := ioutil.ReadAll(rr.Body) + if err != nil { + t.Errorf("Could read token response %v", err) + } + if tc.expectedResponseCode == http.StatusBadRequest || tc.expectedResponseCode == http.StatusUnauthorized { + expectErrorResponse(tc.testName, body, tc.expectedServerResponse, t) + } else if string(body) != tc.expectedServerResponse { + t.Errorf("Unexpected Server Response. Expected %v got %v", tc.expectedServerResponse, string(body)) + } + }() + } +} + +func expectErrorResponse(testCase string, body []byte, expectedError string, t *testing.T) { + jsonMap := make(map[string]interface{}) + err := json.Unmarshal(body, &jsonMap) + if err != nil { + t.Errorf("Unexpected error unmarshalling response: %v", err) + } + if jsonMap["error"] != expectedError { + t.Errorf("Test Case %s expected error %v, received %v", testCase, expectedError, jsonMap["error"]) + } +} + +func TestVerifyCodeResponse(t *testing.T) { + t0 := time.Now() + + now := func() time.Time { return t0 } + + tests := []struct { + testName string + testDeviceRequest storage.DeviceRequest + userCode string + expectedResponseCode int + expectedRedirectPath string + }{ + { + testName: "Unknown user code", + testDeviceRequest: storage.DeviceRequest{ + UserCode: "ABCD-WXYZ", + DeviceCode: "f00bar", + ClientID: "testclient", + Scopes: []string{"openid", "profile", "offline_access"}, + Expiry: now().Add(5 * time.Minute), + }, + userCode: "CODE-TEST", + expectedResponseCode: http.StatusBadRequest, + expectedRedirectPath: "", + }, + { + testName: "Expired user code", + testDeviceRequest: storage.DeviceRequest{ + UserCode: "ABCD-WXYZ", + DeviceCode: "f00bar", + ClientID: "testclient", + Scopes: []string{"openid", "profile", "offline_access"}, + Expiry: now().Add(-5 * time.Minute), + }, + userCode: "ABCD-WXYZ", + expectedResponseCode: http.StatusBadRequest, + expectedRedirectPath: "", + }, + { + testName: "No user code", + testDeviceRequest: storage.DeviceRequest{ + UserCode: "ABCD-WXYZ", + DeviceCode: "f00bar", + ClientID: "testclient", + Scopes: []string{"openid", "profile", "offline_access"}, + Expiry: now().Add(-5 * time.Minute), + }, + userCode: "", + expectedResponseCode: http.StatusBadRequest, + expectedRedirectPath: "", + }, + { + testName: "Valid user code, expect redirect to auth endpoint", + testDeviceRequest: storage.DeviceRequest{ + UserCode: "ABCD-WXYZ", + DeviceCode: "f00bar", + ClientID: "testclient", + Scopes: []string{"openid", "profile", "offline_access"}, + Expiry: now().Add(5 * time.Minute), + }, + userCode: "ABCD-WXYZ", + expectedResponseCode: http.StatusFound, + expectedRedirectPath: "/auth", + }, + } + for _, tc := range tests { + func() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Setup a dex server. + httpServer, s := newTestServer(ctx, t, func(c *Config) { + c.Issuer = c.Issuer + "/non-root-path" + c.Now = now + }) + defer httpServer.Close() + + if err := s.storage.CreateDeviceRequest(tc.testDeviceRequest); err != nil { + t.Errorf("Failed to store device token %v", err) + } + + u, err := url.Parse(s.issuerURL.String()) + if err != nil { + t.Errorf("Could not parse issuer URL %v", err) + } + + u.Path = path.Join(u.Path, "device/auth/verify_code") + data := url.Values{} + data.Set("user_code", tc.userCode) + req, _ := http.NewRequest("POST", u.String(), bytes.NewBufferString(data.Encode())) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") + + rr := httptest.NewRecorder() + s.ServeHTTP(rr, req) + if rr.Code != tc.expectedResponseCode { + t.Errorf("Unexpected Response Type. Expected %v got %v", tc.expectedResponseCode, rr.Code) + } + + u, err = url.Parse(s.issuerURL.String()) + if err != nil { + t.Errorf("Could not parse issuer URL %v", err) + } + u.Path = path.Join(u.Path, tc.expectedRedirectPath) + + location := rr.Header().Get("Location") + if rr.Code == http.StatusFound && !strings.HasPrefix(location, u.Path) { + t.Errorf("Invalid Redirect. Expected %v got %v", u.Path, location) + } + }() + } +} diff --git a/server/handlers.go b/server/handlers.go index 32a81b9868..babd54173f 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -15,11 +15,12 @@ import ( "time" oidc "github.com/coreos/go-oidc" + "github.com/gorilla/mux" + jose "gopkg.in/square/go-jose.v2" + "github.com/dexidp/dex/connector" "github.com/dexidp/dex/server/internal" "github.com/dexidp/dex/storage" - "github.com/gorilla/mux" - jose "gopkg.in/square/go-jose.v2" ) // newHealthChecker returns the healthz handler. The handler runs until the @@ -153,7 +154,7 @@ type discovery struct { Keys string `json:"jwks_uri"` UserInfo string `json:"userinfo_endpoint"` DeviceEndpoint string `json:"device_authorization_endpoint"` - GrantTypes []string `json:"grant_types_supported"'` + GrantTypes []string `json:"grant_types_supported"` ResponseTypes []string `json:"response_types_supported"` Subjects []string `json:"subject_types_supported"` IDTokenAlgs []string `json:"id_token_signing_alg_values_supported"` @@ -1381,18 +1382,10 @@ func (s *Server) handlePasswordGrant(w http.ResponseWriter, r *http.Request, cli } } - s.writeAccessToken(w, idToken, accessToken, refreshToken, expiry) + resp := s.toAccessTokenResponse(idToken, accessToken, refreshToken, expiry) + s.writeAccessToken(w, resp) } -func (s *Server) writeAccessToken(w http.ResponseWriter, idToken, accessToken, refreshToken string, expiry time.Time) { - resp := struct { - AccessToken string `json:"access_token"` - TokenType string `json:"token_type"` - ExpiresIn int `json:"expires_in"` - RefreshToken string `json:"refresh_token,omitempty"` - IDToken string `json:"id_token"` - }{ - type accessTokenReponse struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` diff --git a/server/server.go b/server/server.go index 90e963276d..661fc835db 100644 --- a/server/server.go +++ b/server/server.go @@ -81,7 +81,7 @@ type Config struct { DeviceRequestsValidFor time.Duration // Defaults to 5 minutes // If set, the server will use this connector to handle password grants PasswordConnector string - + GCFrequency time.Duration // Defaults to 5 minutes // If specified, the server will use this function for determining time. diff --git a/server/server_test.go b/server/server_test.go index 8fe84c9a5d..edc00832cd 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -8,11 +8,13 @@ import ( "encoding/pem" "errors" "fmt" + "io/ioutil" "net/http" "net/http/httptest" "net/http/httputil" "net/url" "os" + "path" "reflect" "sort" "strings" @@ -203,19 +205,21 @@ func TestDiscovery(t *testing.T) { } } -// TestOAuth2CodeFlow runs integration tests against a test server. The tests stand up a server -// which requires no interaction to login, logs in through a test client, then passes the client -// and returned token to the test. -func TestOAuth2CodeFlow(t *testing.T) { - clientID := "testclient" - clientSecret := "testclientsecret" - requestedScopes := []string{oidc.ScopeOpenID, "email", "profile", "groups", "offline_access"} +type oauth2Tests struct { + clientID string + tests []test +} - t0 := time.Now() +type test struct { + name string + // If specified these set of scopes will be used during the test case. + scopes []string + // handleToken provides the OAuth2 token response for the integration test. + handleToken func(context.Context, *oidc.Provider, *oauth2.Config, *oauth2.Token, *mock.Callback) error +} - // Always have the time function used by the server return the same time so - // we can predict expected values of "expires_in" fields exactly. - now := func() time.Time { return t0 } +func makeOAuth2Tests(clientID string, clientSecret string, now func() time.Time) oauth2Tests { + requestedScopes := []string{oidc.ScopeOpenID, "email", "profile", "groups", "offline_access"} // Used later when configuring test servers to set how long id_tokens will be valid for. // @@ -223,258 +227,277 @@ func TestOAuth2CodeFlow(t *testing.T) { // so tests can compute the expected "expires_in" field. idTokensValidFor := time.Second * 30 - // Connector used by the tests. - var conn *mock.Callback - oidcConfig := &oidc.Config{SkipClientIDCheck: true} - tests := []struct { - name string - // If specified these set of scopes will be used during the test case. - scopes []string - // handleToken provides the OAuth2 token response for the integration test. - handleToken func(context.Context, *oidc.Provider, *oauth2.Config, *oauth2.Token) error - }{ - { - name: "verify ID Token", - handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token) error { - idToken, ok := token.Extra("id_token").(string) - if !ok { - return fmt.Errorf("no id token found") - } - if _, err := p.Verifier(oidcConfig).Verify(ctx, idToken); err != nil { - return fmt.Errorf("failed to verify id token: %v", err) - } - return nil + return oauth2Tests{ + clientID: clientID, + tests: []test{ + { + name: "verify ID Token", + handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token, conn *mock.Callback) error { + idToken, ok := token.Extra("id_token").(string) + if !ok { + return fmt.Errorf("no id token found") + } + if _, err := p.Verifier(oidcConfig).Verify(ctx, idToken); err != nil { + return fmt.Errorf("failed to verify id token: %v", err) + } + return nil + }, }, - }, - { - name: "fetch userinfo", - handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token) error { - ui, err := p.UserInfo(ctx, config.TokenSource(ctx, token)) - if err != nil { - return fmt.Errorf("failed to fetch userinfo: %v", err) - } - if conn.Identity.Email != ui.Email { - return fmt.Errorf("expected email to be %v, got %v", conn.Identity.Email, ui.Email) - } - return nil + { + name: "fetch userinfo", + handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token, conn *mock.Callback) error { + ui, err := p.UserInfo(ctx, config.TokenSource(ctx, token)) + if err != nil { + return fmt.Errorf("failed to fetch userinfo: %v", err) + } + if conn.Identity.Email != ui.Email { + return fmt.Errorf("expected email to be %v, got %v", conn.Identity.Email, ui.Email) + } + return nil + }, }, - }, - { - name: "verify id token and oauth2 token expiry", - handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token) error { - expectedExpiry := now().Add(idTokensValidFor) + { + name: "verify id token and oauth2 token expiry", + handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token, conn *mock.Callback) error { + expectedExpiry := now().Add(idTokensValidFor) - timeEq := func(t1, t2 time.Time, within time.Duration) bool { - return t1.Sub(t2) < within - } + timeEq := func(t1, t2 time.Time, within time.Duration) bool { + return t1.Sub(t2) < within + } - if !timeEq(token.Expiry, expectedExpiry, time.Second) { - return fmt.Errorf("expected expired_in to be %s, got %s", expectedExpiry, token.Expiry) - } + if !timeEq(token.Expiry, expectedExpiry, time.Second) { + return fmt.Errorf("expected expired_in to be %s, got %s", expectedExpiry, token.Expiry) + } - rawIDToken, ok := token.Extra("id_token").(string) - if !ok { - return fmt.Errorf("no id token found") - } - idToken, err := p.Verifier(oidcConfig).Verify(ctx, rawIDToken) - if err != nil { - return fmt.Errorf("failed to verify id token: %v", err) - } - if !timeEq(idToken.Expiry, expectedExpiry, time.Second) { - return fmt.Errorf("expected id token expiry to be %s, got %s", expectedExpiry, token.Expiry) - } - return nil + rawIDToken, ok := token.Extra("id_token").(string) + if !ok { + return fmt.Errorf("no id token found") + } + idToken, err := p.Verifier(oidcConfig).Verify(ctx, rawIDToken) + if err != nil { + return fmt.Errorf("failed to verify id token: %v", err) + } + if !timeEq(idToken.Expiry, expectedExpiry, time.Second) { + return fmt.Errorf("expected id token expiry to be %s, got %s", expectedExpiry, token.Expiry) + } + return nil + }, }, - }, - { - name: "verify at_hash", - handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token) error { - rawIDToken, ok := token.Extra("id_token").(string) - if !ok { - return fmt.Errorf("no id token found") - } - idToken, err := p.Verifier(oidcConfig).Verify(ctx, rawIDToken) - if err != nil { - return fmt.Errorf("failed to verify id token: %v", err) - } + { + name: "verify at_hash", + handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token, conn *mock.Callback) error { + rawIDToken, ok := token.Extra("id_token").(string) + if !ok { + return fmt.Errorf("no id token found") + } + idToken, err := p.Verifier(oidcConfig).Verify(ctx, rawIDToken) + if err != nil { + return fmt.Errorf("failed to verify id token: %v", err) + } - var claims struct { - AtHash string `json:"at_hash"` - } - if err := idToken.Claims(&claims); err != nil { - return fmt.Errorf("failed to decode raw claims: %v", err) - } - if claims.AtHash == "" { - return errors.New("no at_hash value in id_token") - } - wantAtHash, err := accessTokenHash(jose.RS256, token.AccessToken) - if err != nil { - return fmt.Errorf("computed expected at hash: %v", err) - } - if wantAtHash != claims.AtHash { - return fmt.Errorf("expected at_hash=%q got=%q", wantAtHash, claims.AtHash) - } + var claims struct { + AtHash string `json:"at_hash"` + } + if err := idToken.Claims(&claims); err != nil { + return fmt.Errorf("failed to decode raw claims: %v", err) + } + if claims.AtHash == "" { + return errors.New("no at_hash value in id_token") + } + wantAtHash, err := accessTokenHash(jose.RS256, token.AccessToken) + if err != nil { + return fmt.Errorf("computed expected at hash: %v", err) + } + if wantAtHash != claims.AtHash { + return fmt.Errorf("expected at_hash=%q got=%q", wantAtHash, claims.AtHash) + } - return nil + return nil + }, }, - }, - { - name: "refresh token", - handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token) error { - // have to use time.Now because the OAuth2 package uses it. - token.Expiry = time.Now().Add(time.Second * -10) - if token.Valid() { - return errors.New("token shouldn't be valid") - } + { + name: "refresh token", + handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token, conn *mock.Callback) error { + // have to use time.Now because the OAuth2 package uses it. + token.Expiry = time.Now().Add(time.Second * -10) + if token.Valid() { + return errors.New("token shouldn't be valid") + } - newToken, err := config.TokenSource(ctx, token).Token() - if err != nil { - return fmt.Errorf("failed to refresh token: %v", err) - } - if token.RefreshToken == newToken.RefreshToken { - return fmt.Errorf("old refresh token was the same as the new token %q", token.RefreshToken) - } + newToken, err := config.TokenSource(ctx, token).Token() + if err != nil { + return fmt.Errorf("failed to refresh token: %v", err) + } + if token.RefreshToken == newToken.RefreshToken { + return fmt.Errorf("old refresh token was the same as the new token %q", token.RefreshToken) + } - if _, err := config.TokenSource(ctx, token).Token(); err == nil { - return errors.New("was able to redeem the same refresh token twice") - } - return nil + if _, err := config.TokenSource(ctx, token).Token(); err == nil { + return errors.New("was able to redeem the same refresh token twice") + } + return nil + }, }, - }, - { - name: "refresh with explicit scopes", - handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token) error { - v := url.Values{} - v.Add("client_id", clientID) - v.Add("client_secret", clientSecret) - v.Add("grant_type", "refresh_token") - v.Add("refresh_token", token.RefreshToken) - v.Add("scope", strings.Join(requestedScopes, " ")) - resp, err := http.PostForm(p.Endpoint().TokenURL, v) - if err != nil { - return err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - dump, err := httputil.DumpResponse(resp, true) + { + name: "refresh with explicit scopes", + handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token, conn *mock.Callback) error { + v := url.Values{} + v.Add("client_id", clientID) + v.Add("client_secret", clientSecret) + v.Add("grant_type", "refresh_token") + v.Add("refresh_token", token.RefreshToken) + v.Add("scope", strings.Join(requestedScopes, " ")) + resp, err := http.PostForm(p.Endpoint().TokenURL, v) if err != nil { - panic(err) + return err } - return fmt.Errorf("unexpected response: %s", dump) - } - return nil + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + dump, err := httputil.DumpResponse(resp, true) + if err != nil { + panic(err) + } + return fmt.Errorf("unexpected response: %s", dump) + } + return nil + }, }, - }, - { - name: "refresh with extra spaces", - handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token) error { - v := url.Values{} - v.Add("client_id", clientID) - v.Add("client_secret", clientSecret) - v.Add("grant_type", "refresh_token") - v.Add("refresh_token", token.RefreshToken) - - // go-oidc adds an additional space before scopes when refreshing. - // Since we support that client we choose to be more relaxed about - // scope parsing, disregarding extra whitespace. - v.Add("scope", " "+strings.Join(requestedScopes, " ")) - resp, err := http.PostForm(p.Endpoint().TokenURL, v) - if err != nil { - return err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - dump, err := httputil.DumpResponse(resp, true) + { + name: "refresh with extra spaces", + handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token, conn *mock.Callback) error { + v := url.Values{} + v.Add("client_id", clientID) + v.Add("client_secret", clientSecret) + v.Add("grant_type", "refresh_token") + v.Add("refresh_token", token.RefreshToken) + + // go-oidc adds an additional space before scopes when refreshing. + // Since we support that client we choose to be more relaxed about + // scope parsing, disregarding extra whitespace. + v.Add("scope", " "+strings.Join(requestedScopes, " ")) + resp, err := http.PostForm(p.Endpoint().TokenURL, v) if err != nil { - panic(err) + return err } - return fmt.Errorf("unexpected response: %s", dump) - } - return nil + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + dump, err := httputil.DumpResponse(resp, true) + if err != nil { + panic(err) + } + return fmt.Errorf("unexpected response: %s", dump) + } + return nil + }, }, - }, - { - name: "refresh with unauthorized scopes", - scopes: []string{"openid", "email"}, - handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token) error { - v := url.Values{} - v.Add("client_id", clientID) - v.Add("client_secret", clientSecret) - v.Add("grant_type", "refresh_token") - v.Add("refresh_token", token.RefreshToken) - // Request a scope that wasn't requestd initially. - v.Add("scope", "oidc email profile") - resp, err := http.PostForm(p.Endpoint().TokenURL, v) - if err != nil { - return err - } - defer resp.Body.Close() - if resp.StatusCode == http.StatusOK { - dump, err := httputil.DumpResponse(resp, true) + { + name: "refresh with unauthorized scopes", + scopes: []string{"openid", "email"}, + handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token, conn *mock.Callback) error { + v := url.Values{} + v.Add("client_id", clientID) + v.Add("client_secret", clientSecret) + v.Add("grant_type", "refresh_token") + v.Add("refresh_token", token.RefreshToken) + // Request a scope that wasn't requestd initially. + v.Add("scope", "oidc email profile") + resp, err := http.PostForm(p.Endpoint().TokenURL, v) if err != nil { - panic(err) + return err } - return fmt.Errorf("unexpected response: %s", dump) - } - return nil + defer resp.Body.Close() + if resp.StatusCode == http.StatusOK { + dump, err := httputil.DumpResponse(resp, true) + if err != nil { + panic(err) + } + return fmt.Errorf("unexpected response: %s", dump) + } + return nil + }, }, - }, - { - // This test ensures that the connector.RefreshConnector interface is being - // used when clients request a refresh token. - name: "refresh with identity changes", - handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token) error { - // have to use time.Now because the OAuth2 package uses it. - token.Expiry = time.Now().Add(time.Second * -10) - if token.Valid() { - return errors.New("token shouldn't be valid") - } + { + // This test ensures that the connector.RefreshConnector interface is being + // used when clients request a refresh token. + name: "refresh with identity changes", + handleToken: func(ctx context.Context, p *oidc.Provider, config *oauth2.Config, token *oauth2.Token, conn *mock.Callback) error { + // have to use time.Now because the OAuth2 package uses it. + token.Expiry = time.Now().Add(time.Second * -10) + if token.Valid() { + return errors.New("token shouldn't be valid") + } - ident := connector.Identity{ - UserID: "fooid", - Username: "foo", - Email: "foo@bar.com", - EmailVerified: true, - Groups: []string{"foo", "bar"}, - } - conn.Identity = ident + ident := connector.Identity{ + UserID: "fooid", + Username: "foo", + Email: "foo@bar.com", + EmailVerified: true, + Groups: []string{"foo", "bar"}, + } + conn.Identity = ident - type claims struct { - Username string `json:"name"` - Email string `json:"email"` - EmailVerified bool `json:"email_verified"` - Groups []string `json:"groups"` - } - want := claims{ident.Username, ident.Email, ident.EmailVerified, ident.Groups} + type claims struct { + Username string `json:"name"` + Email string `json:"email"` + EmailVerified bool `json:"email_verified"` + Groups []string `json:"groups"` + } + want := claims{ident.Username, ident.Email, ident.EmailVerified, ident.Groups} - newToken, err := config.TokenSource(ctx, token).Token() - if err != nil { - return fmt.Errorf("failed to refresh token: %v", err) - } - rawIDToken, ok := newToken.Extra("id_token").(string) - if !ok { - return fmt.Errorf("no id_token in refreshed token") - } - idToken, err := p.Verifier(oidcConfig).Verify(ctx, rawIDToken) - if err != nil { - return fmt.Errorf("failed to verify id token: %v", err) - } - var got claims - if err := idToken.Claims(&got); err != nil { - return fmt.Errorf("failed to unmarshal claims: %v", err) - } + newToken, err := config.TokenSource(ctx, token).Token() + if err != nil { + return fmt.Errorf("failed to refresh token: %v", err) + } + rawIDToken, ok := newToken.Extra("id_token").(string) + if !ok { + return fmt.Errorf("no id_token in refreshed token") + } + idToken, err := p.Verifier(oidcConfig).Verify(ctx, rawIDToken) + if err != nil { + return fmt.Errorf("failed to verify id token: %v", err) + } + var got claims + if err := idToken.Claims(&got); err != nil { + return fmt.Errorf("failed to unmarshal claims: %v", err) + } - if diff := pretty.Compare(want, got); diff != "" { - return fmt.Errorf("got identity != want identity: %s", diff) - } - return nil + if diff := pretty.Compare(want, got); diff != "" { + return fmt.Errorf("got identity != want identity: %s", diff) + } + return nil + }, }, }, } +} - for _, tc := range tests { +// TestOAuth2CodeFlow runs integration tests against a test server. The tests stand up a server +// which requires no interaction to login, logs in through a test client, then passes the client +// and returned token to the test. +func TestOAuth2CodeFlow(t *testing.T) { + clientID := "testclient" + clientSecret := "testclientsecret" + requestedScopes := []string{oidc.ScopeOpenID, "email", "profile", "groups", "offline_access"} + + t0 := time.Now() + + // Always have the time function used by the server return the same time so + // we can predict expected values of "expires_in" fields exactly. + now := func() time.Time { return t0 } + + // Used later when configuring test servers to set how long id_tokens will be valid for. + // + // The actual value of 30s is completely arbitrary. We just need to set a value + // so tests can compute the expected "expires_in" field. + idTokensValidFor := time.Second * 30 + + // Connector used by the tests. + var conn *mock.Callback + + tests := makeOAuth2Tests(clientID, clientSecret, now) + for _, tc := range tests.tests { func() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -540,7 +563,7 @@ func TestOAuth2CodeFlow(t *testing.T) { t.Errorf("failed to exchange code for token: %v", err) return } - err = tc.handleToken(ctx, p, oauth2Config, token) + err = tc.handleToken(ctx, p, oauth2Config, token, conn) if err != nil { t.Errorf("%s: %v", tc.name, err) } @@ -1253,3 +1276,160 @@ func TestRefreshTokenFlow(t *testing.T) { t.Errorf("Token refreshed with invalid refresh token, error expected.") } } + +// TestOAuth2DeviceFlow runs device flow integration tests against a test server +func TestOAuth2DeviceFlow(t *testing.T) { + clientID := "testclient" + clientSecret := "" + requestedScopes := []string{oidc.ScopeOpenID, "email", "profile", "groups", "offline_access"} + + t0 := time.Now() + + // Always have the time function used by the server return the same time so + // we can predict expected values of "expires_in" fields exactly. + now := func() time.Time { return t0 } + + // Connector used by the tests. + var conn *mock.Callback + idTokensValidFor := time.Second * 30 + + for _, tc := range makeOAuth2Tests(clientID, clientSecret, now).tests { + func() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Setup a dex server. + httpServer, s := newTestServer(ctx, t, func(c *Config) { + c.Issuer = c.Issuer + "/non-root-path" + c.Now = now + c.IDTokensValidFor = idTokensValidFor + }) + defer httpServer.Close() + + mockConn := s.connectors["mock"] + conn = mockConn.Connector.(*mock.Callback) + + p, err := oidc.NewProvider(ctx, httpServer.URL) + if err != nil { + t.Fatalf("failed to get provider: %v", err) + } + + //Add the Clients to the test server + client := storage.Client{ + ID: clientID, + //Secret: "testclientsecret", + RedirectURIs: []string{"/non-root-path/device/callback"}, + } + if err := s.storage.CreateClient(client); err != nil { + t.Fatalf("failed to create client: %v", err) + } + + //Grab the issuer that we'll reuse for the different endpoints to hit + issuer, err := url.Parse(s.issuerURL.String()) + if err != nil { + t.Errorf("Could not parse issuer URL %v", err) + } + + //Send a new Device Request + codeURL, _ := url.Parse(issuer.String()) + codeURL.Path = path.Join(codeURL.Path, "device/code") + + data := url.Values{} + data.Set("client_id", clientID) + data.Add("scope", strings.Join(requestedScopes, " ")) + //for _, scope := range requestedScopes { + // data.Add("scope", scope) + //} + resp, err := http.PostForm(codeURL.String(), data) + if err != nil { + t.Errorf("Could not request device code: %v", err) + } + defer resp.Body.Close() + responseBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Errorf("Could read device code response %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Errorf("%v - Unexpected Response Type. Expected 200 got %v. Response: %v", tc.name, resp.StatusCode, string(responseBody)) + } + + //Parse the code response + var deviceCode deviceCodeResponse + if err := json.Unmarshal(responseBody, &deviceCode); err != nil { + t.Errorf("Unexpected Device Code Response Format %v", string(responseBody)) + } + + //Mock the user hitting the verification URI and posting the form + verifyURL, _ := url.Parse(issuer.String()) + verifyURL.Path = path.Join(verifyURL.Path, "/device/auth/verify_code") + urlData := url.Values{} + urlData.Set("user_code", deviceCode.UserCode) + resp, err = http.PostForm(verifyURL.String(), urlData) + if err != nil { + t.Errorf("Error Posting Form: %v", err) + } + defer resp.Body.Close() + responseBody, err = ioutil.ReadAll(resp.Body) + if err != nil { + t.Errorf("Could read verification response %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Errorf("%v - Unexpected Response Type. Expected 200 got %v. Response: %v", tc.name, resp.StatusCode, string(responseBody)) + } + + //Hit the Token Endpoint, and try and get an access token + tokenURL, _ := url.Parse(issuer.String()) + tokenURL.Path = path.Join(tokenURL.Path, "/device/token") + v := url.Values{} + v.Add("grant_type", grantTypeDeviceCode) + v.Add("device_code", deviceCode.DeviceCode) + resp, err = http.PostForm(tokenURL.String(), v) + if err != nil { + t.Errorf("Could not request device token: %v", err) + } + defer resp.Body.Close() + responseBody, err = ioutil.ReadAll(resp.Body) + if err != nil { + t.Errorf("Could read device token response %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Errorf("%v - Unexpected Token Response Type. Expected 200 got %v. Response: %v", tc.name, resp.StatusCode, string(responseBody)) + } + + //Parse the response + var tokenRes accessTokenReponse + if err := json.Unmarshal(responseBody, &tokenRes); err != nil { + t.Errorf("Unexpected Device Access Token Response Format %v", string(responseBody)) + } + + token := &oauth2.Token{ + AccessToken: tokenRes.AccessToken, + TokenType: tokenRes.TokenType, + RefreshToken: tokenRes.RefreshToken, + } + raw := make(map[string]interface{}) + json.Unmarshal(responseBody, &raw) // no error checks for optional fields + token = token.WithExtra(raw) + if secs := tokenRes.ExpiresIn; secs > 0 { + token.Expiry = time.Now().Add(time.Duration(secs) * time.Second) + } + + //Run token tests to validate info is correct + // Create the OAuth2 config. + oauth2Config := &oauth2.Config{ + ClientID: client.ID, + ClientSecret: client.Secret, + Endpoint: p.Endpoint(), + Scopes: requestedScopes, + RedirectURL: "/non-root-path/device/callback", + } + if len(tc.scopes) != 0 { + oauth2Config.Scopes = tc.scopes + } + err = tc.handleToken(ctx, p, oauth2Config, token, conn) + if err != nil { + t.Errorf("%s: %v", tc.name, err) + } + }() + } +} diff --git a/server/templates.go b/server/templates.go index 934362b772..dd2678eab6 100644 --- a/server/templates.go +++ b/server/templates.go @@ -250,6 +250,9 @@ func (n byName) Less(i, j int) bool { return n[i].Name < n[j].Name } func (n byName) Swap(i, j int) { n[i], n[j] = n[j], n[i] } func (t *templates) device(r *http.Request, w http.ResponseWriter, postURL string, userCode string, lastWasInvalid bool) error { + if lastWasInvalid { + w.WriteHeader(http.StatusBadRequest) + } data := struct { PostURL string UserCode string diff --git a/storage/conformance/conformance.go b/storage/conformance/conformance.go index 6edc8350ab..224e1bfafd 100644 --- a/storage/conformance/conformance.go +++ b/storage/conformance/conformance.go @@ -843,11 +843,12 @@ func testGC(t *testing.T, s storage.Storage) { } d := storage.DeviceRequest{ - UserCode: userCode, - DeviceCode: storage.NewID(), - ClientID: "client1", - Scopes: []string{"openid", "email"}, - Expiry: expiry, + UserCode: userCode, + DeviceCode: storage.NewID(), + ClientID: "client1", + ClientSecret: "secret1", + Scopes: []string{"openid", "email"}, + Expiry: expiry, } if err := s.CreateDeviceRequest(d); err != nil { @@ -863,9 +864,9 @@ func testGC(t *testing.T, s storage.Storage) { t.Errorf("expected no device garbage collection results, got %#v", result) } } - //if _, err := s.GetDeviceRequest(d.UserCode); err != nil { - // t.Errorf("expected to be able to get auth request after GC: %v", err) - //} + if _, err := s.GetDeviceRequest(d.UserCode); err != nil { + t.Errorf("expected to be able to get auth request after GC: %v", err) + } } if r, err := s.GarbageCollect(expiry.Add(time.Hour)); err != nil { t.Errorf("garbage collection failed: %v", err) @@ -873,18 +874,19 @@ func testGC(t *testing.T, s storage.Storage) { t.Errorf("expected to garbage collect 1 device request, got %d", r.DeviceRequests) } - //TODO add this code back once Getters are written for device requests - //if _, err := s.GetDeviceRequest(d.UserCode); err == nil { - // t.Errorf("expected device request to be GC'd") - //} else if err != storage.ErrNotFound { - // t.Errorf("expected storage.ErrNotFound, got %v", err) - //} + if _, err := s.GetDeviceRequest(d.UserCode); err == nil { + t.Errorf("expected device request to be GC'd") + } else if err != storage.ErrNotFound { + t.Errorf("expected storage.ErrNotFound, got %v", err) + } dt := storage.DeviceToken{ - DeviceCode: storage.NewID(), - Status: "pending", - Token: "foo", - Expiry: expiry, + DeviceCode: storage.NewID(), + Status: "pending", + Token: "foo", + Expiry: expiry, + LastRequestTime: time.Now(), + PollIntervalSeconds: 0, } if err := s.CreateDeviceToken(dt); err != nil { @@ -969,11 +971,12 @@ func testDeviceRequestCRUD(t *testing.T, s storage.Storage) { panic(err) } d1 := storage.DeviceRequest{ - UserCode: userCode, - DeviceCode: storage.NewID(), - ClientID: "client1", - Scopes: []string{"openid", "email"}, - Expiry: neverExpire, + UserCode: userCode, + DeviceCode: storage.NewID(), + ClientID: "client1", + ClientSecret: "secret1", + Scopes: []string{"openid", "email"}, + Expiry: neverExpire, } if err := s.CreateDeviceRequest(d1); err != nil { diff --git a/storage/etcd/etcd.go b/storage/etcd/etcd.go index f41831cd41..e8abe3d08f 100644 --- a/storage/etcd/etcd.go +++ b/storage/etcd/etcd.go @@ -595,7 +595,7 @@ func (c *conn) listDeviceRequests(ctx context.Context) (requests []DeviceRequest func (c *conn) CreateDeviceToken(t storage.DeviceToken) error { ctx, cancel := context.WithTimeout(context.Background(), defaultStorageTimeout) defer cancel() - return c.txnCreate(ctx, keyID(deviceRequestPrefix, t.DeviceCode), fromStorageDeviceToken(t)) + return c.txnCreate(ctx, keyID(deviceTokenPrefix, t.DeviceCode), fromStorageDeviceToken(t)) } func (c *conn) GetDeviceToken(deviceCode string) (t storage.DeviceToken, err error) { diff --git a/storage/etcd/etcd_test.go b/storage/etcd/etcd_test.go index 4c17fdf155..122d7daeac 100644 --- a/storage/etcd/etcd_test.go +++ b/storage/etcd/etcd_test.go @@ -44,6 +44,8 @@ func cleanDB(c *conn) error { passwordPrefix, offlineSessionPrefix, connectorPrefix, + deviceRequestPrefix, + deviceTokenPrefix, } { _, err := c.db.Delete(ctx, prefix, clientv3.WithPrefix()) if err != nil { diff --git a/storage/etcd/types.go b/storage/etcd/types.go index cc8045c90e..def95b5566 100644 --- a/storage/etcd/types.go +++ b/storage/etcd/types.go @@ -219,20 +219,22 @@ func toStorageOfflineSessions(o OfflineSessions) storage.OfflineSessions { // DeviceRequest is a mirrored struct from storage with JSON struct tags type DeviceRequest struct { - UserCode string `json:"user_code"` - DeviceCode string `json:"device_code"` - ClientID string `json:"client_id"` - Scopes []string `json:"scopes"` - Expiry time.Time `json:"expiry"` + UserCode string `json:"user_code"` + DeviceCode string `json:"device_code"` + ClientID string `json:"client_id"` + ClientSecret string `json:"client_secret"` + Scopes []string `json:"scopes"` + Expiry time.Time `json:"expiry"` } func fromStorageDeviceRequest(d storage.DeviceRequest) DeviceRequest { return DeviceRequest{ - UserCode: d.UserCode, - DeviceCode: d.DeviceCode, - ClientID: d.ClientID, - Scopes: d.Scopes, - Expiry: d.Expiry, + UserCode: d.UserCode, + DeviceCode: d.DeviceCode, + ClientID: d.ClientID, + ClientSecret: d.ClientSecret, + Scopes: d.Scopes, + Expiry: d.Expiry, } } diff --git a/storage/kubernetes/types.go b/storage/kubernetes/types.go index 61794ccf24..f856a731c3 100644 --- a/storage/kubernetes/types.go +++ b/storage/kubernetes/types.go @@ -672,10 +672,11 @@ type DeviceRequest struct { k8sapi.TypeMeta `json:",inline"` k8sapi.ObjectMeta `json:"metadata,omitempty"` - DeviceCode string `json:"device_code,omitempty"` - CLientID string `json:"client_id,omitempty"` - Scopes []string `json:"scopes,omitempty"` - Expiry time.Time `json:"expiry"` + DeviceCode string `json:"device_code,omitempty"` + ClientID string `json:"client_id,omitempty"` + ClientSecret string `json:"client_secret,omitempty"` + Scopes []string `json:"scopes,omitempty"` + Expiry time.Time `json:"expiry"` } // AuthRequestList is a list of AuthRequests. @@ -695,21 +696,23 @@ func (cli *client) fromStorageDeviceRequest(a storage.DeviceRequest) DeviceReque Name: strings.ToLower(a.UserCode), Namespace: cli.namespace, }, - DeviceCode: a.DeviceCode, - CLientID: a.ClientID, - Scopes: a.Scopes, - Expiry: a.Expiry, + DeviceCode: a.DeviceCode, + ClientID: a.ClientID, + ClientSecret: a.ClientSecret, + Scopes: a.Scopes, + Expiry: a.Expiry, } return req } func toStorageDeviceRequest(req DeviceRequest) storage.DeviceRequest { return storage.DeviceRequest{ - UserCode: strings.ToUpper(req.ObjectMeta.Name), - DeviceCode: req.DeviceCode, - ClientID: req.CLientID, - Scopes: req.Scopes, - Expiry: req.Expiry, + UserCode: strings.ToUpper(req.ObjectMeta.Name), + DeviceCode: req.DeviceCode, + ClientID: req.ClientID, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + Expiry: req.Expiry, } } diff --git a/storage/sql/crud.go b/storage/sql/crud.go index a85c972b60..b74b76e10d 100644 --- a/storage/sql/crud.go +++ b/storage/sql/crud.go @@ -888,12 +888,12 @@ func (c *conn) delete(table, field, id string) error { func (c *conn) CreateDeviceRequest(d storage.DeviceRequest) error { _, err := c.Exec(` insert into device_request ( - user_code, device_code, client_id, scopes, expiry + user_code, device_code, client_id, client_secret, scopes, expiry ) values ( - $1, $2, $3, $4, $5 + $1, $2, $3, $4, $5, $6 );`, - d.UserCode, d.DeviceCode, d.ClientID, encoder(d.Scopes), d.Expiry, + d.UserCode, d.DeviceCode, d.ClientID, d.ClientSecret, encoder(d.Scopes), d.Expiry, ) if err != nil { if c.alreadyExistsCheck(err) { @@ -930,10 +930,10 @@ func (c *conn) GetDeviceRequest(userCode string) (storage.DeviceRequest, error) func getDeviceRequest(q querier, userCode string) (d storage.DeviceRequest, err error) { err = q.QueryRow(` select - device_code, client_id, scopes, expiry + device_code, client_id, client_secret, scopes, expiry from device_request where user_code = $1; `, userCode).Scan( - &d.DeviceCode, &d.ClientID, decoder(&d.Scopes), &d.Expiry, + &d.DeviceCode, &d.ClientID, &d.ClientSecret, decoder(&d.Scopes), &d.Expiry, ) if err != nil { if err == sql.ErrNoRows { diff --git a/storage/sql/migrate.go b/storage/sql/migrate.go index e399d2b8e6..432305814f 100644 --- a/storage/sql/migrate.go +++ b/storage/sql/migrate.go @@ -235,6 +235,7 @@ var migrations = []migration{ user_code text not null primary key, device_code text not null, client_id text not null, + client_secret text , scopes bytea not null, -- JSON array of strings expiry timestamptz not null );`, diff --git a/storage/storage.go b/storage/storage.go index 005e9190a3..2134678e01 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -392,6 +392,8 @@ type DeviceRequest struct { DeviceCode string //The client ID the code is for ClientID string + //The Client Secret + ClientSecret string //The scopes the device requests Scopes []string //The expire time From f6d8427f32198ec06bf28db90e0d9779988d29b0 Mon Sep 17 00:00:00 2001 From: justin-slowik Date: Tue, 12 May 2020 09:29:15 -0400 Subject: [PATCH 09/15] Added device flow static client to config-dev.yaml Signed-off-by: justin-slowik --- examples/config-dev.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/config-dev.yaml b/examples/config-dev.yaml index 9cd81fa62e..27e375890a 100644 --- a/examples/config-dev.yaml +++ b/examples/config-dev.yaml @@ -96,7 +96,10 @@ staticClients: - 'http://127.0.0.1:5555/callback' name: 'Example App' secret: ZXhhbXBsZS1hcHAtc2VjcmV0 - +# - id: example-device-client +# redirectURIs: +# - /dex/device/callback +# name: 'Static Client for Device Flow' connectors: - type: mockCallback id: mock From 9882ea453f2537468b3f88cca2138e7a2de19369 Mon Sep 17 00:00:00 2001 From: justin-slowik Date: Wed, 13 May 2020 15:38:43 -0400 Subject: [PATCH 10/15] better support for /device/callback redirect uris with public clients. Signed-off-by: justin-slowik --- examples/config-dev.yaml | 3 ++- server/deviceflowhandlers.go | 2 +- server/deviceflowhandlers_test.go | 8 ++++---- server/oauth2.go | 9 ++++++++- server/server.go | 2 +- server/server_test.go | 6 +++--- 6 files changed, 19 insertions(+), 11 deletions(-) diff --git a/examples/config-dev.yaml b/examples/config-dev.yaml index 27e375890a..75126692bc 100644 --- a/examples/config-dev.yaml +++ b/examples/config-dev.yaml @@ -98,8 +98,9 @@ staticClients: secret: ZXhhbXBsZS1hcHAtc2VjcmV0 # - id: example-device-client # redirectURIs: -# - /dex/device/callback +# - /device/callback # name: 'Static Client for Device Flow' +# public: true connectors: - type: mockCallback id: mock diff --git a/server/deviceflowhandlers.go b/server/deviceflowhandlers.go index 39ead50376..d7bb11c59c 100644 --- a/server/deviceflowhandlers.go +++ b/server/deviceflowhandlers.go @@ -374,7 +374,7 @@ func (s *Server) verifyUserCode(w http.ResponseWriter, r *http.Request) { q.Set("client_secret", deviceRequest.ClientSecret) q.Set("state", deviceRequest.UserCode) q.Set("response_type", "code") - q.Set("redirect_uri", path.Join(s.issuerURL.Path, "/device/callback")) + q.Set("redirect_uri", "/device/callback") q.Set("scope", strings.Join(deviceRequest.Scopes, " ")) u.RawQuery = q.Encode() diff --git a/server/deviceflowhandlers_test.go b/server/deviceflowhandlers_test.go index 353069710f..6d1ed7e37d 100644 --- a/server/deviceflowhandlers_test.go +++ b/server/deviceflowhandlers_test.go @@ -128,7 +128,7 @@ func TestDeviceCallback(t *testing.T) { baseAuthCode := storage.AuthCode{ ID: "somecode", ClientID: "testclient", - RedirectURI: "/device/callback", + RedirectURI: deviceCallbackURI, Nonce: "", Scopes: []string{"openid", "profile", "email"}, ConnectorID: "mock", @@ -194,7 +194,7 @@ func TestDeviceCallback(t *testing.T) { testAuthCode: storage.AuthCode{ ID: "somecode", ClientID: "testclient", - RedirectURI: "/device/callback", + RedirectURI: deviceCallbackURI, Nonce: "", Scopes: []string{"openid", "profile", "email"}, ConnectorID: "pic", @@ -210,7 +210,7 @@ func TestDeviceCallback(t *testing.T) { testAuthCode: storage.AuthCode{ ID: "somecode", ClientID: "testclient", - RedirectURI: "/device/callback", + RedirectURI: deviceCallbackURI, Nonce: "", Scopes: []string{"openid", "profile", "email"}, ConnectorID: "pic", @@ -336,7 +336,7 @@ func TestDeviceCallback(t *testing.T) { client := storage.Client{ ID: "testclient", Secret: "", - RedirectURIs: []string{"/device/callback"}, + RedirectURIs: []string{deviceCallbackURI}, } if err := s.storage.CreateClient(client); err != nil { t.Fatalf("failed to create client: %v", err) diff --git a/server/oauth2.go b/server/oauth2.go index 59e132bc45..09402c9117 100644 --- a/server/oauth2.go +++ b/server/oauth2.go @@ -114,6 +114,10 @@ const ( scopeCrossClientPrefix = "audience:server:client_id:" ) +const ( + deviceCallbackURI = "/device/callback" +) + const ( redirectURIOOB = "urn:ietf:wg:oauth:2.0:oob" ) @@ -433,6 +437,9 @@ func (s *Server) parseAuthorizationRequest(r *http.Request) (*storage.AuthReques description := fmt.Sprintf("Unregistered redirect_uri (%q).", redirectURI) return nil, &authErr{"", "", errInvalidRequest, description} } + if redirectURI == deviceCallbackURI && client.Public { + redirectURI = s.issuerURL.Path + deviceCallbackURI + } // From here on out, we want to redirect back to the client with an error. newErr := func(typ, format string, a ...interface{}) *authErr { @@ -574,7 +581,7 @@ func validateRedirectURI(client storage.Client, redirectURI string) bool { return false } - if redirectURI == redirectURIOOB { + if redirectURI == redirectURIOOB || redirectURI == deviceCallbackURI{ return true } diff --git a/server/server.go b/server/server.go index 661fc835db..f4d139d151 100644 --- a/server/server.go +++ b/server/server.go @@ -309,7 +309,7 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy) handleFunc("/device/auth/verify_code", s.verifyUserCode) handleFunc("/device/code", s.handleDeviceCode) handleFunc("/device/token", s.handleDeviceToken) - handleFunc("/device/callback", s.handleDeviceCallback) + handleFunc(deviceCallbackURI, s.handleDeviceCallback) r.HandleFunc(path.Join(issuerURL.Path, "/callback"), func(w http.ResponseWriter, r *http.Request) { // Strip the X-Remote-* headers to prevent security issues on // misconfigured authproxy connector setups. diff --git a/server/server_test.go b/server/server_test.go index edc00832cd..7f0dc4dc42 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -1317,8 +1317,8 @@ func TestOAuth2DeviceFlow(t *testing.T) { //Add the Clients to the test server client := storage.Client{ ID: clientID, - //Secret: "testclientsecret", - RedirectURIs: []string{"/non-root-path/device/callback"}, + RedirectURIs: []string{deviceCallbackURI}, + Public: true, } if err := s.storage.CreateClient(client); err != nil { t.Fatalf("failed to create client: %v", err) @@ -1421,7 +1421,7 @@ func TestOAuth2DeviceFlow(t *testing.T) { ClientSecret: client.Secret, Endpoint: p.Endpoint(), Scopes: requestedScopes, - RedirectURL: "/non-root-path/device/callback", + RedirectURL: deviceCallbackURI, } if len(tc.scopes) != 0 { oauth2Config.Scopes = tc.scopes From f91f2943856637eab6359b7035a5de38ef093ca9 Mon Sep 17 00:00:00 2001 From: justin-slowik Date: Thu, 14 May 2020 09:34:18 -0400 Subject: [PATCH 11/15] gofmt Signed-off-by: justin-slowik --- server/oauth2.go | 2 +- server/server_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/server/oauth2.go b/server/oauth2.go index 09402c9117..2596fd4e00 100644 --- a/server/oauth2.go +++ b/server/oauth2.go @@ -581,7 +581,7 @@ func validateRedirectURI(client storage.Client, redirectURI string) bool { return false } - if redirectURI == redirectURIOOB || redirectURI == deviceCallbackURI{ + if redirectURI == redirectURIOOB || redirectURI == deviceCallbackURI { return true } diff --git a/server/server_test.go b/server/server_test.go index 7f0dc4dc42..8b0447fe8a 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -1316,9 +1316,9 @@ func TestOAuth2DeviceFlow(t *testing.T) { //Add the Clients to the test server client := storage.Client{ - ID: clientID, + ID: clientID, RedirectURIs: []string{deviceCallbackURI}, - Public: true, + Public: true, } if err := s.storage.CreateClient(client); err != nil { t.Fatalf("failed to create client: %v", err) From 140447732636fdc6dcab304439233218b0cdcde2 Mon Sep 17 00:00:00 2001 From: justin-slowik Date: Tue, 2 Jun 2020 14:39:30 -0400 Subject: [PATCH 12/15] Updates based on dexidp pr Signed-off-by: justin-slowik --- server/templates.go | 1 + storage/conformance/conformance.go | 4 ++-- storage/sql/migrate.go | 2 +- storage/storage.go | 5 +++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/server/templates.go b/server/templates.go index dd2678eab6..6681b85177 100644 --- a/server/templates.go +++ b/server/templates.go @@ -31,6 +31,7 @@ var requiredTmpls = []string{ tmplOOB, tmplError, tmplDevice, + tmplDeviceSuccess, } type templates struct { diff --git a/storage/conformance/conformance.go b/storage/conformance/conformance.go index 224e1bfafd..0b73ce1556 100644 --- a/storage/conformance/conformance.go +++ b/storage/conformance/conformance.go @@ -1027,9 +1027,9 @@ func testDeviceTokenCRUD(t *testing.T, s storage.Storage) { //Validate expected result set if got.Status != "complete" { - t.Fatalf("update failed, wanted token status=%#v got %#v", "complete", got.Status) + t.Fatalf("update failed, wanted token status=%v got %v", "complete", got.Status) } if got.Token != "token data" { - t.Fatalf("update failed, wanted token =%#v got %#v", "token data", got.Token) + t.Fatalf("update failed, wanted token =%v got %v", "token data", got.Token) } } diff --git a/storage/sql/migrate.go b/storage/sql/migrate.go index 432305814f..73934b1b5c 100644 --- a/storage/sql/migrate.go +++ b/storage/sql/migrate.go @@ -243,7 +243,7 @@ var migrations = []migration{ create table device_token ( device_code text not null primary key, status text not null, - token text, + token bytea, expiry timestamptz not null, last_request timestamptz not null, poll_interval integer not null diff --git a/storage/storage.go b/storage/storage.go index 2134678e01..d11305e282 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -39,7 +39,7 @@ func NewID() string { } func newSecureID(len int) string { - buff := make([]byte, len) // 128 bit random ID. + buff := make([]byte, len) // random ID. if _, err := io.ReadFull(rand.Reader, buff); err != nil { panic(err) } @@ -122,7 +122,8 @@ type Storage interface { UpdateConnector(id string, updater func(c Connector) (Connector, error)) error UpdateDeviceToken(deviceCode string, updater func(t DeviceToken) (DeviceToken, error)) error - // GarbageCollect deletes all expired AuthCodes,AuthRequests, DeviceRequests, and DeviceTokens. + // GarbageCollect deletes all expired AuthCodes, + // AuthRequests, DeviceRequests, and DeviceTokens. GarbageCollect(now time.Time) (GCResult, error) } From 1ea2892b795eb387de3654b95ea2db25dc758f12 Mon Sep 17 00:00:00 2001 From: justin-slowik Date: Wed, 8 Jul 2020 16:30:08 -0400 Subject: [PATCH 13/15] fix merge error in config.go Signed-off-by: justin-slowik --- cmd/dex/config.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cmd/dex/config.go b/cmd/dex/config.go index cc8a727308..255e1d95d7 100644 --- a/cmd/dex/config.go +++ b/cmd/dex/config.go @@ -103,11 +103,7 @@ func (p *password) UnmarshalJSON(b []byte) error { data.Hash = os.Getenv(data.HashFromEnv) } if len(data.Hash) == 0 { - if len(data.HashFromEnv) > 0 { - data.Hash = os.Getenv(data.HashFromEnv) - } else { - return fmt.Errorf("no password hash provided") - } + return fmt.Errorf("no password hash provided") } // If this value is a valid bcrypt, use it. From 334ecf0482cd48f52ed94ed8b2a57bbe6651c537 Mon Sep 17 00:00:00 2001 From: justin-slowik Date: Wed, 8 Jul 2020 16:36:52 -0400 Subject: [PATCH 14/15] Fixes based on PR comments. Signed-off-by: justin-slowik --- server/deviceflowhandlers_test.go | 16 ++++++++-------- server/server_test.go | 3 --- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/server/deviceflowhandlers_test.go b/server/deviceflowhandlers_test.go index 6d1ed7e37d..f82b15db8f 100644 --- a/server/deviceflowhandlers_test.go +++ b/server/deviceflowhandlers_test.go @@ -61,7 +61,7 @@ func TestHandleDeviceCode(t *testing.T) { }, } for _, tc := range tests { - func() { + t.Run(tc.testName, func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -105,7 +105,7 @@ func TestHandleDeviceCode(t *testing.T) { if tc.expectedResponseCode == http.StatusBadRequest || tc.expectedResponseCode == http.StatusUnauthorized { expectErrorResponse(tc.testName, body, tc.expectedServerResponse, t) } - }() + }) } } @@ -310,7 +310,7 @@ func TestDeviceCallback(t *testing.T) { }, } for _, tc := range tests { - func() { + t.Run(tc.testName, func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -360,7 +360,7 @@ func TestDeviceCallback(t *testing.T) { if rr.Code != tc.expectedResponseCode { t.Errorf("%s: Unexpected Response Type. Expected %v got %v", tc.testName, tc.expectedResponseCode, rr.Code) } - }() + }) } } @@ -494,7 +494,7 @@ func TestDeviceTokenResponse(t *testing.T) { }, } for _, tc := range tests { - func() { + t.Run(tc.testName, func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -544,7 +544,7 @@ func TestDeviceTokenResponse(t *testing.T) { } else if string(body) != tc.expectedServerResponse { t.Errorf("Unexpected Server Response. Expected %v got %v", tc.expectedServerResponse, string(body)) } - }() + }) } } @@ -625,7 +625,7 @@ func TestVerifyCodeResponse(t *testing.T) { }, } for _, tc := range tests { - func() { + t.Run(tc.testName, func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -667,6 +667,6 @@ func TestVerifyCodeResponse(t *testing.T) { if rr.Code == http.StatusFound && !strings.HasPrefix(location, u.Path) { t.Errorf("Invalid Redirect. Expected %v got %v", u.Path, location) } - }() + }) } } diff --git a/server/server_test.go b/server/server_test.go index 8b0447fe8a..114d1fc86f 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -1337,9 +1337,6 @@ func TestOAuth2DeviceFlow(t *testing.T) { data := url.Values{} data.Set("client_id", clientID) data.Add("scope", strings.Join(requestedScopes, " ")) - //for _, scope := range requestedScopes { - // data.Add("scope", scope) - //} resp, err := http.PostForm(codeURL.String(), data) if err != nil { t.Errorf("Could not request device code: %v", err) From 9a7926c19b9d32e70356ce97b8b145508b33879f Mon Sep 17 00:00:00 2001 From: justin-slowik Date: Tue, 14 Jul 2020 10:14:37 -0400 Subject: [PATCH 15/15] Cleaned up Device Flow test log levels Signed-off-by: justin-slowik Remove extraneous "=" from conformance.go Co-authored-by: Joel Speed Additional test for TestHandleDeviceCode Signed-off-by: justin-slowik --- server/deviceflowhandlers.go | 4 +++ server/deviceflowhandlers_test.go | 42 +++++++++++++++++------------- storage/conformance/conformance.go | 2 +- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/server/deviceflowhandlers.go b/server/deviceflowhandlers.go index d7bb11c59c..4a8b382d30 100644 --- a/server/deviceflowhandlers.go +++ b/server/deviceflowhandlers.go @@ -36,6 +36,10 @@ func (s *Server) getDeviceVerificationURI() string { func (s *Server) handleDeviceExchange(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: + // Grab the parameter(s) from the query. + // If "user_code" is set, pre-populate the user code text field. + // If "invalid" is set, set the invalidAttempt boolean, which will display a message to the user that they + // attempted to redeem an invalid or expired user code. userCode := r.URL.Query().Get("user_code") invalidAttempt, err := strconv.ParseBool(r.URL.Query().Get("invalid")) if err != nil { diff --git a/server/deviceflowhandlers_test.go b/server/deviceflowhandlers_test.go index f82b15db8f..5ab3ddb635 100644 --- a/server/deviceflowhandlers_test.go +++ b/server/deviceflowhandlers_test.go @@ -31,7 +31,7 @@ func TestDeviceVerificationURI(t *testing.T) { u, err := url.Parse(s.issuerURL.String()) if err != nil { - t.Errorf("Could not parse issuer URL %v", err) + t.Fatalf("Could not parse issuer URL %v", err) } u.Path = path.Join(u.Path, "/device/auth/verify_code") @@ -49,16 +49,25 @@ func TestHandleDeviceCode(t *testing.T) { tests := []struct { testName string clientID string + requestType string scopes []string expectedResponseCode int expectedServerResponse string }{ { - testName: "New Valid Code", + testName: "New Code", clientID: "test", + requestType: "POST", scopes: []string{"openid", "profile", "email"}, expectedResponseCode: http.StatusOK, }, + { + testName: "Invalid request Type (GET)", + clientID: "test", + requestType: "GET", + scopes: []string{"openid", "profile", "email"}, + expectedResponseCode: http.StatusBadRequest, + }, } for _, tc := range tests { t.Run(tc.testName, func(t *testing.T) { @@ -74,7 +83,7 @@ func TestHandleDeviceCode(t *testing.T) { u, err := url.Parse(s.issuerURL.String()) if err != nil { - t.Errorf("Could not parse issuer URL %v", err) + t.Fatalf("Could not parse issuer URL %v", err) } u.Path = path.Join(u.Path, "device/code") @@ -83,7 +92,7 @@ func TestHandleDeviceCode(t *testing.T) { for _, scope := range tc.scopes { data.Add("scope", scope) } - req, _ := http.NewRequest("POST", u.String(), bytes.NewBufferString(data.Encode())) + req, _ := http.NewRequest(tc.requestType, u.String(), bytes.NewBufferString(data.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") rr := httptest.NewRecorder() @@ -102,9 +111,6 @@ func TestHandleDeviceCode(t *testing.T) { t.Errorf("Unexpected Device Code Response Format %v", string(body)) } } - if tc.expectedResponseCode == http.StatusBadRequest || tc.expectedResponseCode == http.StatusUnauthorized { - expectErrorResponse(tc.testName, body, tc.expectedServerResponse, t) - } }) } } @@ -322,15 +328,15 @@ func TestDeviceCallback(t *testing.T) { defer httpServer.Close() if err := s.storage.CreateAuthCode(tc.testAuthCode); err != nil { - t.Errorf("failed to create auth code: %v", err) + t.Fatalf("failed to create auth code: %v", err) } if err := s.storage.CreateDeviceRequest(tc.testDeviceRequest); err != nil { - t.Errorf("failed to create device request: %v", err) + t.Fatalf("failed to create device request: %v", err) } if err := s.storage.CreateDeviceToken(tc.testDeviceToken); err != nil { - t.Errorf("failed to create device token: %v", err) + t.Fatalf("failed to create device token: %v", err) } client := storage.Client{ @@ -344,7 +350,7 @@ func TestDeviceCallback(t *testing.T) { u, err := url.Parse(s.issuerURL.String()) if err != nil { - t.Errorf("Could not parse issuer URL %v", err) + t.Fatalf("Could not parse issuer URL %v", err) } u.Path = path.Join(u.Path, "device/callback") q := u.Query() @@ -506,16 +512,16 @@ func TestDeviceTokenResponse(t *testing.T) { defer httpServer.Close() if err := s.storage.CreateDeviceRequest(tc.testDeviceRequest); err != nil { - t.Errorf("Failed to store device token %v", err) + t.Fatalf("Failed to store device token %v", err) } if err := s.storage.CreateDeviceToken(tc.testDeviceToken); err != nil { - t.Errorf("Failed to store device token %v", err) + t.Fatalf("Failed to store device token %v", err) } u, err := url.Parse(s.issuerURL.String()) if err != nil { - t.Errorf("Could not parse issuer URL %v", err) + t.Fatalf("Could not parse issuer URL %v", err) } u.Path = path.Join(u.Path, "device/token") @@ -540,7 +546,7 @@ func TestDeviceTokenResponse(t *testing.T) { t.Errorf("Could read token response %v", err) } if tc.expectedResponseCode == http.StatusBadRequest || tc.expectedResponseCode == http.StatusUnauthorized { - expectErrorResponse(tc.testName, body, tc.expectedServerResponse, t) + expectJsonErrorResponse(tc.testName, body, tc.expectedServerResponse, t) } else if string(body) != tc.expectedServerResponse { t.Errorf("Unexpected Server Response. Expected %v got %v", tc.expectedServerResponse, string(body)) } @@ -548,7 +554,7 @@ func TestDeviceTokenResponse(t *testing.T) { } } -func expectErrorResponse(testCase string, body []byte, expectedError string, t *testing.T) { +func expectJsonErrorResponse(testCase string, body []byte, expectedError string, t *testing.T) { jsonMap := make(map[string]interface{}) err := json.Unmarshal(body, &jsonMap) if err != nil { @@ -637,12 +643,12 @@ func TestVerifyCodeResponse(t *testing.T) { defer httpServer.Close() if err := s.storage.CreateDeviceRequest(tc.testDeviceRequest); err != nil { - t.Errorf("Failed to store device token %v", err) + t.Fatalf("Failed to store device token %v", err) } u, err := url.Parse(s.issuerURL.String()) if err != nil { - t.Errorf("Could not parse issuer URL %v", err) + t.Fatalf("Could not parse issuer URL %v", err) } u.Path = path.Join(u.Path, "device/auth/verify_code") diff --git a/storage/conformance/conformance.go b/storage/conformance/conformance.go index 0b73ce1556..a550a530f2 100644 --- a/storage/conformance/conformance.go +++ b/storage/conformance/conformance.go @@ -1030,6 +1030,6 @@ func testDeviceTokenCRUD(t *testing.T, s storage.Storage) { t.Fatalf("update failed, wanted token status=%v got %v", "complete", got.Status) } if got.Token != "token data" { - t.Fatalf("update failed, wanted token =%v got %v", "token data", got.Token) + t.Fatalf("update failed, wanted token %v got %v", "token data", got.Token) } }