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

[All] Added extra_authorize_params to pass extra params in the initial request to the identity provider #338

Merged
merged 2 commits into from
May 18, 2020

Conversation

NickolausDS
Copy link
Contributor

Changes for #244. These changes expose a generic config option for passing in arbitrary options alongside the state parameter. One example is requesting refresh tokens, as in #211, which should look like this:

c.JupyterHub.authenticator_class.extra_authorize_params = {'access_type': 'offline'}

This may also solve #298 in order to select arbitrary accounts for Google. I created an app and tested the following:

c.LocalGoogleOAuthenticator.extra_authorize_params = {'prompt': 'select_account'}

Which generated the following URL:

302 GET /hub/oauth_login?next= -> 
https://accounts.google.com/o/oauth2/v2/auth?
response_type=code&
redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Fhub%2Foauth_callback&
client_id=[client]&
prompt=select_account&
state=[secret]&
scope=openid+email

This appeared to work, although I'm not familiar enough with Google to know if that worked exactly as intended. I tried requesting Google refresh tokens as well, but it didn't seem to want to give me any. I also tested with Globus, and was able to successfully request refresh tokens simply by setting {'access_type': 'offline'}.

@missingcharacter
Copy link
Contributor

Personally, I'd like for extra_params to be set at the Oauthenticator class level, something like this https://github.com/missingcharacter/oauthenticator/commit/62352ab7254e6779c39c8de9658740dcfbc87e39

BTW, tests pass

$ pytest -v ./oauthenticator/tests/
============================================================================================================== test session starts ===============================================================================================================
platform darwin -- Python 3.8.2, pytest-5.4.1, py-1.8.1, pluggy-0.13.1 -- /Users/ricardo/src/oauthenticator/venv/bin/python3
cachedir: .pytest_cache
rootdir: /Users/ricardo/src/oauthenticator
plugins: requests-mock-1.7.0, asyncio-0.10.0, cov-2.8.1
collected 35 items

oauthenticator/tests/test_auth0.py::test_auth0 PASSED                                                                                                                                                                                      [  2%]
oauthenticator/tests/test_azuread.py::test_tenant_id_from_env PASSED                                                                                                                                                                       [  5%]
oauthenticator/tests/test_bitbucket.py::test_bitbucket PASSED                                                                                                                                                                              [  8%]
oauthenticator/tests/test_bitbucket.py::test_team_whitelist PASSED                                                                                                                                                                         [ 11%]
oauthenticator/tests/test_cilogon.py::test_cilogon PASSED                                                                                                                                                                                  [ 14%]
oauthenticator/tests/test_cilogon.py::test_cilogon_alternate_claim PASSED                                                                                                                                                                  [ 17%]
oauthenticator/tests/test_cilogon.py::test_cilogon_additional_claim PASSED                                                                                                                                                                 [ 20%]
oauthenticator/tests/test_cilogon.py::test_cilogon_missing_alternate_claim PASSED                                                                                                                                                          [ 22%]
oauthenticator/tests/test_generic.py::test_generic PASSED                                                                                                                                                                                  [ 25%]
oauthenticator/tests/test_generic.py::test_generic_callable_username_key PASSED                                                                                                                                                            [ 28%]
oauthenticator/tests/test_github.py::test_github PASSED                                                                                                                                                                                    [ 31%]
oauthenticator/tests/test_github.py::test_org_whitelist PASSED                                                                                                                                                                             [ 34%]
oauthenticator/tests/test_gitlab.py::test_gitlab PASSED                                                                                                                                                                                    [ 37%]
oauthenticator/tests/test_gitlab.py::test_group_whitelist PASSED                                                                                                                                                                           [ 40%]
oauthenticator/tests/test_gitlab.py::test_project_id_whitelist PASSED                                                                                                                                                                      [ 42%]
oauthenticator/tests/test_globus.py::test_globus PASSED                                                                                                                                                                                    [ 45%]
oauthenticator/tests/test_globus.py::test_globus_pre_spawn_start PASSED                                                                                                                                                                    [ 48%]
oauthenticator/tests/test_globus.py::test_allow_refresh_tokens PASSED                                                                                                                                                                      [ 51%]
oauthenticator/tests/test_globus.py::test_restricted_domain PASSED                                                                                                                                                                         [ 54%]
oauthenticator/tests/test_globus.py::test_namespaced_domain PASSED                                                                                                                                                                         [ 57%]
oauthenticator/tests/test_globus.py::test_token_exclusion PASSED                                                                                                                                                                           [ 60%]
oauthenticator/tests/test_globus.py::test_revoke_tokens PASSED                                                                                                                                                                             [ 62%]
oauthenticator/tests/test_globus.py::test_custom_logout PASSED                                                                                                                                                                             [ 65%]
oauthenticator/tests/test_globus.py::test_logout_revokes_tokens PASSED                                                                                                                                                                     [ 68%]
oauthenticator/tests/test_google.py::test_google PASSED                                                                                                                                                                                    [ 71%]
oauthenticator/tests/test_google.py::test_hosted_domain PASSED                                                                                                                                                                             [ 74%]
oauthenticator/tests/test_google.py::test_multiple_hosted_domain PASSED                                                                                                                                                                    [ 77%]
oauthenticator/tests/test_google.py::test_admin_google_groups PASSED                                                                                                                                                                       [ 80%]
oauthenticator/tests/test_google.py::test_whitelisted_google_groups PASSED                                                                                                                                                                 [ 82%]
oauthenticator/tests/test_mediawiki.py::test_mediawiki PASSED                                                                                                                                                                              [ 85%]
oauthenticator/tests/test_mediawiki.py::test_login_redirect PASSED                                                                                                                                                                         [ 88%]
oauthenticator/tests/test_oauth2.py::test_serialize_state PASSED                                                                                                                                                                           [ 91%]
oauthenticator/tests/test_okpy.py::test_okpy PASSED                                                                                                                                                                                        [ 94%]
oauthenticator/tests/test_okpy.py::test_no_code PASSED                                                                                                                                                                                     [ 97%]
oauthenticator/tests/test_openshift.py::test_openshift PASSED                                                                                                                                                                              [100%]

================================================================================================================ warnings summary ================================================================================================================
venv/lib/python3.8/site-packages/pytest_asyncio/plugin.py:39: 32 tests with warnings
  /Users/ricardo/src/oauthenticator/venv/lib/python3.8/site-packages/pytest_asyncio/plugin.py:39: PytestDeprecationWarning: direct construction of Function has been deprecated, please use Function.from_parent
    item = pytest.Function(name, parent=collector)

venv/lib/python3.8/site-packages/pytest_asyncio/plugin.py:45: 32 tests with warnings
  /Users/ricardo/src/oauthenticator/venv/lib/python3.8/site-packages/pytest_asyncio/plugin.py:45: PytestDeprecationWarning: direct construction of Function has been deprecated, please use Function.from_parent
    item = pytest.Function(name, parent=collector)  # To reload keywords.

-- Docs: https://docs.pytest.org/en/latest/warnings.html
======================================================================================================== 35 passed, 64 warnings in 0.45s =========================================================================================================

@missingcharacter
Copy link
Contributor

I'm just making my example work, I'm pretty sure there are better ways to do it, I'm not smart enough to figure these out yet.

@NickolausDS
Copy link
Contributor Author

The current changes here expose the extra params for any Oauthenticator using the default LoginHandler (Or a derivative). For example, any OAuthenticator would be able to configure custom params through the Jupyterhub Config file without overriding LoginHandler:

# Config enabling Google Refresh Tokens
from oauthenticator.google import GoogleOAuthenticator
c.JupyterHub.authenticator_class = GoogleOAuthenticator
c.LocalGoogleOAuthenticator.extra_authorize_params = {'access_type': 'offline'}

Or for CILogon

from oauthenticator.cilogon import CILogonOAuthenticator
c.JupyterHub.authenticator_class = CILogonOAuthenticator
c.CILogonOAuthenticator.extra_authorize_params = {'selected_idp': 'https://accounts.google.com'}

Or for Globus

# Enable refresh tokens.
from oauthenticator.globus import LocalGlobusOAuthenticator
c.JupyterHub.authenticator_class = LocalGlobusOAuthenticator
c.LocalGlobusOAuthenticator.extra_authorize_params = {'access_type': 'offline'}

It would be nice to have these params generally configurable for any OAuthentictor.

@missingcharacter
Copy link
Contributor

missingcharacter commented May 5, 2020

@NickolausDS I see that, what I see in my approach that I don't see in yours is that in my approach users could just update to a new version and won't need to change anything for their current setup to work (I guess CILogon only), and if they decide to add more params they can just do that too.

@NickolausDS
Copy link
Contributor Author

For CiLogon, your changes would be a nice way to keep the explicit idp and skin settings.

extra_params is also used by the Generic OAuthenticator, using that would be a bit of a namespace collision. I think the Generic OAuthenticator would override the value in that context, but it would be confusing since they're used for different things (It looks like the Generic OAuthenticator uses extra_params when it POSTs back the Authorization Code, not when setting query params on the initial request to the IdP). Might be good to name it something other than extra_params so it isn't confused with the usage here.

@missingcharacter
Copy link
Contributor

@NickolausDS I went ahead and did it

authorize_redirect_params = Dict(
Unicode(),
help="Add extra_params to authorize_redirect"
).tag(config=True)
def _set_extra_params(self):
if self.authenticator.idp:
self.authorize_redirect_params["selected_idp"] = self.authenticator.idp
if self.authenticator.skin:
self.authorize_redirect_params["skin"] = self.authenticator.skin
cilogon_host = Unicode(os.environ.get("CILOGON_HOST") or "cilogon.org", config=True)

authorize_redirect_params = Dict(
Unicode(),
help="Add extra_params to authorize_redirect"
).tag(config=True)
def _set_extra_params(self):
OAuthLoginHandler.extra_params = self.authorize_redirect_params

tests passed

I guess we should still wait to hear back from the maintainers on what approach they'd prefer

Copy link
Member

@manics manics left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two comments on the code.

I haven't done any functional testing but the change makes sense to me.

oauthenticator/oauth2.py Outdated Show resolved Hide resolved
oauthenticator/oauth2.py Outdated Show resolved Hide resolved
@NickolausDS
Copy link
Contributor Author

@manics I applied both your suggestions. Thank you for reviewing! Also thanks to @missingcharacter for suggestions and testing.

I retested with Globus to make sure I could supply params in the config file. Let me know if you see anything else.

Copy link
Member

@manics manics left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested this with https://developers.google.com/identity/protocols/oauth2/web-server#creatingclient and it works as expected. Thanks!

@manics manics merged commit 2187d1c into jupyterhub:master May 18, 2020
@consideRatio consideRatio changed the title Extend oauth2 params sent to provider on first leg of oauth2 flow [All] Added extra_authorize_params to pass extra params in the initial request to the identity provider Oct 26, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants