Django/Flask : Rewritting an Flask Azure B2C authentication in Django - python

I want to use and Azure B2C authentication in Django, however there is no tutorial in Django for it but in Flask. However I never coded in Flask.
I used the documentation/tutorial of microsoft that share a github with the flask code to do it :
https://learn.microsoft.com/en-us/azure/active-directory-b2c/configure-authentication-sample-python-web-app?tabs=windows
https://github.com/Azure-Samples/ms-identity-python-webapp
I try to convert it in Django however I have an error that I do not understand ! The error message :
Internal Server Error: /login/
TypeError: Object of type HttpResponseRedirect is not JSON serializable
Here is the code
views.py
def index(request) :
if not request.session.get("user"):
return redirect("login")
return render('index.html', user=request.session["user"] )
def login(request):
# Technically we could use empty list [] as scopes to do just sign in,
# here we choose to also collect end user consent upfront
request.session["flow"] = _build_auth_code_flow(scopes=list(json.loads(os.getenv("SCOPE"))))
return render(request, "login.html", {'auth_url' : request.session["flow"]["auth_uri"]})
def authorized(request):
try:
cache = _load_cache(request=request)
result = _build_msal_app(cache=cache).acquire_token_by_auth_code_flow(
request.session.get("flow", {}), request.args)
if "error" in result:
return render("auth_error.html", result=result)
request.session["user"] = result.get("id_token_claims")
_save_cache(cache=cache, request=request)
except ValueError: # Usually caused by CSRF
pass # Simply ignore them
return redirect("index")
def _load_cache(request):
cache = msal.SerializableTokenCache()
if request.session.get("token_cache"):
cache.deserialize(request.ession["token_cache"])
return cache
def _save_cache(cache, request):
if cache.has_state_changed:
request.session["token_cache"] = cache.serialize()
def _build_msal_app(cache=None, authority=None):
return msal.ConfidentialClientApplication(
os.getenv("CLIENT_ID"), authority=authority or os.getenv("AUTHORITY"),
client_credential=os.getenv("CLIENT_SECRET"), token_cache=cache)
def _build_auth_code_flow(authority=None, scopes=None):
return _build_msal_app(authority=authority).initiate_auth_code_flow(
scopes or [],
redirect_uri=redirect('authorized'))
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<h1>Microsoft Identity Python Web App</h1>
<li><a href='{{ auth_url }}'>Sign In</a></li>
<footer style="text-align: right">Powered by MSAL Python {{ version }}</footer>
</body>
</html>
urls.py
from django.urls import path
from . import views
import os
from dotenv import load_dotenv
load_dotenv()
urlpatterns = [
path('index/', views.index, name="index"),
path('login/', views.login, name="login"),
# path(f'{os.getenv("REDIRECT_PATH")}/', views.login, name="authorized"),
path('getAToken/', views.login, name="authorized")
]
Did I do something wrond in the conversion to Django ?
Where does this error come from ? Is it something to set in the azure bc2 ?

Not the required answer (adopting the Flask solution for DJango), but your're right, there are no examples of using Django for aadb2c auth...
What I did, is used "mozilla-django-oidc" which fits well the azure oidc authentification..
More details are here:
https://stackoverflow.com/a/74156780/592737

Related

KeyError: 'Unable to find stateless DjangoApp called app'

Why the DjangoApp app can't be found?
Given result
After executing manage.py runserver in the console Django is starting properly but in case I'm calling http://127.0.0.1:8000/dash_simple/app1/ a KeyError was thrown:
Django Version: 3.1.3
Exception Type: KeyError
Exception Value:
'Unable to find stateless DjangoApp called app'
Expected result
After executing manage.py runserver in the console Django is starting properly and the related page is shown for http://127.0.0.1:8000/dash_simple/app1/
dash_test/urls.py
urlpatterns = [
path('dash_simple/', include ('dash_simple.urls')),
]
dash_test/settings.py
INSTALLED_APPS = [
'dash_simple.apps.DashSimpleConfig',
]
dash_test/dash_simple/apps.py
class DashSimpleConfig(AppConfig):
name = 'dash_simple'
dash_simple/urls.py
urlpatterns = [
path('', views.home, name='home'),
path('app1/', views.app, name='app')
]
dash_simple/views.py
def home(request):
return render(request, template_name='dash_simple/home.html')
def app(request):
return render(request, template_name='dash_simple/app1.html')
dash_simple/templates/dash_simple/app1.html
{% load plotly_dash %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>App 1</title>
</head>
<body>
<H1>I'm App 1</H1>
<p>
{% plotly_app name="app" %}
</p>
</body>
</html>
Faced similar issue. In dash_simple/urls.py you need to import your dash application i.e.:
from dash_simple import your_dash_script_file_name
As simple as that!
For future reference. In my case getting 'Unable to find stateless DjangoApp called "AppName"'. For one of my plotly app it worked and for the other one i did not. I started verifying what one off them had and the other not.
After some hours i noticed this set of imports i made few days ago on the /views of the app that was serving the html template that showed the plot, while setting the first.
from plotly.offline import plot
import plotly.graph_objs as go
from .plot import *
I copied this to the other app that rendered had the view of the second plot. but the compilation failed because the 3rd import was not being found. Because of this i went into the reference of the import in the first plot and it was the module that had all the code of the first plot, so i changed the last import on the views of the django app that rendered my second plot
Basically. My solution was to also do the three imports on the views of the django app that is rendering the html that contains the plot and change the last line to import the script/module of the dash plotly app that i'm looking to render.

Why Django does not generate app name into href paths?

I hope someone could maybe help me, please :
I am quite new to Django and currently trying to implement a simple login/logout for a test page.
However, for some reasons, the Django does not generate the name of application in the href (so where it should be xxxx/main/register, it is only xxx/register).
But when I put the app name manually in the href in Pycharm, it generates it two times (so it becomes xxx/main/main/register).
So for this:
Logout
I got this url:
http://127.0.0.1:8000/logout/
If I write this:
Logout
I got this url:
http://127.0.0.1:8000/main/main/logout/
But I need to get this:
http://127.0.0.1:8000/main/logout/
It worked before, but from one minute to another, it suddenly stopped directing to the good path. And django does the same thing with every links in my site.
main/urls.py:
from django.urls import path
from . import views
app_name = 'main' # here for namespacing of urls.
urlpatterns = [
path("", views.homepage, name="homepage"),
path("register/", views.register, name="register" ),
path("logout/", views.logout_request, name="logout"),
]
main/views.py:
from django.shortcuts import render, redirect
from django.http import HttpResponse
from .models import Tutorial
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.contrib.auth import logout, authenticate, login
from django.contrib import messages
def homepage(request):
return render(request = request,
template_name='main/home.html',
context = {"tutorials":Tutorial.objects.all})
def register(request):
if request.method == "POST":
form = UserCreationForm(request.POST)
if form.is_valid():
user = form.save()
username = form.cleaned_data.get('username')
messages.success(request, f"New account created: {username}")
login(request, user)
return redirect("main:homepage")
else:
for msg in form.error_messages:
messages.error(request, f"{msg}: {form.error_messages[msg]}")
else:
form = UserCreationForm
return render(request = request,
template_name='main/register.html',
context={"form":form})
def logout_request(request):
logout(request)
messages.info(request, "Logged out successfully!")
return redirect("main:homepage")
mysite/urls.py:
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('polls/', include('polls.urls')),
path('main/', include('main.urls')),
path('admin/', admin.site.urls),
path('tinymce/', include('tinymce.urls')),
]
The behaviour you observe has nothing to do with Django, it's a basic html feature: in the first case you're using an absolute path (=> starting with a slash) for your href, in the second one you're using a relative path (NOT starting with a slash) so it's resolved relatively to the current url path whatever it is. You'd have the very same issue with plain static HTML.
This being said, in Django, you should never hardcode urls, but use the {% url <name> %} template tag (or the django.utils.reverse() function in Python code) instead, so you can change your urls in the urls.py files without breaking anything.
<a href="{% url 'logout' %}">
Use the name in href tag that will work.
You should use the url name in the template. It should solve the problem. Like this:
Logout
Or with namespacing:
Logout

Django Template Language Not Rendering

I am brand new to Django and following along with the tutorial. I'm hoping this is just an obvious mistake, but I am unable to get my web browser to render anything written in the Django template language and I can't figure out why.
Here is my directory structure for some context: https://imgur.com/dGNIiDa
project/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('budget/', include('budget.urls')),
path('admin/', admin.site.urls)
]
budget/urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('<int:account_id>/', views.get_account, name='detail'),
]
budget/views.py:
from django.shortcuts import render
from django.http import HttpResponse
from budget.models import Account, Transaction
def get_account(request, account_id):
accts = Account.objects.filter(pk=account_id)
context = {"test": accts}
return render(request, 'budget/detail.html', context)
budget/templates/budget/detail.html:
<p>This is a {{ context.test }}</p>
When I visit localhost:8000/budget/1 in my browser this is all that is rendered: https://imgur.com/j2Vh0yb
Clearly Django is finding the template file and sending it to the browser, but anything that is written inside of {} does not get recognized or rendered at all. I've followed along exactly with the tutorial and I have no idea why it's not working. Any ideas?
You don't need context in the expression in the template; all that you put in the context are "globals" in the template, so try
<p>This is a {{ test }}</p>
instead.
Django's template engine has the unfortunate property of being silent about nonexisting properties, so it's hard to debug stuff like this.

django error: 'subjects' object has no attribute 'get'

I'm working in my first django project. Unfortunately, I'm facing an issue that should be straightforward. my scenario:
models.py
from django.db import models
# Create your models here.
## 1. Model for user registration
class subjects(models.Model):
description = models.CharField(max_length=500)
def __Str__(self):
return self.description
views.py
from django.contrib.auth import login , authenticate
from django.shortcuts import render , redirect
from poc_teaching.models import subjects
# Create your views here.
def list_subj(request):
subj = subjects.objects.all()
return render (request,'subjects.html',{'subjects': subj})
urls.py
from django.conf.urls import url
#from . import views
from poc_teaching import views as poc_views
urlpatterns = [
url('subjects/$',poc_views.subjects,name ='subjects'),
]
html
<head>
"hello world"
</head>
<div>
{{ subjects }}
</div>
I'm getting the error: 'subjects' object has no attribute 'get'
I've gone thru the admin console to add objects. So, I'm pretty sure I should get results. I have gone thru many documents but really I dont understand where is my issue. I appreciate your help.
You are calling the wrong view. Your urlpattern should call the view list_subj.
Change your urlpatterns to this -
urlpatterns = [
url('^subjects/$',poc_views.list_subj,name ='subjects'),
]

Django-Twilio sending SMS on button click

OK possible noob question here: While learning Django, I thought it might be cool to explore telephony with Twilio. My immediate goal is to create a page with a button that, when clicked, causes a "Hello World" SMS to be sent to my phone. After sorting that out I have some ideas for cooler stuff.
I've completed several Django tutorials so far, and made a few little apps with simple views. But nothing I've learned has particularly shed any light on how to do something like this. I've also investigated (and installed) the Django-Twilio app and the Twilio Python Helper Library, but the docs for neither of these show how to send "hello world" SMS's.
Can anyone point out a resource that might show how to do this? Or, if it's trivially easy, just post some example code?
Edit in response to Kevin Burke:
Thanks for getting back to me, Kevin.
After modifying my urls.py to include:
urlpatterns = patterns('',
# ...
url(r'^sms/$', 'django_twilio.views.sms', {
'message': 'Hello world',
'to': '+12223334444',
'sender': '+18882223333',
'status_callback': '/sms/completed/',
}, name = 'send_message'),
# ...
)
and pointing my browser at
http://127.0.0.1:8000/sms/
the following error arises:
Exception Type: TwimlException at /sms/
Exception Value: Invalid method parameter, must be 'GET' or 'POST'
Perhaps this is because I have failed to make appropriate modifications to the view. But I don't have a good way of figuring out what I'm doing wrong from the minimal examples in the tutorial.
/Edit
twilio employee here.
The problem here is that the built in views for django_twilio run through a series of validation checks to make sure they're receiving content from twilio.com and only twilio.com. This is a security measure built into django-twilio.
There are two things you can do:
Make sure your settings.DEBUG = True in your Django settings, this will turn off the validation. You can then send a cURL request on your local machine whilst it is running like this in your terminal:
$ curl http://localhost:8000/sms/
This should return some TWiML like so:
<Response><Sms>Hello world</Sms></Response>
When you're running this online and you want to do to test this, set up your twilio number to point to http://mywebsite.com/sms/ and text the number. Ensure that settings.DEBUG = False and you should get back a message.
If you have anymore problems, let me know.
Here's the official docs: django-twilio official docs. More specifically, read this part about sending SMS: Sending sms messages
Here is a simple solution :
django startproject projectname
urls.py
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^', include('message_api.urls')),
]
settings.py
TWILIO_ACCOUNT_SID = TWILIO_ACCOUNT_SID
TWILIO_AUTH_TOKEN = TWILIO_AUTH_TOKEN
DJANGO_TWILIO_FORGERY_PROTECTION = False
DJANGO_TWILIO_BLACKLIST_CHECK = True
Start new application
python manage.py startapp appname
Inside the app folder:`
urls.py
from django.conf.urls import url
import django_twilio
from . import views
urlpatterns = [
url(r'^api/$', views.home),
url(r'^send/', views.sms),
]
views.py
from django.shortcuts import render
from twilio.rest import Client
from twilio_api import settings
def home(request):
return render(request, 'index.html', {})
def sms(request):
client = Client(settings.TWILIO_ACCOUNT_SID, settings.TWILIO_AUTH_TOKEN)
message = client.messages.create(to='TO NUMBER', from_='YOUR TWILIO NUMBER', body='This message is sent through twilio api using django framework by akshat.')
print(message.sid)
return render(request, 'thankyou.html')
Make a templates directory inside your app folder
index.html
<body>
<button class="btn btn-outline-primary">Send Message</button>
</body>
thankyou.html
<body>
<h1>Success</h1>
</body>
`

Categories

Resources