I have a Google App Engine Python application that interacts with a JIRA Cloud instance.
To date the application makes urlfetch calls to JIRA using Basic Authentication but as it is now making dozens of separate REST calls both POST and GET I was hoping that using cookie authentication would reduce the latency somewhat.
example Basic-Authentication code that works:
result = urlfetch.fetch(jira_url,
deadline=60,
headers={"Authorization": "Basic %s" %
base64.b64encode("%s:%s" % (username, password))
})
and cookie alternative:
result = urlfetch.fetch(jira_url,
deadline=60,
headers={"X-Atlassian-Token": "no-check",
"Cookie": "JSESSIONID=529…snip…C4C"
})
I retrieved the JSESSIONID successfully using the instructions but the cookie query will only return "401 - Unauthorized" error while loading this page. errors as if the cookie has expired.
{
"errorMessages" : [
"You do not have the permission to see the specified issue.",
"Login Required"
],
"errors" : {}
}
I am recalling the cookie from Memcache as most of the interactions are made from a Task Queue and necessarily a separate thread, but the cookie is generated immediately before the recall.
The cookie is not expired. The cause of the issue is that JSESSIONID is not the only cookie which is required for the successful authentication of subsequent requests.
I encountered the same issue and solved it by re-sending all cookies I got from the server when creating the session. In my case (the JIRA server is hosted at Atlassian Cloud) these required cookies are: atlassian.xsrf.token and studio.crowd.tokenkey.
I posted a detailed article on this here: JIRA REST API: Cookie-based Authentication. The example of the code is in C# but the general idea should be clear.
Related
Context:
I have a Django based application. This application has various REST API endpoints where users can gather data. Some of this data needs to be pulled from a third-party API. This external API uses basic authentication.
In order to fetch this data, I have the following code implemented in my endpoint logic.
def metadata(jira_key: str, format=None):
username = "example"
password = "example"
try:
print(f"fetching {jira_key} with '{username}' credentials")
url = f"https://external.api.com/issue/{jira_key}"
session = requests.Session()
session.auth = username, password
headers = {'Content-Type': 'application/json'}
response = session.get(
url, headers=headers)
print(f"response: {response.status_code}")
return response
except Exception as e:
message = {"error": "Uncaught error", "message": str(e)}
return message
Long story short; it works! This endpoint is essentially just a proxy for another API. This is done for security purposes.
However, I have been experiencing lock-outs where permission for the service account needs to be reinstated periodically...
I suspect the session is being generated every time the endpoint is hit.
So my question is this:
How can I implement a persisted request.Session() with basic auth that is established at build time, and reused for each requests?
Your problem is solved by this answer, namely instantiate your session in the ready function of the app config class, which is run only once when starting django and then stored in the application memory.
I want to route my Google Analytics Reporting API request (code will be in AWS Lambda) through a gateway which accepts a REST endpoint only. Since I cant use the Client package method in my interaction with the gateway, I need to query the API as a REST-ful endpoint.
The official document says this (Link) :
Authorization: Bearer {oauth2-token}
GET https://www.googleapis.com/analytics/v3/data/ga
?ids=ga:12345
&start-date=2008-10-01
&end-date=2008-10-31
&metrics=ga:sessions,ga:bounces
I do not know to create the oauth2-token in Python. I have created a service account and have the secrets_json which includes the client id and secret key.
Then client package method as given in this link works. But I need the Rest method only!
Using these, how can I create the oauth2-token ?
You can use Oauth2 for this I have done it in the past but you will need to monitor it. You will need to authorize this code once and save the refresh token. Refresh tokens are long lived they normally dont expire but your code should be able to contact you if it does so that you can authorize it again. If you save the refresh token you can use the last step at any time to request a new access token.
Oauth2 is basicly built up into three calls. I can give you the HTTP calls i will let you work out the Python Google 3 Legged OAuth2 Flow
Authencation and authorization
The first thing you need is the permission of the user. To get that you build a link on the authorization server. This is a HTTP get request you can place it in a normal browser window to test it.
GET https://accounts.google.com/o/oauth2/auth?client_id={clientid}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=https://www.googleapis.com/auth/analytics.readonly&response_type=code
Note on redirect uri. If you are running this on a server or something then use urn:ietf:wg:oauth:2.0:oob it basicly tells the server to return the code back where it came from other wise if you are hosing on a website you can supply a url to the page that will be handling the response.
If the user accepts the above then you will have an authorization code.
Exchange code
What you need to do next is exchange the authorization code returned by the above response and request an access token and a refresh token. THis is a http post call
POST https://accounts.google.com/o/oauth2/token
code=4/X9lG6uWd8-MMJPElWggHZRzyFKtp.QubAT_P-GEwePvB8fYmgkJzntDnaiAI&client_id={ClientId}&client_secret={ClientSecret}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&grant_type=authorization_code
The body parameter should be as i have shown separated by & and the content type of the request is application/x-www-form-urlencoded
Responce
{
"access_token" : "ya29.1.AADtN_VSBMC2Ga2lhxsTKjVQ_ROco8VbD6h01aj4PcKHLm6qvHbNtn-_BIzXMw",
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : "1/J-3zPA8XR1o_cXebV9sDKn_f5MTqaFhKFxH-3PUPiJ4"
}
The access token can be used in all of your requests to the api by adding either an authorization header bearer token with the access token or by sending access_token= as your parameter in your requests.
Refresh access token
Refresh tokens are long lived they should not expire they can so you code should be able to handle that but normally they are good forever. Access tokens are only valid for one hour and you will need to request a new access token.
POST https://accounts.google.com/o/oauth2/token
client_id={ClientId}&client_secret={ClientSecret}&refresh_token=1/ffYmfI0sjR54Ft9oupubLzrJhD1hZS5tWQcyAvNECCA&grant_type=refresh_token
response
{
"access_token" : "ya29.1.AADtN_XK16As2ZHlScqOxGtntIlevNcasMSPwGiE3pe5ANZfrmJTcsI3ZtAjv4sDrPDRnQ",
"token_type" : "Bearer",
"expires_in" : 3600
}
I'm currently stopped in my work because of some authentication work on a project.
I set up a REST API, which needs to have a JWT authentication system.
Some work was already done and I overrode it. So the library used was Python's TurboGears2, and I used PyJWT to manage tokens.
My WS and the token's creation works well. The post method with auth info JSON request's body can create a token, that's sent in the response.
But after that, when I do a 'GET' request on the restricted resource, I can't retrieve the token.
What I do: send a GET request to the restricted resource, with "Authorization: Bearer <TOKEN>" in request headers.
But when I do a 'request.authorization' in my web service function, I always get 'None'.
Do I need to set up a full auth system using TurboGears to access this header?
thanks for help
Where are you trying to access the request.authorization from?
I tried with a newly quickstarted application and modified the index to print the authorization header:
#expose('testauth.templates.index')
def index(self):
"""Handle the front-page."""
print(request.authorization)
return dict(page='index')
And I sent the authorization header from Postman.
It worked fine and printed my test header
Authorization(authtype='Bearer', params='HELLO')
I also tried to disable any auth_backend so that authentication is disabled and it still works as expected.
I was calling github api from my python scripts. if run successfully, it would have made around 3000 calls. However, after 50-60 successful calls, it shows the below message-
{'message': "API rate limit exceeded for 108.169.151.47. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", 'documentation_url': 'https://developer.github.com/v3/#rate-limiting'}
I have read the documentation. I don't have any application to register with to get a client id and client secret.
From my github account, I generated a token. I'm wondering if I can use that to send authenticated requests.
I tried some curl command to log in through my github profile in the git bash, and then run the python script, but it still shows the same message.
Can you suggest a way how can I make use of the good news in the message?
You might want to consider just using basic authentication with your GitHub username and password. The response received should be converted to JSON.
req = requests.get(url, auth=(USERNAME, PASSWORD))
req_json = req.json()
We have this setup:
Central Django server, CSRF and login enabled. Except for the login no action may be performed without logging in previously.
An Angular2 client which connects for almost every call to the central server. The login on the central server is executed from here. CSRF token is available and authentication works.
Another small server which takes files. It is also Django but not CSRF enabled. The client sends files to this server which the central server may never possess or even see. The file upload (using form-data and POST) works fine. However, after a file upload has been completed, we would like this small server to call the central server notifying it of the successful upload.
The problem is the last step. The central server refuses the call, saying we need to be logged in. Can we in any way make the central server believe that the request came from the user who logged in with the Angular2 client? How do we have to set up the CSRF token? We are sending the user's CSRF token he got in the client to the small server.
We are using the python-requests library, Python 3 and Django 1.10.
This is the code we currently have on the small server:
url = settings.CENTRAL_SERVER_URL + 'path/to/endpoint'
# 'request' is the original request object from the Angular2 client
token = get_token(request)
# Call to 'post' results in error code in response ('not logged in')
response = requests.post(url, data=data, headers={'X-CSRFToken': token, 'Referer': url})
I assume the problem is the 'headers' definition. Can it be done at all?
(CSRF enabled = uses CsrfViewMiddleware)
Turns out I was on the right track. It is most important to include the session ID the client got when logging in also in the new request to the central server.
Here is the code:
url = settings.CENTRAL_SERVER_URL + 'path/to/endpoint'
http_x_token = request.META['HTTP_X_CSRFTOKEN']
csrftoken = request.COOKIES['csrftoken']
session_id = request.COOKIES['sessionid']
response = requests.post(url, data=data,
headers={'X-CSRFToken': http_x_token, 'Referer': url},
cookies={'csrftoken': csrftoken, 'sessionid': session_id})
The session ID should always be present in the request from the client.
SessionMiddleware in Django checks for this. If the session ID is present, the user can be found and everything else works as if I was making a request from the client.