Repository: zamzterz/Flask-pyoidc

Issue:
SERVER_NAME requirement
Hi!

I've been playing around today with this great lib but I can't figure out one thing. According to the docs, `SERVER_NAME` must be set in order to the lib to work, but there are no further explanations.

In my case, using the latest version of flask from pip (1.1.2) and the version 3.2.0 of this iib, if I set `SERVER_NAME` to something then all the request that use a different hostname (for example kubernetes liveness and readiness probes, that uses the localhost / localip) get a 404 as an answer. If I don't set `SERVER_NAME` the app won't come up because of the hard requirement of the lib.

Is there some alternative than setting `SERVER_NAME`? 

Thanks in advance!

Discussion:

Patch:
diff --git a/docs/configuration.md b/docs/configuration.md
index 966193f..432d1d9 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -68,8 +68,8 @@ config = ProviderConfiguration([provider configuration], client_metadata=client_
 
 **Note: The redirect URIs registered with the provider MUST include `<application_url>/redirect_uri`,
 where `<application_url>` is the URL of the Flask application.**
-To configure this extension to use a different endpoint, set the
-[`OIDC_REDIRECT_ENDPOINT` configuration parameter](#flask-configuration).
+To configure this extension to use a different location, set the
+[`OIDC_REDIRECT_DOMAIN` and/or `OIDC_REDIRECT_ENDPOINT` configuration parameter](#flask-configuration).
 
 #### Dynamic client registration
 
@@ -87,15 +87,16 @@ config = ProviderConfiguration([provider configuration], client_registration_inf
 The application using this extension **MUST** set the following
 [builtin configuration values of Flask](http://flask.pocoo.org/docs/config/#builtin-configuration-values):
 
-* `SERVER_NAME`: **MUST** be the same as `<flask_url>` if using static client registration.
 * `SECRET_KEY`: This extension relies on [Flask sessions](http://flask.pocoo.org/docs/quickstart/#sessions), which
    requires `SECRET_KEY`.
 
-You may also configure the way the user sessions created by this extension are handled:
+This extension also defines the following configuration parameters:
 
+* `OIDC_REDIRECT_DOMAIN`: Set the domain (which may contain port number) used in the redirect_uri to receive
+  authentication responses. Defaults to the `SERVER_NAME` configured for Flask.
+* `OIDC_REDIRECT_ENDPOINT`: Set the endpoint used in the redirect_uri to receive authentication responses. Defaults to
+  `redirect_uri`, meaning the URL `<application_url>/redirect_uri` needs to be registered with the provider(s).
 * `OIDC_SESSION_PERMANENT`: If set to `True` (which is the default) the user session will be kept until the configured
   session lifetime (see below). If set to `False` the session will be deleted when the user closes the browser.
-* `OIDC_REDIRECT_ENDPOINT`: Set the endpoint used as redirect_uri to receive authentication responses. Defaults to
-  `redirect_uri`, meaning the URL `<application_url>/redirect_uri` needs to be registered with the provider(s).
 * `PERMANENT_SESSION_LIFETIME`: Control how long a user session is valid, see
   [Flask documentation](http://flask.pocoo.org/docs/1.0/config/#PERMANENT_SESSION_LIFETIME) for more information.
diff --git a/src/flask_pyoidc/flask_pyoidc.py b/src/flask_pyoidc/flask_pyoidc.py
index 4eadb25..c5afcea 100644
--- a/src/flask_pyoidc/flask_pyoidc.py
+++ b/src/flask_pyoidc/flask_pyoidc.py
@@ -16,6 +16,7 @@
 import functools
 import json
 import logging
+from urllib.parse import parse_qsl, urlunparse
 
 import flask
 import importlib_resources
@@ -23,7 +24,6 @@ from flask import current_app
 from flask.helpers import url_for
 from oic import rndstr
 from oic.oic.message import EndSessionRequest
-from urllib.parse import parse_qsl
 from werkzeug.utils import redirect
 
 from .auth_response_handler import AuthResponseProcessError, AuthResponseHandler, AuthResponseErrorResponseError
@@ -65,11 +65,20 @@ class OIDCAuthentication:
                          methods=['GET', 'POST'])
 
         # dynamically add the Flask redirect uri to the client info
-        with app.app_context():
-            self.clients = {
-                name: PyoidcFacade(configuration, url_for(self._redirect_uri_endpoint))
-                for (name, configuration) in self._provider_configurations.items()
-            }
+        redirect_uri = self._get_redirect_uri(app)
+        self.clients = {
+            name: PyoidcFacade(configuration, redirect_uri)
+            for (name, configuration) in self._provider_configurations.items()
+        }
+
+    def _get_redirect_uri(self, app):
+        redirect_domain = app.config.get('OIDC_REDIRECT_DOMAIN', app.config.get('SERVER_NAME'))
+
+        if redirect_domain:
+            scheme = app.config.get('PREFERRED_URL_SCHEME', 'http')
+            return urlunparse((scheme, redirect_domain, self._redirect_uri_endpoint, '', '', ''))
+        else:
+            raise ValueError("'OIDC_REDIRECT_DOMAIN' must be configured.")
 
     def _get_post_logout_redirect_uri(self, client):
         if client.post_logout_redirect_uris:


Test patch:
diff --git a/tests/test_flask_pyoidc.py b/tests/test_flask_pyoidc.py
index 67ed041..b0c4fac 100644
--- a/tests/test_flask_pyoidc.py
+++ b/tests/test_flask_pyoidc.py
@@ -501,3 +501,15 @@ class TestOIDCAuthentication(object):
         authn = self.init_app()
         assert authn._redirect_uri_endpoint == 'openid_connect_login'
         assert authn.clients['test_provider']._redirect_uri == 'http://client.example.com/openid_connect_login'
+
+    def test_should_use_custom_redirect_domain(self):
+        self.app.config['PREFERRED_URL_SCHEME'] = 'https'
+        self.app.config['OIDC_REDIRECT_DOMAIN'] = 'custom.example.com'
+        authn = self.init_app()
+        assert authn.clients['test_provider']._redirect_uri == 'https://custom.example.com/redirect_uri'
+
+    def test_should_raise_if_domain_not_specified(self):
+        self.app.config['SERVER_NAME'] = None
+        with pytest.raises(ValueError) as exc_info:
+            self.init_app()
+        assert 'OIDC_REDIRECT_DOMAIN' in str(exc_info.value)