diff options
Diffstat (limited to 'backend/auth/auth.go')
| -rw-r--r-- | backend/auth/auth.go | 109 |
1 files changed, 107 insertions, 2 deletions
diff --git a/backend/auth/auth.go b/backend/auth/auth.go index 401773f..3a292d9 100644 --- a/backend/auth/auth.go +++ b/backend/auth/auth.go @@ -1,17 +1,42 @@ package auth import ( + "bytes" "context" + "encoding/json" + "errors" "fmt" + "net/http" + "net/url" + "github.com/jackc/pgx/v5" "golang.org/x/crypto/bcrypt" "github.com/nsfisis/iosdc-japan-2024-albatross/backend/db" ) -func Login(ctx context.Context, queries *db.Queries, username, password string) (int, error) { +var ( + ErrInvalidRegistrationToken = errors.New("invalid registration token") + ErrNoRegistrationToken = errors.New("no registration token") + ErrForteeLoginFailed = errors.New("fortee login failed") +) + +func Login( + ctx context.Context, + queries *db.Queries, + username string, + password string, + registrationToken *string, +) (int, error) { userAuth, err := queries.GetUserAuthByUsername(ctx, username) if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + err := signup(ctx, queries, username, password, registrationToken) + if err != nil { + return 0, err + } + return Login(ctx, queries, username, password, nil) + } return 0, err } if userAuth.AuthType == "password" { @@ -24,6 +49,86 @@ func Login(ctx context.Context, queries *db.Queries, username, password string) return 0, err } return int(userAuth.UserID), nil + } else if userAuth.AuthType == "fortee" { + if err := verifyForteeAccount(ctx, username, password); err != nil { + return 0, err + } + return int(userAuth.UserID), nil + } + panic(fmt.Sprintf("unexpected auth type: %s", userAuth.AuthType)) +} + +func signup( + ctx context.Context, + queries *db.Queries, + username string, + password string, + registrationToken *string, +) error { + if err := verifyRegistrationToken(ctx, queries, registrationToken); err != nil { + return err + } + if err := verifyForteeAccount(ctx, username, password); err != nil { + return err + } + + // TODO: transaction + userID, err := queries.CreateUser(ctx, username) + if err != nil { + return err + } + if err := queries.CreateUserAuth(ctx, db.CreateUserAuthParams{ + UserID: userID, + AuthType: "fortee", + }); err != nil { + return err + } + return nil +} + +func verifyRegistrationToken(ctx context.Context, queries *db.Queries, registrationToken *string) error { + if registrationToken == nil { + return ErrNoRegistrationToken + } + exists, err := queries.IsRegistrationTokenValid(ctx, *registrationToken) + if err != nil { + return err + } + if !exists { + return ErrInvalidRegistrationToken + } + return nil +} + +func verifyForteeAccount(_ context.Context, username string, password string) error { + reqData := url.Values{} + reqData.Set("username", username) + reqData.Set("password", password) + reqBody := reqData.Encode() + + req, err := http.NewRequest("POST", "https://fortee.jp/api/user/login", bytes.NewBufferString(reqBody)) + if err != nil { + return fmt.Errorf("http.NewRequest failed: %w", err) + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Accept", "application/json") + + client := &http.Client{} + res, err := client.Do(req) + if err != nil { + return fmt.Errorf("client.Do failed: %v", err) + } + defer res.Body.Close() + + resData := struct { + LoggedIn bool `json:"loggedIn"` + }{} + if err := json.NewDecoder(res.Body).Decode(&resData); err != nil { + return fmt.Errorf("json.Decode failed: %v", err) + } + + if !resData.LoggedIn { + return ErrForteeLoginFailed } - return 0, fmt.Errorf("not implemented") + return nil } |
