snok / django-auth-adfs Goto Github PK
View Code? Open in Web Editor NEWA Django authentication backend for Microsoft ADFS and AzureAD
Home Page: http://django-auth-adfs.readthedocs.io/
License: BSD 2-Clause "Simplified" License
A Django authentication backend for Microsoft ADFS and AzureAD
Home Page: http://django-auth-adfs.readthedocs.io/
License: BSD 2-Clause "Simplified" License
Hi,
We are trying to configure ADFS SSO for Django using django-auth-adfs.
We believe we have configured things correctly however we get redirected back to the REDIRECT_URI with the following error
error_description u"MSIS9602: The received 'resource' parameter is invalid. The authorization server can not find a registered resource with the specified identifier."
error u'invalid_resource
Questions:
How can we identify if this is an ADFS issue or a django-auth-adfs issue?
Any guidance on how to either solve the issue or go about troubleshooting would be appreciated.
[DESKTOP_IP] = Ip of my desktop running nginx proxying to gunicorn. Note: Self Signed Certificates are being used.
[FQDN_OF_ADFS_SERVER] = FQDN of Adfs server
[RelyingPartyTrust] = Configured in ADFS and matches Django value
[ClientID] = Configured in ADFS and matches Django value
=====ADFS server configuration ==========
AllowedAuthenticationClassReferences : {}
AutoUpdateEnabled : False
DelegationAuthorizationRules :
EncryptionCertificateRevocationCheck : CheckChainExcludeRoot
PublishedThroughProxy : False
IssuanceAuthorizationRules : @RuleTemplate="AllowAllAuthzRule" => issue(Type =
"http://schemas.microsoft.com/authorization/claims/permit", Value="true");
SigningCertificateRevocationCheck : CheckChainExcludeRoot
WSFedEndpoint :
AdditionalWSFedEndpoint : {}
ClaimsProviderName : {}
IssuanceTransformRules :
ClaimsAccepted : {}
ConflictWithPublishedPolicy : False
EncryptClaims : True
Enabled : True
EncryptionCertificate :
Identifier : {https://[DESKTOP_IP]:10443/oauth2/login}
LastMonitoredTime : 12/31/1899 7:00:00 PM
LastPublishedPolicyCheckSuccessful :
LastUpdateTime : 12/31/1899 7:00:00 PM
MetadataUrl :
MonitoringEnabled : False
Name : Python
NotBeforeSkew : 0
EnableJWT : True
AlwaysRequireAuthentication : False
Notes :
OrganizationInfo :
ImpersonationAuthorizationRules :
AdditionalAuthenticationRules :
ProxyEndpointMappings : {}
ProxyTrustedEndpoints : {}
ProtocolProfile : WsFed-SAML
RequestSigningCertificate : {}
EncryptedNameIdRequired : False
SignedSamlRequestsRequired : False
SamlEndpoints : {}
SamlResponseSignature : AssertionOnly
SignatureAlgorithm : http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
TokenLifetime : 0
AllowedClientTypes : Public
IssueOAuthRefreshTokensTo : NoDevice
========== Django configuration ==============
AUTH_ADFS = {
"SERVER": "[FQDN_OF_ADFS_SERVER]",
"CLIENT_ID": "[ClientID]",
"RESOURCE": "[RelyingPartyTrust]",
# Make sure to read the documentation about the AUDIENCE setting
# when you configured the identifier as a URL!
"AUDIENCE": "microsoft:identityserver:[RelyingPartyTrust]",
"ISSUER": "http://[FQDN_OF_ADFS_SERVER]/services/trust",
"CA_BUNDLE": "/code/CERT.pem",
"CLAIM_MAPPING": {"first_name": "given_name",
"last_name": "family_name",
"email": "email"},
"REDIR_URI": "https://[DESKTOP_IP]:10443/oauth2/login",
}
============================================
SERVER:
parameter seems correct based on docs : http://django-auth-adfs.readthedocs.io/en/latest/configuration.html#server
CLIENT_ID:
http://django-auth-adfs.readthedocs.io/en/latest/configuration.html#client-id
ADFS Admin confirm value is set correctly.
RESOURCE :
http://django-auth-adfs.readthedocs.io/en/latest/configuration.html#resource
Set this to the name of the Relying Party Trust you configured in ADFS (Do not see it in ADFS config provided)
ADFS admin has confirmed it is set correctly.
AUDIENCE:
http://django-auth-adfs.readthedocs.io/en/latest/configuration.html#audience
Set this to the value of the aud claim your ADFS server sends back in the JWT token. If you leave this set to None this claim will not be verified.
Changed to None, no difference
CA_BUNDLE
http://django-auth-adfs.readthedocs.io/en/latest/configuration.html#ca-bundle
Changed to True, no difference.
CLAIM_MAPPING
http://django-auth-adfs.readthedocs.io/en/latest/configuration.html#claim-mapping
Removed, no difference.
REDIR_URI:
http://django-auth-adfs.readthedocs.io/en/latest/configuration.html#redir-uri
Seems correct based on Django ADFS plugin & ADFS redirecting back to that page and displaying errors at that URL
Getting different issues as I try to find my way.
I have followed the instructions best I can.
Environment:
I'm running a lab environment with a Microsoft AD FS server and Domain Controller.
Ubuntu server running my django app.
My AUTH_ADFS settings are as follows.
AUTH_ADFS = {
"SERVER": "lab.local",
"CLIENT_ID": "LogSearch",
"RESOURCE": "logsearch",
#"AUDIENCE": "microsoft:identityserver:https://lab.local/adfs/oauth2/token",
#"SIGNING_CERT": "/webapps/logsearch/adfs.pem",
"ISSUER": "https://lab.local/adfs/oauth2/token",
"CA_BUNDLE": "/webapps/logsearch/adfs.pem",
#"CA_BUNDLE": "False",
"CLAIM_MAPPING": {"first_name": "given_name",
"last_name": "family_name",
"email": "email"},
"REDIR_URI": "https://lab.local/adfs/ls",
}
I have been flipping the settings around a lot. When I do I get different errors. With the current setup:
TypeError at /oauth2/login
'instancemethod' object has no attribute 'getitem'
Request Method: GET
Request URL: http://logsearch.lab.local/oauth2/login
Django Version:
1.10.6
Exception Value:
'instancemethod' object has no attribute 'getitem'
Brg,
Patrik
Hi,
I don't know what I did to mess things up but right now in my development environment I am getting code 401 Login Failed. When I debug on the event viewer on ADFS it says that all good "An Access Token was successfully issued to client: blablabla"
I debug the package:
class OAuth2View(View):
def get(self, request):
"""
Handles the redirect from ADFS to our site.
We try to process the passed authorization code and login the user.Args: request (django.http.request.HttpRequest): A Django Request object """ code = request.GET.get("code", None) print("Code: {}".format(code)) user = authenticate(authorization_code=code) print("user{}".format(user)) if user is not None: if user.is_active: login(request, user) # Redirect to the "after login" page. # Because we got redirected from ADFS, we can't know where the # user came from. if settings.LOGIN_REDIRECT_URL: return redirect(settings.LOGIN_REDIRECT_URL) else: return redirect(django_settings.LOGIN_REDIRECT_URL) else: # Return a 'disabled account' error message return HttpResponse("Account disabled", status=403) else: # Return an 'invalid login' error message return HttpResponse("Login failed", status=401)
And I placed 2 print commands in the very begging of each variable assignation. It turns out that the code is getting populated by the ADFS response on the query string but the user=authenticate command its returning None.
What could possibly be causing this behavior?
My settings:
AUTH_ADFS = {
"SERVER": '[SERVER]',
"CLIENT_ID": 'f9a487be-ae3a-47f5-b91f-2cad4e2f3f5a',
"RESOURCE": 'http://127.0.0.1:8000',
"AUDIENCE": 'http://127.0.0.1:8000',
"ISSUER": 'http://[SERVER]/adfs/services/trust',
"CA_BUNDLE": False,
"CLAIM_MAPPING": {
"email": "email",
"first_name": "given_name",
"last_name": "family_name",
},
"USERNAME_CLAIM": "winaccountname",
"GROUP_CLAIM": "group",
"BOOLEAN_CLAIM_MAPPING": {
"is_staff": "user_is_staff",
"is_superuser": "user_is_superuser"
},
"REDIR_URI": 'http://127.0.0.1:8000/oauth2/login',
}
Why I am always redirected to /accounts/profile/ after login? Can this behavior be modified on config?
How can I consume new claim field in user object for example "Department"?
AUTH_ADFS = {
...
"CLAIM_MAPPING": {"first_name": "given_name",
"last_name": "family_name",
"email": "email",
"department": "department"},
"USERNAME_CLAIM": "winaccountname",
"BOOLEAN_CLAIM_MAPPING": {"is_staff": "user_is_staff",
"is_superuser": "user_is_superuser"},
"GROUP_CLAIM": "group",
...
}
Hello,
In the closed issue #21 you mention that integration with DRF is on your to-do list. Any progress on this the last year?
Everything works perfect on the Django part, but we would like to protect our API with the same logic as we use for front-end.
If you have no intention to implement it in this package, but know how to make it possible, would providing some gists with example setup be possible?
I currently have django-auth-adfs working using Active Directory Functional Level Windows Server 2008R2. My organization is planning to upgrade to the Windows Server 2016 functional level.
Is django-auth-adfs certified to run on the 2012R2 Domain Functional Level?
Are you aware of any known bugs or issues running with 2012R2 Domain Functional Level?
Is django-auth-adfs certified to run on 2016 Domain Functional Level?
Are you aware of any known bugs or issues running with 2016 Domain Functional Level?
Thanks for your help.
Could you perhaps provide any help with setting this up.
Previously when implementing a SAML2 setup, I would define endpoints (/ls and /acs) in the AD server to add a relying party trust. What would the equivalent be for setting this up for ADFS 3?
I have the settings file update with the library etc, and I guess the next step would be pointing it to the correct AD server and getting that AD to add the solution as a Relying Party?
I'd appreciate any help in the matter, I think I might just be missing something small here.
Environment is python 3.5, Django 1.10 and ADFS 3.0
The SIGNING_CERT option appears to have been deprecated. How should we now specify the custom certificate in use?
Hey there! Not sure if this is a bug or if this is user error (probably the latter), but I'm trying to integrate this application with Davidson College's ADFS.
I've configured relying party trust and added AUTH_ADFS to settings.py, but upon calling /oauth2/login, we get a simple "login failed." With verbose debugging on, we receive the error django_auth_adfs.backend django_auth_adfs was called but no authorization code was received
My assumption is that this is the link that should redirect us to our ADFS login page. Is this assumption incorrect?
Config settings below.
AUTH_ADFS = {
"SERVER": "sso.example.com",
"CLIENT_ID": "django.example.com",
"RESOURCE": "django.example.com",
# Make sure to read the documentation about the AUDIENCE setting
# when you configured the identifier as a URL!
"AUDIENCE": "microsoft:identityserver:django.example.com",
"ISSUER": "https://sso.example.com/adfs/services/trust",
"CA_BUNDLE": False,
"CLAIM_MAPPING": { "emailaddress": "emailaddress",
"givenname": "givenname",
"surname": "surname",
"Group": "Group",
"windowsaccountname": "windowsaccountname",
"groupsid": "groupsid",
"title": "title",
"department": "department" },
"USERNAME_CLAIM": "winaccountname",
"GROUP_CLAIM": "group",
"REDIR_URI": "http://django.example.com/oauth2/login",
}
Thank you for your help and patience with my ignorance!
If I wanted to send additional claims (e.g. employee number, department, title, device name, that sort of thing) and affiliate them with the Django auth user that your package creates and authenticates, how would you recommend that I go about doing that? I've seen several guides for extending the vanilla Django auth user model, but what would be the best way to streamline the process with django_auth_adfs?
Hi,
I am glad I found your project because I kinda struggling to set an authentication flow with one of my client's ADFS.
I developed a web app with Vue JS & Django Rest Framework.
I wondered if you would have some insights to share about making Django Rest Framework & Django Auth ADFS work together.
Thanks !
As mentioned in #14 a review of how to logout of Django and ADFS is needed.
I'm trying to plug this module into askbot and I continue getting an error on authenticate() takes exactly 0 arguments (2 given)
which does not make much sense to me since the method clearly is asking for two args. Any help is much appreciated since I'm at my limits here.
Settings
# checkout the documentation for more settings
AUTH_ADFS = {
'TENANT_ID': 'TENANT_ID',
'CLIENT_ID': 'AZURE_CLIENT_ID',
'CLIENT_SECRET': 'AZURE_CLIENT_SECRET',
'RELYING_PARTY_ID': 'AZURE_CLIENT_ID',
'AUDIENCE': 'microsoft:identityserver:unique-name',
'CLAIM_MAPPING': {'first_name': 'given_name',
'last_name': 'family_name',
'email': 'email'},
'REDIR_URI': 'https://CSRF_DOMAIN/oauth2/login',
}
Python 2.7, Django 1.8 failure:
Request Method: | GET
-- | --
https://url.com/oauth2/callback?code=lotsofalphnumericcharaterslikeakeyhere&state=Lw%3d%3d&session_state=123c85a7-1234-1234-21c6-12a9a96e123a
1.8.19
TypeError
authenticate() takes exactly 0 arguments (2 given)
/usr/local/lib/python2.7/dist-packages/django_auth_adfs/views.py in get, line 34
/usr/local/bin/uwsgi
2.7.12
['.', '', '/usr/local/lib/python2.7/dist-packages/pip-19.0.3-py2.7.egg', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-x86_64-linux-gnu', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '.', '/usr/local/lib/python2.7/dist-packages/askbot/deps']
Thu, 21 Feb 2019 03:55:07 +0000
Authenticating against an Azure AD-Directory I'm getting this response:
django_auth_adfs ERROR ADFS server returned an error: AADSTS54005: OAuth2 Authorization code was already redeemed, please retry witha new valid code or use an existing refresh token.
I found this on Microsoft Forum.
Is this a known problem and is there a solution?
The user should be shown a page when the ADFS redirects back with an error instead of a code. The current behavior is that django-auth-adfs throws an exception and the user receives a 500 Server Error page.
The situation I received this error in was:
URL the ADFS redirected to: https://<hostname>/oauth2/login?error=access_denied&error_description=MSIS9619%3a+The+received+request+is+not+authorized+to+access+the+requested+resource.
Exception and traceback:
Traceback:
File "/<path>/env/lib/python3.5/site-packages/django/utils/datastructures.py" in __getitem__
83. list_ = super(MultiValueDict, self).__getitem__(key)
During handling of the above exception ('code'), another exception occurred:
File "/<path>/env/lib/python3.5/site-packages/django/core/handlers/exception.py" in inner
41. response = get_response(request)
File "/<path>/env/lib/python3.5/site-packages/django/core/handlers/base.py" in _legacy_get_response
249. response = self._get_response(request)
File "/<path>/env/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
187. response = self.process_exception_by_middleware(e, request)
File "/<path>/env/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
185. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/<path>/env/lib/python3.5/site-packages/django/views/generic/base.py" in view
68. return self.dispatch(request, *args, **kwargs)
File "/<path>/env/lib/python3.5/site-packages/django/views/generic/base.py" in dispatch
88. return handler(request, *args, **kwargs)
File "/<path>/env/lib/python3.5/site-packages/django_auth_adfs/views.py" in get
19. code = request.GET["code"]
File "/<path>/env/lib/python3.5/site-packages/django/utils/datastructures.py" in __getitem__
85. raise MultiValueDictKeyError(repr(key))
Exception Type: MultiValueDictKeyError at /oauth2/login
Exception Value: "'code'"
Version from pip freeze: django-auth-adfs==0.1.2
Steps to set up authorization in ADSF: https://social.technet.microsoft.com/Forums/en-US/87bb6180-e7e1-4c64-9fbc-4596dfae42bb/adfs-authentication-based-on-group-membership?forum=ADFS
I am done with minimal configurations as exactly suggested by docs (http://django-auth-adfs.readthedocs.io/en/latest/configuration.html --> settings section).
The middleware is redirecting me to ADFS. I authenticate, and get sent back to my REDIR_URL with a #code=xxxxxxx appended.
Currently I have managed to capture this authentication code via AJAX call.
Once we receive a authorization code, there should be token validation end point on the client side (I assume that is resolved by CLIENT_ID in settings.py)
How should the subsequent flow be? I think that link is missing somewhere.
I get the following error once I click on login:
Sorry, but we’re having trouble with signing you in.
AADSTS50011: The reply url specified in the request does not match the reply urls configured for the application: 'xxxxxx-xxxxxx-xxxxxx-xxxxxx-xxxxxx'. <---
the application ID is the native app on Azure
My settings are
LOGIN_URL = 'django_auth_adfs:login'
LOGIN_REDIRECT_URL = '/'
MIDDLEWARE = [
'...
'django_auth_adfs.middleware.LoginRequiredMiddleware',
]
AUTH_ADFS = {
"LOGIN_EXEMPT_URLS":["^system/admin/",
"^account/login/"
"^oauth2/login/",
# "^oauth2/callback/",
],
"TENANT_ID": "xxxxx",
"CLIENT_ID": "xx",
"RELYING_PARTY_ID": "https://xxxx.onmicrosoft.com/xxxx-xxxx-xxx",
"AUDIENCE": "https://xxxx.onmicrosoft.com/xxxx-xxxx-xxxx-xxxx",
"CLAIM_MAPPING": {"first_name": "given_name",
"last_name": "family_name",
"email": "email"},
"REDIR_URI": "https://our-website.com/oauth2/callback",
}
The query string parameters in the URL has redirect_uri as: http://our-website.com/oauth2/callback
In Azure the reply url is set to https://our-website.com/oauth2/callback
Is there anything that I'm missing? Should I specify this reply url somewhere in my settings?
Django 1.11 and django-auth-adfs 1.0.0
Please let me know if you need any more info from my side
Despite 8baf088, it looks like the templates are still not included in the latest published PyPI package:
pip freeze | grep adfs
django-auth-adfs==1.1.2
ls $VIRTUAL_ENV/lib/python3.6/site-packages/django_auth_adfs/
backend.py drf-urls.py middleware.py rest_framework.py urls.py
config.py __init__.py __pycache__ signals.py views.py
As you can see, the templates
dir isn't present.
Hi,
In the doc you mention ADFSv3 as a prerequisite. Our customer is using ADFSv2, did you try on ADFSv2?
Kind regards
Didier
I'm interacting with an ADFS server that has not installed KB4038801 (at least not yet). This means that the configuration object returned has no "end_session_endpoint" attribute and this module crashes at https://github.com/jobec/django-auth-adfs/blob/master/django_auth_adfs/config.py#L179.
Is there any way to still use this IDP without the SLO functionality?
Having this settings file:
AUTH_ADFS = {
....
"CLAIM_MAPPING": {"first_name": "given_name",
"last_name": "family_name",
"email": "email"},
"USERNAME_CLAIM": "winaccountname",
"BOOLEAN_CLAIM_MAPPING": {"is_staff": "user_is_staff",
"is_superuser": "user_is_superuser"},
"GROUP_CLAIM": "group",
....
}
How the GROUP_CLAIM is consumed in Django side?
Right now it's not possible to properly set the is_staff
and is_superuser
fields on the user model of Django. Blocking access to the admin section of Django.
While there is a sort of workaround mentioned in #14 it should be handled in a better way.
Hello, would you consider adding client_secret parameter support? It's required by ADFS for Server application
application group when exchanging code for jwt token. (see https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/overview/ad-fs-scenarios-for-developers#web-browser-to-web-app).
I'll be happy to provide a PR with the fix if you are ok with it.
I have a problem with django-auth-adfs: Login always failed.
I can see that django_auth_adfs.backend.AdfsBackend.authenticate()
will be never called.
The signature of this method is authenticate(self, authorization_code=None)
and django.contrib.auth.authenticate()
will skip the call because of:
try:
inspect.getcallargs(backend.authenticate, **credentials)
except TypeError:
# This backend doesn't accept these credentials as arguments. Try the next one.
continue
TypeError will be raised because **credentials
dict has username
and password
arguments...
So i think there is a missing part somewhere ?!?
Any hints?
I noticed that you mentioned it was on your plan to make this work with DRF. I didn't realize that was the case and tried to get it working but have only gotten to a "DEBUG:django_auth_adfs.backend:django_auth_adfs was called but no authorization code was received
"GET /oauth2/login HTTP/1.1" 401 12
WARNING:django.server:"GET /oauth2/login HTTP/1.1" 401 12"
Just wondering if this should work for DRF or if not do you know of any other options for DRF and ADFS integration
Hi,
First of all, thank you so much for this project.
I'm trying to implement the Azure Integration using this https://github.com/jobec/django-auth-adfs/blob/master/docs/azure_ad_config_guide.rst guide.
I have all required information as per example:
AUTH_ADFS = {
"TENANT_ID": "12345678-90ab-cdef-1234-567890abcdef",
"CLIENT_ID": "480499d2-1f78-4a58-b7bc-03ebf8780af3",
"RELYING_PARTY_ID": "https://examplecom.onmicrosoft.com/338d463b-b82f-490b-567890abcdef",
"AUDIENCE": "https://examplecom.onmicrosoft.com/338d463b-b82f-490b-567890abcdef",
}
When I fire up Django, It gives me a TENANT_ID error...
django.core.exceptions.ImproperlyConfigured: 'TENANT_ID' is not a valid configuration directive
Have you experienced something like that ?
I would appreciate if you could help or at least point me to the right direction.
Thank you.
I've tried configuring this and all i get is Login failed after I've submitted my credentials to ADFS. I get t a long code response in the browser but nothing else.
We managed to integrate django_auth_adfs in our system. And partly it's also working with Azure AD.
but sometimes we got the following error:
ADFS server returned an error: AADSTS50076: Due to a configuration change made by your administrator, or because you moved to a new location, you must use multi-factor authentication to access '-*****--*-0c4812*****'
.
Azure AD is configured with conditional access rules. When you are in a trusted location you don't have to enter 2FA, but if you are not on a trusted location you have to enter 2FA.
When you are on a NOT trusted location, you can login fine.
When you are on a trusted location we got the error above.
ADFS server returned an error: AADSTS50076: Due to a configuration change made by your
administrator, or because you moved to a new location, you must use multi-factor authentication to
access '*******-*****-****-****-0c4812*****'.
Things we tried:
We found an article from Microsoft that explains exactly what happens:
I'm adding django-auth-adfs to an existing project. As per the suggestion in the Django docs, I've created related models to store additional information rather than replacing the user model with AbstractUser
.
I'd like to map a returned claim to one of my related objects (in this case, storing the manager of a user).
From what I can tell from #34 and the FAQ I would need to replace the user model with one inheriting from AbstractUser
to be able to store this data, rather than map a claim to a related field.
Is this correct?
I've tried to use the standard related model syntax of using __
to get to related fields in my claims dictionary with no success.
Having done some testing as part of my setup and I have noticed the following:
Site return redirection:
When a user hits a view/page that requires authentication the user is sent to the ADFS login screen, the user signs in and is returned to the URL defined in the “LOGIN_REDIRECT_URL”
Is there any way of passing the current location over to ADFS so the user can return to the same page, I’m not sure if this is a limitation of this module or ADFS. (It’s my understanding that ADFS can do this.)
So for me this solution works well on sites that require a login to the whole site where the user hits the login page as the first point of call rather than a site that has mixed content which displays differently depending on your authentication state.
Logout:
I’m getting mixed results when it comes to logging out a user.
I assume I would need to logout of both Django and close the ADFS session, logging out of Django is not enough as I found that when I revisit the login process I get automatically authenticated (no ADFS sign in required)
Having a function in your code to provide this in a single template call much like the {{ ADFS_AUTH_URL }} context call would be helpful.
User “Staff” and “Superuser” Status
I didn't see an option to be able to add a user with "Staff" or "Superuser" rights
Hi,
I just wanted to point out that in AD 4.0 you can just specify the Redirect URL http://127.0.0.1:8000 and
leave out the oauth2/callback part because, ADFS will now work with any path under that Redirect URL.
This is to simplify documentation and configuration. Give it a try and let me know!
I've tested this in AD 4.0, don't know if this applies for AD 3.0 or Azure though.
Thanks for the new release!.
Not able to return https for redirect_uri:
def redirect_uri(self, request): self.load_config() return request.build_absolute_uri(reverse("django_auth_adfs:callback"))
https://github.com/jobec/django-auth-adfs/blob/master/django_auth_adfs/config.py#L259
Currently manually replacing http to https
def redirect_uri(self, request): self.load_config() return request.build_absolute_uri(reverse("django_auth_adfs:callback")).replace("http://", "https://")
Im assuming ive missing a setting in either django or django-auth-adfs... scratching my head.
If there isn't an option could a fix be to add a setting?
Is there a way to make timeout to ADFS configurable and add configurable retries?
Some screenshots and sample config from the ADFS site should be included in the documentation.
The idea of disabling SSO is great and its no longer needed for us to open Firefox in incognito mode if we want to use another user. What would be the best way of integrating the SSO solution, but with the option to change the user? I'd rather not do it static and dirty as I first started doing 😄
EDIT: I could say my initial solution was to create something like this, but it don't really handle logouts:
def change_user(request):
url = provider_config.build_authorization_endpoint(request)
password_url = url + "&prompt=login"
return redirect(password_url)
Could we have a link to logout of AD FS at template level opposed to login?
http://django-auth-adfs.readthedocs.io/en/latest/extras.html
...
<a href="{{ ADFS_AUTH_URL }}">Click here to log in</a>
...
pip install django-auth-adfs==0.1.2
fails with django-auth-adfs is in an unsupported or invalid wheel
when trying to install on Python 3.6
. Installation works fine on Python 2.7
.
The middleware is redirecting me to ADFS. I authenticate, and get sent back to my redirect URI with a ?code=xxxxxxx
appended. Then... nothing. I just get a Login Failed message. Looking at the source I suppose this is coming from the authentication flow in django-auth-adfs views.py. Because user = authenticate(authorization_code=code)
returns None (verified by tweaking views.py to print user to the console), a 401 is returned with the Login Failed error message.
I guess I don't really understand what's supposed to happen at this point. There aren't any users in my auth_user table, but I was under the impression django-auth-adfs would create them on the fly.
I also seem to remember seeing claims being printed out in the server console in earlier tests... now I don't even see those coming across.
What could be the missing link?
Set an unusable password on newly created user instead of leaving it empty.
as implemented in f79c93f enforces all users to be deleted before migrating, otherwise it would crash with django.core.exceptions.ValidationError: {'password': ['This field cannot be blank.']}
on login.
Should this be documented in the patch notes?
Hi,
Now that we have our resources correctly configured we are getting the following issue when CA_BUNDLE = False
Exception Type: ImproperlyConfigured
Exception Value: Invalid ADFS token signing certificate
Looking at the code, it seems to relate to the load_pem_x509_certificate(certificate, backend)
method in backend.py
I am using Python 2.7.13.
Could you please add .tar.gz
package on pypi since latest version (0.1.0) is not available when doing pip install django-auth-adfs
? It installs 0.0.5 version.
Here can be seen that only .whl
package was created in latest build.
Good morning,
Did what I could to verify that this wasn't something easily fixable, but one of my two sites using auth-adfs is having issues this morning:
HTTPSConnectionPool(host='login.microsoftonline.com', port=443): Max retries exceeded with url: /<tenant-id>/.well-known/openid-configuration (Caused by NewConnectionError('<urllib3.connection.VerifiedHTTPSConnection object at 0x042E97D0>: Failed to establish a new connection: [Errno 11002] getaddrinfo failed',))
I haven't changed anything, and the other site using an identical setup isn't currently facing any issues. Did I misconfigure something or am I missing something obvious?
Let me know if you'd like me to post any more of the traceback, I'll update this with anything else I find relevant.
Thank you!
As mentioned in #14 some system of redirecting back to the page that triggered the authentication would be nice. This cannot be done through the OAuth2 flow, so some other system would be needed.
I am using a Windows Server 2016 virtual machine to test the integration and I have successfully tested that the ADFS works via https://[MY-ADFDS]/adfs/ls/IdpInitiatedSignon.aspx signon test page. This is a portion that pertains to Django-ADFS in my settings.py file:
AUTH_ADFS = {
"SERVER": "[MY-ADFS]",
"CLIENT_ID": "django_website.adfs.client_id",
"RESOURCE": "django_website.adfs.identifier",
"AUDIENCE": "microsoft:identityserver:django_website.adfs.identifier",
"ISSUER": "http://[MY-ADFS]/adfs/services/trust",
"CA_BUNDLE": False,
"CLAIM_MAPPING": {"first_name": "given_name",
"last_name": "family_name",
"email": "email"},
"USERNAME_CLAIM": "winaccountname",
"GROUP_CLAIM": "group",
"REDIR_URI": "http://[MY-DJANGO-APP]/oauth2/login",
}
But still I am getting the following error:
Description:
Encountered error during federation passive request.Additional Data
Protocol Name:
OAuthAuthorizationProtocolRelying Party:
django_website.adfs.identifierException details:
Microsoft.IdentityServer.Web.Protocols.OAuth.Exceptions.OAuthUnauthorizedClientException: MSIS9321: Received invalid OAuth request. The client 'django_website.adfs.client_id' is forbidden to access the resource 'django_website.adfs.identifier'.
at Microsoft.IdentityServer.Web.Protocols.OAuth.OAuthProtocolContext.ValidateScopes(String scopeParameter, String clientId, String relyingPartyId)
at Microsoft.IdentityServer.Web.Protocols.OAuth.OAuthAuthorization.OAuthAuthorizationRequestContext.ValidateCore()
at Microsoft.IdentityServer.Web.Protocols.ProtocolContext.Validate()
at Microsoft.IdentityServer.Web.Protocols.OAuth.OAuthAuthorization.OAuthAuthorizationProtocolHandler.GetRequiredPipelineBehaviors(ProtocolContext pContext)
at Microsoft.IdentityServer.Web.PassiveProtocolListener.EvaluateHomeRealm(PassiveProtocolHandler protocolHandler, ProtocolContext protocolContext)
at Microsoft.IdentityServer.Web.PassiveProtocolListener.OnGetContext(WrappedHttpListenerContext context)
It makes no sense to my since, I ensured that the Relying Party Trust has the Permit everyone attribute.
Do you have any suggestions?
Mandatory configuration settings are not validated.
cfr issue #1
Some kind of logging is needed to perform better troubleshooting.
Usefull info: http://stackoverflow.com/questions/20282521/django-request-logger-not-propagated-to-root/22336174#22336174
With django_auth_adfs enabled trying to manipulate the auth_group table in any way will error, saying it it unable to find the table. The table however exists, and doing the same manipulations with the module disabled (adding users and groups) will work fine.
website.com and db are placeholders.
Environment:
Request Method: GET
Request URL: http://website.com:8090/oauth2/callback?code=iGSphxaiNECS46oNq94OUA.Dz4A-ntm1ggIAOhiFot6zkGSYv8.f_IUukDnfSynaji5Br3YLHcx9Eq_7Wa6DW3ujG56R1qKgpNrwXuCMhRMzz4B9B5rxrvj1krZeyleaO7wlBVtTEixtaMTo3KhseX-myd3iITe_AWDSvllckGD_88dXj8r_QXlylkdeRi0CLMptwOomkwO6oqm5fOnVdyLKMby3G3rUnwlzysmcI7Se_zdUnTBcVQc5ecDzNmtLuN8zfzMKFzUsxpZ5la4nDav0PhfAXFXqhsCfiwWqphZKGamxdD4SOlgOjwYnTXp-J7Cyf588oemdy95u1NC528GykBpdEO99yvxGAT81Kg5O1sZ8G5AngOfg59Q6JdRCmBDvV3wMQ&state=Lw==
Django Version: 2.0.4
Python Version: 3.6.3
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'console',
'django_celery_results',
'corsheaders',
'django_filters',
'django_extensions',
'widget_tweaks',
'django_tables2',
'haystack',
'rangefilter',
'storages',
'django_auth_adfs']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django_auth_adfs.middleware.LoginRequiredMiddleware']
Traceback:
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/db/backends/utils.py" in _execute
85. return self.cursor.execute(sql, params)
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/db/backends/mysql/base.py" in execute
71. return self.cursor.execute(query, args)
File "/home/watson/.local/lib/python3.6/site-packages/MySQLdb/cursors.py" in execute
250. self.errorhandler(self, exc, value)
File "/home/watson/.local/lib/python3.6/site-packages/MySQLdb/connections.py" in defaulterrorhandler
50. raise errorvalue
File "/home/watson/.local/lib/python3.6/site-packages/MySQLdb/cursors.py" in execute
247. res = self._query(query)
File "/home/watson/.local/lib/python3.6/site-packages/MySQLdb/cursors.py" in _query
412. rowcount = self._do_query(q)
File "/home/watson/.local/lib/python3.6/site-packages/MySQLdb/cursors.py" in _do_query
375. db.query(q)
File "/home/watson/.local/lib/python3.6/site-packages/MySQLdb/connections.py" in query
276. _mysql.connection.query(self, query)
The above exception ((1146, "Table 'db.auth_group' doesn't exist")) was the direct cause of the following exception:
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
35. response = get_response(request)
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
128. response = self.process_exception_by_middleware(e, request)
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
126. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/views/generic/base.py" in view
69. return self.dispatch(request, *args, **kwargs)
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/views/generic/base.py" in dispatch
89. return handler(request, *args, **kwargs)
File "/home/watson/.local/lib/python3.6/site-packages/django_auth_adfs/views.py" in get
34. user = authenticate(request, authorization_code=code)
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/contrib/auth/__init__.py" in authenticate
70. user = _authenticate_with_backend(backend, backend_path, request, credentials)
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/contrib/auth/__init__.py" in _authenticate_with_backend
116. return backend.authenticate(*args, **credentials)
File "/home/watson/.local/lib/python3.6/site-packages/django_auth_adfs/backend.py" in authenticate
266. user = self.process_access_token(access_token, adfs_response)
File "/home/watson/.local/lib/python3.6/site-packages/django_auth_adfs/backend.py" in process_access_token
96. self.update_user_groups(user, claims)
File "/home/watson/.local/lib/python3.6/site-packages/django_auth_adfs/backend.py" in update_user_groups
170. django_groups = [group.name for group in user.groups.all()]
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/db/models/query.py" in __iter__
272. self._fetch_all()
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/db/models/query.py" in _fetch_all
1179. self._result_cache = list(self._iterable_class(self))
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/db/models/query.py" in __iter__
53. results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/db/models/sql/compiler.py" in execute_sql
1067. cursor.execute(sql, params)
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/db/backends/utils.py" in execute
100. return super().execute(sql, params)
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/db/backends/utils.py" in execute
68. return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/db/backends/utils.py" in _execute_with_wrappers
77. return executor(sql, params, many, context)
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/db/backends/utils.py" in _execute
85. return self.cursor.execute(sql, params)
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/db/utils.py" in __exit__
89. raise dj_exc_value.with_traceback(traceback) from exc_value
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/db/backends/utils.py" in _execute
85. return self.cursor.execute(sql, params)
File "/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/django/db/backends/mysql/base.py" in execute
71. return self.cursor.execute(query, args)
File "/home/watson/.local/lib/python3.6/site-packages/MySQLdb/cursors.py" in execute
250. self.errorhandler(self, exc, value)
File "/home/watson/.local/lib/python3.6/site-packages/MySQLdb/connections.py" in defaulterrorhandler
50. raise errorvalue
File "/home/watson/.local/lib/python3.6/site-packages/MySQLdb/cursors.py" in execute
247. res = self._query(query)
File "/home/watson/.local/lib/python3.6/site-packages/MySQLdb/cursors.py" in _query
412. rowcount = self._do_query(q)
File "/home/watson/.local/lib/python3.6/site-packages/MySQLdb/cursors.py" in _do_query
375. db.query(q)
File "/home/watson/.local/lib/python3.6/site-packages/MySQLdb/connections.py" in query
276. _mysql.connection.query(self, query)
Exception Type: ProgrammingError at /oauth2/callback
Exception Value: (1146, "Table 'db.auth_group' doesn't exist")
Working on a Django project (specifically with the Saleor framework) and looking to authenticate against our ADFS 2016 servers. I can get the authentication to work, but only when the user logs in for the first time. After they log in for the first time, proceeds to log out, and tries to log in again...a "duplicate key value violates unique constraint "userprofile_user_email_key" IntegrityError is thrown. The details says the email address already exists.
Request Method: | GET
-- | --
http://localhost:8000/en/oauth2/callback?code=yRuy1iPDyUu3PlGaHqcHpw.l0dBl0hc1ghgAPxu-bZKE6AXCIY.gf96MEQm8zNy8IJQGotqh1JiBshH5n7WOC8ivsqPiPafJvakuapOmRtyj1_V92nUFjN6xgXgOkcI-TSwHQu6wfrIva5nMTf6dvMU9qFWji3Gy1jdn8zVhgh4x2kTyjialSKPsWEXhdrkESm9VaHPVl17j5Y89MlTqJpF2H6FJkKx0w1yACjsbf9Ua8vI3haVHd45UGqy8ElOSmPSYhXmPzLgyStBXQqKGXU_B7FJ8WLRSIqA2DDPi07BLPtACyeCPyXboaGl-ZoUcZtyv3T4af2fkwzzHy3OB387BbVIJFLahrFgDGAPdV5bdevfe_YMVXSzHpx34xEDaD4OH9UIBQ&state=Lw==&client-request-id=785f2896-595c-40f2-e731-0780010000fc
2.1.3
IntegrityError
duplicate key value violates unique constraint "userprofile_user_email_key" DETAIL: Key (email)=([email protected]) already exists.
/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py in _execute, line 85
/usr/local/bin/python
3.6.7
['/app', '/usr/local/lib/python36.zip', '/usr/local/lib/python3.6', '/usr/local/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/site-packages', '/app']
Fri, 7 Dec 2018 07:33:36 -0600
Its my understanding (please correct me if im wrong), when a user a logs in via ADFS, if the user doesn't exist...the user will be created in the Django Postgres database. If the user does exist, a "mapping" will happen and associate the ADFS user to the existing user in the Django Postgres database?
Our AUTH_ADFS config is as follows (had to scrub our information):
AUTH_ADFS = {
"SERVER": "scrubbed URL",
"CLIENT_ID": "scrubbed client_ID",
"RELYING_PARTY_ID": "scrubbed relying party ID",
"AUDIENCE": "microsoft:identityserver:scrubbed",
"CA_BUNDLE": "/app/ca-bundle.pem",
"CLAIM_MAPPING": {"email":"email"},
"GROUP_TO_FLAG_MAPPING": {"is_superuser": "Admins"},
"CONFIG_RELOAD_INTERVAL": 0.1
}
Any ideas?
Hi,
I configured my settings file and ADFS as per the guide. Now my problem is that I don't see any errors in the console or at the server in the Event Viewer. The only behavior I'm getting from visiting a @login_required view is automatic redirection to http://127.0.0.1:8000/accounts/login?next=/my/view
What pointers could you give me to see what I am missing in my configuration to allow users authenticate in my ADFS and then being redirected to the @login_required view?
Thanks
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.