+19
-3
.env.template
+19
-3
.env.template
···
1
-
CLIENT_ID=
2
-
CLIENT_SECRET=
3
-
REDIRECT_URL=
1
+
# Server configuration
2
+
SERVER_PORT=8080
3
+
SERVER_HOST=localhost
4
+
5
+
# Spotify OAuth configuration
6
+
SPOTIFY_CLIENT_ID=
7
+
SPOTIFY_CLIENT_SECRET=
8
+
SPOTIFY_AUTH_URL=https://accounts.spotify.com/authorize
9
+
SPOTIFY_TOKEN_URL=https://accounts.spotify.com/api/token
10
+
SPOTIFY_SCOPES=user-read-currently-playing user-read-email
11
+
12
+
# Callback URLs
13
+
CALLBACK_SPOTIFY=http://localhost:8080/callback/spotify
14
+
15
+
# Tracker settings
16
+
TRACKER_INTERVAL=30
17
+
18
+
# Database settings
19
+
DB_PATH=./piper.db
+6
README.md
+6
README.md
+1
-4
oauth/oauth2.go
+1
-4
oauth/oauth2.go
···
13
13
"golang.org/x/oauth2/spotify"
14
14
)
15
15
16
-
// OAuth2Service represents an OAuth2 service with PKCE support
17
16
type OAuth2Service struct {
18
17
config oauth2.Config
19
18
state string
···
21
20
codeChallenge string
22
21
}
23
22
24
-
// generateRandomState creates a random state string for CSRF protection
25
23
func generateRandomState() string {
26
24
b := make([]byte, 16)
27
25
rand.Read(b)
···
32
30
func NewOAuth2Service(clientID, clientSecret, redirectURI string, scopes []string, provider string) *OAuth2Service {
33
31
var endpoint oauth2.Endpoint
34
32
35
-
// Select the appropriate provider endpoint
36
33
switch strings.ToLower(provider) {
37
34
case "spotify":
38
35
endpoint = spotify.Endpoint
39
36
default:
40
-
// Use custom endpoints if not a predefined provider
37
+
// TODO: support custom endpoints plus lastfm
41
38
endpoint = oauth2.Endpoint{
42
39
AuthURL: "https://example.com/auth",
43
40
TokenURL: "https://example.com/token",
+1
-6
oauth/oauth_manager.go
+1
-6
oauth/oauth_manager.go
···
14
14
SetAccessToken(token string) int64
15
15
}
16
16
17
-
// OAuthServiceManager manages multiple OAuth services
17
+
// manages multiple oauth2 client services
18
18
type OAuthServiceManager struct {
19
19
oauth2Services map[string]*OAuth2Service
20
20
sessionManager *session.SessionManager
21
21
mu sync.RWMutex
22
22
}
23
23
24
-
// NewOAuthServiceManager creates a new OAuthServiceManager
25
24
func NewOAuthServiceManager() *OAuthServiceManager {
26
25
return &OAuthServiceManager{
27
26
oauth2Services: make(map[string]*OAuth2Service),
···
29
28
}
30
29
}
31
30
32
-
// RegisterOAuth2Service adds a new OAuth2 service with PKCE to the manager
33
31
func (m *OAuthServiceManager) RegisterOAuth2Service(name string, service *OAuth2Service) {
34
32
m.mu.Lock()
35
33
defer m.mu.Unlock()
36
34
m.oauth2Services[name] = service
37
35
}
38
36
39
-
// GetOAuth2Service retrieves an OAuth2 service by name
40
37
func (m *OAuthServiceManager) GetOAuth2Service(name string) (*OAuth2Service, bool) {
41
38
m.mu.RLock()
42
39
defer m.mu.RUnlock()
···
44
41
return service, exists
45
42
}
46
43
47
-
// HandleLogin creates a handler for the login endpoint for a specific service
48
44
func (m *OAuthServiceManager) HandleLogin(serviceName string) http.HandlerFunc {
49
45
return func(w http.ResponseWriter, r *http.Request) {
50
46
m.mu.RLock()
···
60
56
}
61
57
}
62
58
63
-
// HandleCallback creates a handler for the callback endpoint for a specific service
64
59
func (m *OAuthServiceManager) HandleCallback(serviceName string, tokenReceiver TokenReceiver) http.HandlerFunc {
65
60
return func(w http.ResponseWriter, r *http.Request) {
66
61
m.mu.RLock()