I'm getting a weird exception while setting and getting the cookie in Django.
I'm getting key error while trying to print the cookie value. Could someone please tell me the reason by looking at the stack trace http://dpaste.com/3M3ZKXW
def sessionHandler(request):
userName = request.GET.get('uname')
response = HttpResponse("Setting the cookie now")
response.set_cookie('cookie_user_name', userName)
return JsonResponse({'Response': 'success'})
def login(request):
cookie_user = request.COOKIES['cookie_user_name']
print("################################")
print(cookie_user)
UserName = {"Name": global_user_name}
return render(request, 'login/login.html', UserName)
Exception Type: KeyError at /NewHandBook/welcome
Exception Value: 'cookie_user_name'
Why it's not working
You are setting the cookie on the response, so only the next request from the client after running through the middleware will have the cookie. With or without the middleware, this first request to the server from any client will never have this cookie set (or any other cookie).
Sidenote:
It's better to use failsafe code here: The login page should not break just because a GET parameter was not set, anyway. It shouldn't even return an error if this is the entry point to your site. I'm guessing you just want to display a nice "welcome back" message.
The way of your cookie is like this:
CLIENT: /login?uname=foobar
-> request w/o cookie
-> middleware sets *response* cookies
-> /login handler checks *request* cookies: None
The way of cookies in general is:
CLIENT: first request
-> server sets response cookies
-> server sends response (preferrably 2**)
CLIENT: displays response and stores cookies
CLIENT: next request with cookies
-> server reads cookies from request (e.g. session+CSRF cookies)
-> server sends response (again with cookies, CSRF-Token etc.)
How to make it work
Cookies are something that the server uses to check back with the client.
In your case you are already sending the info you want from the client via GET parameter. You don't need a cookie in this case, because this is not something you want the client to store but instead you want to process it on the server (and it has already reached the server).
Simplest solution to illustrate:
def login(request):
userName = request.GET.get('uname')
print("################################")
print(userName)
UserName = {"Name": userName}
return render(request, 'login/login.html', UserName)
So, you just need to find a way to store it in the middleware and read it back in the login handler. This is what the session is for. You have a session as soon as the first request comes in from the client (with Django).
The session lives through the login, and you will be able to access any data stored within it as long as the session lives (that is: as long as the session cookie is stored on the client and has not expired).
Make sure to add your middleware to the chain after Django's session middleware.
https://docs.djangoproject.com/en/2.2/topics/http/sessions/
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
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.
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 JavaScript bookmarklet that POSTs information to a (Flask powered) server while the user is on some other page (i.e. not one on my server). I don't want to interrupt the user's browsing by hijacking their session with my server response.
My initial thought was that I could suppress the HTTP response from Flask somehow; prevent it from sending anything to the client so they aren't mysteriously redirected. I was hoping I could do this by perhaps having a null return from a view.
I then thought that might be some HTTP response that lets the client know the information was successfully submitted, but will leave the client on their current page. Suppose a header value like "Here is the result of your request, but you should not alter your current display"?
To answer your amended question, yes there is such a response. From RFC 2616-section 10 (emphasis added):
10.2.5 204 No Content
The server has fulfilled the request but does not need to return an
entity-body, and might want to return updated metainformation. The
response MAY include new or updated metainformation in the form of
entity-headers, which if present SHOULD be associated with the
requested variant.
If the client is a user agent, it SHOULD NOT change its document view
from that which caused the request to be sent. This response is
primarily intended to allow input for actions to take place without
causing a change to the user agent's active document view, although
any new or updated metainformation SHOULD be applied to the document
currently in the user agent's active view.
The 204 response MUST NOT include a message-body, and thus is always
terminated by the first empty line after the header fields.
Thus from flask you can do something like this. Remember, the response must not include a message body, so any data you want to send back should be put into a cookie.
#app.route('/')
def index():
r = flask.Response()
r.set_cookie("My important cookie", value=some_cool_value)
return r, 204
No, it is not possible. Flask is built on Werkzeug, which implements the WSGI spec. The WSGI cycle requires sending a response to each request. Droping the response would require control over the TCP/IP connection at a far lower level even that HTTP. This is outside the domain of WSGI, therefore outside the domain of Flask.
You could return an error code, or an empty body, but you have to return something.
return '' # empty body
When browser firstly request the server, the django will generate a session obj and then write a set cookie header to set session id into response as a cookie. My question is how to get this newly generated sess id without access request.COOKIES, just be aware that at this moment, client didnt know the sessid since the 1st response has not been sent out yet. Thanks.
Try
request.session.session_key
If it doesn't work do request.session.save() before