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
import (
"bufio"
"fmt"
"os"
"strings"
"github.com/mathismqn/godeez/internal/auth"
"github.com/mathismqn/godeez/internal/deezer"
"github.com/spf13/cobra"
"golang.org/x/term"
)
func newLoginCmd() *cobra.Command {
@@ -13,16 +17,16 @@ func newLoginCmd() *cobra.Command {
Short: "Log in to Deezer with your email and password",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
if err := auth.CheckGatewayEnv(); err != nil {
if err := deezer.CheckGatewayEnv(); err != nil {
return err
}
email, password, err := auth.PromptCredentials()
email, password, err := promptCredentials()
if err != nil {
return err
}
_, username, err := auth.Login(cmd.Context(), email, password)
_, username, err := deezer.Login(cmd.Context(), email, password)
if err != nil {
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 (
"fmt"
"github.com/mathismqn/godeez/internal/auth"
"github.com/mathismqn/godeez/internal/deezer"
"github.com/spf13/cobra"
)
@@ -13,7 +13,7 @@ func newLogoutCmd() *cobra.Command {
Short: "Remove stored Deezer credentials",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
if err := auth.Clear(); err != nil {
if err := deezer.ClearCredentials(); err != nil {
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 (
"crypto/cipher"
@@ -13,7 +13,7 @@ var (
blowfishSecretKey = []byte("g4el58wc0zvf9na1")
)
func GetBlowfishKey(trackID string) []byte {
func BlowfishKey(trackID string) []byte {
hash := md5.Sum([]byte(trackID))
hashHex := hex.EncodeToString(hash[:])
+8 -11
View File
@@ -8,17 +8,14 @@ import (
"io"
"net/http"
"strings"
"github.com/mathismqn/godeez/internal/auth"
"github.com/mathismqn/godeez/internal/config"
)
type Client struct {
Session *Session
}
func NewClient(ctx context.Context, appConfig *config.Config) (*Client, error) {
session, err := resolveSession(ctx, appConfig)
func NewClient(ctx context.Context, arlCookie string) (*Client, error) {
session, err := resolveSession(ctx, arlCookie)
if err != nil {
return nil, fmt.Errorf("failed to authenticate: %w", err)
}
@@ -28,14 +25,14 @@ func NewClient(ctx context.Context, appConfig *config.Config) (*Client, error) {
}, nil
}
func resolveSession(ctx context.Context, appConfig *config.Config) (*Session, error) {
if appConfig.ARLCookie != "" {
return Authenticate(ctx, appConfig.ARLCookie)
func resolveSession(ctx context.Context, arlCookie string) (*Session, error) {
if arlCookie != "" {
return authenticate(ctx, arlCookie)
}
var session *Session
validate := func(ctx context.Context, arl string) error {
s, err := Authenticate(ctx, arl)
s, err := authenticate(ctx, arl)
if err != nil {
return err
}
@@ -44,13 +41,13 @@ func resolveSession(ctx context.Context, appConfig *config.Config) (*Session, er
return nil
}
arl, err := auth.Resolve(ctx, validate)
arl, err := resolveARL(ctx, validate)
if err != nil {
return nil, err
}
if session == nil {
session, err = Authenticate(ctx, arl)
session, err = authenticate(ctx, arl)
if err != nil {
return nil, err
}
@@ -1,4 +1,4 @@
package auth
package deezer
import (
"encoding/json"
@@ -19,7 +19,7 @@ type Credentials struct {
ARL string `json:"arl,omitempty"`
}
func Load() (*Credentials, error) {
func LoadCredentials() (*Credentials, error) {
secret, err := keyring.Get(keyringService, keyringUser)
if err != nil {
if errors.Is(err, keyring.ErrNotFound) {
@@ -36,7 +36,7 @@ func Load() (*Credentials, error) {
return &creds, nil
}
func Save(creds *Credentials) error {
func SaveCredentials(creds *Credentials) error {
data, err := json.Marshal(creds)
if err != nil {
return err
@@ -49,7 +49,7 @@ func Save(creds *Credentials) error {
return nil
}
func Clear() error {
func ClearCredentials() error {
if err := keyring.Delete(keyringService, keyringUser); err != nil {
if errors.Is(err, keyring.ErrNotFound) {
return nil
@@ -1,4 +1,4 @@
package crypto
package deezer
import (
"crypto/aes"
@@ -6,7 +6,7 @@ import (
"fmt"
)
func ZeroPad(data []byte) []byte {
func zeroPad(data []byte) []byte {
bs := aes.BlockSize
padded := make([]byte, len(data)+(bs-len(data)%bs)%bs)
copy(padded, data)
@@ -14,11 +14,11 @@ func ZeroPad(data []byte) []byte {
return padded
}
func EncryptECB(key, data []byte) ([]byte, error) {
func ecbEncrypt(key, data []byte) ([]byte, error) {
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)
}
@@ -1,4 +1,4 @@
package auth
package deezer
import (
"bytes"
@@ -14,8 +14,6 @@ import (
"os"
"strings"
"time"
"github.com/mathismqn/godeez/internal/crypto"
)
const (
@@ -72,7 +70,7 @@ func newMobileClient() (*mobileClient, error) {
}, 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)
if err != nil {
return nil, "", err
@@ -117,7 +115,7 @@ func (m *mobileClient) authenticate(ctx context.Context) (string, string, string
return "", "", "", err
}
decrypted, err := crypto.DecryptECB(m.gwKey, encrypted)
decrypted, err := ecbDecrypt(m.gwKey, encrypted)
if err != nil {
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 {
encrypted, err := crypto.EncryptECB([]byte(tokenKey), []byte(token))
encrypted, err := ecbEncrypt([]byte(tokenKey), []byte(token))
if err != nil {
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) {
encryptedPassword, err := crypto.EncryptECB([]byte(userKey), crypto.ZeroPad([]byte(password)))
encryptedPassword, err := ecbEncrypt([]byte(userKey), zeroPad([]byte(password)))
if err != nil {
return "", "", err
}
@@ -1,12 +1,12 @@
package auth
package deezer
import (
"context"
"fmt"
)
func Resolve(ctx context.Context, validate func(ctx context.Context, arl string) error) (string, error) {
creds, err := Load()
func resolveARL(ctx context.Context, validate func(ctx context.Context, arl string) error) (string, error) {
creds, err := LoadCredentials()
if err != nil {
return "", err
}
@@ -35,12 +35,12 @@ func Login(ctx context.Context, email, password string) (string, string, error)
return "", "", err
}
creds, username, err := client.Login(ctx, email, password)
creds, username, err := client.login(ctx, email, password)
if err != nil {
return "", "", err
}
if err := Save(creds); err != nil {
if err := SaveCredentials(creds); err != nil {
return "", "", err
}
+1 -1
View File
@@ -17,7 +17,7 @@ type Session struct {
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)
if err != nil {
return nil, err
+3 -4
View File
@@ -12,7 +12,6 @@ import (
"time"
"github.com/mathismqn/godeez/internal/config"
"github.com/mathismqn/godeez/internal/crypto"
"github.com/mathismqn/godeez/internal/deezer"
"github.com/mathismqn/godeez/internal/fileutil"
"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 {
var err error
c.deezerClient, err = deezer.NewClient(ctx, c.appConfig)
c.deezerClient, err = deezer.NewClient(ctx, c.appConfig.ARLCookie)
if err != nil {
return err
}
@@ -155,7 +154,7 @@ func (c *Client) downloadTrack(ctx context.Context, resource deezer.Resource, tr
fileName := track.Filename(c.kind, mediaFormat)
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 {
fileutil.DeleteFile(outputPath)
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 {
buffer, err = crypto.DecryptBlowfish(buffer, key)
buffer, err = deezer.DecryptBlowfish(buffer, key)
if err != nil {
return err
}