def prompt_for_user_token(client_id: str, client_secret: str, redirect_uri: str, scope=None) -> RefreshingToken: """ Prompt for manual authentication. Open a web browser for the user to log in with Spotify. Prompt to paste the URL after logging in to parse the `code` URL parameter. Parameters ---------- client_id client ID client_secret client secret redirect_uri whitelisted redirect URI scope access rights as a space-separated list Returns ------- RefreshingToken automatically refreshing user token """ cred = RefreshingCredentials(client_id, client_secret, redirect_uri) url = cred.user_authorisation_url(scope, show_dialog=True) print('Opening browser for Spotify login...') webbrowser.open(url) redirected = input('Please paste redirect URL: ').strip() code = parse_code_from_url(redirected) return cred.request_user_token(code)
def request_client_token(client_id: str, client_secret: str) -> RefreshingToken: """ Request for client credentials. Parameters ---------- client_id client ID client_secret client secret Returns ------- RefreshingToken automatically refreshing client token """ cred = RefreshingCredentials(client_id, client_secret) return cred.request_client_token()
def refresh_user_token(client_id: str, client_secret: str, refresh_token: str) -> RefreshingToken: """ Request a refreshed user token. Parameters ---------- client_id client ID client_secret client secret refresh_token refresh token Returns ------- RefreshingToken automatically refreshing user token """ cred = RefreshingCredentials(client_id, client_secret) return cred.refresh_user_token(refresh_token)
def on_submit_creds(self) -> None: """ Checking if the submitted credentials were correct, and starting the logging-in step. """ # Obtaining the input data form_client_id = self.web_form.client_id form_client_secret = self.web_form.client_secret logging.info("Input creds: '%s' & '%s'", form_client_id, form_client_secret) # Checking that the data isn't empty empty_field = False if form_client_id == '': self.web_form.input_client_id.highlight() empty_field = True else: self.web_form.input_client_id.undo_highlight() if form_client_secret == '': self.web_form.input_client_secret.highlight() empty_field = True else: self.web_form.input_client_secret.undo_highlight() if empty_field: return # Hiding the form and showing the web browser for the next step self.web_form.hide() self.browser.show() # Creating the request URL to obtain the authorization token self.creds = RefreshingCredentials( form_client_id, form_client_secret, self.redirect_uri) self.scope = scopes.user_read_currently_playing url = self.creds.user_authorisation_url(self.scope) self.browser.url = url
class SpotifyWebPrompt(QWidget): """ This widget handles all the interaction with the user to obtain the credentials for the Spotify Web API. """ done = Signal(RefreshingToken) def __init__(self, client_id: str, client_secret: str, redirect_uri: str, *args) -> None: """ Starts the API initialization flow, which is the following: 1. The user inputs the credentials. ** self.on_submit_creds is called ** 2. The user logs in. ** self.on_login is called ** 3. If the credentials were correct, the Web API is set up and started from outside this widget. Otherwise, go back to step 1. ** self.done is emitted ** """ super().__init__(*args) logging.info("Initializing the Spotify Web API prompt interface") self.redirect_uri = redirect_uri # Creating the layout self.layout = QHBoxLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) self.layout.setSpacing(0) # The web form for the user to input the credentials. self.web_form = SpotifyWebForm(client_id=client_id, client_secret=client_secret) # on_submit_spotify_web creds will be called once the credentials have # been input. self.web_form.button.clicked.connect(self.on_submit_creds) self.layout.addWidget(self.web_form) # The web browser for the user to login and grant access. # It's hidden at the beggining and will appear once the credentials # are input. self.browser = WebBrowser() self.browser.hide() # The initial screen with the web form will be shown if the user # clicks on the Go Back button. self.browser.go_back_button.pressed.connect( lambda: (self.browser.hide(), self.web_form.show())) # Any change in the browser URL will redirect to on__login to check if # the login was succesful. self.browser.web_view.urlChanged.connect(self.on_login) self.layout.addWidget(self.browser) @property def client_id(self) -> str: return self.web_form.client_id @property def client_secret(self) -> str: return self.web_form.client_secret @Slot() def on_submit_creds(self) -> None: """ Checking if the submitted credentials were correct, and starting the logging-in step. """ # Obtaining the input data form_client_id = self.web_form.client_id form_client_secret = self.web_form.client_secret logging.info("Input creds: '%s' & '%s'", form_client_id, form_client_secret) # Checking that the data isn't empty empty_field = False if form_client_id == '': self.web_form.input_client_id.highlight() empty_field = True else: self.web_form.input_client_id.undo_highlight() if form_client_secret == '': self.web_form.input_client_secret.highlight() empty_field = True else: self.web_form.input_client_secret.undo_highlight() if empty_field: return # Hiding the form and showing the web browser for the next step self.web_form.hide() self.browser.show() # Creating the request URL to obtain the authorization token self.creds = RefreshingCredentials( form_client_id, form_client_secret, self.redirect_uri) self.scope = scopes.user_read_currently_playing url = self.creds.user_authorisation_url(self.scope) self.browser.url = url @Slot() def on_login(self) -> None: """ This function is called once the user has logged into Spotify to obtain the access token. Part of this function is a reimplementation of `tekore.prompt_user_token`. It does the same thing but in a more automatized way, because Qt has access over the web browser too. """ url = self.browser.url logging.info("Now at: %s", url) # If the URL isn't the Spotify response URI (localhost), do nothing if url.find(self.redirect_uri) == -1: return # Trying to get the auth token from the URL with Tekore's # parse_code_from_url(), which throws a KeyError if the URL doesn't # contain an auth token or if it contains more than one. try: code = parse_code_from_url(url) except KeyError as e: logging.info("ERROR: %s", str(e)) return # Now the user token has to be requested to Spotify, while # checking for errors to make sure the credentials were correct. # This will only happen with the client secret because it's only # checked when requesting the user token. try: # A RefreshingToken is used instead of a regular Token so that # it's automatically refreshed before it expires. self.creds is # of type `RefreshingCredentials`, so it returns always a # RefreshingToken. token = self.creds.request_user_token(code) except OAuthError as e: self.browser.hide() self.web_form.show() self.web_form.show_error(str(e)) return # Removing the GUI elements used to obtain the credentials self.layout.removeWidget(self.web_form) self.web_form.hide() self.layout.removeWidget(self.browser) self.browser.hide() # Finally starting the Web API self.done.emit(token)
def _initialise(self): return RefreshingCredentials(self.client_id, self.client_secret, self.redirect_uri)