feat(auth): replace hand-rolled auth with Ory Kratos — phase 2 backend
Backend CI / Test (push) Has been cancelled
Backend CI / Contract Tests (push) Has been cancelled
Backend CI / Lint (push) Has been cancelled
Backend CI / Secret Scanning (push) Has been cancelled
Backend CI / Build (push) Has been cancelled

Delegates all credential management (login, register, password reset,
email verification, social sign-in) to Ory Kratos. The Go API now acts
as a resource server: the new KratosAuth middleware validates sessions
against the Kratos whoami endpoint, writes the local User mirror into
Echo context, and all existing domain handlers continue working
unchanged. Hand-rolled token auth, AuthToken model, apple_auth/
google_auth services, and the auth refresh flow are removed. Tests are
updated to use the fake-token middleware pattern so existing integration
assertions require no rewrite.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-05-18 17:55:56 -05:00
parent b66151ddd9
commit 81578f6e27
36 changed files with 927 additions and 7002 deletions
+21 -20
View File
@@ -22,6 +22,14 @@ import (
"github.com/treytartt/honeydue-api/internal/validator"
)
// authUserKey and authTokenKey mirror middleware.AuthUserKey / middleware.AuthTokenKey.
// We duplicate the string constants here to avoid an import cycle
// (testutil <- middleware <- repositories <- testutil).
const (
authUserKey = "auth_user"
authTokenKey = "auth_token"
)
var (
i18nOnce sync.Once
testDBCounter uint64
@@ -52,9 +60,6 @@ func SetupTestDB(t *testing.T) *gorm.DB {
err = db.AutoMigrate(
&models.User{},
&models.UserProfile{},
&models.AuthToken{},
&models.ConfirmationCode{},
&models.PasswordResetCode{},
&models.AdminUser{},
&models.Residence{},
&models.ResidenceType{},
@@ -73,8 +78,6 @@ func SetupTestDB(t *testing.T) *gorm.DB {
&models.NotificationPreference{},
&models.APNSDevice{},
&models.GCMDevice{},
&models.AppleSocialAuth{},
&models.GoogleSocialAuth{},
&models.TaskReminderLog{},
&models.UserSubscription{},
&models.SubscriptionSettings{},
@@ -177,29 +180,24 @@ func ParseJSONArray(t *testing.T, body []byte) []map[string]interface{} {
return result
}
// CreateTestUser creates a test user in the database
func CreateTestUser(t *testing.T, db *gorm.DB, username, email, password string) *models.User {
// CreateTestUser creates a test user in the database.
// password is accepted for API compatibility but ignored — Kratos owns credentials.
// A synthetic KratosID is generated so the user satisfies the unique-index constraint.
func CreateTestUser(t *testing.T, db *gorm.DB, username, email, _ string) *models.User {
t.Helper()
user := &models.User{
KratosID: "test-kratos-" + username,
Username: username,
Email: email,
IsActive: true,
}
err := user.SetPassword(password)
require.NoError(t, err)
err = db.Create(user).Error
err := db.Create(user).Error
require.NoError(t, err)
return user
}
// CreateTestToken creates an auth token for a user
func CreateTestToken(t *testing.T, db *gorm.DB, userID uint) *models.AuthToken {
token, err := models.GetOrCreateToken(db, userID)
require.NoError(t, err)
return token
}
// CreateTestResidenceType creates a test residence type
func CreateTestResidenceType(t *testing.T, db *gorm.DB, name string) *models.ResidenceType {
rt := &models.ResidenceType{Name: name}
@@ -362,12 +360,15 @@ func AssertStatusCode(t *testing.T, w *httptest.ResponseRecorder, expected int)
require.Equal(t, expected, w.Code, "unexpected status code: %s", w.Body.String())
}
// MockAuthMiddleware creates middleware that sets a test user in context
// MockAuthMiddleware creates middleware that sets a test user in context.
// Uses the same context keys as KratosAuth (authUserKey / authTokenKey) so
// handlers are unaware of the swap. The constants are duplicated here to
// avoid an import cycle (testutil <- middleware <- repositories <- testutil).
func MockAuthMiddleware(user *models.User) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
c.Set("auth_user", user)
c.Set("auth_token", "test-token")
c.Set(authUserKey, user)
c.Set(authTokenKey, "test-token")
return next(c)
}
}