[GH-ISSUE #852] Web-app with user logins for playlist creation #524

Closed
opened 2026-02-27 23:23:07 +03:00 by kerem · 7 comments
Owner

Originally created by @ccolas on GitHub (Sep 9, 2022).
Original GitHub issue: https://github.com/spotipy-dev/spotipy/issues/852

Hi,
I'm building an app to create playlists, so I need the app users to log in and give me the right to do so.

I want to host it on huggingface's spaces (https://huggingface.co/spaces).

Locally, my app works well, it opens a log in page and I can create my playlists. I use:

os.environ['SPOTIPY_REDIRECT_URI'] = 'http://localhost:8080/'
scope = "playlist-modify-public"
token = util.prompt_for_user_token(scope=scope)
sp = spotipy.Spotify(auth=token) 

Now I want to put it online. I update the SPOTIPY_REDIRECT_URI to the home page of my app both on the dashboard and in the app script.

Here is the error I get:

2022-09-09 17:33:34.597 User 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.
2022-09-09 17:33:34.598 Opened https://accounts.spotify.com/authorize?client_id=394d4c00bf404a97977b0b494a571679&response_type=code&redirect_uri=https%3A%2F%2Fhuggingface.co%2Fspaces%2Fccolas%2FEmotionPlaylist%2F&scope=playlist-modify-public in your browser
2022-09-09 17:33:34.598 Uncaught app exception
Traceback (most recent call last):
  File "/home/user/.local/lib/python3.8/site-packages/spotipy/oauth2.py", line 115, in _get_user_input
    return raw_input(prompt)
NameError: name 'raw_input' is not defined

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/user/.local/lib/python3.8/site-packages/streamlit/scriptrunner/script_runner.py", line 554, in _run_script
    exec(code, module.__dict__)
  File "/home/user/app/app.py", line 265, in <module>
    setup_streamlite()
  File "/home/user/app/app.py", line 123, in setup_streamlite
    sp, user_id = get_client()
  File "/home/user/app/app.py", line 41, in get_client
    token = util.prompt_for_user_token(scope=scope)
  File "/home/user/.local/lib/python3.8/site-packages/spotipy/util.py", line 99, in prompt_for_user_token
    code = sp_oauth.get_auth_response()
  File "/home/user/.local/lib/python3.8/site-packages/spotipy/oauth2.py", line 499, in get_auth_response
    return self._get_auth_response_interactive(open_browser=open_browser)
  File "/home/user/.local/lib/python3.8/site-packages/spotipy/oauth2.py", line 450, in _get_auth_response_interactive
    response = self._get_user_input(prompt)
  File "/home/user/.local/lib/python3.8/site-packages/spotipy/oauth2.py", line 117, in _get_user_input
    return input(prompt)
EOFError: EOF when reading a line

This seems related to this issue and to that one

From what I read there, this has to do with the fact that the server cannot open a new tab for the login page. There seem to be some complicated trick to generate a long-term token for a given user (me), then to use that to connect.
But I want to have many users and I can't ask them to each go through the generation of this long term token.

So what's the solution? For sure there must be a way to build a non-local spotify app that requires users login and allows playlist creation.

What did I miss?

Originally created by @ccolas on GitHub (Sep 9, 2022). Original GitHub issue: https://github.com/spotipy-dev/spotipy/issues/852 Hi, I'm building an app to create playlists, so I need the app users to log in and give me the right to do so. I want to host it on huggingface's spaces (https://huggingface.co/spaces). Locally, my app works well, it opens a log in page and I can create my playlists. I use: ``` os.environ['SPOTIPY_REDIRECT_URI'] = 'http://localhost:8080/' scope = "playlist-modify-public" token = util.prompt_for_user_token(scope=scope) sp = spotipy.Spotify(auth=token) ``` Now I want to put it online. I update the SPOTIPY_REDIRECT_URI to the home page of my app both on the dashboard and in the app script. Here is the error I get: ``` 2022-09-09 17:33:34.597 User 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. 2022-09-09 17:33:34.598 Opened https://accounts.spotify.com/authorize?client_id=394d4c00bf404a97977b0b494a571679&response_type=code&redirect_uri=https%3A%2F%2Fhuggingface.co%2Fspaces%2Fccolas%2FEmotionPlaylist%2F&scope=playlist-modify-public in your browser 2022-09-09 17:33:34.598 Uncaught app exception Traceback (most recent call last): File "/home/user/.local/lib/python3.8/site-packages/spotipy/oauth2.py", line 115, in _get_user_input return raw_input(prompt) NameError: name 'raw_input' is not defined During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/user/.local/lib/python3.8/site-packages/streamlit/scriptrunner/script_runner.py", line 554, in _run_script exec(code, module.__dict__) File "/home/user/app/app.py", line 265, in <module> setup_streamlite() File "/home/user/app/app.py", line 123, in setup_streamlite sp, user_id = get_client() File "/home/user/app/app.py", line 41, in get_client token = util.prompt_for_user_token(scope=scope) File "/home/user/.local/lib/python3.8/site-packages/spotipy/util.py", line 99, in prompt_for_user_token code = sp_oauth.get_auth_response() File "/home/user/.local/lib/python3.8/site-packages/spotipy/oauth2.py", line 499, in get_auth_response return self._get_auth_response_interactive(open_browser=open_browser) File "/home/user/.local/lib/python3.8/site-packages/spotipy/oauth2.py", line 450, in _get_auth_response_interactive response = self._get_user_input(prompt) File "/home/user/.local/lib/python3.8/site-packages/spotipy/oauth2.py", line 117, in _get_user_input return input(prompt) EOFError: EOF when reading a line ``` This seems related to [this issue](https://github.com/plamere/spotipy/issues/835) and to [that one](https://github.com/plamere/spotipy/issues/632) From what I read [there](https://github.com/plamere/spotipy/issues/632#issuecomment-986044056), this has to do with the fact that the server cannot open a new tab for the login page. There seem to be some [complicated trick](https://github.com/plamere/spotipy/issues/632#issuecomment-986044056) to generate a long-term token for a given user (me), then to use that to connect. **But I want to have many users and I can't ask them to each go through the generation of this long term token**. So what's the solution? For sure there must be a way to build a non-local spotify app that requires users login and allows playlist creation. What did I miss?
kerem 2026-02-27 23:23:07 +03:00
  • closed this issue
  • added the
    question
    label
Author
Owner

@git-eri commented on GitHub (Sep 10, 2022):

I think you have multiple problems with your project but let me give you some tipps to improve and work with the spotify authorization scheme.

Take a good look at session handlers and how the spotify authorization flow works. Especially if you are using Flask you can take very valuable informations from the example using the FlaskSessionCacheHandler app.py. That one works great example to extend with your own ideas.

Your current app depends on sending back the url spotify gives you to your app. But there is a way simpler method by using cache-/session handlers.

Then the cache handler will store the auth & refresh token for you for every session, that is how i understood it worked.
Then your users just have to login with spotify and you will be redirected to your app automatically and the user gets a cookie assigned. If you don't use Flask you can use Redis for example.

I myself got most of the informations to build a multi-user app from the examples so i hope i could help you out.
(If you are using Flask and are trying to use the FlaskSessionCacheHandler please refer to this issue#838)

<!-- gh-comment-id:1242763994 --> @git-eri commented on GitHub (Sep 10, 2022): I think you have multiple problems with your project but let me give you some tipps to improve and work with the spotify authorization scheme. Take a good look at session handlers and how the spotify authorization flow works. Especially if you are using Flask you can take very valuable informations from the example using the FlaskSessionCacheHandler [app.py](https://github.com/plamere/spotipy/blob/master/examples/app.py). That one works great example to extend with your own ideas. Your current app depends on sending back the url spotify gives you to your app. But there is a way simpler method by using cache-/session handlers. Then the cache handler will store the auth & refresh token for you for every session, that is how i understood it worked. Then your users just have to login with spotify and you will be redirected to your app automatically and the user gets a cookie assigned. If you don't use Flask you can use Redis for example. I myself got most of the informations to build a multi-user app from the examples so i hope i could help you out. (If you are using Flask and are trying to use the FlaskSessionCacheHandler please refer to this [issue#838](https://github.com/plamere/spotipy/issues/838#issuecomment-1180726732))
Author
Owner

@ccolas commented on GitHub (Sep 13, 2022):

Thanks, I checked the cache handler stuff and found a way to achieve what i wanted.
Here it is with comments.

scope = "playlist-modify-public"
cache_handler = StreamlitCacheHandler(session)  # same as the FlaskSessionCacheHandler
auth_manager = spotipy.oauth2.SpotifyOAuth(scope=scope,
                                                                         cache_handler=cache_handler,
                                                                         show_dialog=True)
# if there is no cached token, open the sign in page
if not auth_manager.validate_token(cache_handler.get_cached_token()):
   auth_url = auth_manager.get_authorize_url()  # log in url

   # if you're redirected from the sign in page, there is a code in the url
   if 'code' in st.experimental_get_query_params():  
       auth_manager.get_access_token(st.experimental_get_query_params()['code'])  # use the code to generate the token
       sp = spotipy.Spotify(auth_manager=auth_manager)  
   else:  # if no code, add a button linking to the log in url
       add_button(auth_url, 'Log in')  # this adds a button linking to the authorization page
return sp
<!-- gh-comment-id:1245024338 --> @ccolas commented on GitHub (Sep 13, 2022): Thanks, I checked the cache handler stuff and found a way to achieve what i wanted. Here it is with comments. ``` scope = "playlist-modify-public" cache_handler = StreamlitCacheHandler(session) # same as the FlaskSessionCacheHandler auth_manager = spotipy.oauth2.SpotifyOAuth(scope=scope, cache_handler=cache_handler, show_dialog=True) # if there is no cached token, open the sign in page if not auth_manager.validate_token(cache_handler.get_cached_token()): auth_url = auth_manager.get_authorize_url() # log in url # if you're redirected from the sign in page, there is a code in the url if 'code' in st.experimental_get_query_params(): auth_manager.get_access_token(st.experimental_get_query_params()['code']) # use the code to generate the token sp = spotipy.Spotify(auth_manager=auth_manager) else: # if no code, add a button linking to the log in url add_button(auth_url, 'Log in') # this adds a button linking to the authorization page return sp ```
Author
Owner

@jamesschiffres commented on GitHub (Oct 18, 2022):

@ccolas running into the same issue - where is StreamlitCacheHandler being imported from? also where does the 'session' argument come from? I am using Django

<!-- gh-comment-id:1281748656 --> @jamesschiffres commented on GitHub (Oct 18, 2022): @ccolas running into the same issue - where is StreamlitCacheHandler being imported from? also where does the 'session' argument come from? I am using Django
Author
Owner

@ccolas commented on GitHub (Oct 18, 2022):

The StreamlitCacheHandler is a copy paste from the Flask one I defined myself:

class StreamlitCacheHandler(spotipy.cache_handler.CacheHandler):
"""
A cache handler that stores the token info in the session framework
provided by streamlit.
"""
    def __init__(self, session):
        self.session = session

    def get_cached_token(self):
        token_info = None
        try:
            token_info = self.session["token_info"]
        except KeyError:
            print("Token not found in the session")

        return token_info

    def save_token_to_cache(self, token_info):
        try:
            self.session["token_info"] = token_info
        except Exception as e:
            print("Error saving token to cache: " + str(e))

With streamlit, the session is streamlit.session_state. It's a dict that stays cached across page reloads. I'm assuming Django has something similar, so you should use this.

<!-- gh-comment-id:1281956288 --> @ccolas commented on GitHub (Oct 18, 2022): The StreamlitCacheHandler is a copy paste from the Flask one I defined myself: ``` class StreamlitCacheHandler(spotipy.cache_handler.CacheHandler): """ A cache handler that stores the token info in the session framework provided by streamlit. """ def __init__(self, session): self.session = session def get_cached_token(self): token_info = None try: token_info = self.session["token_info"] except KeyError: print("Token not found in the session") return token_info def save_token_to_cache(self, token_info): try: self.session["token_info"] = token_info except Exception as e: print("Error saving token to cache: " + str(e)) ``` With streamlit, the session is streamlit.session_state. It's a dict that stays cached across page reloads. I'm assuming Django has something similar, so you should use this.
Author
Owner

@sutaminajing40 commented on GitHub (Mar 6, 2023):

@ccolas
I have a question about one previous code.
I use streamlit to create web apps.
Is the 'session' argument in line 2 flask.session? And what does the return sp in the last line mean?

<!-- gh-comment-id:1455435632 --> @sutaminajing40 commented on GitHub (Mar 6, 2023): @ccolas I have a question about one previous code. I use streamlit to create web apps. Is the 'session' argument in line 2 flask.session? And what does the return sp in the last line mean?
Author
Owner

@ccolas commented on GitHub (Mar 7, 2023):

hi
the session argument is streamlit's session state: streamlit.session_state. If you use streamlit you might not need flask? I don't know how flask works.
sp is the spotify client, once created you can use it with the spotify api, eg sp.recommendation_genre_seeds()['genres'] gives you the list of spotify genres, etc.

<!-- gh-comment-id:1457503429 --> @ccolas commented on GitHub (Mar 7, 2023): hi the session argument is streamlit's session state: `streamlit.session_state`. If you use streamlit you might not need flask? I don't know how flask works. sp is the spotify client, once created you can use it with the spotify api, eg `sp.recommendation_genre_seeds()['genres']` gives you the list of spotify genres, etc.
Author
Owner

@sutaminajing40 commented on GitHub (Mar 12, 2023):

@git-eri
@ccolas
Thanks for the reply.
I have made the following changes to my code to make use of what I have been taught.

class StreamlitCacheHandler(spotipy.cache_handler.CacheHandler):
    """
    A cache handler that stores the token info in the session framework
    provided by streamlit.
    """
    def __init__(self, session):
        self.session = session

    def get_cached_token(self):
        token_info = None
        try:
            token_info = self.session["token_info"]
        except KeyError:
            print("Token not found in the session")

        return token_info

    def save_token_to_cache(self, token_info):
        try:
            self.session["token_info"] = token_info
        except Exception as e:
            print("Error saving token to cache: " + str(e))


def authorization():
    scope = "playlist-modify-public"
    cache_handler = StreamlitCacheHandler(st.session_state)  # same as the FlaskSessionCacheHandler
    auth_manager = spotipy.oauth2.SpotifyOAuth(scope=scope,
                                                cache_handler=cache_handler,
                                                show_dialog=True)
    # if there is no cached token, open the sign in page
    if not auth_manager.validate_token(cache_handler.get_cached_token()):
        auth_url = auth_manager.get_authorize_url()  # log in url

    # if you're redirected from the sign in page, there is a code in the url
    if 'code' in st.experimental_get_query_params():  
        auth_manager.get_access_token(st.experimental_get_query_params()['code'])  # use the code to generate the token
        sp = spotipy.Spotify(auth_manager=auth_manager)  
    else:  # if no code, add a button linking to the log in url
        st.button(auth_url, 'Log in')  # this adds a button linking to the authorization page
    return sp

However, the following error code is displayed.This was found to be the case with the variable 'sp' not containing any authentication information, i.e. the authentication was not successful. Is there anything wrong with my code?

  File "/home/appuser/venv/lib/python3.9/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 563, in _run_script

    exec(code, module.__dict__)

  File "/app/rms_sourse/app.py", line 208, in <module>

    main()

  File "/app/rms_sourse/app.py", line 18, in main

    sp = authorization()

  File "/app/rms_sourse/app.py", line 71, in authorization

    return sp

UnboundLocalError: local variable 'sp' referenced before assignment
<!-- gh-comment-id:1465061267 --> @sutaminajing40 commented on GitHub (Mar 12, 2023): @git-eri @ccolas Thanks for the reply. I have made the following changes to my code to make use of what I have been taught. ``` class StreamlitCacheHandler(spotipy.cache_handler.CacheHandler): """ A cache handler that stores the token info in the session framework provided by streamlit. """ def __init__(self, session): self.session = session def get_cached_token(self): token_info = None try: token_info = self.session["token_info"] except KeyError: print("Token not found in the session") return token_info def save_token_to_cache(self, token_info): try: self.session["token_info"] = token_info except Exception as e: print("Error saving token to cache: " + str(e)) def authorization(): scope = "playlist-modify-public" cache_handler = StreamlitCacheHandler(st.session_state) # same as the FlaskSessionCacheHandler auth_manager = spotipy.oauth2.SpotifyOAuth(scope=scope, cache_handler=cache_handler, show_dialog=True) # if there is no cached token, open the sign in page if not auth_manager.validate_token(cache_handler.get_cached_token()): auth_url = auth_manager.get_authorize_url() # log in url # if you're redirected from the sign in page, there is a code in the url if 'code' in st.experimental_get_query_params(): auth_manager.get_access_token(st.experimental_get_query_params()['code']) # use the code to generate the token sp = spotipy.Spotify(auth_manager=auth_manager) else: # if no code, add a button linking to the log in url st.button(auth_url, 'Log in') # this adds a button linking to the authorization page return sp ``` However, the following error code is displayed.This was found to be the case with the variable 'sp' not containing any authentication information, i.e. the authentication was not successful. Is there anything wrong with my code? ``` File "/home/appuser/venv/lib/python3.9/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 563, in _run_script exec(code, module.__dict__) File "/app/rms_sourse/app.py", line 208, in <module> main() File "/app/rms_sourse/app.py", line 18, in main sp = authorization() File "/app/rms_sourse/app.py", line 71, in authorization return sp UnboundLocalError: local variable 'sp' referenced before assignment ```
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/spotipy#524
No description provided.