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.
Related
I'm writing a web-app that has to support Single-Sign-On through Saml. The back-end is made with FastAPI and the front end is made in react. Nginx is used to proxy requests to the backend. I have an endpoint for logins that returns a redirect request to an identity provider. When accessing the endpoint directly through the browser everything works as expected and the user is redirected to an external login page.
I want to be able to use a similar redirect when a user who isn't authorized tries to access the page. My attempt at this consists of returning a redirect response, when a http exception is raised, to my login endpoint which should then redirect to the external login page. Doing this prompts a 404 response from the idp.
The login endpoint is defined like this in FastAPI:
#app.get("/saml/login")
async def sso(request: Request):
req = await prepare_from_fastapi_request(request)
auth = Saml2_Auth_patched(req, saml_settings)
callback_url = auth.login(return_to="https://website.com/")
response = RedirectResponse(url=callback_url)
return response
And the exception handler like this:
#app.exception_handler(StarletteHTTPException)
async def unauthorized_handler(request: Request, exc):
response = RedirectResponse(url="/api/saml/login")
return response
This looks more like a front-end issue since as you are saying everything works as expected when you access the endpoint in the browser.
I think the issue here is that the library you are using to make API requests from your front end application does not follow redirect. For example axios does not do that out of the box. You can see the conversation here: https://github.com/axios/axios/issues/1350
In the same conversation they mention another dependency you can use to address this issue: https://www.npmjs.com/package/follow-redirects
I am dealing with an API which has token based session management where token validation occurs at server side. The token has an expiry time.
import requests
payload = {"username": username, "password": password}
response = requests.post(url, json=payload)
token = response.json()['token']
ses = requests.Session()
ses.cookies.set('_token', token, domain='<domain>', path='/')
using above code i am maintaining the session at global level
I had to restart my application again and again to recreate the session,
To deal with that i used the status code check to recreate the session if the status code is 4xx
Is there a better and effective way to handle server side sessions?
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.
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.
I am trying to access the Azure AD Graph API using the Python requests library. My steps are to first get the authorization code. Then, using the authorization code, I request an access token/refresh token and then finally query the API.
When I go through the browser, I am able to get my authorization code. I copy that over to get the access token. However, I've been unable to do the same with a Python script. I'm stuck at the part where I get the authorization code.
My script returns a response code of 200, but the response headers don't include that field. I would've expected the new URL with the code to be in the response headers. I would have also expected a response code of 301.
Does anyone know why my response headers don't have the auth code? Also, given the auth code, how would I pull it out to then get the access/refresh tokens using Python?
My code is below:
import requests
s = requests.Session()
s.auth = (USERNAME, PASSWORD)
# Authorize URL
authorize_url = 'https://login.microsoftonline.com/%s/oauth2/authorize' % TENANT_ID
# Token endpoint.
token_url = 'https://login.microsoftonline.com/%s/oauth2/token' % TENANT_ID
payload = { 'response_type': 'code',
'client_id': CLIENT_ID,
'redirect_uri': REDIRECT_URI
}
request = s.get(authorize_url, json=payload, allow_redirects=True)
print request.headers
It looks that you are implementing with Authorization Code Grant Flow via python requests. As the flow shows, the response of the request of authorize_url will redirect to a SSO page of your AD tenant. After your user login on, it will redirect to the location which set in redirect_uri with code as the URL parameters. E.G. http://localhost/?code=AAABAAAAiL...
And your code seems cannot simply display a html page with JavaScript allowed, so it will not redirect to the login on page.
So you can refer to # theadriangreen’s suggestion to implement with a python web server application.
Otherwise, you can refer to Microsoft Azure Active Directory Authentication Library (ADAL) for Python, which is a python package for acquiring access token from AD and can be easily integrated in your python application.