AWS SSO is finally available in the Tokyo region, so I tried single sign-on with Django.
Create a custom SAML 2.0 application with AWS SSO. Error handling is not performed to confirm that it works to the minimum necessary.
--Use ʻAWS SSO ID Storeas the AWS SSO ID source --Select
Pythonas the development language --Use
Django` for web application frameworks
--Allow HTTPS communication even during development
#Creating a virtual environment
$ python -m venv ~/envs/example-awssso
#Start-up
$ source ~/envs/example-awssso/bin/activate
#Django installation
(example-awssso) $ python -m pip install Django
#Version confirmation
(example-awssso) $ python -m django --version
3.1.1
#Project creation
(example-awssso) $ django-admin startproject webapp .
It is for development
. Allow SSL communication. Please do not use it in a production environment.
I will omit the creation of a self-signed certificate.
(example-awssso) $ pip install django-sslserver
There was a lot of support for SAML authentication, but I decided to try python3-saml
.
Resolve the dependency of xmlsec
before installing python3-saml
.
https://github.com/onelogin/python3-saml https://pypi.org/project/xmlsec/
#xmlsec dependency resolution
(example-awssso) $ brew install libxml2 libxmlsec1 pkg-config
#Installation
(example-awssso) $ pip install python3-saml
AWS SSO
Set up your custom application in the AWS Management Console.
https: // localhost: 8000 / sso /
in [Application Start URL] of "Application Properties".https: // localhost: 8000 / acs /
in [Application ACS URL]https: // localhost: 8000 / metadata /
in [Application SAML Target]$ (user: subject}
, persistent
in Subject
,
$ {user: name} , ʻunspecified
in the attributes※6
Please refer to the sample code of Django on GitHub of python3-saml.
(example-awssso) $ mkdir saml
#Create an empty file for configuration
(example-awssso) $ touch saml/settings.json
(example-awssso) $ touch saml/advanced_settings.json
advanced_settings.json
{
"security": {
"nameIdEncrypted": false,
"authnRequestsSigned": false,
"logoutRequestSigned": false,
"logoutResponseSigned": false,
"signMetadata": false,
"wantMessagesSigned": false,
"wantAssertionsSigned": false,
"wantNameId": true,
"wantNameIdEncrypted": false,
"signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
"digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256"
},
"contactPerson": {
"technical": {
"givenName": "technical_name",
"emailAddress": "[email protected]"
},
"support": {
"givenName": "support_name",
"emailAddress": "[email protected]"
}
},
"organization": {
"en-US": {
"name": "sp_test",
"displayname": "SP test",
"url": "https://localhost:8000"
}
}
}
settings.json
Edit and save the following settings from the contents of the AWS SSO SAML metadata file `.
sp
idp
ʻSet from the contents of the AWS SSO SAML metadata file. The values in
()` are the attribute values of the metadata file.
{
"strict": true,
"debug": true,
"sp": {
"entityId": "https://localhost:8000/metadata/",
"assertionConsumerService": {
"url": "https://localhost:8000/acs/",
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
},
"singleLogoutService": {
"url": "https://localhost:8000/sls/",
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
},
"NameIDFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
"x509cert": "",
"privateKey": ""
},
"idp": {
"entityId": "https://portal.sso.ap-northeast-1.amazonaws.com/saml/assertion/<AWS SSO ID>",
"singleSignOnService": {
"url": "https://portal.sso.ap-northeast-1.amazonaws.com/saml/assertion/<AWS SSO ID>",
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
},
"singleLogoutService": {
"url": "https://portal.sso.ap-northeast-1.amazonaws.com/saml/logout/<AWS SSO ID>",
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
},
"x509cert": "<AWS SSO certificate>"
}
}
webapp/settings.py
Add template directory settings.
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [],
+ 'DIRS': [
+ os.path.join(BASE_DIR, 'templates')
+ ],
Add other settings to the file
+ INSTALLED_APPS += ['sslserver']
+
+ # session
+ SESSION_ENGINE = 'django.contrib.sessions.backends.file'
+
+ # ssl
+ SECURE_SSL_REDIRECT = True
+ SESSION_COOKIE_SECURE = True
+ CSRF_COOKIE_SECURE = True
+
+ # python3-saml
+ SAML_FOLDER = os.path.join(BASE_DIR, 'saml')
+
+ # login, logout
+ LOGIN_REDIRECT_URL = '/'
+ LOGOUT_REDIRECT_URL = '/'
webapp/urls.py
Define the URL required for SSO. views.py will be newly created after this.
from django.contrib import admin
from django.urls import path
+ from django.contrib.auth.views import LogoutView
+ from .views import index, sso, acs, metadata
urlpatterns = [
path('admin/', admin.site.urls),
+ path('', index, name='index'),
+ path('sso/', sso, name='sso'),
+ path('acs/', acs, name='acs'),
+ path('logout/', LogoutView.as_view(), name='logout'),
+ path('metadata/', metadata),
]
webapp/views.py
As mentioned in the source comment, create the following view.
(example-awssso) $ touch webapp/views.py
from django.conf import settings
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseServerError
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from onelogin.saml2.auth import OneLogin_Saml2_Auth
from onelogin.saml2.settings import OneLogin_Saml2_Settings
from onelogin.saml2.utils import OneLogin_Saml2_Utils
def init_saml_auth(params):
"""SAML client initialization
Client initialization parameters, saml/settings.json ・ saml/advanced_settings.Initialize with json file
"""
auth = OneLogin_Saml2_Auth(params, custom_base_path=settings.SAML_FOLDER)
return auth
def prepare_django_request(request):
"""Get SAML client initialization parameters
Generate SAML client initialization parameters from HTTP request object
"""
params = {
'https': 'on' if request.is_secure() else 'off',
'http_host': request.META['HTTP_HOST'],
'script_name': request.META['PATH_INFO'],
'server_port': request.META['SERVER_PORT'],
'get_data': request.GET.copy(),
'post_data': request.POST.copy()
}
return params
def index(request):
"""TOP page display
Render the TOP page
"""
#Context parameter initialization
attributes = False
if 'samlUserdata' in request.session:
if len(request.session['samlUserdata']) > 0:
attributes = request.session['samlUserdata'].items()
return render(request, 'index.html', { 'attributes': attributes, })
@csrf_exempt
def sso(request):
"""AWS SSO redirect
Redirect to AWS SSO
"""
#Get initialization parameters
prepare_params = prepare_django_request(request)
#Initialization
auth = init_saml_auth(prepare_params)
return HttpResponseRedirect(auth.login())
@csrf_exempt
def acs(request):
"""Assertion verification
Verify that the user has permission to access the application
"""
#Get initialization parameters
prepare_params = prepare_django_request(request)
#Initialization
auth = init_saml_auth(prepare_params)
request_id = None
if 'AuthNRequestID' in request.session:
request_id = request.session['AuthNRequestID']
auth.process_response(request_id=request_id)
errors = auth.get_errors()
if not errors:
if 'AuthNRequestID' in request.session:
del request.session['AuthNRequestID']
request.session['samlUserdata'] = auth.get_attributes()
request.session['samlNameId'] = auth.get_nameid()
request.session['samlNameIdFormat'] = auth.get_nameid_format()
request.session['samlNameIdNameQualifier'] = auth.get_nameid_nq()
request.session['samlNameIdSPNameQualifier'] = auth.get_nameid_spnq()
request.session['samlSessionIndex'] = auth.get_session_index()
return HttpResponseRedirect(auth.redirect_to('/'))
def metadata(request):
"""SP metadata display
Output metadata
"""
saml_settings = OneLogin_Saml2_Settings(settings=None, custom_base_path=settings.SAML_FOLDER, sp_validation_only=True)
metadata = saml_settings.get_sp_metadata()
errors = saml_settings.validate_metadata(metadata)
if len(errors) == 0:
return HttpResponse(content=metadata, content_type='text/xml')
else:
return HttpResponseServerError(content=', '.join(errors))
# SECRET_KEY setting
(example-awssso) $ export DJANGO_SECRET_KEY='01234567890123456789012345678901234567890123456789'
#Start Django
(example-awssso) $ python ./manage runsslserver 0.0.0.0:8000 --certificate ./certs/localhost.crt.pem --key ./certs/localhost.key.pem
Go to https: // localhost: 8000 /
.
Select [Login]. Since it will be redacted to AWS SSO, log in as the user created in the ID store.
The user information recorded in the session from the SAML response is displayed on the screen.
It's now possible to centrally manage what IAM users used to access the AWS Management Console, and easily manage access and users to all the accounts that hang from AWS Organizations.
In addition, AWS SSO account assignment API and CloudFormation support have been added the other day, so it seems that automation can be flexibly supported.
Recommended Posts