From 18ff58324ce97fae55a0ea81df250340f88d9f4f Mon Sep 17 00:00:00 2001 From: Kentaro Mizuki <66548698+harsssh@users.noreply.github.com> Date: Tue, 27 Aug 2024 00:01:00 +0900 Subject: [PATCH 1/4] =?UTF-8?q?Auth=20Service=20=E3=81=AB=E3=82=BB?= =?UTF-8?q?=E3=83=83=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E6=A4=9C=E8=A8=BC?= =?UTF-8?q?=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/model/session.go | 16 ++++++++++------ backend/app/service/auth/dto.go | 10 ++++++++++ backend/app/service/auth/interface.go | 1 + backend/app/service/auth/service.go | 18 ++++++++++++++++++ 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/backend/app/model/session.go b/backend/app/model/session.go index 5b045e2..383c163 100644 --- a/backend/app/model/session.go +++ b/backend/app/model/session.go @@ -12,6 +12,14 @@ type Session struct { expiresAt time.Time } +func NewSessionWithoutID(userID uint, expiresAt time.Time) *Session { + return &Session{userID: userID, expiresAt: expiresAt} +} + +func NewSession(id uuid.UUID, userID uint, expiresAt time.Time) *Session { + return &Session{id: mo.Some(id), userID: userID, expiresAt: expiresAt} +} + func (s *Session) IsIDPresent() bool { return s.id.IsPresent() } @@ -36,10 +44,6 @@ func (s *Session) ExpiresAt() time.Time { return s.expiresAt } -func NewSessionWithoutID(userID uint, expiresAt time.Time) *Session { - return &Session{userID: userID, expiresAt: expiresAt} -} - -func NewSession(id uuid.UUID, userID uint, expiresAt time.Time) *Session { - return &Session{id: mo.Some(id), userID: userID, expiresAt: expiresAt} +func (s *Session) IsExpired() bool { + return time.Now().After(s.expiresAt) } diff --git a/backend/app/service/auth/dto.go b/backend/app/service/auth/dto.go index 684b8a2..f5aefb0 100644 --- a/backend/app/service/auth/dto.go +++ b/backend/app/service/auth/dto.go @@ -1,5 +1,7 @@ package auth +import "github.com/google/uuid" + type OAuthProvider string const ( @@ -30,3 +32,11 @@ type OAuthCallbackInput struct { type OAuthCallbackOutput struct { SessionID string } + +type ValidateSessionInput struct { + SessionID uuid.UUID +} + +type ValidateSessionOutput struct { + IsValid bool +} diff --git a/backend/app/service/auth/interface.go b/backend/app/service/auth/interface.go index d0a9408..6f3cea4 100644 --- a/backend/app/service/auth/interface.go +++ b/backend/app/service/auth/interface.go @@ -5,6 +5,7 @@ type IAuthService interface { SignIn(SignInInput) (SignInOutput, error) SignOut(SignOutInput) (SignOutOutput, error) OAuthCallback(OAuthCallbackInput) (OAuthCallbackOutput, error) + ValidateSession(ValidateSessionInput) (ValidateSessionOutput, error) } type OAuthClient interface { diff --git a/backend/app/service/auth/service.go b/backend/app/service/auth/service.go index 544262e..4bf3ff7 100644 --- a/backend/app/service/auth/service.go +++ b/backend/app/service/auth/service.go @@ -184,3 +184,21 @@ func (s *Service) parseJWT(token string) (*StateClaims, error) { return claims, nil } + +func (s *Service) ValidateSession(input ValidateSessionInput) (ValidateSessionOutput, error) { + maybeSession, err := s.sessionRepo.FindByID(input.SessionID) + if err != nil { + return ValidateSessionOutput{}, err + } + + if maybeSession.IsAbsent() { + return ValidateSessionOutput{IsValid: false}, nil + } + session := maybeSession.MustGet() + + if session.IsExpired() { + return ValidateSessionOutput{IsValid: false}, nil + } + + return ValidateSessionOutput{IsValid: true}, nil +} From 6f1a7cd720cd2f886b6a7cbd6123825f7c4bb3ea Mon Sep 17 00:00:00 2001 From: Kentaro Mizuki <66548698+harsssh@users.noreply.github.com> Date: Tue, 27 Aug 2024 00:15:53 +0900 Subject: [PATCH 2/4] =?UTF-8?q?SignInStatus=20=E3=81=AE=E8=BF=BD=E5=8A=A0,?= =?UTF-8?q?=20buf=20generate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/gen/sudoku/auth/v1/auth.pb.go | 162 +++++++++++++----- frontend/lib/gen/sudoku/auth/v1/auth.pb.dart | 14 ++ .../lib/gen/sudoku/auth/v1/auth.pbenum.dart | 17 ++ .../lib/gen/sudoku/auth/v1/auth.pbjson.dart | 20 ++- proto/sudoku/auth/v1/auth.proto | 7 + 5 files changed, 173 insertions(+), 47 deletions(-) diff --git a/backend/app/gen/sudoku/auth/v1/auth.pb.go b/backend/app/gen/sudoku/auth/v1/auth.pb.go index fb6d3c2..a1ad939 100644 --- a/backend/app/gen/sudoku/auth/v1/auth.pb.go +++ b/backend/app/gen/sudoku/auth/v1/auth.pb.go @@ -68,6 +68,55 @@ func (OAuthProvider) EnumDescriptor() ([]byte, []int) { return file_sudoku_auth_v1_auth_proto_rawDescGZIP(), []int{0} } +type SignInStatus int32 + +const ( + SignInStatus_SIGN_IN_STATUS_UNSPECIFIED SignInStatus = 0 + SignInStatus_SIGN_IN_STATUS_ALREADY_SIGNED_IN SignInStatus = 1 + SignInStatus_SIGN_IN_STATUS_REQUIRES_SIGNING_IN SignInStatus = 2 +) + +// Enum value maps for SignInStatus. +var ( + SignInStatus_name = map[int32]string{ + 0: "SIGN_IN_STATUS_UNSPECIFIED", + 1: "SIGN_IN_STATUS_ALREADY_SIGNED_IN", + 2: "SIGN_IN_STATUS_REQUIRES_SIGNING_IN", + } + SignInStatus_value = map[string]int32{ + "SIGN_IN_STATUS_UNSPECIFIED": 0, + "SIGN_IN_STATUS_ALREADY_SIGNED_IN": 1, + "SIGN_IN_STATUS_REQUIRES_SIGNING_IN": 2, + } +) + +func (x SignInStatus) Enum() *SignInStatus { + p := new(SignInStatus) + *p = x + return p +} + +func (x SignInStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SignInStatus) Descriptor() protoreflect.EnumDescriptor { + return file_sudoku_auth_v1_auth_proto_enumTypes[1].Descriptor() +} + +func (SignInStatus) Type() protoreflect.EnumType { + return &file_sudoku_auth_v1_auth_proto_enumTypes[1] +} + +func (x SignInStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SignInStatus.Descriptor instead. +func (SignInStatus) EnumDescriptor() ([]byte, []int) { + return file_sudoku_auth_v1_auth_proto_rawDescGZIP(), []int{1} +} + type SignInRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -121,7 +170,8 @@ type SignInResponse struct { unknownFields protoimpl.UnknownFields // クライアントはこの URL にリダイレクトして, フローを開始する - AuthorizationUrl string `protobuf:"bytes,1,opt,name=authorization_url,json=authorizationUrl,proto3" json:"authorization_url,omitempty"` + AuthorizationUrl string `protobuf:"bytes,1,opt,name=authorization_url,json=authorizationUrl,proto3" json:"authorization_url,omitempty"` + Status SignInStatus `protobuf:"varint,2,opt,name=status,proto3,enum=sudoku.auth.v1.SignInStatus" json:"status,omitempty"` } func (x *SignInResponse) Reset() { @@ -163,6 +213,13 @@ func (x *SignInResponse) GetAuthorizationUrl() string { return "" } +func (x *SignInResponse) GetStatus() SignInStatus { + if x != nil { + return x.Status + } + return SignInStatus_SIGN_IN_STATUS_UNSPECIFIED +} + type SignOutRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -249,39 +306,50 @@ var file_sudoku_auth_v1_auth_proto_rawDesc = []byte{ 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x73, 0x75, 0x64, 0x6f, 0x6b, 0x75, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x08, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x22, 0x3d, 0x0a, 0x0e, 0x53, 0x69, 0x67, 0x6e, 0x49, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x22, 0x73, 0x0a, 0x0e, 0x53, 0x69, 0x67, 0x6e, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x22, 0x10, 0x0a, 0x0e, 0x53, 0x69, 0x67, 0x6e, 0x4f, 0x75, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, - 0x4f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x4a, 0x0a, 0x0d, 0x4f, - 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x1a, - 0x4f, 0x41, 0x55, 0x54, 0x48, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x55, - 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, - 0x4f, 0x41, 0x55, 0x54, 0x48, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x47, - 0x49, 0x54, 0x48, 0x55, 0x42, 0x10, 0x01, 0x32, 0xa2, 0x01, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x06, 0x53, 0x69, 0x67, 0x6e, 0x49, - 0x6e, 0x12, 0x1d, 0x2e, 0x73, 0x75, 0x64, 0x6f, 0x6b, 0x75, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x49, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1e, 0x2e, 0x73, 0x75, 0x64, 0x6f, 0x6b, 0x75, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x4a, 0x0a, 0x07, 0x53, 0x69, 0x67, 0x6e, 0x4f, 0x75, 0x74, 0x12, 0x1e, 0x2e, 0x73, 0x75, - 0x64, 0x6f, 0x6b, 0x75, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, - 0x6e, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x73, 0x75, - 0x64, 0x6f, 0x6b, 0x75, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, - 0x6e, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x9b, 0x01, 0x0a, - 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x75, 0x64, 0x6f, 0x6b, 0x75, 0x2e, 0x61, 0x75, 0x74, 0x68, - 0x2e, 0x76, 0x31, 0x42, 0x09, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, - 0x5a, 0x20, 0x73, 0x75, 0x64, 0x6f, 0x6b, 0x75, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x73, 0x75, 0x64, - 0x6f, 0x6b, 0x75, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x75, 0x74, 0x68, - 0x76, 0x31, 0xa2, 0x02, 0x03, 0x53, 0x41, 0x58, 0xaa, 0x02, 0x0e, 0x53, 0x75, 0x64, 0x6f, 0x6b, - 0x75, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0e, 0x53, 0x75, 0x64, 0x6f, - 0x6b, 0x75, 0x5c, 0x41, 0x75, 0x74, 0x68, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x1a, 0x53, 0x75, 0x64, - 0x6f, 0x6b, 0x75, 0x5c, 0x41, 0x75, 0x74, 0x68, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x10, 0x53, 0x75, 0x64, 0x6f, 0x6b, 0x75, - 0x3a, 0x3a, 0x41, 0x75, 0x74, 0x68, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x34, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x73, 0x75, 0x64, 0x6f, 0x6b, 0x75, 0x2e, + 0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x49, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x10, 0x0a, 0x0e, + 0x53, 0x69, 0x67, 0x6e, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x11, + 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2a, 0x4a, 0x0a, 0x0d, 0x4f, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x1a, 0x4f, 0x41, 0x55, 0x54, 0x48, 0x5f, 0x50, 0x52, 0x4f, 0x56, + 0x49, 0x44, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x4f, 0x41, 0x55, 0x54, 0x48, 0x5f, 0x50, 0x52, 0x4f, 0x56, + 0x49, 0x44, 0x45, 0x52, 0x5f, 0x47, 0x49, 0x54, 0x48, 0x55, 0x42, 0x10, 0x01, 0x2a, 0x7c, 0x0a, + 0x0c, 0x53, 0x69, 0x67, 0x6e, 0x49, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x0a, + 0x1a, 0x53, 0x49, 0x47, 0x4e, 0x5f, 0x49, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x24, 0x0a, + 0x20, 0x53, 0x49, 0x47, 0x4e, 0x5f, 0x49, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, + 0x41, 0x4c, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f, 0x53, 0x49, 0x47, 0x4e, 0x45, 0x44, 0x5f, 0x49, + 0x4e, 0x10, 0x01, 0x12, 0x26, 0x0a, 0x22, 0x53, 0x49, 0x47, 0x4e, 0x5f, 0x49, 0x4e, 0x5f, 0x53, + 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x53, 0x5f, 0x53, + 0x49, 0x47, 0x4e, 0x49, 0x4e, 0x47, 0x5f, 0x49, 0x4e, 0x10, 0x02, 0x32, 0xa2, 0x01, 0x0a, 0x0b, + 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x06, 0x53, + 0x69, 0x67, 0x6e, 0x49, 0x6e, 0x12, 0x1d, 0x2e, 0x73, 0x75, 0x64, 0x6f, 0x6b, 0x75, 0x2e, 0x61, + 0x75, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x49, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x73, 0x75, 0x64, 0x6f, 0x6b, 0x75, 0x2e, 0x61, 0x75, + 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x07, 0x53, 0x69, 0x67, 0x6e, 0x4f, 0x75, 0x74, 0x12, + 0x1e, 0x2e, 0x73, 0x75, 0x64, 0x6f, 0x6b, 0x75, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x73, 0x75, 0x64, 0x6f, 0x6b, 0x75, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x42, 0x9b, 0x01, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x75, 0x64, 0x6f, 0x6b, 0x75, 0x2e, + 0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x42, 0x09, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x20, 0x73, 0x75, 0x64, 0x6f, 0x6b, 0x75, 0x2f, 0x67, 0x65, 0x6e, + 0x2f, 0x73, 0x75, 0x64, 0x6f, 0x6b, 0x75, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x3b, + 0x61, 0x75, 0x74, 0x68, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x53, 0x41, 0x58, 0xaa, 0x02, 0x0e, 0x53, + 0x75, 0x64, 0x6f, 0x6b, 0x75, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0e, + 0x53, 0x75, 0x64, 0x6f, 0x6b, 0x75, 0x5c, 0x41, 0x75, 0x74, 0x68, 0x5c, 0x56, 0x31, 0xe2, 0x02, + 0x1a, 0x53, 0x75, 0x64, 0x6f, 0x6b, 0x75, 0x5c, 0x41, 0x75, 0x74, 0x68, 0x5c, 0x56, 0x31, 0x5c, + 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x10, 0x53, 0x75, + 0x64, 0x6f, 0x6b, 0x75, 0x3a, 0x3a, 0x41, 0x75, 0x74, 0x68, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -296,26 +364,28 @@ func file_sudoku_auth_v1_auth_proto_rawDescGZIP() []byte { return file_sudoku_auth_v1_auth_proto_rawDescData } -var file_sudoku_auth_v1_auth_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_sudoku_auth_v1_auth_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_sudoku_auth_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_sudoku_auth_v1_auth_proto_goTypes = []any{ (OAuthProvider)(0), // 0: sudoku.auth.v1.OAuthProvider - (*SignInRequest)(nil), // 1: sudoku.auth.v1.SignInRequest - (*SignInResponse)(nil), // 2: sudoku.auth.v1.SignInResponse - (*SignOutRequest)(nil), // 3: sudoku.auth.v1.SignOutRequest - (*SignOutResponse)(nil), // 4: sudoku.auth.v1.SignOutResponse + (SignInStatus)(0), // 1: sudoku.auth.v1.SignInStatus + (*SignInRequest)(nil), // 2: sudoku.auth.v1.SignInRequest + (*SignInResponse)(nil), // 3: sudoku.auth.v1.SignInResponse + (*SignOutRequest)(nil), // 4: sudoku.auth.v1.SignOutRequest + (*SignOutResponse)(nil), // 5: sudoku.auth.v1.SignOutResponse } var file_sudoku_auth_v1_auth_proto_depIdxs = []int32{ 0, // 0: sudoku.auth.v1.SignInRequest.provider:type_name -> sudoku.auth.v1.OAuthProvider - 1, // 1: sudoku.auth.v1.AuthService.SignIn:input_type -> sudoku.auth.v1.SignInRequest - 3, // 2: sudoku.auth.v1.AuthService.SignOut:input_type -> sudoku.auth.v1.SignOutRequest - 2, // 3: sudoku.auth.v1.AuthService.SignIn:output_type -> sudoku.auth.v1.SignInResponse - 4, // 4: sudoku.auth.v1.AuthService.SignOut:output_type -> sudoku.auth.v1.SignOutResponse - 3, // [3:5] is the sub-list for method output_type - 1, // [1:3] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 1, // 1: sudoku.auth.v1.SignInResponse.status:type_name -> sudoku.auth.v1.SignInStatus + 2, // 2: sudoku.auth.v1.AuthService.SignIn:input_type -> sudoku.auth.v1.SignInRequest + 4, // 3: sudoku.auth.v1.AuthService.SignOut:input_type -> sudoku.auth.v1.SignOutRequest + 3, // 4: sudoku.auth.v1.AuthService.SignIn:output_type -> sudoku.auth.v1.SignInResponse + 5, // 5: sudoku.auth.v1.AuthService.SignOut:output_type -> sudoku.auth.v1.SignOutResponse + 4, // [4:6] is the sub-list for method output_type + 2, // [2:4] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_sudoku_auth_v1_auth_proto_init() } @@ -378,7 +448,7 @@ func file_sudoku_auth_v1_auth_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_sudoku_auth_v1_auth_proto_rawDesc, - NumEnums: 1, + NumEnums: 2, NumMessages: 4, NumExtensions: 0, NumServices: 1, diff --git a/frontend/lib/gen/sudoku/auth/v1/auth.pb.dart b/frontend/lib/gen/sudoku/auth/v1/auth.pb.dart index 945f931..64e8c84 100644 --- a/frontend/lib/gen/sudoku/auth/v1/auth.pb.dart +++ b/frontend/lib/gen/sudoku/auth/v1/auth.pb.dart @@ -70,11 +70,15 @@ class SignInRequest extends $pb.GeneratedMessage { class SignInResponse extends $pb.GeneratedMessage { factory SignInResponse({ $core.String? authorizationUrl, + SignInStatus? status, }) { final $result = create(); if (authorizationUrl != null) { $result.authorizationUrl = authorizationUrl; } + if (status != null) { + $result.status = status; + } return $result; } SignInResponse._() : super(); @@ -83,6 +87,7 @@ class SignInResponse extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SignInResponse', package: const $pb.PackageName(_omitMessageNames ? '' : 'sudoku.auth.v1'), createEmptyInstance: create) ..aOS(1, _omitFieldNames ? '' : 'authorizationUrl') + ..e(2, _omitFieldNames ? '' : 'status', $pb.PbFieldType.OE, defaultOrMaker: SignInStatus.SIGN_IN_STATUS_UNSPECIFIED, valueOf: SignInStatus.valueOf, enumValues: SignInStatus.values) ..hasRequiredFields = false ; @@ -116,6 +121,15 @@ class SignInResponse extends $pb.GeneratedMessage { $core.bool hasAuthorizationUrl() => $_has(0); @$pb.TagNumber(1) void clearAuthorizationUrl() => clearField(1); + + @$pb.TagNumber(2) + SignInStatus get status => $_getN(1); + @$pb.TagNumber(2) + set status(SignInStatus v) { setField(2, v); } + @$pb.TagNumber(2) + $core.bool hasStatus() => $_has(1); + @$pb.TagNumber(2) + void clearStatus() => clearField(2); } class SignOutRequest extends $pb.GeneratedMessage { diff --git a/frontend/lib/gen/sudoku/auth/v1/auth.pbenum.dart b/frontend/lib/gen/sudoku/auth/v1/auth.pbenum.dart index 5e708b4..3a707bf 100644 --- a/frontend/lib/gen/sudoku/auth/v1/auth.pbenum.dart +++ b/frontend/lib/gen/sudoku/auth/v1/auth.pbenum.dart @@ -30,5 +30,22 @@ class OAuthProvider extends $pb.ProtobufEnum { const OAuthProvider._($core.int v, $core.String n) : super(v, n); } +class SignInStatus extends $pb.ProtobufEnum { + static const SignInStatus SIGN_IN_STATUS_UNSPECIFIED = SignInStatus._(0, _omitEnumNames ? '' : 'SIGN_IN_STATUS_UNSPECIFIED'); + static const SignInStatus SIGN_IN_STATUS_ALREADY_SIGNED_IN = SignInStatus._(1, _omitEnumNames ? '' : 'SIGN_IN_STATUS_ALREADY_SIGNED_IN'); + static const SignInStatus SIGN_IN_STATUS_REQUIRES_SIGNING_IN = SignInStatus._(2, _omitEnumNames ? '' : 'SIGN_IN_STATUS_REQUIRES_SIGNING_IN'); + + static const $core.List values = [ + SIGN_IN_STATUS_UNSPECIFIED, + SIGN_IN_STATUS_ALREADY_SIGNED_IN, + SIGN_IN_STATUS_REQUIRES_SIGNING_IN, + ]; + + static final $core.Map<$core.int, SignInStatus> _byValue = $pb.ProtobufEnum.initByValue(values); + static SignInStatus? valueOf($core.int value) => _byValue[value]; + + const SignInStatus._($core.int v, $core.String n) : super(v, n); +} + const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/frontend/lib/gen/sudoku/auth/v1/auth.pbjson.dart b/frontend/lib/gen/sudoku/auth/v1/auth.pbjson.dart index eae113b..7421528 100644 --- a/frontend/lib/gen/sudoku/auth/v1/auth.pbjson.dart +++ b/frontend/lib/gen/sudoku/auth/v1/auth.pbjson.dart @@ -27,6 +27,22 @@ final $typed_data.Uint8List oAuthProviderDescriptor = $convert.base64Decode( 'Cg1PQXV0aFByb3ZpZGVyEh4KGk9BVVRIX1BST1ZJREVSX1VOU1BFQ0lGSUVEEAASGQoVT0FVVE' 'hfUFJPVklERVJfR0lUSFVCEAE='); +@$core.Deprecated('Use signInStatusDescriptor instead') +const SignInStatus$json = { + '1': 'SignInStatus', + '2': [ + {'1': 'SIGN_IN_STATUS_UNSPECIFIED', '2': 0}, + {'1': 'SIGN_IN_STATUS_ALREADY_SIGNED_IN', '2': 1}, + {'1': 'SIGN_IN_STATUS_REQUIRES_SIGNING_IN', '2': 2}, + ], +}; + +/// Descriptor for `SignInStatus`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List signInStatusDescriptor = $convert.base64Decode( + 'CgxTaWduSW5TdGF0dXMSHgoaU0lHTl9JTl9TVEFUVVNfVU5TUEVDSUZJRUQQABIkCiBTSUdOX0' + 'lOX1NUQVRVU19BTFJFQURZX1NJR05FRF9JThABEiYKIlNJR05fSU5fU1RBVFVTX1JFUVVJUkVT' + 'X1NJR05JTkdfSU4QAg=='); + @$core.Deprecated('Use signInRequestDescriptor instead') const SignInRequest$json = { '1': 'SignInRequest', @@ -45,13 +61,15 @@ const SignInResponse$json = { '1': 'SignInResponse', '2': [ {'1': 'authorization_url', '3': 1, '4': 1, '5': 9, '10': 'authorizationUrl'}, + {'1': 'status', '3': 2, '4': 1, '5': 14, '6': '.sudoku.auth.v1.SignInStatus', '10': 'status'}, ], }; /// Descriptor for `SignInResponse`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List signInResponseDescriptor = $convert.base64Decode( 'Cg5TaWduSW5SZXNwb25zZRIrChFhdXRob3JpemF0aW9uX3VybBgBIAEoCVIQYXV0aG9yaXphdG' - 'lvblVybA=='); + 'lvblVybBI0CgZzdGF0dXMYAiABKA4yHC5zdWRva3UuYXV0aC52MS5TaWduSW5TdGF0dXNSBnN0' + 'YXR1cw=='); @$core.Deprecated('Use signOutRequestDescriptor instead') const SignOutRequest$json = { diff --git a/proto/sudoku/auth/v1/auth.proto b/proto/sudoku/auth/v1/auth.proto index ab1f75f..dfa6fe3 100644 --- a/proto/sudoku/auth/v1/auth.proto +++ b/proto/sudoku/auth/v1/auth.proto @@ -15,6 +15,12 @@ enum OAuthProvider { OAUTH_PROVIDER_GITHUB = 1; } +enum SignInStatus { + SIGN_IN_STATUS_UNSPECIFIED = 0; + SIGN_IN_STATUS_ALREADY_SIGNED_IN = 1; + SIGN_IN_STATUS_REQUIRES_SIGNING_IN = 2; +} + message SignInRequest { OAuthProvider provider = 1; } @@ -22,6 +28,7 @@ message SignInRequest { message SignInResponse { // クライアントはこの URL にリダイレクトして, フローを開始する string authorization_url = 1; + SignInStatus status = 2; } message SignOutRequest {} From 0a5603b5674697a1d9c7c5b2e7e312d60dee243b Mon Sep 17 00:00:00 2001 From: Kentaro Mizuki <66548698+harsssh@users.noreply.github.com> Date: Tue, 27 Aug 2024 00:35:53 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=E3=82=BB=E3=83=83=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3ID=E3=81=8C=E6=8C=AF=E3=82=89=E3=82=8C=E3=81=A6?= =?UTF-8?q?=E3=81=84=E3=81=9F=E3=82=89,=20ALREADY=5FSIGNED=5FIN=20?= =?UTF-8?q?=E3=82=92=E8=BF=94=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/handler/auth/handler.go | 44 +++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/backend/app/handler/auth/handler.go b/backend/app/handler/auth/handler.go index da53016..d513e75 100644 --- a/backend/app/handler/auth/handler.go +++ b/backend/app/handler/auth/handler.go @@ -4,6 +4,7 @@ import ( "connectrpc.com/connect" "context" "errors" + "github.com/google/uuid" "net/http" authv1 "sudoku/gen/sudoku/auth/v1" authS "sudoku/service/auth" @@ -29,6 +30,39 @@ func convertProvider(provider authv1.OAuthProvider) (authS.OAuthProvider, error) } func (h *Handler) SignIn(ctx context.Context, req *connect.Request[authv1.SignInRequest]) (*connect.Response[authv1.SignInResponse], error) { + var cookies []*http.Cookie + + sessionCookie := req.Header().Get(SessionCookieName) + if sessionCookie != "" { + // 無効なセッションクッキーを削除させる用 + c := http.Cookie{ + Name: SessionCookieName, + MaxAge: -1, + } + + sessionID, err := uuid.Parse(sessionCookie) + if err != nil { + // サインイン処理は継続 + cookies = append(cookies, &c) + } else { + output, err := h.authService.ValidateSession(authS.ValidateSessionInput{ + SessionID: sessionID, + }) + if err != nil { + return nil, connect.NewError(connect.CodeInternal, err) + } + + if output.IsValid { + return connect.NewResponse(&authv1.SignInResponse{ + Status: authv1.SignInStatus_SIGN_IN_STATUS_ALREADY_SIGNED_IN, + }), nil + } + + // サインイン処理は継続 + cookies = append(cookies, &c) + } + } + provider, err := convertProvider(req.Msg.Provider) if err != nil { return nil, connect.NewError(connect.CodeInvalidArgument, err) @@ -42,18 +76,22 @@ func (h *Handler) SignIn(ctx context.Context, req *connect.Request[authv1.SignIn res := connect.NewResponse(&authv1.SignInResponse{ AuthorizationUrl: output.AuthorizationURL, + Status: authv1.SignInStatus_SIGN_IN_STATUS_REQUIRES_SIGNING_IN, }) + // Cookie として返していいのか? // TODO: セキュリティを考慮して属性を追加 // Secure を付けたいが, 開発環境で cookie が送られなくて困りそう // SameSite: strict だとコールバックに cookie が送られなさそう - cookie := http.Cookie{ + cookies = append(cookies, &http.Cookie{ Name: "state_jwt", Value: output.StateJWT, HttpOnly: true, + }) + + for _, cookie := range cookies { + res.Header().Set("Set-Cookie", cookie.String()) } - // Cookie として返していいのか? - res.Header().Set("Set-Cookie", cookie.String()) return res, nil } From 096535e9fbf1abb0f5b25c356b2858910edb88dd Mon Sep 17 00:00:00 2001 From: Kentaro Mizuki <66548698+harsssh@users.noreply.github.com> Date: Tue, 27 Aug 2024 00:40:24 +0900 Subject: [PATCH 4/4] go generate --- backend/app/service/auth/mock_service.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/backend/app/service/auth/mock_service.go b/backend/app/service/auth/mock_service.go index 61c86fd..74f51b7 100644 --- a/backend/app/service/auth/mock_service.go +++ b/backend/app/service/auth/mock_service.go @@ -83,6 +83,21 @@ func (mr *MockIAuthServiceMockRecorder) SignOut(arg0 any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignOut", reflect.TypeOf((*MockIAuthService)(nil).SignOut), arg0) } +// ValidateSession mocks base method. +func (m *MockIAuthService) ValidateSession(arg0 ValidateSessionInput) (ValidateSessionOutput, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateSession", arg0) + ret0, _ := ret[0].(ValidateSessionOutput) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidateSession indicates an expected call of ValidateSession. +func (mr *MockIAuthServiceMockRecorder) ValidateSession(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateSession", reflect.TypeOf((*MockIAuthService)(nil).ValidateSession), arg0) +} + // MockOAuthClient is a mock of OAuthClient interface. type MockOAuthClient struct { ctrl *gomock.Controller