44from src .adapters .auth .supabase .mappers import SupabaseAuthMappers
55from src .core .config import AuthConfig , SupabaseConfig
66from src .core .exceptions import AuthenticationError
7- from src .domain .interfaces .auth_repository import AuthRepository
87from src .domain .models .auth_session import (
98 AuthSession ,
10- LoginRequest ,
11- OAuthCallback ,
12- OAuthRequest ,
13- RegisterRequest ,
14- TokenPair ,
9+ SpotifyOAuthCallback ,
10+ SpotifyOAuthRequest ,
1511)
1612from src .domain .models .user import User
1713from src .infrastructure .logging import get_logger
1814
1915logger = get_logger ("adapters.auth.supabase.repository" )
2016
2117
22- class SupabaseAuthRepository ( AuthRepository ) :
18+ class SupabaseAuthRepository :
2319 """Supabase implementation of AuthRepository using OAuth."""
2420
2521 def __init__ (self , supabase_config : SupabaseConfig , auth_config : AuthConfig ):
2622 self .client = SupabaseClient (supabase_config , auth_config )
2723 self .mappers = SupabaseAuthMappers ()
2824
29- # OAuth methods (primary)
30- async def get_oauth_url (self , request : OAuthRequest ) -> str :
31- """Get OAuth authorization URL for the specified provider ."""
25+ # Spotify OAuth methods
26+ async def get_spotify_oauth_url (self , request : SpotifyOAuthRequest ) -> str :
27+ """Get Spotify OAuth authorization URL."""
3228 try :
3329 return await self .client .get_oauth_url (
34- provider = request . provider ,
30+ provider = "spotify" ,
3531 redirect_url = request .redirect_url ,
3632 scopes = request .scopes ,
3733 )
3834 except Exception as e :
39- logger .error (f"OAuth URL generation failed: { str (e )} " )
40- raise AuthenticationError (f"OAuth URL generation failed: { str (e )} " )
35+ logger .error (f"Spotify OAuth URL generation failed: { str (e )} " )
36+ raise AuthenticationError (f"Spotify OAuth URL generation failed: { str (e )} " )
4137
42- async def exchange_oauth_code (
43- self , callback : OAuthCallback
38+ async def exchange_spotify_oauth_code (
39+ self , callback : SpotifyOAuthCallback
4440 ) -> tuple [User , AuthSession ]:
45- """Exchange OAuth code for user and session."""
41+ """Exchange Spotify OAuth code for user and session."""
4642 try :
4743 # Exchange the code for a session
4844 session_data = await self .client .exchange_oauth_code (callback .code )
4945
5046 if not session_data .get ("user" ):
51- raise AuthenticationError ("OAuth exchange failed: No user returned" )
47+ raise AuthenticationError (
48+ "Spotify OAuth exchange failed: No user returned"
49+ )
5250
53- # Map to domain models
54- provider = "spotify" # Default for now, could be extracted from callback
55- user = self .mappers .user_from_oauth_session (session_data , provider )
56- session = self .mappers .session_from_supabase (session_data , provider )
51+ # Map to domain models - always Spotify
52+ user = self .mappers .user_from_oauth_session (session_data , "spotify" )
53+ session = self .mappers .session_from_supabase (session_data , "spotify" )
5754
58- logger .info (f"OAuth exchange successful for user: { user .email } " )
55+ logger .info (f"Spotify OAuth exchange successful for user: { user .email } " )
5956 return user , session
6057 except Exception as e :
61- logger .error (f"OAuth code exchange failed: { str (e )} " )
58+ logger .error (f"Spotify OAuth code exchange failed: { str (e )} " )
6259 if isinstance (e , AuthenticationError ):
6360 raise
64- raise AuthenticationError (f"OAuth exchange failed: { str (e )} " )
61+ raise AuthenticationError (f"Spotify OAuth exchange failed: { str (e )} " )
6562
66- async def refresh_oauth_session (self , refresh_token : str ) -> AuthSession :
67- """Refresh OAuth session using refresh token."""
63+ async def refresh_spotify_session (self , refresh_token : str ) -> AuthSession :
64+ """Refresh Spotify OAuth session using refresh token."""
6865 try :
6966 # Set the refresh token and refresh the session
7067 session_data = await self .client .refresh_session ()
7168
72- provider = "spotify" # Should be stored/determined from context
73- session = self .mappers .session_from_supabase (session_data , provider )
69+ # Always Spotify
70+ session = self .mappers .session_from_supabase (session_data , "spotify" )
7471
75- logger .info ("OAuth session refreshed successfully" )
72+ logger .info ("Spotify OAuth session refreshed successfully" )
7673 return session
7774 except Exception as e :
78- logger .error (f"OAuth session refresh failed: { str (e )} " )
79- raise AuthenticationError (f"Session refresh failed: { str (e )} " )
75+ logger .error (f"Spotify OAuth session refresh failed: { str (e )} " )
76+ raise AuthenticationError (f"Spotify session refresh failed: { str (e )} " )
8077
8178 # Session management
8279 async def verify_session_token (self , token : str ) -> User | None :
@@ -86,9 +83,6 @@ async def verify_session_token(self, token: str) -> User | None:
8683 if not user_data :
8784 return None
8885
89- # Determine provider from user metadata or default to spotify
90- provider = user_data .get ("app_metadata" , {}).get ("provider" , "spotify" )
91-
9286 user = self .mappers .user_from_supabase (user_data )
9387 logger .debug (f"Session token verified for user: { user .email } " )
9488 return user
@@ -136,114 +130,3 @@ async def get_user_by_auth_id(self, auth_id: str) -> User | None:
136130 except Exception as e :
137131 logger .error (f"User lookup by auth_id failed: { str (e )} " )
138132 return None
139-
140- # Fallback email/password methods (optional)
141- async def register_user (self , request : RegisterRequest ) -> User :
142- """Register a new user with email/password (fallback)."""
143- try :
144- response = await self .client .sign_up_with_email (
145- request .email , request .password
146- )
147-
148- if not response .get ("user" ):
149- raise AuthenticationError ("Registration failed: No user returned" )
150-
151- user = self .mappers .user_from_supabase (response ["user" ])
152- logger .info (f"User registered successfully: { user .email } " )
153- return user
154- except Exception as e :
155- logger .error (f"Registration failed: { str (e )} " )
156- if isinstance (e , AuthenticationError ):
157- raise
158- raise AuthenticationError (f"Registration failed: { str (e )} " )
159-
160- async def authenticate_user (self , request : LoginRequest ) -> User :
161- """Authenticate user with email/password (fallback)."""
162- try :
163- response = await self .client .sign_in_with_email (
164- request .email , request .password
165- )
166-
167- if not response .get ("user" ):
168- raise AuthenticationError ("Authentication failed: Invalid credentials" )
169-
170- user = self .mappers .user_from_supabase (response ["user" ])
171- logger .info (f"User authenticated successfully: { user .email } " )
172- return user
173- except Exception as e :
174- logger .error (f"Authentication failed: { str (e )} " )
175- if isinstance (e , AuthenticationError ):
176- raise
177- raise AuthenticationError ("Invalid email or password" )
178-
179- async def generate_tokens (self , user : User , remember_me : bool = False ) -> TokenPair :
180- """Generate access and refresh tokens for user."""
181- try :
182- token_data = self .client .generate_jwt_tokens (
183- user .auth_id or user .id , remember_me
184- )
185- tokens = self .mappers .tokens_from_data (token_data )
186- logger .debug (f"Tokens generated for user: { user .email } " )
187- return tokens
188- except Exception as e :
189- logger .error (f"Token generation failed: { str (e )} " )
190- raise AuthenticationError (f"Token generation failed: { str (e )} " )
191-
192- async def verify_access_token (self , token : str ) -> User | None :
193- """Verify access token and return user."""
194- try :
195- user_data = await self .client .get_user_from_token (token )
196- if not user_data :
197- return None
198-
199- user = self .mappers .user_from_supabase (user_data )
200- logger .debug (f"Access token verified for user: { user .email } " )
201- return user
202- except Exception as e :
203- logger .warning (f"Access token verification failed: { str (e )} " )
204- return None
205-
206- async def verify_refresh_token (self , token : str ) -> User | None :
207- """Verify refresh token and return user."""
208- try :
209- # For refresh tokens, we verify the JWT structure first
210- user_data = await self .client .get_user_from_token (token )
211- if not user_data :
212- return None
213-
214- user = self .mappers .user_from_supabase (user_data )
215- logger .debug (f"Refresh token verified for user: { user .email } " )
216- return user
217- except Exception as e :
218- logger .warning (f"Refresh token verification failed: { str (e )} " )
219- return None
220-
221- async def refresh_tokens (self , refresh_token : str ) -> TokenPair :
222- """Generate new tokens from refresh token."""
223- try :
224- # First verify the refresh token
225- user = await self .verify_refresh_token (refresh_token )
226- if not user :
227- raise AuthenticationError ("Invalid refresh token" )
228-
229- # Generate new tokens
230- token_data = self .client .generate_jwt_tokens (user .auth_id or user .id )
231- tokens = self .mappers .tokens_from_data (token_data )
232- logger .info (f"Tokens refreshed for user: { user .email } " )
233- return tokens
234- except Exception as e :
235- logger .error (f"Token refresh failed: { str (e )} " )
236- if isinstance (e , AuthenticationError ):
237- raise
238- raise AuthenticationError (f"Token refresh failed: { str (e )} " )
239-
240- async def revoke_token (self , token : str ) -> bool :
241- """Revoke a token (logout)."""
242- try :
243- result = await self .client .sign_out (token )
244- logger .info ("Token revoked successfully" )
245- return result
246- except Exception as e :
247- logger .warning (f"Token revocation failed: { str (e )} " )
248- # Be permissive with logout
249- return True
0 commit comments