Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Okta OAuth component #1594

Merged
merged 3 commits into from
Sep 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/user_guide/Authentication.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"* `github`: GitHub\n",
"* `gitlab`: GitLab\n",
"* `google`: Google\n",
"* `okta`: Okta\n",
"\n",
"We will go through the process of configuring each of these individually later but for now all we need to know that the `oauth_provider` can be set on the commandline using the `--oauth-provider` CLI argument to `panel serve` or the `PANEL_OAUTH_PROVIDER` environment variable.\n",
"\n",
Expand Down Expand Up @@ -167,6 +168,10 @@
"\n",
"Google provides a guide about [configuring a OAuth application](https://developers.google.com/identity/protocols/oauth2/native-app). By default nothing except the `oauth_key` and `oauth_secret` are required but to access Google services you may also want to override the default `scope` via the `oauth_extra_params`.\n",
"\n",
"### **Okta**\n",
"\n",
"Okta provides a guide about [configuring OAuth2](https://developer.okta.com/docs/concepts/oauth-openid/). You must provide an `oauth_key` and `oauth_secret` but in most other ordinary setups you will also have to provide a `url` via the `oauth_extra_params` and if you have set up a custom authentication server (i.e. not 'default') with Okta you must also provide 'server', the `oauth_extra_params` should then look something like this: `{'server': 'custom', 'url': 'dev-***.okta.com'}`\n",
"\n",
"### Plugins\n",
"\n",
"The Panel OAuth providers are pluggable, in other words downstream libraries may define their own Tornado `RequestHandler` to be used with Panel. To register such a component the `setup.py` of the downstream package should register an entry_point that Panel can discover. To read more about entry points see the [Python documentation](https://packaging.python.org/specifications/entry-points/). A custom OAuth request handler in your library may be registered as follows:\n",
Expand Down
48 changes: 47 additions & 1 deletion panel/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ class GithubLoginHandler(OAuthLoginHandler, OAuth2Mixin):

_USER_KEY = 'login'



class BitbucketLoginHandler(OAuthLoginHandler, OAuth2Mixin):

Expand Down Expand Up @@ -495,7 +496,12 @@ async def _fetch_access_token(self, code, redirect_uri, client_id, client_secret
def _on_auth(self, id_token, access_token):
decoded = decode_id_token(id_token)
user_key = config.oauth_jwt_user or self._USER_KEY
user = decoded[user_key]
if user_key in decoded:
user = decoded[user_key]
else:
log.error("%s token payload did not contain expected '%s'." %
(type(self).__name__, user_key))
raise HTTPError(400, "OAuth token payload missing user information")
self.set_secure_cookie('user', user)
if state.encryption:
access_token = state.encryption.encrypt(access_token.encode('utf-8'))
Expand Down Expand Up @@ -533,6 +539,45 @@ def _OAUTH_USER_URL(self):
return self._OAUTH_USER_URL_.format(**config.oauth_extra_params)


class OktaLoginHandler(OAuthIDTokenLoginHandler, OAuth2Mixin):
"""Okta OAuth2 Authentication

To authenticate with Okta you first need to set up and configure
in the Okta developer console.
"""

_EXTRA_TOKEN_PARAMS = {
'grant_type': 'authorization_code',
'response_type': 'code,token,id_token'
}

_OAUTH_ACCESS_TOKEN_URL_ = 'https://{0}/oauth2/{1}/v1/token'
_OAUTH_AUTHORIZE_URL_ = 'https://{0}/oauth2/{1}/v1/authorize'
_OAUTH_USER_URL_ = 'https://{0}/oauth2/{1}/v1/userinfo?access_token='

_USER_KEY = 'email'

_SCOPE = ['openid', 'email', 'profile']

@property
def _OAUTH_ACCESS_TOKEN_URL(self):
url = config.oauth_extra_params.get('url', 'okta.com')
server = config.oauth_extra_params.get('server', 'default')
return self._OAUTH_ACCESS_TOKEN_URL_.format(url, server)

@property
def _OAUTH_AUTHORIZE_URL(self):
url = config.oauth_extra_params.get('url', 'okta.com')
server = config.oauth_extra_params.get('server', 'default')
return self._OAUTH_AUTHORIZE_URL_.format(url, server)

@property
def _OAUTH_USER_URL(self):
url = config.oauth_extra_params.get('url', 'okta.com')
server = config.oauth_extra_params.get('server', 'default')
return self._OAUTH_USER_URL_.format(url, server)


class GoogleLoginHandler(OAuthIDTokenLoginHandler, OAuth2Mixin):

_API_BASE_HEADERS = {
Expand Down Expand Up @@ -587,6 +632,7 @@ def logout_handler(self):
'google': GoogleLoginHandler,
'github': GithubLoginHandler,
'gitlab': GitLabLoginHandler,
'okta': OktaLoginHandler
}

# Populate AUTH Providers from external extensions
Expand Down