89 lines
2.7 KiB
Go
89 lines
2.7 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"net/mail"
|
|
"strings"
|
|
|
|
"galaxy/backend/internal/auth"
|
|
)
|
|
|
|
// ed25519PublicKeyLen is the fixed size of a raw Ed25519 public key. The
|
|
// OpenAPI contract documents `client_public_key` as a "Standard
|
|
// base64-encoded raw 32-byte Ed25519 public key"; the handler enforces
|
|
// the length after decode so the auth service always operates on the
|
|
// canonical shape.
|
|
const ed25519PublicKeyLen = 32
|
|
|
|
// deviceSessionPayload is the JSON body the internal session endpoints
|
|
// emit. It mirrors the `DeviceSession` schema in `openapi.yaml`.
|
|
type deviceSessionPayload struct {
|
|
DeviceSessionID string `json:"device_session_id"`
|
|
UserID string `json:"user_id"`
|
|
Status string `json:"status"`
|
|
ClientPublicKey string `json:"client_public_key,omitempty"`
|
|
CreatedAt string `json:"created_at"`
|
|
RevokedAt *string `json:"revoked_at,omitempty"`
|
|
LastSeenAt *string `json:"last_seen_at,omitempty"`
|
|
}
|
|
|
|
func deviceSessionToWire(s auth.Session) deviceSessionPayload {
|
|
out := deviceSessionPayload{
|
|
DeviceSessionID: s.DeviceSessionID.String(),
|
|
UserID: s.UserID.String(),
|
|
Status: s.Status,
|
|
CreatedAt: s.CreatedAt.UTC().Format("2006-01-02T15:04:05.000Z07:00"),
|
|
}
|
|
if len(s.ClientPublicKey) > 0 {
|
|
out.ClientPublicKey = base64.StdEncoding.EncodeToString(s.ClientPublicKey)
|
|
}
|
|
if s.RevokedAt != nil {
|
|
formatted := s.RevokedAt.UTC().Format("2006-01-02T15:04:05.000Z07:00")
|
|
out.RevokedAt = &formatted
|
|
}
|
|
if s.LastSeenAt != nil {
|
|
formatted := s.LastSeenAt.UTC().Format("2006-01-02T15:04:05.000Z07:00")
|
|
out.LastSeenAt = &formatted
|
|
}
|
|
return out
|
|
}
|
|
|
|
// validateEmail returns the trimmed value when raw parses as an
|
|
// addr-spec, or "" when raw is malformed. Auth normalises to lowercase
|
|
// internally; the handler only enforces the syntactic shape.
|
|
func validateEmail(raw string) string {
|
|
addr, err := mail.ParseAddress(strings.TrimSpace(raw))
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return addr.Address
|
|
}
|
|
|
|
// decodeClientPublicKey decodes the wire-format client_public_key into
|
|
// raw bytes and validates the length. Standard base64 (with padding) is
|
|
// the canonical encoding documented in `openapi.yaml`.
|
|
func decodeClientPublicKey(raw string) ([]byte, bool) {
|
|
decoded, err := base64.StdEncoding.DecodeString(strings.TrimSpace(raw))
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
if len(decoded) != ed25519PublicKeyLen {
|
|
return nil, false
|
|
}
|
|
return decoded, true
|
|
}
|
|
|
|
// isDecimalCodeOfLength reports whether s is a string of exactly want
|
|
// ASCII digits.
|
|
func isDecimalCodeOfLength(s string, want int) bool {
|
|
if len(s) != want {
|
|
return false
|
|
}
|
|
for i := 0; i < len(s); i++ {
|
|
if s[i] < '0' || s[i] > '9' {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|