U
    'cih                     @   s<  d dddddgZ ddlZddlZddlZddlZddlZddlmZ ddl	Z	ddl
Z
ddlmZmZ dd	lmZmZ ddlZdd
lmZmZ ddlmZmZ ddlmZmZmZmZ eeZdd Zdd Z G dd dZ!G dd  d e!Z"G dd de!Z#G dd de!Z$G dd de!Z%G dd deZ&e&fddZ'dS )SpotifyClientCredentialsSpotifyOAuthSpotifyOauthErrorSpotifyStateErrorSpotifyImplicitGrantSpotifyPKCE    N)BaseHTTPRequestHandler
HTTPServer)	parse_qslurlparse)CacheFileHandlerCacheHandler)r   r   )CLIENT_CREDS_ENV_VARSREQUESTS_SESSIONget_host_portnormalize_scopec                 C   s0   t t| d | d}dd|d iS )N:asciiAuthorizationzBasic )base64	b64encodestrencodedecode)	client_idclient_secretauth_header r   2/tmp/pip-unpacked-wheel-5vxoxy0m/spotipy/oauth2.py_make_authorization_headers   s    r   c                 C   s<   t | }| pt|}|d kr8d| d| d}t||S )NzNo z. Pass it or set a z environment variable.)r   osgetenvr   )valueZenv_keyZenv_valZ_valmsgr   r   r   _ensure_value&   s    r$   c                   @   s   e Zd Zdd Zdd Zedd Zejdd Zedd	 Zejd
d	 Zedd Z	e	jdd Z	e
dd Ze
dd Ze
dd Zdd Zdd ZdS )SpotifyAuthBasec                 C   s:   t |tjr|| _n"|r$t | _nddlm} || _d S )Nr   )api)
isinstancerequestsSession_sessionr&   )selfrequests_sessionr&   r   r   r   __init__0   s    zSpotifyAuthBase.__init__c                 C   s   t |S N)r   )r+   scoper   r   r   _normalize_scope:   s    z SpotifyAuthBase._normalize_scopec                 C   s   | j S r.   )
_client_idr+   r   r   r   r   =   s    zSpotifyAuthBase.client_idc                 C   s   t |d| _d S )Nr   )r$   r1   r+   valr   r   r   r   A   s    c                 C   s   | j S r.   )_client_secretr2   r   r   r   r   E   s    zSpotifyAuthBase.client_secretc                 C   s   t |d| _d S )Nr   )r$   r5   r3   r   r   r   r   I   s    c                 C   s   | j S r.   )_redirect_urir2   r   r   r   redirect_uriM   s    zSpotifyAuthBase.redirect_uric                 C   s   t |d| _d S )Nr7   )r$   r6   r3   r   r   r   r7   Q   s    c                 C   s,   z
t | W S  tk
r&   t|  Y S X d S r.   )	raw_input	NameErrorinput)promptr   r   r   _get_user_inputU   s    
zSpotifyAuthBase._get_user_inputc                 C   s   t t }| d | dk S )N
expires_at<   inttime)
token_infonowr   r   r   is_token_expired\   s    z SpotifyAuthBase.is_token_expiredc                 C   s4   | rt |  nt  } |r&t | nt  }| |kS r.   )setsplit)Zneedle_scopeZhaystack_scoper   r   r   _is_scope_subseta   s    z SpotifyAuthBase._is_scope_subsetc                 C   sh   |j }z | }|d}|d}W n" tk
rH   |jp>d }d }Y nX td| d| ||dd S )Nerrorerror_descriptionzerror: z, error_description: )rH   rI   )responsejsonget
ValueErrortextr   )r+   
http_errorrJ   Zerror_payloadrH   rI   r   r   r   _handle_oauth_errori   s    


z#SpotifyAuthBase._handle_oauth_errorc                 C   s&   t | ddr"t| jtr"| j  dS )z+Make sure the connection (pool) gets closedr*   N)getattrr'   r*   r   closer2   r   r   r   __del__}   s    zSpotifyAuthBase.__del__N)__name__
__module____qualname__r-   r0   propertyr   setterr   r7   staticmethodr<   rD   rG   rP   rS   r   r   r   r   r%   /   s,   









r%   c                       s<   e Zd ZdZd fdd	ZdddZdd	 Zd
d Z  ZS )r   &https://accounts.spotify.com/api/tokenNTc                    sh   t  | || _|| _|| _|| _|r\t|jtsTt	dt
t| d t
t || _nt | _dS )a[  
        Creates a Client Credentials Flow Manager.

        The Client Credentials flow is used in server-to-server authentication.
        Only endpoints that do not access user information can be accessed.
        This means that endpoints that require authorization scopes cannot be accessed.
        The advantage, however, of this authorization flow is that it does not require any
        user interaction

        You can either provide a client_id and client_secret to the
        constructor or set SPOTIPY_CLIENT_ID and SPOTIPY_CLIENT_SECRET
        environment variables

        Parameters:
             * client_id: Must be supplied or set as environment variable
             * client_secret: Must be supplied or set as environment variable
             * proxies: Optional, proxy for the requests library to route through
             * requests_session: A Requests session
             * requests_timeout: Optional, tell Requests to stop waiting for a response after
                                 a given number of seconds
             * cache_handler: An instance of the `CacheHandler` class to handle
                              getting and saving cached authorization tokens.
                              Optional, will otherwise use `CacheFileHandler`.
                              (takes precedence over `cache_path` and `username`)

        2cache_handler must be a subclass of CacheHandler:  != N)superr-   r   r   proxiesrequests_timeout
issubclass	__class__r   AssertionErrorr   typecache_handlerr   )r+   r   r   r^   r,   r_   rd   ra   r   r   r-      s    $z!SpotifyClientCredentials.__init__c                 C   sn   |rt jdtdd |r@| j }|r@| |s@|r8|S |d S |  }| |}| j| |rf|S |d S )aI  
        If a valid access token is in memory, returns it
        Else fetches a new token and returns it

            Parameters:
            - as_dict: (deprecated) a boolean indicating if returning the access token
                as a token_info dictionary, otherwise it will be returned
                as a string.
        You're using 'as_dict = True'.get_access_token will return the token string directly in future versions. Please adjust your code accordingly, or use get_cached_token instead.   
stacklevelaccess_token)	warningswarnDeprecationWarningrd   get_cached_tokenrD   _request_access_token _add_custom_values_to_token_infosave_token_to_cache)r+   as_dictcheck_cacherB   r   r   r   get_access_token   s    
	

z)SpotifyClientCredentials.get_access_tokenc              
   C   s   ddi}t | j| j}td| j d| d|  z4| jj| j||d| j| j	d}|
  | }|W S  tjjk
r } z| | W 5 d}~X Y nX dS )	z%Gets client credentials access token 
grant_typeZclient_credentialsSending POST request to  with Headers:  and Body: Tdataheadersverifyr^   timeoutN)r   r   r   loggerdebugOAUTH_TOKEN_URLr*   postr^   r_   raise_for_statusrK   r(   
exceptions	HTTPErrorrP   )r+   payloadr{   rJ   rB   rO   r   r   r   ro      s(     z.SpotifyClientCredentials._request_access_tokenc                 C   s   t t |d  |d< |S `
        Store some values that aren't directly provided by a Web API
        response.
        
expires_inr=   r?   r+   rB   r   r   r   rp      s    z9SpotifyClientCredentials._add_custom_values_to_token_info)NNNTNN)TT)	rT   rU   rV   r   r-   rt   ro   rp   __classcell__r   r   re   r   r      s         2
c                       s   e Zd ZdZdZdZd' fdd	Zd	d
 Zd(ddZdd Z	e
dd Zdd Zdd Zd)ddZdd Zd*ddZd+ddZd,ddZdd  Zd!d" Zd#d$ Zd%d& Z  ZS )-r   zP
    Implements Authorization Code Flow for Spotify's OAuth implementation.
    &https://accounts.spotify.com/authorizerZ   NFTc                    s   t  |
 || _|| _|| _|| _| || _|s8|rRt	dt
 |rRt	d |rt|jtstdtt| d tt || _n |pttd }t||d| _|| _|| _|	| _|| _dS )aP  
        Creates a SpotifyOAuth object

        Parameters:
             * client_id: Must be supplied or set as environment variable
             * client_secret: Must be supplied or set as environment variable
             * redirect_uri: Must be supplied or set as environment variable
             * state: Optional, no verification is performed
             * scope: Optional, either a list of scopes or comma separated string of scopes.
                      e.g, "playlist-read-private,playlist-read-collaborative"
             * cache_path: (deprecated) Optional, will otherwise be generated
                           (takes precedence over `username`)
             * username: (deprecated) Optional or set as environment variable
                         (will set `cache_path` to `.cache-{username}`)
             * proxies: Optional, proxy for the requests library to route through
             * show_dialog: Optional, interpreted as boolean
             * requests_session: A Requests session
             * requests_timeout: Optional, tell Requests to stop waiting for a response after
                                 a given number of seconds
             * open_browser: Optional, whether the web browser should be opened to
                             authorize a user
             * cache_handler: An instance of the `CacheHandler` class to handle
                              getting and saving cached authorization tokens.
                              Optional, will otherwise use `CacheFileHandler`.
                              (takes precedence over `cache_path` and `username`)
        a  Specifying cache_path or username as arguments to SpotifyOAuth will be deprecated. Instead, please create a CacheFileHandler instance with the desired cache_path and username and pass it to SpotifyOAuth as the cache_handler. For example:

	from spotipy.oauth2 import CacheFileHandler
	handler = CacheFileHandler(cache_path=cache_path, username=username)
	sp = spotipy.SpotifyOAuth(client_id, client_secret, redirect_uri, cache_handler=handler)~A cache_handler has been specified along with a cache_path or username. The cache_path and username arguments will be ignored.r[   r\   client_usernameusername
cache_pathN)r]   r-   r   r   r7   stater0   r/   rk   rl   rm   r`   ra   r   rb   r   rc   rd   r    r!   r   r   r^   r_   show_dialogopen_browser)r+   r   r   r7   r   r/   r   r   r^   r   r,   r_   r   rd   re   r   r   r-      s<    +

zSpotifyOAuth.__init__c                 C   sF   |d krd S d|ks&|  | j|d s*d S | |rB| |d }|S Nr/   refresh_tokenrG   r/   rD   refresh_access_tokenr   r   r   r   validate_tokenR  s     
zSpotifyOAuth.validate_tokenc                 C   sf   | j d| jd}| jr | j|d< |dkr.| j}|dk	r>||d< | jrLd|d< t|}| j d| S )	z3 Gets the URL to use to authorize this app
        coder   response_typer7   r/   Nr   Tr   ?r   r7   r/   r   r   urllibparse	urlencodeOAUTH_AUTHORIZE_URLr+   r   r   Z	urlparamsr   r   r   get_authorize_urlc  s    

zSpotifyOAuth.get_authorize_urlc                 C   s"   |  |\}}|dkr|S |S dS z} Parse the response code in the given response url

            Parameters:
                - url - the response url
        Nparse_auth_response_urlr+   url_r   r   r   r   parse_response_codex  s    z SpotifyOAuth.parse_response_codec                    sN   t | j}tt| d kr8td d   d dt fdddD S )NrH   !Received error from auth server: )rH   c                 3   s   | ]}  |V  qd S r.   rL   .0paramformr   r   	<genexpr>  s     z7SpotifyOAuth.parse_auth_response_url.<locals>.<genexpr>)r   r   )r   querydictr
   r   tuple)r   query_sr   r   r   r     s    
z$SpotifyOAuth.parse_auth_response_urlc                 C   s   t | j| jS r.   )r   r   r   r2   r   r   r   r     s    z(SpotifyOAuth._make_authorization_headersc                 C   sT   |   }z t| td| d W n& tjk
rN   td|  Y nX d S NzOpened z in your browserzPlease navigate here: r   
webbrowseropenr~   infoErrorrH   )r+   auth_urlr   r   r   _open_auth_url  s    
zSpotifyOAuth._open_auth_urlc                 C   sb   |r|    d}n|  }d| d}| |}t|\}}| jd k	r^| j|kr^t| j||S N&Enter the URL you were redirected to: zGo to the following URL: z'
Enter the URL you were redirected to: )r   r   r<   r   r   r   r   r+   r   r;   r   rJ   r   r   r   r   r   _get_auth_response_interactive  s    

z+SpotifyOAuth._get_auth_response_interactivec                 C   sl   t |}|   |  |jd k	r*|jn>| jd k	rP|j| jkrPt| j|jn|jd k	r`|jS tdd S )N3Server listening on localhost has not been accessed)start_local_http_serverr   handle_requestrH   r   r   	auth_coder   r+   redirect_portserverr   r   r   _get_auth_response_local_server  s    

z,SpotifyOAuth._get_auth_response_local_serverc                 C   s   t d t| j}t|j\}}|dkr4t d |jdkrP|dkrPt d |d kr^| j}|r|dkr|jdkr|r| 	|S t d| d| d	 | j
|d
S NUser authentication requires interaction with your web browser. Once you enter your credentials and give authorization, you will be redirected to a url.  Paste that url you were directed to to complete the authorization.	localhostzUsing 'localhost' as a redirect URI is being deprecated. Use a loopback IP address such as 127.0.0.1 to ensure your app remains functional.http	127.0.0.1r   zhRedirect URIs using HTTP are being deprecated. To ensure your app remains functional, use HTTPS instead.zUsing `z8` as redirect URI without a port. Specify a port (e.g. `z:8080`) to allow automatic retrieval of authentication code instead of having to copy and paste the URL your browser is redirected to.)r   )r~   r   r   r7   r   netlocwarningschemer   r   r   r+   r   redirect_inforedirect_hostr   r   r   r   get_auth_response  s.    


zSpotifyOAuth.get_auth_responsec                 C   s   |r|  |S |  S r.   )r   r   r+   rJ   r   r   r   get_authorization_code  s    
z#SpotifyOAuth.get_authorization_codec           	   
   C   sD  |rt jdtdd |rX| | j }|dk	rX| |rH| |d }|rP|S |d S | j|pf| 	 dd}| j
r~| j
|d	< | jr| j|d
< |  }td| j d| d|  zX| jj| j||d| j| jd}|  | }| |}| j| |r|n|d W S  tjjk
r> } z| | W 5 d}~X Y nX dS )aW   Gets the access token for the app given the code

            Parameters:
                - code: the response code
                - as_dict: (deprecated) a boolean indicating if returning the access token
                            as a token_info dictionary, otherwise it will be returned
                            as a string.
        rf   rg   rh   Nr   rj   authorization_code)r7   r   ru   r/   r   rv   rw   rx   Try   )rk   rl   rm   r   rd   rn   rD   r   r7   r   r/   r   r   r~   r   r   r*   r   r^   r_   r   rK   rp   rq   r(   r   r   rP   )	r+   r   rr   rs   rB   r   r{   rJ   rO   r   r   r   rt     sP    	




zSpotifyOAuth.get_access_tokenc              
   C   s   |dd}|   }td| j d| d|  zX| jj| j||| j| jd}|  |	 }| 
|}d|krx||d< | j| |W S  tjjk
r } z| | W 5 d }~X Y nX d S )Nr   )r   ru   rv   rw   rx   rz   r{   r^   r}   )r   r~   r   r   r*   r   r^   r_   r   rK   rp   rd   rq   r(   r   r   rP   r+   r   r   r{   rJ   rB   rO   r   r   r   r     s,    
z!SpotifyOAuth.refresh_access_tokenc                 C   s&   t t |d  |d< | j|d< |S r   r   r=   r/   r@   rA   r/   r   r   r   r   rp   <  s    
z-SpotifyOAuth._add_custom_values_to_token_infoc                 C   s   t dt | | j S ) Gets the cached token for the app

            .. deprecated::
            This method is deprecated and may be removed in a future version.
        aH  Calling get_cached_token directly on the SpotifyOAuth object will be deprecated. Instead, please specify a CacheFileHandler instance as the cache_handler in SpotifyOAuth and use the CacheFileHandler's get_cached_token method. You can replace:
	sp.get_cached_token()

With:
	sp.validate_token(sp.cache_handler.get_cached_token())rk   rl   rm   r   rd   rn   r2   r   r   r   rn   E  s    zSpotifyOAuth.get_cached_tokenc                 C   s   t dt | j| d S NzCalling _save_token_info directly on the SpotifyOAuth object will be deprecated. Instead, please specify a CacheFileHandler instance as the cache_handler in SpotifyOAuth and use the CacheFileHandler's save_token_to_cache method.rk   rl   rm   rd   rq   r   r   r   r   _save_token_infoT  s
    zSpotifyOAuth._save_token_info)NNNNNNNNFTNTN)N)F)N)N)NTT)rT   rU   rV   __doc__r   r   r-   r   r   r   rY   r   r   r   r   r   r   r   rt   r   rp   rn   r   r   r   r   re   r   r      sB                R



)

;	c                       s   e Zd ZdZdZdZd+ fdd	Zdd	 Zd
d Zd,ddZ	d-ddZ
d.ddZdd Zd/ddZd0ddZdd Zdd Zdd Zd1dd Zd!d" Zd#d$ Zed%d& Zd'd( Zd)d* Z  ZS )2r   a   Implements PKCE Authorization Flow for client apps

    This auth manager enables *user and non-user* endpoints with only
    a client ID, redirect URI, and username. When the app requests
    an access token for the first time, the user is prompted to
    authorize the new client app. After authorizing the app, the client
    app is then given both access and refresh tokens. This is the
    preferred way of authorizing a mobile/desktop client.

    r   rZ   NTc                    s   t  |	 || _|| _|| _| || _|s2|rLtdt	 |rLtd |rt
t|ts~tdtt| d tt || _n |pttd }t||d| _|| _|| _d| _d| _d| _d| _|
| _dS )	a  
        Creates Auth Manager with the PKCE Auth flow.

        Parameters:
             * client_id: Must be supplied or set as environment variable
             * redirect_uri: Must be supplied or set as environment variable
             * state: Optional, no verification is performed
             * scope: Optional, either a list of scopes or comma separated string of scopes.
                      e.g, "playlist-read-private,playlist-read-collaborative"
             * cache_path: (deprecated) Optional, will otherwise be generated
                           (takes precedence over `username`)
             * username: (deprecated) Optional or set as environment variable
                         (will set `cache_path` to `.cache-{username}`)
             * proxies: Optional, proxy for the requests library to route through
             * requests_timeout: Optional, tell Requests to stop waiting for a response after
                                 a given number of seconds
             * requests_session: A Requests session
             * open_browser: Optional, whether the web browser should be opened to
                             authorize a user
             * cache_handler: An instance of the `CacheHandler` class to handle
                              getting and saving cached authorization tokens.
                              Optional, will otherwise use `CacheFileHandler`.
                              (takes precedence over `cache_path` and `username`)
        a  Specifying cache_path or username as arguments to SpotifyPKCE will be deprecated. Instead, please create a CacheFileHandler instance with the desired cache_path and username and pass it to SpotifyPKCE as the cache_handler. For example:

	from spotipy.oauth2 import CacheFileHandler
	handler = CacheFileHandler(cache_path=cache_path, username=username)
	sp = spotipy.SpotifyImplicitGrant(client_id, client_secret, redirect_uri, cache_handler=handler)r   type(cache_handler): r\   r   r   ZS256N)r]   r-   r   r7   r   r0   r/   rk   rl   rm   r`   rc   r   rb   r   rd   r    r!   r   r   r^   r_   _code_challenge_methodcode_verifiercode_challenger   r   )r+   r   r7   r   r/   r   r   r^   r_   r,   r   rd   re   r   r   r-   n  s8    %	
zSpotifyPKCE.__init__c                 C   s&   ddl }|dd}ddl}||S )z Spotify PCKE code verifier - See step 1 of the reference guide below
        Reference:
        https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow-with-proof-key-for-code-exchange-pkce
        r   N!   `   )randomrandintsecretsZtoken_urlsafe)r+   r   lengthr   r   r   r   _get_code_verifier  s    zSpotifyPKCE._get_code_verifierc                 C   sB   ddl }ddl}|| jd }||d}|ddS )z Spotify PCKE code challenge - See step 1 of the reference guide below
        Reference:
        https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow-with-proof-key-for-code-exchange-pkce
        r   Nutf-8= )	r   hashlibsha256r   r   digesturlsafe_b64encoder   replace)r+   r   r   Zcode_challenge_digestr   r   r   r   _get_code_challenge  s
    zSpotifyPKCE._get_code_challengec                 C   sn   | j s|   | jd| j| j| j d}| jr6| j|d< |dkrD| j}|dk	rT||d< t|}| j	 d| S )+ Gets the URL to use to authorize this app r   )r   r   r7   Zcode_challenge_methodr   r/   Nr   r   )
r   get_pkce_handshake_parametersr   r7   r   r/   r   r   r   r   r   r   r   r   r     s     

zSpotifyPKCE.get_authorize_urlc                 C   sV   |  |}z t| td| d W n& tjk
rP   td|  Y nX d S r   r   r+   r   r   r   r   r   r     s    

zSpotifyPKCE._open_auth_urlc                 C   s   t d t| j}t|j\}}|d kr0| j}|dkrBt d |jdkr^|dkr^t d |r|dkr|jdkr|r| 	|S t d| d| d	 | j
|d
S r   )r~   r   r   r7   r   r   r   r   r   r   r   r   r   r   r   _get_auth_response  s.    


zSpotifyPKCE._get_auth_responsec                 C   st   t |}|   |  | jd k	r<|j| jkr<t| j|j|jd k	rL|jS |jd k	rhtd|j ntdd S )Nz"Received error from OAuth server: r   )r   r   r   r   r   r   rH   r   r   r   r   r   r     s    

z+SpotifyPKCE._get_auth_response_local_serverFc                 C   sh   |s
| j r|   d}n|  }d| d}| |}| |\}}| jd k	rd| j|krdt| j||S r   )r   r   r   r<   r   r   r   r   r   r   r   r   &  s    

z*SpotifyPKCE._get_auth_response_interactivec                 C   s   |r|  |S |  S r.   )r   r   r   r   r   r   r   4  s    
z"SpotifyPKCE.get_authorization_codec                 C   sF   |d krd S d|ks&|  | j|d s*d S | |rB| |d }|S r   r   r   r   r   r   r   9  s     
zSpotifyPKCE.validate_tokenc                 C   s   t t |d  |d< |S r   r?   r   r   r   r   rp   J  s    z,SpotifyPKCE._add_custom_values_to_token_infoc                 C   s   |   | _|  | _d S r.   )r   r   r   r   r2   r   r   r   r   R  s    
z)SpotifyPKCE.get_pkce_handshake_parametersc              
   C   s"  |r<|  | j }|dk	r<| |r4| |d }|d S | jdksP| jdkrX|   | jd|ph| 	 | j
| jd}ddi}td| j d	| d
|  zN| jj| j||d| j| jd}|  | }| |}| j| |d W S  tjjk
r } z| | W 5 d}~X Y nX dS )a   Gets the access token for the app

            If the code is not given and no cached token is used, an
            authentication window will be shown to the user to get a new
            code.

            Parameters:
                - code - the response code from authentication
                - check_cache - if true, checks for a locally stored token
                                before requesting a new token
        Nr   rj   r   )r   ru   r   r7   r   Content-Type!application/x-www-form-urlencodedrv   rw   rx   Try   )r   rd   rn   rD   r   r   r   r   r   r   r7   r~   r   r   r*   r   r^   r_   r   rK   rp   rq   r(   r   r   rP   )r+   r   rs   rB   r   r{   rJ   rO   r   r   r   rt   V  sD    



zSpotifyPKCE.get_access_tokenc              
   C   s   |d| j d}ddi}td| j d| d|  zX| jj| j||| j| jd}|  |	 }| 
|}d|kr|||d< | j| |W S  tjjk
r } z| | W 5 d }~X Y nX d S )	Nr   )r   ru   r   r   r   rv   rw   rx   r   )r   r~   r   r   r*   r   r^   r_   r   rK   rp   rd   rq   r(   r   r   rP   r   r   r   r   r     s.    
z SpotifyPKCE.refresh_access_tokenc                 C   s"   |  |\}}|dkr|S |S dS r   r   r   r   r   r   r     s    zSpotifyPKCE.parse_response_codec                 C   s
   t | S r.   )r   r   )r   r   r   r   r     s    z#SpotifyPKCE.parse_auth_response_urlc                 C   s   t dt | | j S )NaG  Calling get_cached_token directly on the SpotifyPKCE object will be deprecated. Instead, please specify a CacheFileHandler instance as the cache_handler in SpotifyOAuth and use the CacheFileHandler's get_cached_token method. You can replace:
	sp.get_cached_token()

With:
	sp.validate_token(sp.cache_handler.get_cached_token())r   r2   r   r   r   rn     s    zSpotifyPKCE.get_cached_tokenc                 C   s   t dt | j| d S r   r   r   r   r   r   r     s
    zSpotifyPKCE._save_token_info)NNNNNNNNTTN)N)N)N)F)N)NT)rT   rU   rV   r   r   r   r-   r   r   r   r   r   r   r   r   r   rp   r   rt   r   r   rY   r   rn   r   r   r   r   re   r   r   _  sB              L


(


7

c                   @   s|   e Zd ZdZdZdddZdd Zdd
dZdddZdddZ	e
dd Zd ddZd!ddZdd Zdd Zdd ZdS )"r   a   Implements Implicit Grant Flow for client apps

    This auth manager enables *user and non-user* endpoints with only
    a client secret, redirect uri, and username. The user will need to
    copy and paste a URI from the browser every hour.

    Security Warning
    -----------------
    The OAuth standard no longer recommends the Implicit Grant Flow for
    client-side code. Spotify has implemented the OAuth-suggested PKCE
    extension that removes the need for a client secret in the
    Authentication Code flow. Use the SpotifyPKCE auth manager instead
    of SpotifyImplicitGrant.

    SpotifyPKCE contains all the functionality of
    SpotifyImplicitGrant, plus automatic response retrieval and
    refreshable tokens. Only a few replacements need to be made:

    * get_auth_response()['access_token'] ->
      get_access_token(get_authorization_code())
    * get_auth_response() ->
      get_access_token(get_authorization_code()); get_cached_token()
    * parse_response_token(url)['access_token'] ->
      get_access_token(parse_response_code(url))
    * parse_response_token(url) ->
      get_access_token(parse_response_code(url)); get_cached_token()

    The security concern in the Implicit Grant flow is that the token is
    returned in the URL and can be intercepted through the browser. A
    request with an authorization code and proof of origin could not be
    easily intercepted without a compromised network.
    r   NFc	           	      C   s   t d || _|| _|| _|s$|r>tdt |r>td |rxtt	|t
sptdtt	| d tt
 || _n |pttd }t||d| _| || _|| _d| _dS )	a   Creates Auth Manager using the Implicit Grant flow

        **See help(SpotifyImplicitGrant) for full Security Warning**

        Parameters
        ----------
        * client_id: Must be supplied or set as environment variable
        * redirect_uri: Must be supplied or set as environment variable
        * state: May be supplied, no verification is performed
        * scope: Optional, either a list of scopes or comma separated string of scopes.
                 e.g, "playlist-read-private,playlist-read-collaborative"
        * cache_handler: An instance of the `CacheHandler` class to handle
                              getting and saving cached authorization tokens.
                              May be supplied, will otherwise use `CacheFileHandler`.
                              (takes precedence over `cache_path` and `username`)
        * cache_path: (deprecated) May be supplied, will otherwise be generated
                      (takes precedence over `username`)
        * username: (deprecated) May be supplied or set as environment variable
                    (will set `cache_path` to `.cache-{username}`)
        * show_dialog: Interpreted as boolean
        zSpotify is deprecating the Implicit Grant Flow for client-side code. Use the SpotifyPKCE auth manager instead of SpotifyImplicitGrant. For more details and a guide to switching, see help(SpotifyImplicitGrant).a  Specifying cache_path or username as arguments to SpotifyImplicitGrant will be deprecated. Instead, please create a CacheFileHandler instance with the desired cache_path and username and pass it to SpotifyImplicitGrant as the cache_handler. For example:

	from spotipy.oauth2 import CacheFileHandler
	handler = CacheFileHandler(cache_path=cache_path, username=username)
	sp = spotipy.SpotifyImplicitGrant(client_id, client_secret, redirect_uri, cache_handler=handler)r   r   r\   r   r   N)r~   r   r   r7   r   rk   rl   rm   r`   rc   r   rb   r   rd   r    r!   r   r   r0   r/   r   r*   )	r+   r   r7   r   r/   r   r   r   rd   r   r   r   r-     s.    


zSpotifyImplicitGrant.__init__c                 C   s<   |d krd S d|ks&|  | j|d s*d S | |r8d S |S )Nr/   )rG   r/   rD   r   r   r   r   r   8  s     
z#SpotifyImplicitGrant.validate_tokenTc                 C   sf   |r.|  | j }|dks.| |s.|d S |r>| |}n
| |}| |}| j| |d S )a    Gets Auth Token from cache (preferred) or user interaction

        Parameters
        ----------
        * state: May be given, overrides (without changing) self.state
        * response: URI with token, can break expiration checks
        * check_cache: Interpreted as boolean
        Nrj   )r   rd   rn   rD   parse_response_tokenr   rp   rq   )r+   r   rJ   rs   rB   r   r   r   rt   G  s    

z%SpotifyImplicitGrant.get_access_tokenc                 C   sf   | j d| jd}| jr | j|d< |dkr.| j}|dk	r>||d< | jrLd|d< t|}| j d| S )	r   tokenr   r/   Nr   Tr   r   r   r   r   r   r   r   a  s    

z&SpotifyImplicitGrant.get_authorize_urlc                 C   sH   |  |\}}}}|dkr | j}|dk	r:||kr:t||||||dS )z3 Parse the response code in the given response url N)rj   
token_typer   r   )r   r   r   )r+   r   r   Zremote_stater   Zt_typeZexp_inr   r   r   r   u  s    
 z)SpotifyImplicitGrant.parse_response_tokenc                    s   t | }|j}|j}tdd |p&|p&| dD  d krVtd d   d dd krnt d  d< t fd	dd
D S )Nc                 s   s   | ]}| d V  qdS )r   N)rF   )r   ir   r   r   r     s     z?SpotifyImplicitGrant.parse_auth_response_url.<locals>.<genexpr>&rH   r   r   )r   r   c                 3   s   | ]}  |V  qd S r.   r   r   r   r   r   r     s     )r   rj   r   r   )r   fragmentr   r   rF   r   r@   r   )r   Zurl_componentsZ
fragment_sr   r   r   r   r     s    z,SpotifyImplicitGrant.parse_auth_response_urlc                 C   sV   |  |}z t| td| d W n& tjk
rP   td|  Y nX d S r   r   r   r   r   r   r     s    

z#SpotifyImplicitGrant._open_auth_urlc                 C   sl   t d t| j}t|j\}}|dkrB|jdkrB|rBt d | | t d t	
d}| ||S )z1 Gets a new auth **token** with user interaction r   r   r   zUsing a local redirect URI with a port, likely expecting automatic retrieval. Due to technical limitations, the authentication token cannot be automatically retrieved and must be copied and pasted.zJPaste that url you were directed to in order to complete the authorizationr   )r~   r   r   r7   r   r   r   r   r   r   r<   r   )r+   r   r   r   r   rJ   r   r   r   r     s    





z&SpotifyImplicitGrant.get_auth_responsec                 C   s&   t t |d  |d< | j|d< |S r   r   r   r   r   r   rp     s    
z5SpotifyImplicitGrant._add_custom_values_to_token_infoc                 C   s   t dt | | j S )r   aP  Calling get_cached_token directly on the SpotifyImplicitGrant object will be deprecated. Instead, please specify a CacheFileHandler instance as the cache_handler in SpotifyOAuth and use the CacheFileHandler's get_cached_token method. You can replace:
	sp.get_cached_token()

With:
	sp.validate_token(sp.cache_handler.get_cached_token())r   r2   r   r   r   rn     s    z%SpotifyImplicitGrant.get_cached_tokenc                 C   s   t dt | j| d S )NzCalling _save_token_info directly on the SpotifyImplicitGrant object will be deprecated. Instead, please specify a CacheFileHandler instance as the cache_handler in SpotifyOAuth and use the CacheFileHandler's save_token_to_cache method.r   r   r   r   r   r     s
    z%SpotifyImplicitGrant._save_token_info)NNNNNNFN)NNT)N)N)N)N)rT   rU   rV   r   r   r-   r   rt   r   r   rY   r   r   r   rp   rn   r   r   r   r   r   r     s2            
E   






	c                   @   s$   e Zd Zdd Zdd Zdd ZdS )RequestHandlerc              
   C   s   d  | j _| j _z$t| j\}}|| j _|| j _W n* tk
r^ } z|| j _W 5 d }~X Y nX | d | 	dd | 
  | j jrd}n2| j jrdtt| j j d}n| d d S | d| d	 d S )
N   r   z	text/htmlZ
successfulzfailed ()z2<html><body><h1>Invalid request</h1></body></html>zK<html>
<script>
window.close()
</script>
<body>
<h1>Authentication status: z</h1>
This window can be closed.
<script>
window.close()
</script>
<button class="closeButton" style="cursor: pointer" onclick="window.close();">
Close Window
</button>
</body>
</html>)r   r   rH   r   r   pathr   r   Zsend_responseZsend_headerZend_headershtmlescaper   _write)r+   r   r   rH   statusr   r   r   do_GET  s&    

zRequestHandler.do_GETc                 C   s   | j |dS )Nr   )wfilewriter   )r+   rN   r   r   r   r
    s    zRequestHandler._writec                 G   s   d S r.   r   )r+   formatargsr   r   r   log_message   s    zRequestHandler.log_messageN)rT   rU   rV   r  r
  r  r   r   r   r   r    s   %r  c                 C   s*   t d| f|}d|_d |_d |_d |_|S )Nr   T)r	   allow_reuse_addressr   Zauth_token_formrH   )porthandlerr   r   r   r   r     s    r   )(__all__r   r  loggingr    rA   urllib.parseparser   rk   r   Zhttp.serverr   r	   r
   r   r(   Zspotipy.cache_handlerr   r   Zspotipy.exceptionsr   r   Zspotipy.utilr   r   r   r   	getLoggerrT   r~   r   r$   r%   r   r   r   r   r  r   r   r   r   r   <module>   sH   	
	Tv  h  s  	-