I am interested in having the user send a request in this fashion, using requests:
import requests
url = "https://postman-echo.com/basic-auth"
username = "postman"
password = "password"
response = requests.get(url, auth=(username, password))
Then in my Django model I need to be able to verify that those auth credentials are matching with the right user.
I cannot access request.auth and there are no auth fields in request.META.
What is the right way to access those credentials from the Django side?
I found the answer here.
auth_header = request.META['HTTP_AUTHORIZATION']
encoded_credentials = auth_header.split(' ')[1] # Removes "Basic " to isolate credentials
decoded_credentials = base64.b64decode(encoded_credentials).decode("utf-8").split(':')
Related
In a Django view I can use the cookies from the incoming request and pass it into another request as means of authentication and make requests to other views in the project using the requests module.
def someview(request):
cookies = request.COOKIES
csrftoken = cookies['csrftoken']
baseurl = settings.BASEURL
url = f"{baseurl}/api/csvuploads/"
payload = {...}
resp = requests.post(
url,
data=payload,
cookies=cookies,
headers={'X-CSRFToken': csrftoken},
)
I need to have a management command running periodically (as a cronjob) to have a similar logic, the problem is that the management command will run in a container in Kuberentes so I need to create an user, a request, authenticate, and login so I can make more requests.
How can I recreate the same request with session, cookies and everything if the request is not coming from a view?
My first attempt was to use RequestFactory from the test framework but I couldn't figure out how to create the request with the session and cookies from the authentication.
def handle(self, *args, **options):
factory = RequestFactory()
request = factory.post(
"/admin/login",
{'username': username, 'password': password}
)
user = authenticate(request, username=username, password=password)
request.user = user
I know I could do this with the test Client() but would it be a good practice to use a testing tool to make requests in a production-like script?
I'm pretty new to the world of authentication / keycloak, and after reading the keycloak doc I still can't wrap my head around the following situation.
Let's assume there's an existing web-application with keycloak as IAM.
Now, I need to implement a Flask/dash application which provides a service with business client specific data and dedicated API endpoints for all the business clients. Let's say the base URL is website.com/myapp/
Each business client has users in keycloak and some, not all, should access the data at the according endpoint, website.com/myapp/client1/
user_1 can view /client1/, user_2 and user_3 can view /client2/
That means I have to hardcode somewhere (database?) which user belongs to a client endpoint, correct?
In keycloak I've setup a client (myapp) with service accounts enabled
I gave the client a role view
I gave a user the role view
With the client_id and client_secret I can obtain the access_token and make all the known requests via the keycloak API.
The API routes in Flask/dash have a decorator function to check if the user is logged in and has the general view permission for myapp.
This works fine, but the problem is I need to pass the user_id to check if the keycloak session is active or not.
How do I check for the logged in user_id or user_name who wants to access /myapp/ so that I can validate the view permission and match to the dedicated Flask route?
authentication:
def authentication(user_id):
url = 'http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/token'
payload = 'grant_type=client_credentials'
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
response = requests.request('POST', url, headers=headers, data=payload, auth=HTTPBasicAuth(client_id, client_secret))
token = json.loads(response.text)
return get_client_roles(token['access_token']), get_sessions(token['access_token'], user_id)
def get_sessions(token, user_id):
url = f'http://127.0.0.1:8080/auth/admin/realms/master/users/{user_id}/sessions'
payload = {}
headers = { 'Authorization': 'Bearer '+token }
response = requests.request('GET', url, headers=headers, data=payload)
sessions = json.loads(response.text)
return sessions
def get_client_roles(token):
url = 'http://127.0.0.1:8080/auth/admin/realms/master/clients/'+id+'/roles/view/users'
payload = {}
headers = { 'Authorization': 'Bearer '+token }
response = requests.request('GET', url, headers=headers, data=payload)
client_roles = json.loads(response.text)
return client_roles
With that I can check if user_x is logged in and has role view for myapp
But unfortunately I need to hardcode the user_id in my Flask/dash file:
excerption from Flask/dash:
dash_app1 = dash.Dash(server = app, name = 'Dashboard client1', url_base_pathname='/client1/')
dash_app2 = dash.Dash(server = app, name = 'Dashboard client2', url_base_pathname='/client2/')
...
user_id = 'dsdgbc-fd68-41c3-b6d-34fds343436aa75' # how to retrieve and not hardcode?
#app.route("/client1/", endpoint='route1')
#login_required
def render_dashboard():
return redirect('/client1/')
The user_id is the parameter in authentication(user_id) in the decorator function
I am using JWT auth to login users. Username and password are sent in Body, however, in the customized response, an anonymUser is always returned. I think the problem is that in settings.py stands 'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION', and when I generate a token before and then send it in Headers the user is identified. Bit the thing is, that I cannot use 2 views in order to generate token and decode it, everything has to be in one view and I don't know how to login the user in the view and then get token and decode it.
#api_view(('POST',))
def check_token(request):
token_refresh = RefreshToken.for_user(request.user)
print(request.user) # AnonymUser
print(request.user.id) # None
print(str(token_refresh.access_token))
data = {'token': str(token_refresh.access_token), 'refresh_token': str(token_refresh)}
aT = str.encode(str(token_refresh.access_token))
try:
valid_data = TokenBackend(algorithm='HS256').decode(aT, verify=False)
print(valid_data)
data['uuid'] = valid_data['user_id']
data['validUntil'] = valid_data['exp']
data['clientId'] = 'default'
return JsonResponse(data)
except ValidationError as v:
print("Validation error", v)
The answer can be found here
The The request.user is set by the django.contrib.auth.middleware.AuthenticationMiddleware.
So the request.user does not know about JWT as it is using Djangos authentication system. You can read about using JWT with Django here
I'm creating a view which is expected to be accessed by a bot passing a username and password in the header. (It's a google feed bot to be specific). However, I can never seem to access the username and password to authenticate the bot's credentials. request.GET.get('username') and request.GET.get('password') both return None since both request.GET and request.POST return empty QueryDicts. I am using Postman with basic authentication to test my requests.
Here is my code from views.py:
def authenticate_bot(request):
username = request.GET.get('username')
password = request.GET.get('password')
feed_bot = authenticate(username=username, password=password)
if feed_bot is not None:
# Confirmed as user credentials.
login(request, feed_bot)
How do I retrieve the username and password from my basic authentication header?
Thank you nthall for pointing me in the right direction - finding the request.META dictionary was key.
Since I couldn't find much in the way of resources which explained the process, I'll post the entire Django process for retrieving and authenticating data from the Authorization header here.
import base64
from django.contrib.auth import authenticate
def header_auth_view(request):
auth_header = request.META['HTTP_AUTHORIZATION']
encoded_credentials = auth_header.split(' ')[1] # Removes "Basic " to isolate credentials
decoded_credentials = base64.b64decode(encoded_credentials).decode("utf-8").split(':')
username = decoded_credentials[0]
password = decoded_credentials[1]
feed_bot = authenticate(username=username, password=password)
# if the credentials are correct, then the feed_bot is not None, but is a User object.
Django capitalizes and affixes the 'HTTP_' prefix to any header passed in the request, and as nthall correctly pointed out, it can be accessed via request.META.
I isolate the base64 encoded information, which is in the format 'Basic username:password' by splitting the header over the space so it's just 'username:password'. Then I decode using base64 and then decode the result to convert the byte-like string to a utf-8 string. Then it's just a matter of isolating the username and password. Then go through the process of authentication.
Neither request.GET nor request.POST refers to request headers. The data you're looking for is most likely available in the dictionary at request.META -- more details on that in the HttpRequest docs. Not sure about the details of your setup but it sounds like you'd be looking for request.META['username'] and request.META['password']
I found this snippet of code that helps me authenticate a user and then create a rest_framework token for them. The client I am using is a native android app and I will get the access token from the client side and post it to django in the ObtainAuth class.
Here is the code for the server side.
#psa('social:complete')
def register_by_access_token(request, backend):
backend = request.strategy.backend
# Split by spaces and get the array
auth = get_authorization_header(request).split()
if not auth or auth[0].lower() != b'token':
msg = 'No token header provided.'
return msg
if len(auth) == 1:
msg = 'Invalid token header. No credentials provided.'
return msg
access_token = auth[1]
user = backend.do_auth(access_token)
return user
class ObtainAuthToken(APIView):
model = Token
serializer_class = AuthTokenSerializer
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
def post(self,request):
serializer = self.serializer_class(data= request.DATA)
if backend == 'auth':
if serializer.is_valid:
token, created = Token.objects.get_or_create(user=serializer.object['user'])
if token:
return Response({'token': token.key})
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
else:
user = register_by_access_token(request, backend)
if user and user.is_active:
token, created = Token.objects.get_or_create(user=user)
return Response({'id': user.id, 'email': user.email, 'firstname': user.first_name, 'userRole': 'user', 'token': token.key})
The register_by_access_token method will get the facebook access token and then create a user with the rest_framework.It takes a request and the backend to be used e.g 'facebook'.
If a user logs in with my backend then the backend is 'auth' and it uses the normal process of retrieving the email and password and then giving me a token to use.As detailed here
My question is how do I post the authentication backend be it 'facebook' or 'auth' so that I can receive the token?
What I've tried.
I have tried sending the backend type ('facebook' or 'auth') with the access token but I get an error that the method takes 3 arguments and I've only provided 2.
I've tried making the url take a backend like this:
url(r'^login/(?P<backend>[^/]+)/$',views.ObtainAuthToken.as_view())
then sending the access token to a url like this mysite.com:8000/login/facebook.
None of these work and I don't have much expereience with psa or django to know how to pass this parameter.
How do I send which backend to use so that it can be accepted by the method? If anyone has ever had this use case please help me out.
according to my understanding social login requires a access token , so when you are login with facebook when you call 'mysite.com:8000/login/facebook' it is expecting a access token,
for my project i defined my urls as 'url(r'^login/(?P[^/]+)/$',register_by_access_token,name='register_by_access_token')',with the token i am sending it completes the login, for facebook i send backend as 'facebook' for google i send backend as 'google-oauth2' but both the case i am sending a token given my respective backend, when you are not using a third party backend you wont get the token and the login expects that.
so my suggestion is if you are going with auth use normal login post, not the same URL.