def init_app(self, provider_metadata_extras=None, client_metadata_extras=None, **kwargs): required_provider_metadata = { 'issuer': self.PROVIDER_BASEURL, 'authorization_endpoint': self.PROVIDER_BASEURL + '/auth', 'jwks_uri': self.PROVIDER_BASEURL + '/jwks' } if provider_metadata_extras: required_provider_metadata.update(provider_metadata_extras) provider_metadata = ProviderMetadata(**required_provider_metadata) required_client_metadata = { 'client_id': self.CLIENT_ID, 'client_secret': 'secret1' } if client_metadata_extras: required_client_metadata.update(client_metadata_extras) client_metadata = ClientMetadata(**required_client_metadata) provider_configurations = { self.PROVIDER_NAME: ProviderConfiguration(provider_metadata=provider_metadata, client_metadata=client_metadata, **kwargs) } authn = OIDCAuthentication(provider_configurations) authn.init_app(self.app) return authn
def get_client_metadata(): client_id = os.getenv('CLIENT_ID') client_secret = os.getenv('CLIENT_SECRET') if client_id == None or client_secret == None: return None else: return ClientMetadata(client_id, client_secret)
def init_app(self, provider_metadata_extras=None, client_metadata_extras=None, **kwargs): required_provider_metadata = { "issuer": self.PROVIDER_BASEURL, "authorization_endpoint": self.PROVIDER_BASEURL + "/auth", "jwks_uri": self.PROVIDER_BASEURL + "/jwks", } if provider_metadata_extras: required_provider_metadata.update(provider_metadata_extras) provider_metadata = ProviderMetadata(**required_provider_metadata) required_client_metadata = { "client_id": self.CLIENT_ID, "client_secret": "secret1" } if client_metadata_extras: required_client_metadata.update(client_metadata_extras) client_metadata = ClientMetadata(**required_client_metadata) provider_configurations = { self.PROVIDER_NAME: ProviderConfiguration(provider_metadata=provider_metadata, client_metadata=client_metadata, **kwargs) } authn = OIDCAuthentication(provider_configurations) authn.init_app(self.app) return authn
def test_should_not_register_dynamic_client_if_client_metadata_is_given( self): client_metadata = ClientMetadata(client_id='client1', client_secret='secret1') provider_config = ProviderConfiguration( provider_metadata=self.provider_metadata(), client_metadata=client_metadata) provider_config.register_client(None) assert provider_config._client_metadata == client_metadata
def test_should_not_register_dynamic_client_if_client_metadata_is_given( self): client_metadata = ClientMetadata( client_id='client1', client_secret='secret1', redirect_uris=['https://client.example.com/redirect']) provider_config = ProviderConfiguration( provider_metadata=self.provider_metadata(), client_metadata=client_metadata) provider_config.register_client([]) assert provider_config._client_metadata == client_metadata
def parse_oidc_config(logout_uri): oidc_config = dict() with open('provider.json') as provider_file: provider_data = json.load(provider_file) for provider in provider_data: config = ProviderConfiguration( issuer=provider_data[provider]['issuer'], client_metadata=ClientMetadata( provider_data[provider]['client_id'], provider_data[provider]['client_secret'], post_logout_redirect_uris=[logout_uri + '/logout'])) oidc_config.update({provider: config}) return oidc_config
def test_should_detect_nonce_mismatch(self, client_mock): client = PyoidcFacade( ProviderConfiguration( provider_metadata=ProviderMetadata(issuer=self.ISSUER), client_metadata=ClientMetadata(client_id=self.CLIENT_ID)), redirect_uri='https://client.example.com/redirect') client.exchange_authorization_code = MagicMock( return_value=self.TOKEN_RESPONSE) auth_request = { 'state': self.AUTH_RESPONSE['state'], 'nonce': 'other_nonce' } with pytest.raises(InvalidIdTokenError): AuthResponseHandler(client).process_auth_response( self.AUTH_RESPONSE, auth_request)
'SERVER_NAME': 'localhost:5000', 'SECRET_KEY': 'dev_key', # make sure to change this!! 'PERMANENT_SESSION_LIFETIME': 86400, 'PREFERRED_URL_SCHEME': 'http', 'DEBUG': True, 'SQLALCHEMY_DATABASE_URI': 'sqlite:///test.db' }) # app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db' db = SQLAlchemy(app) ISSUER1 = 'http://*****:*****@app.route('/', methods=['POST', 'GET']) # @auth.oidc_auth(PROVIDER_NAME1)
from os import environ as env from flask import Flask, jsonify, render_template, redirect, session from flask_pyoidc import OIDCAuthentication from flask_pyoidc.provider_configuration import ProviderConfiguration, ClientMetadata from flask_pyoidc.user_session import UserSession from dotenv import load_dotenv load_dotenv() app = Flask(__name__) app.config.update( OIDC_REDIRECT_URI = 'http://*****:*****@app.route('/login') @auth.oidc_auth('default') def login(): user_session = UserSession(session, 'default') return redirect('/') @app.route('/logout') @auth.oidc_logout
# OpenID Connect configuration app.config.update({'OIDC_REDIRECT_URI': APP_BASE_URI + '/' + 'redirect_uri'}) # Flask web framework configuration # See http://flask.pocoo.org/docs/0.12/config/ app.config.update({ 'SECRET_KEY': 'flask_session_key', # make sure to change this!! 'JSONIFY_PRETTYPRINT_REGULAR': True, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(minutes=1).total_seconds(), }) auth_provider_config = ProviderConfiguration(issuer=AUTH_PROVIDER_BASE_URI, client_metadata=ClientMetadata( APP_CLIENT_ID, APP_SECRET)) auth = OIDCAuthentication({AUTH_PROVIDER_NAME: auth_provider_config}) def get_my_groups(user_session): headers = {"Authorization": f"Bearer {user_session.access_token}"} response = requests.get(GROUPS_BASE_URI + '/' + MY_GROUPS_PATH, headers=headers) response.raise_for_status() return response.json() @app.route('/') @auth.oidc_auth(AUTH_PROVIDER_NAME) def login1(): user_session = UserSession(flask.session)
authorization_endpoint=os.environ.get( "GPM_OIDC_AUTHORIZATION_ENDPOINT"), jwks_uri=os.environ.get("GPM_OIDC_JWKS_URI"), token_endpoint=os.environ.get("GPM_OIDC_TOKEN_ENDPOINT"), token_introspection_endpoint=os.environ.get( "GPM_OIDC_INTROSPECTION_ENDPOINT"), userinfo_endpoint=os.environ.get("GPM_OIDC_USERINFO_ENDPOINT"), end_session_endpoint=os.environ.get("GPM_OIDC_END_SESSION_ENDPOINT"), ) provider_config = ProviderConfiguration( issuer=os.environ.get("GPM_OIDC_ISSUER"), provider_metadata=provider_metadata, session_refresh_interval_seconds=10, client_metadata=ClientMetadata( client_id=os.environ.get("GPM_OIDC_CLIENT_ID"), client_secret=os.environ.get("GPM_OIDC_CLIENT_SECRET"), ), ) auth = OIDCAuthentication({"oidc": provider_config}, app) else: app.logger.info("RUNNING WITH AUTHENTICATION DISABLED") # This snippet tries to detect if the app is running on a K8S cluster or locally try: app.logger.info("Attempting init with KUBECONFIG") config.load_kube_config() app.logger.info( f"KUBECONFIG '{config.kube_config.KUBE_CONFIG_DEFAULT_LOCATION}' successfuly loaded." ) app.config["MODE"] = "KUBECONFIG"
app.config.from_envvar('COGNITO_DEMO_SETTINGS') app.config.update({ 'OIDC_REDIRECT_URI': 'http://*****:*****@app.route('/') @auth.oidc_auth('cognito') def index(): user_session = UserSession(flask.session)
authorization_endpoint=os.environ.get( 'GPM_OIDC_AUTHORIZATION_ENDPOINT'), jwks_uri=os.environ.get('GPM_OIDC_JWKS_URI'), token_endpoint=os.environ.get('GPM_OIDC_TOKEN_ENDPOINT'), token_introspection_endpoint=os.environ.get( 'GPM_OIDC_INTROSPECTION_ENDPOINT'), userinfo_endpoint=os.environ.get('GPM_OIDC_USERINFO_ENDPOINT'), end_session_endpoint=os.environ.get('GPM_OIDC_END_SESSION_ENDPOINT'), ) provider_config = ProviderConfiguration( issuer=os.environ.get('GPM_OIDC_ISSUER'), provider_metadata=provider_metadata, session_refresh_interval_seconds=10, client_metadata=ClientMetadata( client_id=os.environ.get('GPM_OIDC_CLIENT_ID'), client_secret=os.environ.get('GPM_OIDC_CLIENT_SECRET'))) auth = OIDCAuthentication({'oidc': provider_config}, app) else: app.logger.info('RUNNING WITH AUTHENTICATION DISABLED') def dict_to_li(my_dict, html): """Recursive function to convert dict items into <li> html tags""" if my_dict is None: return html for k, v in my_dict.items(): app.logger.debug("Processing %s, %s" % (k, v)) if not isinstance(v, dict): html += '<li>%s: %s</li>' % (k, v)
def client_metadata(self): return ClientMetadata( client_id=self.oidc_config.client_id(), client_secret=self.oidc_config.client_secret(), )
# config logger.info("Choosing config") if 'prod' in os.environ.get('ENVIRONMENT').lower(): logger.info("Using production config") app.config.from_object(config.ProductionConfig()) else: # Only log flask debug in development mode. logger.info("Using development config") logging.basicConfig(level=logging.DEBUG) app.config.from_object(config.DevelopmentConfig()) # setup oidc oidc_config = config.OIDCConfig() auth0_Config = ProviderConfiguration( issuer='https://{}'.format(oidc_config.OIDC_DOMAIN), client_metadata=ClientMetadata(oidc_config.OIDC_CLIENT_ID, oidc_config.OIDC_CLIENT_SECRET)) oidc = OIDCAuthentication({'auth0': auth0_Config}, app=app) #websec headers: headers = { 'Content-Security-Policy': ("default-src 'self'; form-action 'self'; connect-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self'; script-src 'self' ; style-src 'self' https://fonts.googleapis.com/;" ) } @app.route('/') @add_response_headers(headers=headers) def main_page(): return render_template("main_page.html")
'OIDC_REDIRECT_URI': os.getenv('FULL_HOSTNAME') + '/redirect_uri', 'SECRET_KEY': 'my_not_so_dirty_secret_key', 'PERMANENT_SESSION_LIFETIME': 1800, # session time in second (30 minutes) 'DEBUG': os.getenv("FLASK_DEBUG", False) }) # General setup based on the obtained configuration # Configure database access app.config['SQLALCHEMY_DATABASE_URI'] = DB2_URI app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_ECHO'] = False # Configure access to App ID service for the OpenID Connect client appID_clientinfo = ClientMetadata(client_id=APPID_CLIENT_ID, client_secret=APPID_SECRET) appID_config = ProviderConfiguration(issuer=APPID_OAUTH_SERVER_URL, client_metadata=appID_clientinfo) # Initialize OpenID Connect client auth = OIDCAuthentication({'default': appID_config}, app) # Initialize SQLAlchemy for our database db = SQLAlchemy(app, session_options={'autocommit': True}) # Three (3) decorators that wrap the auth decorators. See the comments # in the ELSE for the background def security_decorator_auth(f): @wraps(f) @auth.oidc_auth('default') def decorated_function(*args, **kwargs):
'application/json', 'application/javascript', 'application/trig' ], ) app.json_encoder = FixedJSONEncoder app.url_map.converters['job'] = JobConverter app.url_map.converters['type'] = TypeConverter CORS(app) auth = OIDCAuthentication( { 'default': ProviderConfiguration( issuer=os.environ['OIDC_SERVER'], client_metadata=ClientMetadata( client_id=os.environ['OIDC_CLIENT_ID'], client_secret=os.environ['OIDC_CLIENT_SECRET']), auth_request_params={'scope': ['openid', 'email', 'profile']}, ) }, app) if 'OIDC_SERVER' in os.environ and len( os.environ['OIDC_SERVER']) > 0 else None socketio = SocketIO(app, cors_allowed_origins='*') socketio.start_background_task(emit_database_events) def authenticated(func): @functools.wraps(func) def wrapper(*args, **kwargs): if auth: user_session = UserSession(session, 'default')
app = Flask(__name__) csrf = CSRFProtect(app) csrf.init_app(app) # Get app config from absolute file path if os.path.exists(os.path.join(os.getcwd(), "config.py")): app.config.from_pyfile(os.path.join(os.getcwd(), "config.py")) else: app.config.from_pyfile(os.path.join(os.getcwd(), "config.env.py")) db = SQLAlchemy(app) # OIDC Authentication CSH_AUTH = ProviderConfiguration(issuer=app.config["OIDC_ISSUER"], client_metadata=ClientMetadata( app.config["OIDC_CLIENT_ID"], app.config["OIDC_CLIENT_SECRET"])) GOOGLE_AUTH = ProviderConfiguration(issuer=app.config["GOOGLE_ISSUER"], client_metadata=ClientMetadata( app.config["GOOGLE_CLIENT_ID"], app.config["GOOGLE_CLIENT_SECRET"]), auth_request_params={'scope': ['email', 'profile', 'openid']}) auth = OIDCAuthentication({'default': CSH_AUTH, 'google': GOOGLE_AUTH}, app) auth.init_app(app) # Flask-Login Manager login_manager = LoginManager() login_manager.init_app(app) login_manager.login_view = 'login'
app_config.from_pyfile(os.path.join(os.getcwd(), "config.env.py")) app.config.update(app_config) db: SQLAlchemy = SQLAlchemy(app) migrate = flask_migrate.Migrate(app, db) # Disable SSL certificate verification warning requests.packages.urllib3.disable_warnings() auth = OIDCAuthentication( { 'default': ProviderConfiguration( issuer=app_config['OIDC_ISSUER'], client_metadata=ClientMetadata( client_id=app_config['OIDC_CLIENT_ID'], client_secret=app_config['OIDC_CLIENT_SECRET'])) }, app) if "LDAP_BIND_DN" in app.config: ldap = LDAPWrapper( CSHLDAP( app.config['LDAP_BIND_DN'], app.config['LDAP_BIND_PW'], )) else: ldap = LDAPWrapper( None, app.config.get("EBOARD_UIDS", "").split(","), app.config.get("RTP_UIDS", "").split(","), app.config.get("ORGANIZER_UIDS", "").split(","),
import os app.config.update({ 'OIDC_REDIRECT_URI': os.environ.get('REDIRECT_URI'), 'SECRET_KEY': os.environ.get('SECRET'), 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=7).total_seconds(), 'DEBUG': True }) provider_config = ProviderConfiguration( issuer=os.environ.get('OAUTH_SERVER_URL'), client_metadata=ClientMetadata(os.environ.get('CLIENT_ID'), os.environ.get('SECRET'))) PROVIDER = 'provider' auth = OIDCAuthentication({PROVIDER: provider_config}, app) @app.route('/') @app.route('/index') def index(): return render_template('index.html', title='Home') @app.route('/login') @auth.oidc_auth(PROVIDER) def login(): user_session = UserSession(flask.session)
class TestPyoidcFacade(object): PROVIDER_BASEURL = 'https://op.example.com' PROVIDER_METADATA = ProviderMetadata(PROVIDER_BASEURL, PROVIDER_BASEURL + '/auth', PROVIDER_BASEURL + '/jwks') CLIENT_METADATA = ClientMetadata('client1', 'secret1') def test_registered_client_metadata_is_forwarded_to_pyoidc(self): config = ProviderConfiguration( provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA) facade = PyoidcFacade(config, REDIRECT_URI) assert facade._client.registration_response def test_no_registered_client_metadata_is_handled(self): config = ProviderConfiguration( provider_metadata=self.PROVIDER_METADATA, client_registration_info=ClientRegistrationInfo()) facade = PyoidcFacade(config, REDIRECT_URI) assert not facade._client.registration_response def test_is_registered(self): unregistered = ProviderConfiguration( provider_metadata=self.PROVIDER_METADATA, client_registration_info=ClientRegistrationInfo()) registered = ProviderConfiguration( provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA) assert PyoidcFacade(unregistered, REDIRECT_URI).is_registered() is False assert PyoidcFacade(registered, REDIRECT_URI).is_registered() is True @responses.activate def test_register(self): registration_endpoint = self.PROVIDER_BASEURL + '/register' responses.add(responses.POST, registration_endpoint, json=self.CLIENT_METADATA.to_dict()) provider_metadata = self.PROVIDER_METADATA.copy( registration_endpoint=registration_endpoint) unregistered = ProviderConfiguration( provider_metadata=provider_metadata, client_registration_info=ClientRegistrationInfo()) facade = PyoidcFacade(unregistered, REDIRECT_URI) facade.register() assert facade.is_registered() is True def test_authentication_request(self): extra_user_auth_params = {'foo': 'bar', 'abc': 'xyz'} config = ProviderConfiguration( provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA, auth_request_params=extra_user_auth_params) state = 'test_state' nonce = 'test_nonce' facade = PyoidcFacade(config, REDIRECT_URI) extra_lib_auth_params = {'foo': 'baz', 'qwe': 'rty'} auth_request = facade.authentication_request(state, nonce, extra_lib_auth_params) assert auth_request.startswith( self.PROVIDER_METADATA['authorization_endpoint']) auth_request_params = dict(parse_qsl(urlparse(auth_request).query)) expected_auth_params = { 'scope': 'openid', 'response_type': 'code', 'client_id': self.CLIENT_METADATA['client_id'], 'redirect_uri': REDIRECT_URI, 'state': state, 'nonce': nonce } expected_auth_params.update(extra_user_auth_params) expected_auth_params.update(extra_lib_auth_params) assert auth_request_params == expected_auth_params def test_parse_authentication_response(self): facade = PyoidcFacade( ProviderConfiguration(provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA), REDIRECT_URI) auth_code = 'auth_code-1234' state = 'state-1234' auth_response = AuthorizationResponse(**{ 'state': state, 'code': auth_code }) parsed_auth_response = facade.parse_authentication_response( auth_response.to_dict()) assert isinstance(parsed_auth_response, AuthorizationResponse) assert parsed_auth_response.to_dict() == auth_response.to_dict() def test_parse_authentication_response_handles_error_response(self): facade = PyoidcFacade( ProviderConfiguration(provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA), REDIRECT_URI) error_response = AuthorizationErrorResponse(**{ 'error': 'invalid_request', 'state': 'state-1234' }) parsed_auth_response = facade.parse_authentication_response( error_response) assert isinstance(parsed_auth_response, AuthorizationErrorResponse) assert parsed_auth_response.to_dict() == error_response.to_dict() @responses.activate def test_parse_authentication_response_preserves_id_token_jwt(self): facade = PyoidcFacade( ProviderConfiguration(provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA), REDIRECT_URI) state = 'state-1234' now = int(time.time()) id_token, id_token_signing_key = signed_id_token({ 'iss': self.PROVIDER_METADATA['issuer'], 'sub': 'test_sub', 'aud': 'client1', 'exp': now + 1, 'iat': now }) responses.add(responses.GET, self.PROVIDER_METADATA['jwks_uri'], json={'keys': [id_token_signing_key.serialize()]}) auth_response = AuthorizationResponse(**{ 'state': state, 'id_token': id_token }) parsed_auth_response = facade.parse_authentication_response( auth_response) assert isinstance(parsed_auth_response, AuthorizationResponse) assert parsed_auth_response['state'] == state assert parsed_auth_response['id_token_jwt'] == id_token @pytest.mark.parametrize( 'request_func,expected_token_request', [(lambda facade: facade.exchange_authorization_code('auth-code'), { 'grant_type': 'authorization_code', 'code': 'auth-code', 'redirect_uri': REDIRECT_URI }), (lambda facade: facade.refresh_token('refresh-token'), { 'grant_type': 'refresh_token', 'refresh_token': 'refresh-token', 'redirect_uri': REDIRECT_URI })]) @responses.activate def test_token_request(self, request_func, expected_token_request): token_endpoint = self.PROVIDER_BASEURL + '/token' now = int(time.time()) id_token_claims = { 'iss': self.PROVIDER_METADATA['issuer'], 'sub': 'test_user', 'aud': [self.CLIENT_METADATA['client_id']], 'exp': now + 1, 'iat': now, 'nonce': 'test_nonce' } id_token_jwt, id_token_signing_key = signed_id_token(id_token_claims) token_response = AccessTokenResponse(access_token='test_access_token', token_type='Bearer', id_token=id_token_jwt) responses.add(responses.POST, token_endpoint, json=token_response.to_dict()) provider_metadata = self.PROVIDER_METADATA.copy( token_endpoint=token_endpoint) facade = PyoidcFacade( ProviderConfiguration(provider_metadata=provider_metadata, client_metadata=self.CLIENT_METADATA), REDIRECT_URI) responses.add(responses.GET, self.PROVIDER_METADATA['jwks_uri'], json={'keys': [id_token_signing_key.serialize()]}) token_response = request_func(facade) assert isinstance(token_response, AccessTokenResponse) expected_token_response = token_response.to_dict() expected_token_response['id_token'] = id_token_claims expected_token_response['id_token_jwt'] = id_token_jwt assert token_response.to_dict() == expected_token_response token_request = dict(parse_qsl(responses.calls[0].request.body)) assert token_request == expected_token_request @responses.activate def test_token_request_handles_error_response(self): token_endpoint = self.PROVIDER_BASEURL + '/token' token_response = TokenErrorResponse( error='invalid_request', error_description='test error description') responses.add(responses.POST, token_endpoint, json=token_response.to_dict(), status=400) provider_metadata = self.PROVIDER_METADATA.copy( token_endpoint=token_endpoint) facade = PyoidcFacade( ProviderConfiguration(provider_metadata=provider_metadata, client_metadata=self.CLIENT_METADATA), REDIRECT_URI) assert facade.exchange_authorization_code('1234') == token_response def test_token_request_handles_missing_provider_token_endpoint(self): facade = PyoidcFacade( ProviderConfiguration(provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA), REDIRECT_URI) assert facade.exchange_authorization_code('1234') is None @pytest.mark.parametrize('userinfo_http_method', ['GET', 'POST']) @responses.activate def test_configurable_userinfo_endpoint_method_is_used( self, userinfo_http_method): userinfo_endpoint = self.PROVIDER_BASEURL + '/userinfo' userinfo_response = OpenIDSchema(sub='user1') responses.add(userinfo_http_method, userinfo_endpoint, json=userinfo_response.to_dict()) provider_metadata = self.PROVIDER_METADATA.copy( userinfo_endpoint=userinfo_endpoint) facade = PyoidcFacade( ProviderConfiguration(provider_metadata=provider_metadata, client_metadata=self.CLIENT_METADATA, userinfo_http_method=userinfo_http_method), REDIRECT_URI) assert facade.userinfo_request('test_token') == userinfo_response def test_no_userinfo_request_is_made_if_no_userinfo_http_method_is_configured( self): facade = PyoidcFacade( ProviderConfiguration(provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA, userinfo_http_method=None), REDIRECT_URI) assert facade.userinfo_request('test_token') is None def test_no_userinfo_request_is_made_if_no_userinfo_endpoint_is_configured( self): facade = PyoidcFacade( ProviderConfiguration(provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA), REDIRECT_URI) assert facade.userinfo_request('test_token') is None def test_no_userinfo_request_is_made_if_no_access_token(self): provider_metadata = self.PROVIDER_METADATA.copy( userinfo_endpoint=self.PROVIDER_BASEURL + '/userinfo') facade = PyoidcFacade( ProviderConfiguration(provider_metadata=provider_metadata, client_metadata=self.CLIENT_METADATA), REDIRECT_URI) assert facade.userinfo_request(None) is None
from flask_pyoidc.provider_configuration import ClientMetadata, ProviderConfiguration from flask_pyoidc.user_session import UserSession from redis import RedisError from api.my_flask import app from utility.create_config import create_config config = create_config() client_id = config.get('Secrets-Section', 'client-id') client_secret = config.get('Secrets-Section', 'client-secret') redirect_uris = config.get('Web-Section', 'redirect-oidc').split() issuer = config.get('Web-Section', 'issuer') my_uri = config.get('Web-Section', 'my-uri') client_metadata = ClientMetadata(client_id=client_id, client_secret=client_secret, redirect_uris=redirect_uris) provider_config = ProviderConfiguration(issuer=issuer, client_metadata=client_metadata) ietf_auth = OIDCAuthentication({'default': provider_config}) class YangCatalogAdminBlueprint(Blueprint): def __init__(self, name, import_name, static_folder=None, static_url_path=None, template_folder=None, url_prefix=None, subdomain=None,
'localhost:5000', 'SECRET_KEY': 'dev_key', # make sure to change this!! 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=7).total_seconds(), 'PREFERRED_URL_SCHEME': 'http', 'DEBUG': True }) ISSUER1 = 'https://provider1.example.com' CLIENT1 = 'client@provider1' PROVIDER_NAME1 = 'provider1' PROVIDER_CONFIG1 = ProviderConfiguration(issuer=ISSUER1, client_metadata=ClientMetadata( CLIENT1, 'secret1')) ISSUER2 = 'https://provider2.example.com' CLIENT2 = 'client@provider2' PROVIDER_NAME2 = 'provider2' PROVIDER_CONFIG2 = ProviderConfiguration(issuer=ISSUER2, client_metadata=ClientMetadata( CLIENT2, 'secret2')) auth = OIDCAuthentication({ PROVIDER_NAME1: PROVIDER_CONFIG1, PROVIDER_NAME2: PROVIDER_CONFIG2 }) @app.route('/') @auth.oidc_auth(PROVIDER_NAME1) def login1():
openid_config['scope'] = ['openid', 'email', 'profile'] app = Flask(__name__) app.config.update( SECRET_KEY=''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(16)), SERVER_NAME=f'{socket.getfqdn()}:5000', JSONIFY_PRETTYPRINT_REGULAR=True, ) provider_config = ProviderConfiguration( auth_request_params={'scope': openid_config['scope']}, issuer=openid_config['issuer'], client_metadata=ClientMetadata( client_id=openid_config['client_id'], client_secret=openid_config['client_secret'], )) auth = OIDCAuthentication({'default': provider_config}, app) @app.route('/') @auth.oidc_auth('default') def login(): user_session = UserSession(flask.session, provider_name='default').userinfo print(user_session) return jsonify(user_session) @app.route('/logout') @auth.oidc_logout
# Fetch the version number from the npm package file with open(os.path.join(_root_dir, 'package.json')) as package_file: app.config['VERSION'] = json.load(package_file)['version'] # Logger configuration logging.getLogger().setLevel(app.config['LOG_LEVEL']) app.logger.info('Launching packet v' + app.config['VERSION']) app.logger.info('Using the {} realm'.format(app.config['REALM'])) # Initialize the extensions db = SQLAlchemy(app) migrate = Migrate(app, db) app.logger.info('SQLAlchemy pointed at ' + repr(db.engine.url)) APP_CONFIG = ProviderConfiguration(issuer=app.config['OIDC_ISSUER'], client_metadata=ClientMetadata(app.config['OIDC_CLIENT_ID'], app.config['OIDC_CLIENT_SECRET'])) # Initialize Onesignal Notification apps csh_onesignal_client = onesignal.Client(user_auth_key=app.config['ONESIGNAL_USER_AUTH_KEY'], app_auth_key=app.config['ONESIGNAL_CSH_APP_AUTH_KEY'], app_id=app.config['ONESIGNAL_CSH_APP_ID']) intro_onesignal_client = onesignal.Client(user_auth_key=app.config['ONESIGNAL_USER_AUTH_KEY'], app_auth_key=app.config['ONESIGNAL_INTRO_APP_AUTH_KEY'], app_id=app.config['ONESIGNAL_INTRO_APP_ID']) # OIDC Auth auth = OIDCAuthentication({'app': APP_CONFIG}, app) # LDAP _ldap = csh_ldap.CSHLDAP(app.config['LDAP_BIND_DN'], app.config['LDAP_BIND_PASS'])
app.config.from_pyfile('settings.py') issuer = app.config['OIDC_ISSUER'] client = app.config['OIDC_CLIENT'] secret = app.config['OIDC_SECRET'] auth_params = app.config['OIDC_SCOPES'] second_factor = { 'id_token': { 'acr': { 'essential': True, 'value': 'https://refeds.org/profile/mfa' } } } client_metadata = ClientMetadata(client_id=client, client_secret=secret) config = ProviderConfiguration(issuer=issuer, client_metadata=client_metadata, auth_request_params=auth_params) auth = OIDCAuthentication({'default': config}, app) # Used for returning the authn response to the browser def get_user_data(): if flask.session: user = UserSession(flask.session) app.logger.info(f'{user.id_token["sub"]} logged in') data = { 'access_token': user.access_token, 'id_token': user.id_token,
class TestPyoidcFacade(object): PROVIDER_BASEURL = "http://rp.example.com" PROVIDER_METADATA = ProviderMetadata(PROVIDER_BASEURL, PROVIDER_BASEURL + "/auth", PROVIDER_BASEURL + "/jwks") CLIENT_METADATA = ClientMetadata("client1", "secret1") CLIENT_DOMAIN = "client.example.com" REDIRECT_URI = "redirect_uri" FULL_REDIRECT_URI = "http://client.example.com/redirect_uri" @pytest.fixture(autouse=True) def create_flask_app(self): self.app = Flask(__name__) self.app.add_url_rule("/redirect_uri", "redirect_uri") self.app.config.update({ "SERVER_NAME": self.CLIENT_DOMAIN, "SECRET_KEY": "test_key" }) def test_registered_client_metadata_is_forwarded_to_pyoidc(self): config = ProviderConfiguration( provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA, ) facade = PyoidcFacade(config, self.REDIRECT_URI) assert facade._client.registration_response def test_no_registered_client_metadata_is_handled(self): config = ProviderConfiguration( provider_metadata=self.PROVIDER_METADATA, client_registration_info=ClientRegistrationInfo(), ) facade = PyoidcFacade(config, self.REDIRECT_URI) assert not facade._client.registration_response def test_is_registered(self): unregistered = ProviderConfiguration( provider_metadata=self.PROVIDER_METADATA, client_registration_info=ClientRegistrationInfo(), ) registered = ProviderConfiguration( provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA, ) assert PyoidcFacade(unregistered, self.REDIRECT_URI).is_registered() is False assert PyoidcFacade(registered, self.REDIRECT_URI).is_registered() is True @responses.activate def test_register(self): registration_endpoint = self.PROVIDER_BASEURL + "/register" responses.add(responses.POST, registration_endpoint, json=self.CLIENT_METADATA.to_dict()) provider_metadata = self.PROVIDER_METADATA.copy( registration_endpoint=registration_endpoint) unregistered = ProviderConfiguration( provider_metadata=provider_metadata, client_registration_info=ClientRegistrationInfo(), ) facade = PyoidcFacade(unregistered, self.REDIRECT_URI) facade.register() assert facade.is_registered() is True def test_authentication_request(self): extra_user_auth_params = {"foo": "bar", "abc": "xyz"} config = ProviderConfiguration( provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA, auth_request_params=extra_user_auth_params, ) state = "test_state" nonce = "test_nonce" facade = PyoidcFacade(config, self.REDIRECT_URI) extra_lib_auth_params = {"foo": "baz", "qwe": "rty"} auth_request = facade.authentication_request(state, nonce, self.FULL_REDIRECT_URI, extra_lib_auth_params) assert auth_request.startswith( self.PROVIDER_METADATA["authorization_endpoint"]) auth_request_params = dict(parse_qsl(urlparse(auth_request).query)) expected_auth_params = { "scope": "openid", "response_type": "code", "client_id": self.CLIENT_METADATA["client_id"], "redirect_uri": self.FULL_REDIRECT_URI, "state": state, "nonce": nonce, } expected_auth_params.update(extra_user_auth_params) expected_auth_params.update(extra_lib_auth_params) assert auth_request_params == expected_auth_params def test_parse_authentication_response(self): facade = PyoidcFacade( ProviderConfiguration( provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA, ), self.REDIRECT_URI, ) auth_code = "auth_code-1234" state = "state-1234" auth_response = AuthorizationResponse(**{ "state": state, "code": auth_code }) parsed_auth_response = facade.parse_authentication_response( auth_response.to_dict()) assert isinstance(parsed_auth_response, AuthorizationResponse) assert parsed_auth_response.to_dict() == auth_response.to_dict() def test_parse_authentication_response_handles_error_response(self): facade = PyoidcFacade( ProviderConfiguration( provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA, ), self.REDIRECT_URI, ) error_response = AuthorizationErrorResponse(**{ "error": "invalid_request", "state": "state-1234" }) parsed_auth_response = facade.parse_authentication_response( error_response) assert isinstance(parsed_auth_response, AuthorizationErrorResponse) assert parsed_auth_response.to_dict() == error_response.to_dict() @responses.activate def test_parse_authentication_response_preserves_id_token_jwt(self): facade = PyoidcFacade( ProviderConfiguration( provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA, ), self.REDIRECT_URI, ) state = "state-1234" now = int(time.time()) id_token, id_token_signing_key = signed_id_token({ "iss": self.PROVIDER_METADATA["issuer"], "sub": "test_sub", "aud": "client1", "exp": now + 1, "iat": now, }) responses.add( responses.GET, self.PROVIDER_METADATA["jwks_uri"], json={"keys": [id_token_signing_key.serialize()]}, ) auth_response = AuthorizationResponse(**{ "state": state, "id_token": id_token }) parsed_auth_response = facade.parse_authentication_response( auth_response) assert isinstance(parsed_auth_response, AuthorizationResponse) assert parsed_auth_response["state"] == state assert parsed_auth_response["id_token_jwt"] == id_token @responses.activate def test_token_request(self): token_endpoint = self.PROVIDER_BASEURL + "/token" now = int(time.time()) id_token_claims = { "iss": self.PROVIDER_METADATA["issuer"], "sub": "test_user", "aud": [self.CLIENT_METADATA["client_id"]], "exp": now + 1, "iat": now, "nonce": "test_nonce", } id_token_jwt, id_token_signing_key = signed_id_token(id_token_claims) token_response = AccessTokenResponse(access_token="test_access_token", token_type="Bearer", id_token=id_token_jwt) responses.add(responses.POST, token_endpoint, json=token_response.to_dict()) provider_metadata = self.PROVIDER_METADATA.copy( token_endpoint=token_endpoint) facade = PyoidcFacade( ProviderConfiguration( provider_metadata=provider_metadata, client_metadata=self.CLIENT_METADATA, ), self.REDIRECT_URI, ) auth_code = "auth_code-1234" responses.add( responses.GET, self.PROVIDER_METADATA["jwks_uri"], json={"keys": [id_token_signing_key.serialize()]}, ) with self.app.app_context(): token_response = facade.token_request(auth_code) assert isinstance(token_response, AccessTokenResponse) expected_token_response = token_response.to_dict() expected_token_response["id_token"] = id_token_claims expected_token_response["id_token_jwt"] = id_token_jwt assert token_response.to_dict() == expected_token_response token_request = dict(parse_qsl(responses.calls[0].request.body)) expected_token_request = { "grant_type": "authorization_code", "code": auth_code, "redirect_uri": self.FULL_REDIRECT_URI } assert token_request == expected_token_request @responses.activate def test_token_request_handles_error_response(self): token_endpoint = self.PROVIDER_BASEURL + "/token" token_response = TokenErrorResponse( error="invalid_request", error_description="test error description") responses.add(responses.POST, token_endpoint, json=token_response.to_dict(), status=400) provider_metadata = self.PROVIDER_METADATA.copy( token_endpoint=token_endpoint) facade = PyoidcFacade( ProviderConfiguration( provider_metadata=provider_metadata, client_metadata=self.CLIENT_METADATA, ), self.REDIRECT_URI, ) with self.app.app_context(): assert facade.token_request("1234") == token_response def test_token_request_handles_missing_provider_token_endpoint(self): facade = PyoidcFacade( ProviderConfiguration( provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA, ), self.REDIRECT_URI, ) assert facade.token_request("1234") is None @pytest.mark.parametrize("userinfo_http_method", ["GET", "POST"]) @responses.activate def test_configurable_userinfo_endpoint_method_is_used( self, userinfo_http_method): userinfo_endpoint = self.PROVIDER_BASEURL + "/userinfo" userinfo_response = OpenIDSchema(sub="user1") responses.add(userinfo_http_method, userinfo_endpoint, json=userinfo_response.to_dict()) provider_metadata = self.PROVIDER_METADATA.copy( userinfo_endpoint=userinfo_endpoint) facade = PyoidcFacade( ProviderConfiguration( provider_metadata=provider_metadata, client_metadata=self.CLIENT_METADATA, userinfo_http_method=userinfo_http_method, ), self.REDIRECT_URI, ) assert facade.userinfo_request("test_token") == userinfo_response def test_no_userinfo_request_is_made_if_no_userinfo_http_method_is_configured( self): facade = PyoidcFacade( ProviderConfiguration( provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA, userinfo_http_method=None, ), self.REDIRECT_URI, ) assert facade.userinfo_request("test_token") is None def test_no_userinfo_request_is_made_if_no_userinfo_endpoint_is_configured( self): facade = PyoidcFacade( ProviderConfiguration( provider_metadata=self.PROVIDER_METADATA, client_metadata=self.CLIENT_METADATA, ), self.REDIRECT_URI, ) assert facade.userinfo_request("test_token") is None def test_no_userinfo_request_is_made_if_no_access_token(self): provider_metadata = self.PROVIDER_METADATA.copy( userinfo_endpoint=self.PROVIDER_BASEURL + "/userinfo") facade = PyoidcFacade( ProviderConfiguration( provider_metadata=provider_metadata, client_metadata=self.CLIENT_METADATA, ), self.REDIRECT_URI, ) assert facade.userinfo_request(None) is None
'DEBUG': True}) # General setup based on the obtained configuration # Configure database access if dbInfo['port']==50001: # if we are on the SSL port, add additional parameter for the driver app.config['SQLALCHEMY_DATABASE_URI']=dbInfo['uri']+"Security=SSL;" else: app.config['SQLALCHEMY_DATABASE_URI']=dbInfo['uri'] app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=False app.config['SQLALCHEMY_ECHO']=False # Configure access to App ID service for the OpenID Connect client appID_clientinfo=ClientMetadata(client_id=appIDInfo['clientId'],client_secret=appIDInfo['secret']) appID_config = ProviderConfiguration(issuer=appIDInfo['oauthServerUrl'],client_metadata=appID_clientinfo) # Initialize OpenID Connect client auth=OIDCAuthentication({'default': appID_config}, app) # Initialize BasicAuth, needed for token access to data basicauth = HTTPBasicAuth() # Initialize SQLAlchemy for our database db = SQLAlchemy(app, session_options={'autocommit': True}) # Encoder to handle some raw data correctly def alchemyencoder(obj): """JSON encoder function for SQLAlchemy special classes.""" if isinstance(obj, datetime.date): return obj.isoformat()
def create_app(config=None): app = Flask(__name__, instance_relative_config=True) # Load application config from various sources # ------------------------------------------------------------------------------ # 1. Defaults from this package app.config.from_object(DefaultConfig) # 2. From a config.py file in the application directory app.config.from_pyfile(filename=os.path.join(os.getcwd(), "config.py"), silent=True) # 3. From a dynamically configurable file location if os.environ.get('CONFIG_LOCATION'): app.config.from_pyfile(filename=os.environ.get('CONFIG_LOCATION'), silent=False) # 4. Testing config if config: app.config.from_mapping(config) # 5. Load some final computed config # NOTE: This is placed here as it relies on other config values that # may be configured after the user provided config for example. oidc_logout_redirect_uri = os.environ.get( 'OIDC_LOGOUT_REDIRECT_URI', 'https://' + app.config['SERVER_NAME'] + '/logout') oidc_auth_request_params = json.loads( os.environ.get('OIDC_AUTH_REQUEST_PARAMS', '{}')) if oidc_auth_request_params: if app.config['OIDC_EXTRA_AUTH_REQUEST_PARAMS']: app.logger.warning( 'OIDC_EXTRA_AUTH_REQUEST_PARAMS is being overridden by OIDC_AUTH_REQUEST_PARAMS being explicitly set.' ) else: oidc_auth_request_params['scope'] = " ".join( re.split(",| ", app.config['OIDC_SCOPE'])) if app.config['OIDC_EXTRA_AUTH_REQUEST_PARAMS']: oidc_auth_request_params.update( app.config['OIDC_EXTRA_AUTH_REQUEST_PARAMS']) app.config.from_mapping({ 'OIDC_LOGOUT_REDIRECT_URI': oidc_logout_redirect_uri, 'OIDC_CLIENT_METADATA': { 'client_id': app.config['OIDC_CLIENT_ID'], 'client_secret': app.config['OIDC_CLIENT_SECRET'], 'post_logout_redirect_uris': str.split(oidc_logout_redirect_uri, ",") }, 'OIDC_AUTH_REQUEST_PARAMS': oidc_auth_request_params, }) # Initialize OpenID Connect extension # ------------------------------------------------------------------------------ # The client metadata will be consumed no matter what... # https://github.com/zamzterz/Flask-pyoidc#dynamic-provider-configuration client_metadata = ClientMetadata(**app.config['OIDC_CLIENT_METADATA']) # ... but if explicit OIDC provider information is provided, we use that # instead of the information dynamically provided by the # .well-known/openid-configuration endpoint. if app.config['OIDC_PROVIDER_METADATA']: provider_metadata = ProviderMetadata( **app.config['OIDC_PROVIDER_METADATA']) provider = ProviderConfiguration( provider_metadata=provider_metadata, client_metadata=client_metadata, auth_request_params=app.config['OIDC_AUTH_REQUEST_PARAMS'], ) else: provider = ProviderConfiguration( issuer=app.config['OIDC_ISSUER'], client_metadata=client_metadata, auth_request_params=app.config['OIDC_AUTH_REQUEST_PARAMS'], ) auth = OIDCAuthentication( provider_configurations={ 'default': provider, }, app=app, ) # The /health endpoint returns a JSON string like... # {"hostname": "a3731af16461", "status": "success", "timestamp": 1551186453.8854501, "results": []} health = HealthCheck(app, "/health") # If an OAuth error response is received, either in the authentication or # token response, it will be passed to the "error view". @auth.error_view def error(error=None, error_description=None): return jsonify({'error': error, 'message': error_description}) @app.route('/') def index(): """ If a user tries to access this application directly, just redirect them to Discourse. :return: Redirect to the configurated DISCOURSE_URL """ return redirect(app.config.get('DISCOURSE_URL'), 302) @app.route('/sso/login') def payload_check(): """ Verify the payload and signature coming from a Discourse server and if correct redirect to the authentication page after saving the nonce in the session as discourse_nonce. :return: The redirection page to the authentication page """ # Get payload and signature from Discourse request payload = request.args.get('sso', '') signature = request.args.get('sig', '') if not payload or not signature: app.logger.info( '/sso/login -> 400: missing payload="%s" or signature="%s"', payload, signature) abort(400) app.logger.debug('Request to login with payload="%s" signature="%s"', payload, signature) app.logger.debug('Session Secret Key: %s', app.secret_key) app.logger.debug('SSO Secret Key: %s', app.config.get('DISCOURSE_SECRET_KEY')) # Calculate and compare request signature dig = hmac.new( app.config.get('DISCOURSE_SECRET_KEY', '').encode('utf-8'), payload.encode('utf-8'), hashlib.sha256).hexdigest() app.logger.debug('Calculated hash: %s', dig) if dig != signature: app.logger.info( '/sso/login -> 400: dig / signature mismatch. dig="%s" and signature="%s"', dig, signature) abort(400) # Decode the payload and store in session decoded_msg = base64.b64decode(payload).decode('utf-8') session[ 'discourse_nonce'] = decoded_msg # This can't just be 'nonce' as Flask-pyoidc will steamroll it # Redirect to authorization endpoint return redirect(url_for('sso_auth')) @app.route('/sso/auth') @auth.oidc_auth('default') def sso_auth(): """ Read the user attributes provided by Flask-pyoidc and create the payload to send to Discourse. :return: The redirection page to Discourse """ # Check to make sure we have a valid session if 'discourse_nonce' not in session: app.logger.info( '/sso/auth -> 403: discourse_nonce not found in session, arriving here without coming from /sso/login?' ) abort(403) attribute_map = app.config.get('USERINFO_SSO_MAP') sso_attributes = {} userinfo = session['userinfo'] # Check if the provided userinfo should be used to set information to be # passed to discourse. Do it by checking if the userinfo field is... # 1. explicitly mapped using the provided map # 2. if it can match one of the known attributes with discourse_ prefixed # 3. if it can match one of the known attributes directly for userinfo_key, userinfo_value in userinfo.items(): attribute_key = attribute_map.get(userinfo_key) if attribute_key: pass elif userinfo_key in [ "discourse_" + attr for attr in ALL_ATTRIBUTES ]: attribute_key = userinfo_key[len("discourse_"):] elif userinfo_key in ALL_ATTRIBUTES: attribute_key = userinfo_key if attribute_key: if attribute_key in BOOL_ATTRIBUTES: userinfo_value = "false" if str.lower( str(userinfo_value)) in ['false', 'f', '0'] else "true" sso_attributes[attribute_key] = userinfo_value # Check if we have a default value that should be used default_sso_attributes = app.config.get('DEFAULT_SSO_ATTRIBUTES') for default_attribute_key, default_attribute_value in default_sso_attributes.items( ): if default_attribute_key not in sso_attributes: sso_attributes[default_attribute_key] = default_attribute_value # Check if we got the required attributes for required_attribute in REQUIRED_ATTRIBUTES: if not sso_attributes.get(required_attribute): app.logger.info( f'/sso/auth -> 403: {required_attribute} not found in userinfo: ' + json.dumps(session['userinfo'])) abort(403) # All systems are go! app.logger.debug( f'Authenticating "{sso_attributes.get("external_id")}", named "{sso_attributes.get("name")}" with email: "{sso_attributes.get("email")}"' ) # Construct the response inner query parameters query = session['discourse_nonce'] for sso_attribute_key, sso_attribute_value in sso_attributes.items(): query += f'&{sso_attribute_key}={quote(str(sso_attribute_value))}' app.logger.debug('Query string to return: %s', query) # Encode response query_b64 = base64.b64encode(query.encode('utf-8')) app.logger.debug('Base64 query string to return: %s', query_b64) # Build URL-safe response query_urlenc = quote(query_b64) app.logger.debug('URLEnc query string to return: %s', query_urlenc) # Generate signature for response sig = hmac.new( app.config.get('DISCOURSE_SECRET_KEY').encode('utf-8'), query_b64, hashlib.sha256, ).hexdigest() app.logger.debug('Signature: %s', sig) # Build redirect URL redirect_url = (app.config.get('DISCOURSE_URL') + '/session/sso_login?' 'sso=' + query_urlenc + '&sig=' + sig) # Redirect back to Discourse return redirect(redirect_url) @app.route('/logout') @auth.oidc_logout def logout(): """ Handle logging a user out. Flask-pyoidc does the heavy lifting here. :return: Redirect to the application index """ return redirect(url_for('index'), 302) @app.errorhandler(403) def attribute_not_provided(error): """ Render a custom error page in case the IdP authenticate the user but does not provide the requested attributes :type error: object """ app.logger.info(f'403: error: "{error}"') return render_template('403.html'), 403 return app
from flask import Flask, jsonify from flask_pyoidc import OIDCAuthentication from flask_pyoidc.provider_configuration import ProviderConfiguration, ClientMetadata from flask_pyoidc.user_session import UserSession app = Flask(__name__) # See http://flask.pocoo.org/docs/0.12/config/ app.config.update({"OIDC_REDIRECT_URI": "http://*****:*****@app.route("/") @auth.oidc_auth(PROVIDER_NAME) def login(): user_session = UserSession(flask.session) return jsonify(access_token=user_session.access_token, id_token=user_session.id_token, userinfo=user_session.userinfo) @app.route("/logout")