Spotify getting acces token on a server - is Spotify API that bad? - python

I have a Python application that calls the Spotify API and then analyses the result (serach about song information and save them in a file). The only problem I got is that there is no way to acquire an authorization token with this poorly implemented API.
Neither the package "spotify" nor spotipy or any other third party library provides me this feature. And obviously if I have a server, I do not run any GUI or so, so I can not see any prompt.
Unfortunately I am not a script kiddie who plays with Node.js on his/her home Windows...
Are there any way to get a fresh token each time without using the fanciest GUI prompt?
headers = {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": "Bearer TEMPORARY_TOKEN_FROM_SPOTIFY"
}
response = requests.get("https://api.spotify.com/v1/me/player/currently-playing", headers=headers)
This is the current code that works the temporary access token from spotify webpage, but it expires really short.
The other problem with the fancypants GUI: If you login with your account, each API request will result in your own user account specific thing, for example, the "now playing" feature.

You could use the Authorisation Code Flow as mentioned and use the Refresh token part of that to renew your tokens that way you only need to log in once, however you could also use the Client Credentials Flow but this does expose your Client Id and Client Secret which is only recommended for server-side applications but doesn't ask for a login but also doesn't access any user specific functionality. You can find out more about the flows available from the Authorisation Guide

Related

Cloud Tasks masks Bearer Token (To a Public Cloud Run)

I have a public Cloud Run, authenticated by JWT Token. Working 100%.
The logic inside the Cloud Run to decode the token is in python:
def decode_jwt(token: str) -> dict:
try:
decoded_token = jwt.decode(
token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
return decoded_token if decoded_token["expires"] >= time.time() else None
except Exception as e:
raise InvalidTokenError
The Cloud Run is publicly available using a custom domain.
Now, I want to do some requests to the Cloud Run, using Cloud Tasks (each request have different parameters, created previously by a Cloud Functions).
In the Cloud Tasks, I create each task with a "Bearer {token}" parameter
Cloud Task Headers Code:
task["http_request"]["headers"] = \
{"Authorization": f"Bearer {token}",
"Accept": "application/json"}
First situation:
When I create the task without the "oidc_token" parameter in the http_request creation.
Cloud Run returns "403 Forbidden", and never reach the decode_jwt function inside cloud run.
Cloud Task http_request Code:
task = {
"http_request": {
"http_method": tasks_v2.HttpMethod.POST,
"url": url,
}
}
Second situation:
I add an "oidc_token".
task = {
"http_request": {
"http_method": tasks_v2.HttpMethod.POST,
"url": url,
"oidc_token": {
"service_account_email": "service-task#xxxxx.iam.gserviceaccount.com",
}
}
Now, the request reach the Cloud Run decode_jwt function, and the log in Cloud Run returns "InvalidTokenError".
Extra: I added a logging.info to expose the token received in Cloud Run, and is not the token I passed in the Cloud Task Creation.
Problem Summary:
you have a public (allUsers) Cloud Run service.
you have created your own authorization mechanism (HS256 - HMAC with SHA-256).
you want to assign a custom token for the HTTP Authorization Bearer value.
Cloud Run authorization is managed by IAP.
Authorization for the Cloud Run service is managed by the Identity Aware Proxy (IAP). If you add an HTTP Authorization Bearer token, IAP will verify that token. That step fails for your custom token which results in an HTTP 403 Forbidden error.
Cloud Tasks supports two types of HTTP Authorization Bearer tokens. OAuth Access tokens and OIDC Identity tokens. You cannot use your own token value to replace the supported types.
That leaves you with two options:
Enhance your code to support Google signed OIDC Identity Tokens.
Use a custom HTTP header that supports your custom token format.
Note: I do not recommend using HS256. HS256 is a symmetric algorithm which means the secret must be known to both sides in order to validate the payload. RS256 is an asymmetric algorithm which uses private/public key pairs. To verify only requires the public key. This is one of the strong design features of Google's use of private keys for service accounts and identities. If you switch to Google's method, all of the hard work is done for you.
You have to specificy the audience of your Cloud Run service, like that
task = {
"http_request": { # Specify the type of request.
"http_method": tasks_v2.HttpMethod.POST,
"url": url, # The full url path that the task will be sent to.
"oidc_token": {
"service_account_email": "service-task#xxxxx.iam.gserviceaccount.com",
"audience": base url of Cloud Run, no /sub/path
}
}

Authenticate with AWS ALB / Cognito

I am trying to authorize with an ALB from python. As I understand the ALB looks for "AWSELBAuthSessionCookie" cookies before letting you to the website. I also see these cookies when logging into the application myself (using username and password). Question is how do I obtain the values of these cookies if I want to authenticate myself to the website/api from a python program. Has anybody done this before?
I had the exact same problem and could only make it work using an API Gateway since they allow authorization via JWT in the authorization header of the request. This can easily be done in Python, e.g.
import boto3
import requests
client = boto3.client(
"cognito-idp",
region_name="<aws region of the cognito app client>"
)
response = client.initiate_auth(
ClientId="<cognito app client ID>",
AuthFlow="USER_PASSWORD_AUTH",
AuthParameters={
"USERNAME": "<username>",
"PASSWORD": "<password>",
"SECRET_HASH": "<secret hash>",
},
)
token = response["AuthenticationResult"]["AccessToken"]
headers = {"Authorization": f"Bearer {token}"}
requests.get("<api gateway url>", headers=headers)
However, I also needed to allow authorization via the Cognito UI. Thus, I had to use both the ALB and API Gateway.
While this solved the issue of making my application available both from the browser (i.e. for humans) as well as from code (i.e. for machines), it introduced a lot of additional AWS components I had to use. And, as a disadvantegous side effect, the API has a request payload limit of 10MB that cannot be increased. This is another issue for me.
I know it's been a year, but if you've solved the issue, feel free to share your solution.

Protect an API by using OAuth 2.0 with Azure Active Directory and API Management

I want to Protect amy API by using OAuth 2.0 with Azure Active Directory and API Management.
I have added my API in API management and I'm following this article https://learn.microsoft.com/en-in/azure/api-management/api-management-howto-protect-backend-with-aad.
This doc uses Azure developer console as sample app client to call the API endpoints but how can I creat my own app which will generate the auth code and I can call my APIs with using that application.
I tried using sample apps however I'm new for this so I'm not sure how to move further. So can you please help me with any python or node base code for this?
Thanks
Not sure where you are stuck. If you are a beginner for OAuth 2.0 with Azure Active Directory in Python, you can first dig into the code sample: Python authentication samples for Microsoft Graph, which shows how to use the Microsoft ADAL for Python for authentication. Please pay attention to sample_adal.py file.
#APP.route('/login/authorized')
def authorized():
"""Handler for the application's Redirect Uri."""
code = flask.request.args['code']
auth_state = flask.request.args['state']
if auth_state != SESSION.auth_state:
raise Exception('state returned to redirect URL does not match!')
auth_context = adal.AuthenticationContext(config.AUTHORITY_URL, api_version=None)
token_response = auth_context.acquire_token_with_authorization_code(
code, config.REDIRECT_URI, config.RESOURCE, config.CLIENT_ID, config.CLIENT_SECRET)
SESSION.headers.update({'Authorization': f"Bearer {token_response['accessToken']}",
'User-Agent': 'adal-sample',
'Accept': 'application/json',
'Content-Type': 'application/json',
'SdkVersion': 'sample-python-adal',
'return-client-request-id': 'true'})
return flask.redirect('/graphcall')

How to write a python script to authenticate to Azure DevOps REST API and get the access token?

How can I authenticate to Azure DevOps REST API in a python script?
I found that there are 2 methods :
Using personal access token (PAT)
Using OAuth 2.0
I am using the second method. Followed the steps in this documentation:
https://learn.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/oauth?view=azure-devops
I wrote this function to autherize to azure DevOps using OAuth 2.0:
def get_authenticated():
client_id = < my client ID as a string >
state = "user1"
scope = "vso.graph_manage%20vso.identity_manage%20vso.profile_write%20vso.project_manage%20vso.tokenadministration%20vso.tokens"
callback_URL = < Callback URL to my azure devops account >
# Azure DevOps Services authorization endpoint
Auth_URL = "https://app.vssps.visualstudio.com/oauth2/authorize?client_id=" + client_id + "&response_type=Assertion&state=" + state + "&scope=" + scope + "&redirect_uri=" + callback_URL
headers = {'Accept': 'application/json;api-version=1.0'}
print(Auth_URL)
response = requests.get(Auth_URL,headers = headers)
print(response)
print(response.status_code)
print(response.headers['content-type'])
response.raise_for_status()
But when calling this function, output I am getting is:
<Response [203]>
203
text/html; charset=utf-8
The auth URL is correct because when I tried to access the same URL in a browser it successfully redirects to a form to enter azure user credentials.
The expected behavior of the script is, when the auth_url is requested, Azure DevOps Services should ask the user to authorize. I think that should be done by prompting for username&password in terminal/via a browser.
I am totally new to python scripting and REST APIs.
Can someone help me by pointing out the faults in my code or pointing to some samples?
The http error 203 indicates that the returned metainformation is not a definitive set of the object from a server with a copy of the object, but is from a private overlaid web. In your code,you added headers = {'Accept': 'application/json;api-version=1.0'}, but in fact the content type should be application/x-www-form-urlencoded.
You can use some OAuth2 library for python to authenticate to Azure DevOps REST API, such as OAuthLib. It includes sevelral samples.
Also, you can refer to following topic, hope it is helpful for you.
Tutorial for using requests_oauth2

Authenticating with the REST web API of MS Dynamics CRM 2016 online

I'm trying to access the new REST API for building a server-to-server interface to integrate the CRM with other applications such as the web-shop etc.
I have tried both ways of getting an access-token from Azure AD:
Client credentials
import adal
token_response = adal.acquire_token_with_client_credentials(
'https://login.microsoftonline.com/abcdefgh-1234-5678-a1b1-morerandomstuff',
client_id,
secret
)
and user/password
import adal
token_response = adal.acquire_token_with_username_password(
'https://login.microsoftonline.com/abcdefgh-1234-5678-a1b1-morerandomstuff',
'interface#crm.my_domain.com',
'my_password'
)
In both cases, token_response gets a token-object, containing accessToken, refreshToken, expiresIn and so on. So I don't think there's an error up to this point.
Then I try to make a simple request to the web API:
headers = {'Authorization': '%s %s' % (token_response.get('tokenType'),
token_response.get('accessToken'))}
r = requests.get('https://domain.api.crm4.dynamics.com/api/data/v8.0/Product',
headers=headers)
This always returns a HTTP 401 - Unauthorized: Access is denied.
('WWW-Authenticate', 'Bearer error=invalid_token,
error_description=Error during token validation!,
authorization_uri=https://login.windows.net/eabcdefgh-1234-5678-a1b1-morerandomstuff/oauth2/authorize,
resource_id=https://domain.api.crm4.dynamics.com/')
The user that tries to make the request has Office-365-Administrator privileges and in the CRM has all manager roles and the administrator role. Which for my taste is even a bit much, but I read somewhere, that the user has to have office-365 admin privileges.
In the Azure AD there is an Application configured which has "delegated rights" to the CRM ("Access CRM Online as organization users").
What am I missing here? Or is my REST-get-request wrong?
Microsoft documentation for the new API is practically nonexistent - whenever you click some link you get docs for the older APIs (organization API etc.)
acquire_token_with_username_password has an optional parameter for specifying which resource you want access to:
resource (str, optional): The resource you are accessing. Defaults to
'https://management.core.windows.net/'.
You should thus be able to specify that you want access to CRM by adding resource as an argument to acquire_token_with_username_password:
token_response = adal.acquire_token_with_username_password(
'https://login.microsoftonline.com/abcdefgh-1234-5678-a1b1-morerandomstuff',
'interface#crm.my_domain.com',
'my_password',
resource='https://domain.crm4.dynamics.com'
)
This should give you a proper token for accessing CRM.
After getting the correct token, you also need to modify your Web API call slightly (from Product to products):
r = requests.get('https://domain.api.crm4.dynamics.com/api/data/v8.0/products',
headers=headers)

Categories