refactor: fold auth and crypto into deezer domain

This commit is contained in:
Mathis Maquenne
2026-08-05 11:40:14 +02:00
parent 16c71c8688
commit dbd7837b17
11 changed files with 60 additions and 72 deletions
+26 -4
View File
@@ -1,10 +1,14 @@
package cmd package cmd
import ( import (
"bufio"
"fmt" "fmt"
"os"
"strings"
"github.com/mathismqn/godeez/internal/auth" "github.com/mathismqn/godeez/internal/deezer"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"golang.org/x/term"
) )
func newLoginCmd() *cobra.Command { func newLoginCmd() *cobra.Command {
@@ -13,16 +17,16 @@ func newLoginCmd() *cobra.Command {
Short: "Log in to Deezer with your email and password", Short: "Log in to Deezer with your email and password",
Args: cobra.NoArgs, Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if err := auth.CheckGatewayEnv(); err != nil { if err := deezer.CheckGatewayEnv(); err != nil {
return err return err
} }
email, password, err := auth.PromptCredentials() email, password, err := promptCredentials()
if err != nil { if err != nil {
return err return err
} }
_, username, err := auth.Login(cmd.Context(), email, password) _, username, err := deezer.Login(cmd.Context(), email, password)
if err != nil { if err != nil {
return err return err
} }
@@ -33,3 +37,21 @@ func newLoginCmd() *cobra.Command {
}, },
} }
} }
func promptCredentials() (string, string, error) {
fmt.Print("Email: ")
line, err := bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil {
return "", "", err
}
email := strings.TrimSpace(line)
fmt.Print("Password: ")
passwordBytes, err := term.ReadPassword(int(os.Stdin.Fd()))
fmt.Println()
if err != nil {
return "", "", err
}
return email, string(passwordBytes), nil
}
+2 -2
View File
@@ -3,7 +3,7 @@ package cmd
import ( import (
"fmt" "fmt"
"github.com/mathismqn/godeez/internal/auth" "github.com/mathismqn/godeez/internal/deezer"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -13,7 +13,7 @@ func newLogoutCmd() *cobra.Command {
Short: "Remove stored Deezer credentials", Short: "Remove stored Deezer credentials",
Args: cobra.NoArgs, Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if err := auth.Clear(); err != nil { if err := deezer.ClearCredentials(); err != nil {
return err return err
} }
-28
View File
@@ -1,28 +0,0 @@
package auth
import (
"bufio"
"fmt"
"os"
"strings"
"golang.org/x/term"
)
func PromptCredentials() (string, string, error) {
fmt.Print("Email: ")
line, err := bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil {
return "", "", err
}
email := strings.TrimSpace(line)
fmt.Print("Password: ")
passwordBytes, err := term.ReadPassword(int(os.Stdin.Fd()))
fmt.Println()
if err != nil {
return "", "", err
}
return email, string(passwordBytes), nil
}
@@ -1,4 +1,4 @@
package crypto package deezer
import ( import (
"crypto/cipher" "crypto/cipher"
@@ -13,7 +13,7 @@ var (
blowfishSecretKey = []byte("g4el58wc0zvf9na1") blowfishSecretKey = []byte("g4el58wc0zvf9na1")
) )
func GetBlowfishKey(trackID string) []byte { func BlowfishKey(trackID string) []byte {
hash := md5.Sum([]byte(trackID)) hash := md5.Sum([]byte(trackID))
hashHex := hex.EncodeToString(hash[:]) hashHex := hex.EncodeToString(hash[:])
+8 -11
View File
@@ -8,17 +8,14 @@ import (
"io" "io"
"net/http" "net/http"
"strings" "strings"
"github.com/mathismqn/godeez/internal/auth"
"github.com/mathismqn/godeez/internal/config"
) )
type Client struct { type Client struct {
Session *Session Session *Session
} }
func NewClient(ctx context.Context, appConfig *config.Config) (*Client, error) { func NewClient(ctx context.Context, arlCookie string) (*Client, error) {
session, err := resolveSession(ctx, appConfig) session, err := resolveSession(ctx, arlCookie)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to authenticate: %w", err) return nil, fmt.Errorf("failed to authenticate: %w", err)
} }
@@ -28,14 +25,14 @@ func NewClient(ctx context.Context, appConfig *config.Config) (*Client, error) {
}, nil }, nil
} }
func resolveSession(ctx context.Context, appConfig *config.Config) (*Session, error) { func resolveSession(ctx context.Context, arlCookie string) (*Session, error) {
if appConfig.ARLCookie != "" { if arlCookie != "" {
return Authenticate(ctx, appConfig.ARLCookie) return authenticate(ctx, arlCookie)
} }
var session *Session var session *Session
validate := func(ctx context.Context, arl string) error { validate := func(ctx context.Context, arl string) error {
s, err := Authenticate(ctx, arl) s, err := authenticate(ctx, arl)
if err != nil { if err != nil {
return err return err
} }
@@ -44,13 +41,13 @@ func resolveSession(ctx context.Context, appConfig *config.Config) (*Session, er
return nil return nil
} }
arl, err := auth.Resolve(ctx, validate) arl, err := resolveARL(ctx, validate)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if session == nil { if session == nil {
session, err = Authenticate(ctx, arl) session, err = authenticate(ctx, arl)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -1,4 +1,4 @@
package auth package deezer
import ( import (
"encoding/json" "encoding/json"
@@ -19,7 +19,7 @@ type Credentials struct {
ARL string `json:"arl,omitempty"` ARL string `json:"arl,omitempty"`
} }
func Load() (*Credentials, error) { func LoadCredentials() (*Credentials, error) {
secret, err := keyring.Get(keyringService, keyringUser) secret, err := keyring.Get(keyringService, keyringUser)
if err != nil { if err != nil {
if errors.Is(err, keyring.ErrNotFound) { if errors.Is(err, keyring.ErrNotFound) {
@@ -36,7 +36,7 @@ func Load() (*Credentials, error) {
return &creds, nil return &creds, nil
} }
func Save(creds *Credentials) error { func SaveCredentials(creds *Credentials) error {
data, err := json.Marshal(creds) data, err := json.Marshal(creds)
if err != nil { if err != nil {
return err return err
@@ -49,7 +49,7 @@ func Save(creds *Credentials) error {
return nil return nil
} }
func Clear() error { func ClearCredentials() error {
if err := keyring.Delete(keyringService, keyringUser); err != nil { if err := keyring.Delete(keyringService, keyringUser); err != nil {
if errors.Is(err, keyring.ErrNotFound) { if errors.Is(err, keyring.ErrNotFound) {
return nil return nil
@@ -1,4 +1,4 @@
package crypto package deezer
import ( import (
"crypto/aes" "crypto/aes"
@@ -6,7 +6,7 @@ import (
"fmt" "fmt"
) )
func ZeroPad(data []byte) []byte { func zeroPad(data []byte) []byte {
bs := aes.BlockSize bs := aes.BlockSize
padded := make([]byte, len(data)+(bs-len(data)%bs)%bs) padded := make([]byte, len(data)+(bs-len(data)%bs)%bs)
copy(padded, data) copy(padded, data)
@@ -14,11 +14,11 @@ func ZeroPad(data []byte) []byte {
return padded return padded
} }
func EncryptECB(key, data []byte) ([]byte, error) { func ecbEncrypt(key, data []byte) ([]byte, error) {
return ecbTransform(key, data, (cipher.Block).Encrypt) return ecbTransform(key, data, (cipher.Block).Encrypt)
} }
func DecryptECB(key, data []byte) ([]byte, error) { func ecbDecrypt(key, data []byte) ([]byte, error) {
return ecbTransform(key, data, (cipher.Block).Decrypt) return ecbTransform(key, data, (cipher.Block).Decrypt)
} }
@@ -1,4 +1,4 @@
package auth package deezer
import ( import (
"bytes" "bytes"
@@ -14,8 +14,6 @@ import (
"os" "os"
"strings" "strings"
"time" "time"
"github.com/mathismqn/godeez/internal/crypto"
) )
const ( const (
@@ -72,7 +70,7 @@ func newMobileClient() (*mobileClient, error) {
}, nil }, nil
} }
func (m *mobileClient) Login(ctx context.Context, email, password string) (*Credentials, string, error) { func (m *mobileClient) login(ctx context.Context, email, password string) (*Credentials, string, error) {
token, tokenKey, userKey, err := m.authenticate(ctx) token, tokenKey, userKey, err := m.authenticate(ctx)
if err != nil { if err != nil {
return nil, "", err return nil, "", err
@@ -117,7 +115,7 @@ func (m *mobileClient) authenticate(ctx context.Context) (string, string, string
return "", "", "", err return "", "", "", err
} }
decrypted, err := crypto.DecryptECB(m.gwKey, encrypted) decrypted, err := ecbDecrypt(m.gwKey, encrypted)
if err != nil { if err != nil {
return "", "", "", err return "", "", "", err
} }
@@ -130,7 +128,7 @@ func (m *mobileClient) authenticate(ctx context.Context) (string, string, string
} }
func (m *mobileClient) checkToken(ctx context.Context, token, tokenKey string) error { func (m *mobileClient) checkToken(ctx context.Context, token, tokenKey string) error {
encrypted, err := crypto.EncryptECB([]byte(tokenKey), []byte(token)) encrypted, err := ecbEncrypt([]byte(tokenKey), []byte(token))
if err != nil { if err != nil {
return err return err
} }
@@ -156,7 +154,7 @@ func (m *mobileClient) checkToken(ctx context.Context, token, tokenKey string) e
} }
func (m *mobileClient) userAuth(ctx context.Context, email, password, userKey string) (string, string, error) { func (m *mobileClient) userAuth(ctx context.Context, email, password, userKey string) (string, string, error) {
encryptedPassword, err := crypto.EncryptECB([]byte(userKey), crypto.ZeroPad([]byte(password))) encryptedPassword, err := ecbEncrypt([]byte(userKey), zeroPad([]byte(password)))
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
@@ -1,12 +1,12 @@
package auth package deezer
import ( import (
"context" "context"
"fmt" "fmt"
) )
func Resolve(ctx context.Context, validate func(ctx context.Context, arl string) error) (string, error) { func resolveARL(ctx context.Context, validate func(ctx context.Context, arl string) error) (string, error) {
creds, err := Load() creds, err := LoadCredentials()
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -35,12 +35,12 @@ func Login(ctx context.Context, email, password string) (string, string, error)
return "", "", err return "", "", err
} }
creds, username, err := client.Login(ctx, email, password) creds, username, err := client.login(ctx, email, password)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
if err := Save(creds); err != nil { if err := SaveCredentials(creds); err != nil {
return "", "", err return "", "", err
} }
+1 -1
View File
@@ -17,7 +17,7 @@ type Session struct {
Premium bool Premium bool
} }
func Authenticate(ctx context.Context, arlCookie string) (*Session, error) { func authenticate(ctx context.Context, arlCookie string) (*Session, error) {
jar, err := cookiejar.New(nil) jar, err := cookiejar.New(nil)
if err != nil { if err != nil {
return nil, err return nil, err
+3 -4
View File
@@ -12,7 +12,6 @@ import (
"time" "time"
"github.com/mathismqn/godeez/internal/config" "github.com/mathismqn/godeez/internal/config"
"github.com/mathismqn/godeez/internal/crypto"
"github.com/mathismqn/godeez/internal/deezer" "github.com/mathismqn/godeez/internal/deezer"
"github.com/mathismqn/godeez/internal/fileutil" "github.com/mathismqn/godeez/internal/fileutil"
"github.com/mathismqn/godeez/internal/store" "github.com/mathismqn/godeez/internal/store"
@@ -55,7 +54,7 @@ func (c *Client) Run(ctx context.Context, opts Options, id string) error {
func (c *Client) initDeezerClient(ctx context.Context, opts Options) error { func (c *Client) initDeezerClient(ctx context.Context, opts Options) error {
var err error var err error
c.deezerClient, err = deezer.NewClient(ctx, c.appConfig) c.deezerClient, err = deezer.NewClient(ctx, c.appConfig.ARLCookie)
if err != nil { if err != nil {
return err return err
} }
@@ -155,7 +154,7 @@ func (c *Client) downloadTrack(ctx context.Context, resource deezer.Resource, tr
fileName := track.Filename(c.kind, mediaFormat) fileName := track.Filename(c.kind, mediaFormat)
outputPath := path.Join(outputDir, fileName) outputPath := path.Join(outputDir, fileName)
key := crypto.GetBlowfishKey(track.ID) key := deezer.BlowfishKey(track.ID)
if err := c.streamToFile(dlCtx, stream, outputPath, key); err != nil { if err := c.streamToFile(dlCtx, stream, outputPath, key); err != nil {
fileutil.DeleteFile(outputPath) fileutil.DeleteFile(outputPath)
return downloadResult{err: fmt.Errorf("failed to stream to file: %w", err)} return downloadResult{err: fmt.Errorf("failed to stream to file: %w", err)}
@@ -213,7 +212,7 @@ func (c *Client) streamToFile(ctx context.Context, stream io.ReadCloser, outputP
} }
if chunk%3 == 0 && totalRead == chunkSize { if chunk%3 == 0 && totalRead == chunkSize {
buffer, err = crypto.DecryptBlowfish(buffer, key) buffer, err = deezer.DecryptBlowfish(buffer, key)
if err != nil { if err != nil {
return err return err
} }