ultisuite-backend/internal/mail/credentials/credential.go
2026-05-24 00:03:36 +02:00

102 lines
2.3 KiB
Go

package credentials
import "time"
type AuthType string
const (
AuthPassword AuthType = "password"
AuthOAuth2 AuthType = "oauth2"
)
// Credential is the decrypted mail account secret material.
type Credential struct {
AuthType AuthType
Username string
Password string
AccessToken string
RefreshToken string
Expiry time.Time
OAuthProvider string // google | microsoft
}
func (c Credential) IsOAuth() bool {
return c.AuthType == AuthOAuth2
}
func (c Credential) NeedsRefresh() bool {
if !c.IsOAuth() || c.RefreshToken == "" {
return false
}
if c.Expiry.IsZero() {
return false
}
return time.Now().Add(2 * time.Minute).After(c.Expiry)
}
type storedPayload struct {
AuthType string `json:"auth_type,omitempty"`
Username string `json:"username"`
Password string `json:"password,omitempty"`
AccessToken string `json:"access_token,omitempty"`
RefreshToken string `json:"refresh_token,omitempty"`
Expiry string `json:"expiry,omitempty"`
OAuthProvider string `json:"oauth_provider,omitempty"`
}
func (c Credential) toStored() storedPayload {
p := storedPayload{
AuthType: string(c.AuthType),
Username: c.Username,
Password: c.Password,
AccessToken: c.AccessToken,
RefreshToken: c.RefreshToken,
OAuthProvider: c.OAuthProvider,
}
if !c.Expiry.IsZero() {
p.Expiry = c.Expiry.UTC().Format(time.RFC3339)
}
if p.AuthType == "" {
p.AuthType = string(AuthPassword)
}
return p
}
func storedToCredential(p storedPayload) (Credential, error) {
authType := AuthType(p.AuthType)
if authType == "" {
authType = AuthPassword
}
c := Credential{
AuthType: authType,
Username: p.Username,
Password: p.Password,
AccessToken: p.AccessToken,
RefreshToken: p.RefreshToken,
OAuthProvider: p.OAuthProvider,
}
if p.Expiry != "" {
t, err := time.Parse(time.RFC3339, p.Expiry)
if err != nil {
return Credential{}, err
}
c.Expiry = t
}
if c.Username == "" {
return Credential{}, errIncomplete
}
switch authType {
case AuthPassword:
if c.Password == "" {
return Credential{}, errIncomplete
}
case AuthOAuth2:
if c.AccessToken == "" {
return Credential{}, errIncomplete
}
default:
return Credential{}, errUnsupportedAuth
}
return c, nil
}