72 lines
1.4 KiB
Go
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)
|
|
}
|