-
Notifications
You must be signed in to change notification settings - Fork 898
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add session creation endpoint (#1969)
- Loading branch information
1 parent
b0872b8
commit b643256
Showing
6 changed files
with
141 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package admin | ||
|
||
type CreateSessionTokenDto struct { | ||
UserID string `json:"user_id" validate:"required,uuid4"` | ||
UserAgent string `json:"user_agent"` | ||
IpAddress string `json:"ip_address" validate:"omitempty,ip"` | ||
} | ||
|
||
type CreateSessionTokenResponse struct { | ||
SessionToken string `json:"session_token"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
package handler | ||
|
||
import ( | ||
"fmt" | ||
"github.com/gofrs/uuid" | ||
"github.com/labstack/echo/v4" | ||
auditlog "github.com/teamhanko/hanko/backend/audit_log" | ||
"github.com/teamhanko/hanko/backend/config" | ||
"github.com/teamhanko/hanko/backend/dto" | ||
"github.com/teamhanko/hanko/backend/dto/admin" | ||
"github.com/teamhanko/hanko/backend/persistence" | ||
"github.com/teamhanko/hanko/backend/persistence/models" | ||
"github.com/teamhanko/hanko/backend/session" | ||
"net/http" | ||
) | ||
|
||
type SessionAdminHandler struct { | ||
cfg *config.Config | ||
persister persistence.Persister | ||
sessionManger session.Manager | ||
auditLogger auditlog.Logger | ||
} | ||
|
||
func NewSessionAdminHandler(cfg *config.Config, persister persistence.Persister, sessionManager session.Manager, auditLogger auditlog.Logger) SessionAdminHandler { | ||
return SessionAdminHandler{ | ||
cfg: cfg, | ||
persister: persister, | ||
sessionManger: sessionManager, | ||
auditLogger: auditLogger, | ||
} | ||
} | ||
|
||
func (h *SessionAdminHandler) Generate(ctx echo.Context) error { | ||
var body admin.CreateSessionTokenDto | ||
if err := (&echo.DefaultBinder{}).BindBody(ctx, &body); err != nil { | ||
return dto.ToHttpError(err) | ||
} | ||
|
||
if err := ctx.Validate(body); err != nil { | ||
return dto.ToHttpError(err) | ||
} | ||
|
||
userID, err := uuid.FromString(body.UserID) | ||
if err != nil { | ||
return echo.NewHTTPError(http.StatusBadRequest, "failed to parse userId as uuid").SetInternal(err) | ||
} | ||
|
||
user, err := h.persister.GetUserPersister().Get(userID) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if user == nil { | ||
return echo.NewHTTPError(http.StatusNotFound, "user not found") | ||
} | ||
|
||
var emailDTO *dto.EmailJwt | ||
if email := user.Emails.GetPrimary(); email != nil { | ||
emailDTO = dto.JwtFromEmailModel(email) | ||
} | ||
|
||
encodedToken, rawToken, err := h.sessionManger.GenerateJWT(userID, emailDTO) | ||
if err != nil { | ||
return fmt.Errorf("failed to generate JWT: %w", err) | ||
} | ||
|
||
if h.cfg.Session.ServerSide.Enabled { | ||
activeSessions, err := h.persister.GetSessionPersister().ListActive(userID) | ||
if err != nil { | ||
return fmt.Errorf("failed to list active sessions: %w", err) | ||
} | ||
|
||
// remove all server side sessions that exceed the limit | ||
if len(activeSessions) >= h.cfg.Session.ServerSide.Limit { | ||
for i := h.cfg.Session.ServerSide.Limit - 1; i < len(activeSessions); i++ { | ||
err = h.persister.GetSessionPersister().Delete(activeSessions[i]) | ||
if err != nil { | ||
return fmt.Errorf("failed to remove latest session: %w", err) | ||
} | ||
} | ||
} | ||
|
||
sessionID, _ := rawToken.Get("session_id") | ||
|
||
expirationTime := rawToken.Expiration() | ||
sessionModel := models.Session{ | ||
ID: uuid.FromStringOrNil(sessionID.(string)), | ||
UserID: userID, | ||
UserAgent: body.UserAgent, | ||
IpAddress: body.IpAddress, | ||
CreatedAt: rawToken.IssuedAt(), | ||
UpdatedAt: rawToken.IssuedAt(), | ||
ExpiresAt: &expirationTime, | ||
LastUsed: rawToken.IssuedAt(), | ||
} | ||
|
||
err = h.persister.GetSessionPersister().Create(sessionModel) | ||
if err != nil { | ||
return fmt.Errorf("failed to store session: %w", err) | ||
} | ||
} | ||
|
||
response := admin.CreateSessionTokenResponse{ | ||
SessionToken: encodedToken, | ||
} | ||
|
||
err = h.auditLogger.Create(ctx, models.AuditLogLoginSuccess, user, nil, auditlog.Detail("api", "admin")) | ||
if err != nil { | ||
return fmt.Errorf("could not create audit log: %w", err) | ||
} | ||
|
||
return ctx.JSON(http.StatusOK, response) | ||
} |
2 changes: 2 additions & 0 deletions
2
backend/persistence/migrations/20241106171500_change_sessions.down.fizz
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
change_column("sessions", "user_agent", "string", {"null": false}) | ||
change_column("sessions", "ip_address", "string", {"null": false}) |
2 changes: 2 additions & 0 deletions
2
backend/persistence/migrations/20241106171500_change_sessions.up.fizz
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
change_column("sessions", "user_agent", "string", {"null": true}) | ||
change_column("sessions", "ip_address", "string", {"null": true}) |