Skip to content

Commit

Permalink
Merge pull request #29 from EringiShimeji/auth/check-already-signed-in
Browse files Browse the repository at this point in the history
ログイン済みの場合は認可エンドポイントのURLを発行しない
  • Loading branch information
harsssh authored Aug 26, 2024
2 parents 83fed89 + 096535e commit eb454eb
Show file tree
Hide file tree
Showing 11 changed files with 268 additions and 56 deletions.
162 changes: 116 additions & 46 deletions backend/app/gen/sudoku/auth/v1/auth.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 41 additions & 3 deletions backend/app/handler/auth/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
Expand All @@ -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
}
Expand Down
16 changes: 10 additions & 6 deletions backend/app/model/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand All @@ -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)
}
10 changes: 10 additions & 0 deletions backend/app/service/auth/dto.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package auth

import "github.com/google/uuid"

type OAuthProvider string

const (
Expand Down Expand Up @@ -30,3 +32,11 @@ type OAuthCallbackInput struct {
type OAuthCallbackOutput struct {
SessionID string
}

type ValidateSessionInput struct {
SessionID uuid.UUID
}

type ValidateSessionOutput struct {
IsValid bool
}
1 change: 1 addition & 0 deletions backend/app/service/auth/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
15 changes: 15 additions & 0 deletions backend/app/service/auth/mock_service.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions backend/app/service/auth/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Loading

0 comments on commit eb454eb

Please sign in to comment.