feat: authsession service
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"galaxy/authsession/internal/domain/common"
|
||||
"galaxy/authsession/internal/domain/devicesession"
|
||||
)
|
||||
|
||||
// NormalizeString trims surrounding Unicode whitespace from value.
|
||||
func NormalizeString(value string) string {
|
||||
return strings.TrimSpace(value)
|
||||
}
|
||||
|
||||
// ParseEmail trims value and validates it against the frozen public e-mail
|
||||
// contract.
|
||||
func ParseEmail(value string) (common.Email, error) {
|
||||
email := common.Email(NormalizeString(value))
|
||||
if err := email.Validate(); err != nil {
|
||||
return "", InvalidRequest(err.Error())
|
||||
}
|
||||
|
||||
return email, nil
|
||||
}
|
||||
|
||||
// ParseChallengeID trims value and validates it as one challenge identifier.
|
||||
func ParseChallengeID(value string) (common.ChallengeID, error) {
|
||||
challengeID := common.ChallengeID(NormalizeString(value))
|
||||
if err := challengeID.Validate(); err != nil {
|
||||
return "", InvalidRequest(err.Error())
|
||||
}
|
||||
|
||||
return challengeID, nil
|
||||
}
|
||||
|
||||
// ParseDeviceSessionID trims value and validates it as one device-session
|
||||
// identifier.
|
||||
func ParseDeviceSessionID(value string) (common.DeviceSessionID, error) {
|
||||
deviceSessionID := common.DeviceSessionID(NormalizeString(value))
|
||||
if err := deviceSessionID.Validate(); err != nil {
|
||||
return "", InvalidRequest(err.Error())
|
||||
}
|
||||
|
||||
return deviceSessionID, nil
|
||||
}
|
||||
|
||||
// ParseUserID trims value and validates it as one user identifier.
|
||||
func ParseUserID(value string) (common.UserID, error) {
|
||||
userID := common.UserID(NormalizeString(value))
|
||||
if err := userID.Validate(); err != nil {
|
||||
return "", InvalidRequest(err.Error())
|
||||
}
|
||||
|
||||
return userID, nil
|
||||
}
|
||||
|
||||
// ParseRequiredCode trims value and validates it as a required non-empty
|
||||
// confirmation code.
|
||||
func ParseRequiredCode(value string) (string, error) {
|
||||
code := NormalizeString(value)
|
||||
if code == "" {
|
||||
return "", InvalidRequest("code must not be empty")
|
||||
}
|
||||
|
||||
return code, nil
|
||||
}
|
||||
|
||||
// ParseClientPublicKey trims value and validates it as the standard
|
||||
// base64-encoded raw 32-byte Ed25519 public key expected by the public auth
|
||||
// contract.
|
||||
func ParseClientPublicKey(value string) (common.ClientPublicKey, error) {
|
||||
normalized := NormalizeString(value)
|
||||
if normalized == "" {
|
||||
return common.ClientPublicKey{}, InvalidClientPublicKey()
|
||||
}
|
||||
|
||||
decoded, err := base64.StdEncoding.Strict().DecodeString(normalized)
|
||||
if err != nil || len(decoded) != ed25519.PublicKeySize {
|
||||
return common.ClientPublicKey{}, InvalidClientPublicKey()
|
||||
}
|
||||
|
||||
key, err := common.NewClientPublicKey(ed25519.PublicKey(decoded))
|
||||
if err != nil {
|
||||
return common.ClientPublicKey{}, InvalidClientPublicKey()
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// ParseRevokeReasonCode trims value and validates it as one machine-readable
|
||||
// revoke reason code.
|
||||
func ParseRevokeReasonCode(value string) (common.RevokeReasonCode, error) {
|
||||
code := common.RevokeReasonCode(NormalizeString(value))
|
||||
if err := code.Validate(); err != nil {
|
||||
return "", InvalidRequest(err.Error())
|
||||
}
|
||||
|
||||
return code, nil
|
||||
}
|
||||
|
||||
// ParseRevokeActorType trims value and validates it as one machine-readable
|
||||
// revoke actor type.
|
||||
func ParseRevokeActorType(value string) (common.RevokeActorType, error) {
|
||||
actorType := common.RevokeActorType(NormalizeString(value))
|
||||
if err := actorType.Validate(); err != nil {
|
||||
return "", InvalidRequest(err.Error())
|
||||
}
|
||||
|
||||
return actorType, nil
|
||||
}
|
||||
|
||||
// ParseOptionalActorID trims value and validates it as one optional stable
|
||||
// actor identifier.
|
||||
func ParseOptionalActorID(value string) (string, error) {
|
||||
actorID := NormalizeString(value)
|
||||
if actorID != value {
|
||||
return "", InvalidRequest("actor_id must not contain surrounding whitespace")
|
||||
}
|
||||
|
||||
return actorID, nil
|
||||
}
|
||||
|
||||
// BuildRevocation validates one revoke request payload and returns the domain
|
||||
// revocation metadata applied to a session mutation.
|
||||
func BuildRevocation(reasonCode string, actorType string, actorID string, at time.Time) (devicesession.Revocation, error) {
|
||||
if at.IsZero() {
|
||||
return devicesession.Revocation{}, InternalError(fmt.Errorf("revocation time must not be zero"))
|
||||
}
|
||||
|
||||
parsedReasonCode, err := ParseRevokeReasonCode(reasonCode)
|
||||
if err != nil {
|
||||
return devicesession.Revocation{}, err
|
||||
}
|
||||
parsedActorType, err := ParseRevokeActorType(actorType)
|
||||
if err != nil {
|
||||
return devicesession.Revocation{}, err
|
||||
}
|
||||
parsedActorID, err := ParseOptionalActorID(actorID)
|
||||
if err != nil {
|
||||
return devicesession.Revocation{}, err
|
||||
}
|
||||
|
||||
revocation := devicesession.Revocation{
|
||||
At: at.UTC(),
|
||||
ReasonCode: parsedReasonCode,
|
||||
ActorType: parsedActorType,
|
||||
ActorID: parsedActorID,
|
||||
}
|
||||
if err := revocation.Validate(); err != nil {
|
||||
return devicesession.Revocation{}, InternalError(fmt.Errorf("build revocation: %w", err))
|
||||
}
|
||||
|
||||
return revocation, nil
|
||||
}
|
||||
Reference in New Issue
Block a user