Making Spotipy program user-friendly - python

I've created a simple Python program using Spotipy that shows some recommended tracks based on the tracks downloaded in the user device. But I'm having some trouble on making the program user-friendly.
First of all, is there any problem by sharing my Client ID and my Client Secret with the user by, for example, uploading my code in GitHub? Can I use Redirect URI as being http://localhost/ or should I create a website for my program for securing purposes? In Username field, it should be the username of the account to be analyzed or it can be anything, like "Brian Rogers"?
In the authentication part, it shows the user in Python console the following message:
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.
Opening https://... in your browser
Enter the URL you were redirected to:
My question is: since I'm managing to use Tkinter, how can I redirect the input from the Tkinter input box to the Python console?
Finally, how long does the authentication token take to expire? And if so, how to renew it (if possible, so that only the user enters when they run the program for the first time)?
Thanks in advance for the patient!

I'll address all your questions one by one.
is there any problem by sharing my Client ID and my Client Secret with the user by, for example, uploading my code in GitHub?
One should always avoid putting personal credentials in the source. If someone misuses your credentials, you'll be the one who will be held responsible because they are YOUR credentials. In any case, the only havoc I can imagine one could cause is to spam requests to Spotify's API which I believe Spotify's API already has protections and will drop further requests if it detects request spam. I've had and seen some projects put their Spotify and YouTube API credentials by creating special accounts for generating API credentials for their projects, in their source code and push to GitHub to make the tool easier to setup for use.
Can I use Redirect URI as being http://localhost/ or should I create a website for my program for securing purposes? In Username field, it should be the username of the account to be analyzed or it can be anything, like "Brian Rogers"?
As you're only searching for relevant tracks on Spotify, I believe you probably don't need to access the personal information of the Spotify user whose credentials you are using. If so, you can avoid both passing the username and verifying the redirect URI, by using oauth2.SpotifyClientCredentials to authorize yourself:
import spotipy
import spotipy.oauth2 as oauth2
credentials = oauth2.SpotifyClientCredentials(
client_id=client_id,
client_secret=client_secret)
token = credentials.get_access_token()
# This won't prompt for verification of Redirect URI
sp = spotipy.Spotify(auth=token)
My question is: since I'm managing to use Tkinter, how can I redirect the input from the Tkinter input box to the Python console?
You won't need to, if you use oauth2.SpotifyClientCredentials as mentioned above.
Finally, how long does the authentication token take to expire? And if so, how to renew it (if possible, so that only the user enters when they run the program for the first time)?
As of writing this, the token stays valid for exactly one hour. You can confirm by checking the value of credentials.token_info["expires_in"] which displays the time in seconds.
Also, spotipy raises spotipy.client.SpotifyException when a dependent method has been called but the token has already expired. So, you could catch this exception and overwrite your previous spotipy.client.Spotify instance with a new one. At the minimal you would do something similar to this:
import spotipy
import spotipy.oauth2 as oauth2
def authenticate_calls():
credentials = oauth2.SpotifyClientCredentials(
client_id=client_id,
client_secret=client_secret,
)
token = credentials.get_access_token()
sp = spotipy.Spotify(auth=token)
return sp
sp = authenticate_calls()
try:
do_something_that_needs_authentication(sp)
except spotipy.client.SpotifyException:
sp = authenticate_calls()
do_something_that_needs_authentication(sp)
You could also create a decorator function which would refresh the token if expired and decorate your functions with it!

Related

How to redirect cognito login to website that requires authentication?

What I'm trying to do is create a seamless transition where you login to cognito and one way or another you get redirected to your API Gateway resource that requires cognito token authorization automatically.
my understanding of how to solve this problem:
you login to cognito and are redirected to a public API Gateway endpoint that is a lambda function. The lambda function takes the code that cognito passed as a header and uses that to generate a token. Next I want it to redirect to my website that requires cognito authentication and be logged in.
I'm not even quite sure if this approach is the appropriate one to take but I'm struggling to find resources out there explaining how you go about this.
one more questions that might help me understand the problem better:
Are headers held by your current session when you redirect to another link? I ask this because I thought (or atleast this is what I've seen in the youtube tutorials I've watched) that authentication is essentially JUST a header. If you have a valid header token="long random key" then you're authenticated. You want this header to stay in your session when you hop around your website right?
Can you redirect to a different url from lambda and hold onto your headers? Cause this is essentially what I'm trying to do.
Also I highlight the word automatically because every single youtube video I've seen shows them saying oh just change the word code in the login url to token like this: "https://domain123.auth.us-west-2.amazoncognito.com/login?client_id=19shq1tf1n1&response_type=token&scope=aws.cognito.signin.user.admin+email+openid+phone+profile&redirect_uri=https://actuallywebsiteIwanttogoto.com"
but then the problem is that it gives the access_token in the hash of the url when you login successfully. These people showing how to use cognito are manually extracting the access_token from the url and calling a get request on there demo website with the access_token as a header manually. I wanna know how to automate this!!!

How to add login credential via python script?

I am sure this is an answered question somewhere, but I could not find a solution for me (if you know a duplicate feel free to mark it).
My goal is to query a RESTful API. To do that I need to authenticate. The only way available for me for this API is via web-browser.
Basically, I am loading URL which redirects to another URL. In the second URL, there is a form where I need to input username and password to authenticate. Currently, I input the credentials manually via a web browser.
My question is: Can I do this programmatically such that I don't have to manually input the credentials via a web browser?
Note: the webpage is not verified.
Attempt 1
I tried the following commands, but none worked:
curl -k -u username:password https://myurl:port
It did not return anything.
Attempt 2
BASE_URL = 'https://myurl:port'
username = 'username'
password = 'password'
r = requests.get(BASE_URL, auth=(username, password), verify=False)
print(r.status_code, r.reason)
it prints <Response [200]> but it does not authenticate. This is because when I try to query an API endpoint - it says I am unauthorized. When I input the credentials manually in the browser - then the endpoint query works.
Attempt 3
Finally, I thought (but did not do) I need some way to read the login page and input credentials by getting the html elements and then clicking log in (with selenium for example), but this seems too rough and error-prone. Maybe there is a more elegant way.
Any ideas of how I can do this?
Basically, I am loading URL which redirects to another URL. In the
second URL, there is a form where I need to input username and
password to authenticate
From your description it sounds like you are accessing an API using OAuth2 with the authorization code grant flow ?...which by design requires the user (=resource owner) to authorise (via the form) your app (=client) to access his/her data provided by the API (=resource server). in this case, using basic auth (Attempt 1 and 2) will not help you as the API expects a token, not username/password. you'd probably need a refresh token that does not expire and would allow your client to request a fresh access token each time it wants to access the API...
It all depends on the authentication mechanism used by your API...I'd first figure out if your API is indeed using OAuth2, and if so, learn about authentication flows (e.g. https://www.udemy.com/course/learn-oauth-2/) ...client credentials flow is probably what you'd want if the API allows for it...
update: Attempt 3 might be worth a try, i've never done it though. you might be able to send the credentials by submitting the corresponding form data via python requests...then that should in theory provide you with an authorization code which you can use to get a token...
Thanks for showing us your attempts.
If the attempt 2 returns 200 status code. It means it does work which means that basic auth works. Simple solution is to just pass auth=(username, password) to all of your api calls.
Or you can use requests.Session() to authenticate once and keep the session provided that server supports session authentication.
Hope this helps.

invalid_grant Error when Authenticating oauth Request - Google Drive

I am attempting to get access to a customer's Google Drive to download their files. When testing the code on my own google drive, I am able to successfully download files. However, when I get the oauth code from them, I get the error:
oauth2client.client.FlowExchangeError: invalid_grant
After looking at some of the other answers, it has been suggested that you ensure access_type='offline', which seems to be the default and I can see in the generated url that this parameter is set. It also sounds like the code they give back may only be valid for an hour, however I have tried to use it within the hour and still no luck. Any other suggestions for how to avoid this problem?
Here is the code I have been running:
from oauth2client import client
import webbrowser
flow = client.flow_from_clientsecrets(
'client_secrets.json',
scope='https://www.googleapis.com/auth/drive.readonly',
redirect_uri='urn:ietf:wg:oauth:2.0:oob')
auth_uri = flow.step1_get_authorize_url()
webbrowser.open(auth_uri)
print auth_uri
auth_code = raw_input('Enter the auth code: ')
credentials = flow.step2_exchange(auth_code)
http_auth = credentials.authorize(httplib2.Http())
It errors on the flow.step2_exchange line.
There are three standard causes for this error that I am aware of.
The time is off on your PC. Solution: check it.
You are trying to use an authentication code more then once.
You can only use the authentication code once to get the refresh token.
You are testing and authenticating the code more then once. Each time you authenticate the code you get a valid refresh token. You can get up to 25 refresh tokens and then they stop working. Solution: make sure that every time you refresh the access you are using that refresh token and not an older one.
Also google as a Python tutorial that shows you how to access drive using the Python Client library it may make things easer for you. Python QuickStart

Python 3: Spotify Add Song To Public Playlist

So currently I have in place a system using Flask running on my localhost:8080 to add 1 song at a time to a public Spotify playlist. Unfortunately how I have implemented this it requires a browser to add a song. What I want to be able to do is URLLIB or possibly the REQUESTS library to do this without a browser. I have been unsuccessful in my attempts to do this bot URLLIB and REQUESTS only see the first page of my local host it is never redirected to my call back in the code that I linked below.
My Implementation
How would I go about implemented a non browser interface to add a song to a playlist? (Mind you this is python 3)
In order to add a song to a playlist, you need the playlist's owner to grant access to your application, so you will need a web interface at some point to carry out this step.
It's important that you obtain the access token using the Authorization Code Flow since that's how you will get an access token and a refresh token. Use that refresh token in your app to obtain access tokens without having the user to re-authorize your app.
So in brief:
Implement a web site that uses the Authorization Code. A user logs in and you obtain an access token and refresh token. Using the refresh token you will be able to generate access tokens without the user having to input their credentials.
Take the refresh token and include it in your script. Before making the request to add a track, obtain a fresh access token using the refresh token.
It's a bit cumbersome but that's the only way to use OAuth2 without exposing the user's username+password to an app.

gdata-python-api + Analytics with simple auth

I'm working on converting a Python script using the Google gdata API client + user/pass authentication to something more suitable for production (an API key). I am pretty frustrated with the muddled state of their documentation on authentication. I admittedly don't have a great grasp of OAuth2, but it seems like it's way more complicated for my usage case, which is: Hit Google Analytics every 24 hours to get the X most popular articles on our site.
In this scenario, we're not dealing with modifying someone's personal data, and all activity is centered on one account. It doesn't seem like OAuth2 is worth the complexity for something so simple.
I see that on the Google API Console (https://code.google.com/apis/console/), I've registered there and notice that there's a "Simple API Access" section with one key beneath the "Client ID for web applications" (which appears to be OAuth2). There's also the Google domain update page, https://www.google.com/accounts/UpdateDomain, but that appears to be OAuth related.
Is there any way to use this Simple API Access key (not OAuth) for retrieving analytics data with the Python gdata client, and if so, does anyone have any authentication examples? I already have the data retrieval stuff working once authenticated, but I'm using the user/pass approach, which is not appropriate for production.
Greg,
If you are already using the library gdata-python-client, this is relatively easy to do if you are the only user that your application will be authorizing.
The general mechanisms were detailed in a blog post in September, 2011, but I'll describe them here for completeness.
Part 1: Go to the APIs console and start a new project.
Part 2: From the project, go to "Services" and enable "Analytics API"
Part 3: From the project, go to "API Access" and click "Create an OAuth 2.0 client ID..." (you'll need to provide a product name, though the value you provide won't matter). When asked for the application type, select "Installed Application" and then "Create client ID". Since you will be the only user, you will only need one refresh token, and you can get this by authorizing from a desktop application a single time.
Part 4: Get your client id and client secret from the APIs console and then create an empty token:
import gdata.gauth
CLIENT_ID = 'id-from-apis-console'
CLIENT_SECRET = 'secret-from-apis-console'
SCOPE = 'https://www.google.com/analytics/feeds/' # Default scope for analytics
token = gdata.gauth.OAuth2Token(
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
scope=SCOPE,
user_agent='application-name-goes-here')
I got the scope from GData FAQ, though I'm not sure if it is correct.
Part 5: Use the token to create authorization URL for you to visit:
url = token.generate_authorize_url(redirect_uri='urn:ietf:wg:oauth:2.0:oob')
Since your application is an "Installed Application", your redirect URI is the default 'urn:ietf:wg:oauth:2.0:oob'. (Also note, the blog post had a typo and used the keyword argument redirect_url.)
Part 6: Visit the url and authorize your application to make requests on behalf of your account. After authorizing, you'll be redirected to a page with a code on it. This code will be used to exchange for an access token and a long-lived refresh token. The code has a life of 10 minutes and the access token has a life of an hour. The refresh token will allow you to get new access tokens for signing requests in perpetuity (or until you revoke the permission from your account).
Part 7: Use the code to get an access token:
code = 'random-string-from-redirected-page'
token.get_access_token(code) # This returns the token, but also changes the state
This again differs slightly from the blog post, because we are using an installed application.
Part 8: With the token you can now make all requests you want to make to the analytics client:
import gdata.analytics.client
client = gdata.analytics.client.AnalyticsClient()
token.authorize(client)
This is the big money right here. When an access token expires, the API requests signed with that token are rejected. However, by authorizing the client as above, when the said requests fail, the token attempts to use the refresh token to obtain a new access token. If it successfully obtains a new access token, the client resends the original API request, signed with the new access token.
I don't know anything about the Analytics API so I won't provide any more details there.
Future Use Note 1: Saving information for future use. You can re-use this from different places and after this use very easily. There are methods called token_to_blob and token_from_blob provided by the library that allow turning a token into a string and converting out of a string:
saved_blob_string = gdata.gauth.token_to_blob(token)
Once you have done this, you can store the string in a file and kill your running Python process. When you'd like to use it again:
saved_blob_string = retrieve_string_from_file() # You'll need to implement this
token = gdata.gauth.token_from_blob(saved_blob_string)
Future Use Note 2: This token will be able to be used to authorize a client and perform all your magic again and again, so long as you have the refresh token around. If for some reason you would like to get an access token again without calling token.generate_authorize_url, you'll need to manually set this on the object:
token.redirect_uri = 'urn:ietf:wg:oauth:2.0:oob'
Future Use Note 3: Also, if you lose your refresh token and would like to get another one without having to go to the browser to revoke the original, you can use the approval_prompt parameter to get a new refresh token by visiting the url generated by:
url = token.generate_authorize_url(
redirect_uri='urn:ietf:wg:oauth:2.0:oob',
approval_prompt='force')

Categories