Files
Super-HomeworkManager/internal/service/totp_service.go
2026-03-24 18:40:38 +09:00

72 lines
1.4 KiB
Go

package service
import (
"bytes"
"encoding/base64"
"fmt"
"image/png"
"net/url"
"github.com/pquerna/otp"
totplib "github.com/pquerna/otp/totp"
)
type TOTPService struct{}
func NewTOTPService() *TOTPService {
return &TOTPService{}
}
type TOTPSetupData struct {
Secret string
QRCodeB64 string
OTPAuthURL string
}
func (s *TOTPService) GenerateSecret(email, issuer string) (*TOTPSetupData, error) {
key, err := totplib.Generate(totplib.GenerateOpts{
Issuer: issuer,
AccountName: email,
})
if err != nil {
return nil, err
}
return s.buildSetupData(key)
}
func (s *TOTPService) SetupDataFromSecret(secret, email, issuer string) (*TOTPSetupData, error) {
otpURL := fmt.Sprintf("otpauth://totp/%s:%s?secret=%s&issuer=%s",
url.PathEscape(issuer),
url.PathEscape(email),
url.QueryEscape(secret),
url.QueryEscape(issuer),
)
key, err := otp.NewKeyFromURL(otpURL)
if err != nil {
return nil, err
}
return s.buildSetupData(key)
}
func (s *TOTPService) buildSetupData(key *otp.Key) (*TOTPSetupData, error) {
img, err := key.Image(200, 200)
if err != nil {
return nil, err
}
var buf bytes.Buffer
if err := png.Encode(&buf, img); err != nil {
return nil, err
}
return &TOTPSetupData{
Secret: key.Secret(),
QRCodeB64: base64.StdEncoding.EncodeToString(buf.Bytes()),
OTPAuthURL: key.URL(),
}, nil
}
func (s *TOTPService) Validate(secret, code string) bool {
return totplib.Validate(code, secret)
}