Commit effd19d7 authored by Lukas Burgey's avatar Lukas Burgey
Browse files

Add direct auth redirect using query parameters

This resolves #2
parent e9dddd00
...@@ -5,7 +5,7 @@ map $http_upgrade $connection_upgrade { ...@@ -5,7 +5,7 @@ map $http_upgrade $connection_upgrade {
} }
upstream django { upstream django {
server unix://home/feudal/backend/feudal.sock; server unix://home/feudal/feudalBackend/feudal.sock;
} }
upstream websocket { upstream websocket {
...@@ -23,37 +23,37 @@ server { ...@@ -23,37 +23,37 @@ server {
listen 443 ssl http2 default_server; listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server; listen [::]:443 ssl http2 default_server;
server_name hdf-portal.data.kit.edu; server_name hdf-portal-dev.data.kit.edu;
charset utf-8; charset utf-8;
client_max_body_size 75M; client_max_body_size 75M;
ssl on; ssl on;
ssl_certificate /etc/ssl/hdf-portal.data.kit.edu/chain.pem; ssl_certificate /etc/ssl/hdf-portal-dev.data.kit.edu/chain.pem;
ssl_trusted_certificate /etc/ssl/hdf-portal.data.kit.edu/fullchain.pem; ssl_trusted_certificate /etc/ssl/hdf-portal-dev.data.kit.edu/fullchain.pem;
ssl_certificate_key /etc/ssl/hdf-portal.data.kit.edu/key.pem; ssl_certificate_key /etc/ssl/hdf-portal-dev.data.kit.edu/key.pem;
ssl_dhparam /etc/nginx/cert/dhparam.pem;
ssl_session_cache shared:SSL:20m; ssl_session_cache shared:SSL:20m;
ssl_session_timeout 180m; ssl_session_timeout 180m;
ssl_prefer_server_ciphers on; ssl_prefer_server_ciphers on;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DHE+AES128:!ADH:!AECDH:!MD5; ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DHE+AES128:!ADH:!AECDH:!MD5;
#ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_protocols TLSv1.1 TLSv1.2; ssl_protocols TLSv1.1 TLSv1.2;
ssl_dhparam /etc/nginx/cert/dhparam.pem;
ssl_stapling on; ssl_stapling on;
ssl_stapling_verify on; ssl_stapling_verify on;
resolver 141.3.175.65 141.3.175.66; resolver 141.3.175.65 141.3.175.66;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
root /home/feudal/webpage/dist; root /home/feudal/feudalWebpage/dist;
index index.html; index index.html;
location /backend/static { location /backend/static {
alias /home/feudal/backend/static; alias /home/feudal/feudalBackend/static;
} }
location /backend { location /backend {
uwsgi_pass django; uwsgi_pass django;
include /home/feudal/backend/example-config/uwsgi_params; include /home/feudal/config/uwsgi_params;
} }
location /frontend { location /frontend {
...@@ -68,6 +68,9 @@ server { ...@@ -68,6 +68,9 @@ server {
} }
location / { location / {
if ($arg_idp) {
return 301 /backend/auth/v1/request$is_args$args;
}
rewrite "^$" /frontend; rewrite "^$" /frontend;
} }
} }
......
import json import json
import logging import logging
import urllib
from django.contrib.auth import authenticate, login, logout from django.contrib.auth import authenticate, login, logout
from django.core.exceptions import ImproperlyConfigured
from django.shortcuts import redirect from django.shortcuts import redirect
from django.views import View from django.views import View
from oic import rndstr from oic import rndstr
...@@ -21,39 +23,63 @@ LOGGER = logging.getLogger(__name__) ...@@ -21,39 +23,63 @@ LOGGER = logging.getLogger(__name__)
IDP_COOKIE_NAME = 'idp_id' IDP_COOKIE_NAME = 'idp_id'
def idp_id_from_request(request): class AuthException(Exception):
pass
def select_oidc_config(request):
issuer_uri_urlenc = request.GET.get('idp', None)
idp_id = request.COOKIES.get(IDP_COOKIE_NAME, None) idp_id = request.COOKIES.get(IDP_COOKIE_NAME, None)
if idp_id is not None:
return idp_id
LOGGER.info('idp_id cookie was not set on request %s', request)
return default_idp().id
# IdP selection using the 'idp' url parameter
try:
if issuer_uri_urlenc is not None:
issuer_uri = urllib.parse.unquote(issuer_uri_urlenc)
return OIDCConfig.objects.get(issuer_uri=issuer_uri)
class AuthException(Exception): # IdP selection using a cookie
pass elif idp_id is not None:
return OIDCConfig.objects.get(id=idp_id)
return default_idp()
except OIDCConfig.DoesNotExist:
LOGGER.error("Failed to select IdP. Serving default IdP")
return default_idp()
class Auth(View): class Auth(View):
def get(self, request): def get(self, request):
try: try:
state = rndstr() state = rndstr()
idp_id = idp_id_from_request(request)
oidc_config = OIDCConfig.objects.get(id=idp_id)
utils.set_session(request, 'state', state) utils.set_session(request, 'state', state)
utils.set_session(request, 'idp_id', idp_id)
# select an idp according to query parameters / cookies
oidc_config = select_oidc_config(request)
# store the idp used for the authentication in the users session
utils.set_session(request, 'idp_id', oidc_config.id)
# construct the auth redirect
auth_redirect = oidc_config.get_auth_request( auth_redirect = oidc_config.get_auth_request(
oidc_config.oidc_client, oidc_config.oidc_client,
state, state,
) )
LOGGER.debug('Auth: redirecting %s to IdP %s', state, oidc_config) LOGGER.debug('Auth: redirecting %s to IdP %s', state, oidc_config)
# include query parameters in the redirect to the idp
if request.GET.items().length > 1:
urlparams = request.GET.copy()
if 'idp' in urlparams:
del urlparams['idp']
return redirect(auth_redirect+'&'+urlparams.urlencode())
return redirect(auth_redirect) return redirect(auth_redirect)
except OIDCConfig.DoesNotExist: except ImproperlyConfigured:
LOGGER.error('OIDCConfig is not available') LOGGER.error('No OIDCConfig is not available')
# the error is deleted from the session when the state is delivered # the error is deleted from the session when the state is delivered
request.session['error'] = 'Server Error' request.session['error'] = 'Server Error'
...@@ -64,7 +90,9 @@ class AuthCallback(View): ...@@ -64,7 +90,9 @@ class AuthCallback(View):
def get(self, request): def get(self, request):
try: try:
state = utils.get_session(request, 'state', None) state = utils.get_session(request, 'state', None)
idp_id = utils.get_session(request, 'idp_id', default_idp().id) idp_id = utils.get_session(request, 'idp_id', None)
if idp_id is None:
LOGGER.error("Session for %s does not contain an idp_id. Hence we don't now which idp authenticated the user", state)
oidc_config = OIDCConfig.objects.get(id=idp_id) oidc_config = OIDCConfig.objects.get(id=idp_id)
oidc_client = oidc_config.oidc_client oidc_client = oidc_config.oidc_client
...@@ -79,8 +107,7 @@ class AuthCallback(View): ...@@ -79,8 +107,7 @@ class AuthCallback(View):
raise AuthException('Erroneous callback from IdP {}'.format(oidc_config)) raise AuthException('Erroneous callback from IdP {}'.format(oidc_config))
if not state == aresp['state']: if not state == aresp['state']:
LOGGER.error('AuthCallback: states do not match') raise AuthException('AuthCallback: States do not match')
raise AuthException('AuthCallbackStates do not match')
ac_token_response = ( ac_token_response = (
oidc_client.do_access_token_request( oidc_client.do_access_token_request(
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment