Dear
I have a flask app (Airflow) and I wrote a generic oauth module that worked fine with Github but Okta returned
400: Bad Request
BAD REQUESTYour request resulted in an error.
Go to Homepage
Identity Provider: Unknown
Error Code: invalid_request
Description: The ‘redirect_uri’ parameter must be an absolute URI that is whitelisted in the client app settings.
I already added my domain and all possible links to Trusted Origin (CORS and Redirect) also added them in Login redirect URIs in the App Configurations
Same module works fine with Github by changing the urls and client id and client secret
import flask_login
# Need to expose these downstream
# pylint: disable=unused-import
from flask_login import (current_user,
logout_user,
login_required,
login_user)
# pylint: enable=unused-import
from flask import url_for, redirect, request
from flask_oauthlib.client import OAuth
from airflow import models, configuration, settings
from airflow.configuration import AirflowConfigException
from airflow.utils.log.logging_mixin import LoggingMixin
from airflow.exceptions import AirflowConfigException
_log = LoggingMixin().log
def get_config_param(param):
try:
return str(configuration.get('generic_oauth', param))
except AirflowConfigException:
return None
class GenericOAuthUser(models.User):
def __init__(self, user):
self.user = user
def is_active(self):
'''Required by flask_login'''
return True
def is_authenticated(self):
'''Required by flask_login'''
return True
def is_anonymous(self):
'''Required by flask_login'''
return False
def get_id(self):
'''Returns the current user id as required by flask_login'''
return self.user.get_id()
def data_profiling(self):
'''Provides access to data profiling tools'''
return True
def is_superuser(self):
'''Access all the things'''
return True
class AuthenticationError(Exception):
pass
class GenericOAuthBackend(object):
def __init__(self):
self.login_manager = flask_login.LoginManager()
self.login_manager.login_view = 'airflow.login'
self.flask_app = None
self.oauth = None
self.base_url = get_config_param('base_url')
self.oauth_url = get_config_param('oauth_url')
self.access_token_url = get_config_param('access_token_url')
self.request_token_url = get_config_param('request_token_url')
self.client_id = get_config_param('client_id')
self.client_secret = get_config_param('client_secret')
self.scopes = get_config_param('scopes') # openid,profile,email
self.provider_name = get_config_param('provider_name')
self.oauth_callback_url = get_config_param('callback_url')
self.oauth_user_info_url = get_config_param('user_info_url')
def init_app(self, flask_app):
self.flask_app = flask_app
self.login_manager.init_app(self.flask_app)
self.oauth = OAuth(self.flask_app).remote_app(
self.provider_name,
consumer_key=self.client_id,
consumer_secret=self.client_secret,
# need read:org to get team member list
request_token_params={'scope': self.scopes},
base_url=self.base_url,
request_token_url=self.request_token_url,
access_token_method='POST',
access_token_url=self.access_token_url,
authorize_url=self.oauth_url
)
self.login_manager.user_loader(self.load_user)
self.flask_app.add_url_rule(self.oauth_callback_url or '/oauth-callback', 'generic_oauth_callback', self.oauth_callback)
def login(self, request):
_log.info('Redirecting user to OAuth login')
return self.oauth.authorize(callback=url_for('generic_oauth_callback',
_scheme='https',
_external=True, next=request.args.get('next') or request.referrer or None))
def get_user_profile_info(self, token):
resp = self.oauth.get(self.oauth_user_info_url, token=(token, ''))
if not resp or resp.status != 200:
raise AuthenticationError(
'Failed to fetch user profile, status ({0})'.format(
resp.status if resp else 'None'))
return resp.data['login'], resp.data['email']
def load_user(self, userid):
if not userid or userid == 'None':
return None
session = settings.Session()
user = session.query(models.User).filter(
models.User.id == int(userid)).first()
session.expunge_all()
session.commit()
session.close()
return GenericOAuthUser(user)
def oauth_callback(self):
_log.info('OAuth callback called')
next_url = request.args.get('next') or url_for('admin.index')
resp = self.oauth.authorized_response()
_log.info(resp)
try:
if resp is None:
raise AuthenticationError(
'Null response from %s, denying access.' % self.provider_name
)
access_token = resp['access_token']
username, email = self.get_user_profile_info(access_token)
except AuthenticationError:
_log.exception('')
return redirect(url_for('airflow.noaccess'))
session = settings.Session()
user = session.query(models.User).filter(
models.User.username == username).first()
if not user:
user = models.User(
username=username,
email=email,
is_superuser=False)
session.merge(user)
session.commit()
login_user(GenericOAuthUser(user))
session.commit()
session.close()
return redirect(next_url)
login_manager = GenericOAuthBackend()
def login(self, request):
return login_manager.login(request)
this is returning None in the code when setup with Okta
resp = self.oauth.authorized_response()
my redirect URI / callback / Login Initiate URI are
and I configured the module with the following
- base_url : https://company.okta.com
- authorize_url : https://company.okta.com/oauth2/default/v1/authorize
- token_url : https://company.okta.com/oauth2/default/v1/token
- user_info_url: https://company.okta.com/oauth2/default/v1/userinfo