diff --git a/plugins/unity_integration.py b/plugins/unity_integration.py
new file mode 100644
index 0000000000000000000000000000000000000000..0e15dc679275a1506bf7158fcc5e14e0e13b11a1
--- /dev/null
+++ b/plugins/unity_integration.py
@@ -0,0 +1,87 @@
+import os, random, string
+from authlib.integrations.flask_client import OAuth
+from flask import url_for, redirect
+from flask_login import login_user
+from flask_appbuilder import expose, BaseView as AppBuilderBaseView
+from airflow.utils.airflow_flask_app import get_airflow_app
+from airflow.plugins_manager import AirflowPlugin
+import logging
+import os
+
+log = logging.getLogger(__name__)
+log.setLevel(os.getenv("AIRFLOW__LOGGING__FAB_LOGGING_LEVEL", "INFO"))
+
+FAB_ADMIN_ROLE = "Admin"
+FAB_VIEWER_ROLE = "Viewer"
+FAB_PUBLIC_ROLE = "Public"  # The "Public" role is given no permissions
+
+app= get_airflow_app()
+oauth = OAuth(app)
+oauth.register(
+    name='unity',
+    client_id=os.getenv("OAUTH_CLIENT_ID"),
+    server_metadata_url=os.getenv("OAUTH_METADATA_URL"),
+    client_secret=os.getenv("OAUTH_CLIENT_SECRET"),
+    client_kwargs={
+        'scope' : 'openid email profile eflows'
+    }
+
+)
+
+class UnityIntegrationLoginView(AppBuilderBaseView):
+    #@expose("/unity_login")
+    @app.route('/unity_login')
+    def unity_login():
+        redirect_uri = url_for('unity_authorize', _external=True)
+        return oauth.unity.authorize_redirect(redirect_uri)
+class UnityIntegrationAuthView(AppBuilderBaseView):
+    #@expose("/unity_authorize")
+    @app.route('/unity_authorize')
+    async def authorize():
+        token = await oauth.unity.authorize_access_token()
+        user = await oauth.unity.userinfo(token=token)
+        # get relevant data from token
+        email = user['email']
+        persistent_identifier = user["sub"]
+        first_name = user["given_name"]
+        last_name = user["family_name"]
+        admin_access = user.get('eflows:dlsAccess', False)
+        role = FAB_VIEWER_ROLE
+        if admin_access:
+            role = FAB_ADMIN_ROLE
+
+        # check airflow user backend
+        # check if user already exists, if not create it (with long random password)
+        sec_manager = app.appbuilder.sm
+        fab_user = sec_manager.find_user('username')
+        if fab_user is None: # TODO check if None is the rioght thing to compare to
+            characters = string.ascii_letters + string.digits + string.punctuation
+            fab_user = sec_manager.add_user(
+                username=persistent_identifier,
+                first_name=first_name,
+                last_name=last_name,
+                email=email,
+                role=role,
+                password=''.join(random.choice(characters) for i in range(20))
+            )
+        # login as that user
+        login_user(fab_user, remember=False)
+        return redirect('/')
+
+v_unity_login_view = UnityIntegrationLoginView()
+v_unity_login_package = {
+    "name": "Unity Login View",
+    "category": "Unity Integration",
+    "view": v_unity_login_view,
+}
+
+v_unity_auth_view = UnityIntegrationAuthView()
+v_unity_auth_package = {
+    "name": "Unity Auth View",
+    "category": "Unity Integration",
+    "view": v_unity_auth_view,
+}
+
+class UnityIntegrationPlugin(AirflowPlugin):
+    name = "unity_integration"
+    appbuilder_views = [v_unity_auth_package, v_unity_login_package]
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 56ab98f8d773d6dcf27ca965676e41b8894ada38..07fe347b80e12898c5612ee5d90ffe565de828df 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,3 +9,4 @@ apache-airflow-providers-sftp
 airflow-datacat-integration>=0.1.4
 flask-oidc 
 aiohttp
+authlib
diff --git a/webserver_config.py b/webserver_config.py
deleted file mode 100644
index 4e06053be7d4864ea3aa93d0f9109e84ff78bf25..0000000000000000000000000000000000000000
--- a/webserver_config.py
+++ /dev/null
@@ -1,186 +0,0 @@
-import os, logging, json, posixpath
-
-from airflow import configuration as conf
-from airflow.www.security import AirflowSecurityManager
-from flask import abort, make_response, redirect
-from flask_appbuilder.security.manager import AUTH_OID
-from flask_appbuilder.security.views import AuthOIDView
-from flask_appbuilder.views import ModelView, SimpleFormView, expose
-from flask_login import login_user
-from flask_oidc import OpenIDConnect
-
-logger = logging.getLogger(__name__)
-
-# Set the OIDC field that should be used
-NICKNAME_OIDC_FIELD = 'nickname'
-FULL_NAME_OIDC_FIELD = 'name'
-GROUPS_OIDC_FIELD = 'groups'
-EMAIL_FIELD = 'email'
-SUB_FIELD = 'sub'  # User ID
-
-
-# Convert groups from comma separated string to list
-ALLOWED_GROUPS = os.environ.get('ALLOWED_GROUPS')
-if ALLOWED_GROUPS:
-    ALLOWED_GROUPS = [g.strip() for g in ALLOWED_GROUPS.split(',')]
-else: ALLOWED_GROUPS = []
-
-if ALLOWED_GROUPS:
-    logger.debug('AirFlow access requires membership to one of the following groups: %s'
-        % ', '.join(ALLOWED_GROUPS))
-
-
-# Extending AuthOIDView
-class AuthOIDCView(AuthOIDView):
-
-    @expose('/login/', methods=['GET', 'POST'])
-    def login(self, flag=True):
-
-        sm = self.appbuilder.sm
-        oidc = sm.oid
-
-        @self.appbuilder.sm.oid.require_login
-        def handle_login():
-            user = sm.auth_user_oid(oidc.user_getfield(EMAIL_FIELD))
-
-            # Group membership required
-            if ALLOWED_GROUPS:
-
-                # Fetch group membership information from GitLab
-                groups = oidc.user_getinfo([GROUPS_OIDC_FIELD]).get(GROUPS_OIDC_FIELD, [])
-                intersection = set(ALLOWED_GROUPS) & set(groups)
-                logger.debug('AirFlow user member of groups in ACL list: %s' % ', '.join(intersection))
-
-                # Unable to find common groups, prevent login
-                if not intersection:
-                    return abort(403)
-
-            # Create user (if it doesn't already exist)
-            if user is None:
-                info = oidc.user_getinfo([
-                    NICKNAME_OIDC_FIELD,
-                    FULL_NAME_OIDC_FIELD,
-                    GROUPS_OIDC_FIELD,
-                    SUB_FIELD,
-                    EMAIL_FIELD,
-                    "profile"
-                ])
-                full_name = info.get(FULL_NAME_OIDC_FIELD)
-                if " " in full_name:
-                    full_name = full_name.split(" ")
-                    first_name = full_name[0]
-                    last_name = full_name[1]
-                else:
-                    first_name = full_name
-                    last_name = ""
-                user = sm.add_user(
-                    username=info.get(NICKNAME_OIDC_FIELD),
-                    first_name=first_name,
-                    last_name=last_name,
-                    email=info.get(EMAIL_FIELD),
-                    role=sm.find_role(sm.auth_user_registration_role)
-                )
-
-            login_user(user, remember=False)
-            return redirect(self.appbuilder.get_url_for_index)
-
-        return handle_login()
-
-    @expose('/logout/', methods=['GET', 'POST'])
-    def logout(self):
-        oidc = self.appbuilder.sm.oid
-        if not oidc.credentials_store:
-            return redirect('/login/')
-        self.revoke_token()
-        oidc.logout()
-        super(AuthOIDCView, self).logout()
-        response = make_response("You have been signed out")
-        return response
-
-    def revoke_token(self):
-        """ Revokes the provided access token. Sends a POST request to the token revocation endpoint
-        """
-        import aiohttp
-        import asyncio
-        import json
-        oidc = self.appbuilder.sm.oid
-        sub = oidc.user_getfield(SUB_FIELD)
-        config = oidc.credentials_store
-        config = config.get(str(sub))
-        config = json.loads(config)
-        payload = {
-            "token": config['access_token'],
-            "token_type_hint": "refresh_token"
-        }
-        auth = aiohttp.BasicAuth(config['client_id'], config['client_secret'])
-        # Sends an asynchronous POST request to revoke the token
-      
-        async def revoke():
-            async with aiohttp.ClientSession() as session:
-                async with session.post(self.appbuilder.app.config.get('OIDC_LOGOUT_URI'), data=payload, auth=auth) as response:
-                    logging.info(f"Revoke response {response.status}")
-
-        loop = asyncio.new_event_loop()
-        asyncio.set_event_loop(loop)
-        loop.run_until_complete(revoke())
-
-
-
-class OIDCSecurityManager(AirflowSecurityManager):
-    """
-    Custom security manager class that allows using the OpenID Connection authentication method.
-    """
-    def __init__(self, appbuilder):
-        super(OIDCSecurityManager, self).__init__(appbuilder)
-        if self.auth_type == AUTH_OID:
-           self.oid = OpenIDConnect(self.appbuilder.get_app)
-           self.authoidview = AuthOIDCView
-
-
-basedir = os.path.abspath(os.path.dirname(__file__))
-
-SECURITY_MANAGER_CLASS = OIDCSecurityManager
-# The SQLAlchemy connection string.
-SQLALCHEMY_DATABASE_URI = conf.get('core', 'SQL_ALCHEMY_CONN')
-
-# Flask-WTF flag for CSRF
-CSRF_ENABLED = True
-
-AUTH_TYPE = AUTH_OID
-OIDC_CLIENT_SECRETS = 'client_secret.json'  # Configuration file for OIDC
-OIDC_COOKIE_SECURE= False
-OIDC_ID_TOKEN_COOKIE_SECURE = False
-OIDC_REQUIRE_VERIFIED_EMAIL = False
-OIDC_USER_INFO_ENABLED = True
-CUSTOM_SECURITY_MANAGER = OIDCSecurityManager
-
-# Ensure that the secrets file exists
-if not os.path.exists(OIDC_CLIENT_SECRETS):
-    ValueError('Unable to load OIDC client configuration. %s does not exist.' % OIDC_CLIENT_SECRETS)
-
-# Parse client_secret.json for scopes and logout URL
-with open(OIDC_CLIENT_SECRETS) as f:
-    OIDC_APPCONFIG = json.loads(f.read())
-
-# Ensure that the logout/revoke URL is specified in the client secrets file
-UNITY_OIDC_URL = OIDC_APPCONFIG.get('web', {}).get('issuer')
-if not UNITY_OIDC_URL:
-    raise ValueError('Invalid OIDC client configuration, GitLab OIDC URI not specified.')
-
-OIDC_SCOPES = OIDC_APPCONFIG.get('OIDC_SCOPES', ['openid', 'email', 'profile'])  # Scopes that should be requested.
-OIDC_LOGOUT_URI = posixpath.join(UNITY_OIDC_URL, 'oauth/revoke') # OIDC logout URL
-
-
-# Allow user self registration
-AUTH_USER_REGISTRATION = False
-
-# Default role to provide to new users
-AUTH_USER_REGISTRATION_ROLE = os.environ.get('AUTH_USER_REGISTRATION_ROLE', 'Public')
-
-AUTH_ROLE_ADMIN = 'Admin'
-AUTH_ROLE_PUBLIC = "Public"
-
-
-OPENID_PROVIDERS = [
-   {'name': 'Unity', 'url': posixpath.join(UNITY_OIDC_URL, 'oauth/authorize')}
-]
\ No newline at end of file