Skip to content
This repository has been archived by the owner on Jun 23, 2023. It is now read-only.

Token exchange support #165

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from

Conversation

ctriant
Copy link
Contributor

@ctriant ctriant commented Dec 9, 2021

No description provided.

@nsklikas nsklikas requested review from rohe, peppelinux and nsklikas and removed request for rohe and peppelinux December 13, 2021 11:05
src/oidcop/oidc/token.py Outdated Show resolved Hide resolved
src/oidcop/oidc/token.py Outdated Show resolved Hide resolved
src/oidcop/oidc/token.py Outdated Show resolved Hide resolved
src/oidcop/oidc/token.py Outdated Show resolved Hide resolved
src/oidcop/oidc/token.py Outdated Show resolved Hide resolved
@ctriant ctriant marked this pull request as ready for review December 23, 2021 14:45
src/oidcop/oidc/token.py Outdated Show resolved Hide resolved
src/oidcop/oidc/token.py Outdated Show resolved Hide resolved
src/oidcop/oidc/token.py Outdated Show resolved Hide resolved
src/oidcop/oidc/token.py Outdated Show resolved Hide resolved
src/oidcop/oidc/token.py Outdated Show resolved Hide resolved
src/oidcop/oidc/token.py Outdated Show resolved Hide resolved
src/oidcop/oidc/token.py Outdated Show resolved Hide resolved
src/oidcop/oidc/token.py Outdated Show resolved Hide resolved
@ctriant ctriant force-pushed the refresh_token_exchange branch 2 times, most recently from a75d7ef to 1eeb2f8 Compare January 12, 2022 10:18
src/oidcop/oidc/token.py Outdated Show resolved Hide resolved
Comment on lines 467 to 484
fun = importer(self.config["token_exchange"]["policy"][""]["callable"])
kwargs = self.config["token_exchange"]["policy"][""]["kwargs"]
Copy link
Contributor

@nsklikas nsklikas Jan 12, 2022

Choose a reason for hiding this comment

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

What if that doesn't exist? Should we simply pass if a function isn't defined and accept the request?

_session_info = _mngr.get_session_info(
session_id=sid, grant=True)
except Exception:
logger.error("Error retrieving token exchabge session information")
Copy link
Contributor

@nsklikas nsklikas Jan 19, 2022

Choose a reason for hiding this comment

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

Typo Error retrieving token exchange session information

Comment on lines 605 to 606
if not resource:
pass
Copy link
Contributor

@nsklikas nsklikas Jan 19, 2022

Choose a reason for hiding this comment

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

This if clause is unnecessary

error_description=f"Exchange {request['subject_token_type']} to refresh token forbbiden",
)

scopes = list(set(request.get("scope", ["openid"])).intersection(kwargs.get("scope", ["openid"])))
Copy link
Contributor

Choose a reason for hiding this comment

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

Why set the scope default value to openid?

Comment on lines 618 to 619
if not audience:
pass
Copy link
Contributor

Choose a reason for hiding this comment

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

This if clause is unnecessary

@ctriant ctriant force-pushed the refresh_token_exchange branch from dd1e2c4 to 30f269e Compare January 20, 2022 10:25
issued_at: int = 0,
expires_in: int = 0,
expires_at: int = 0,
revoked: bool = False,
token_map: Optional[dict] = None,
users: list = None,
users: list = None
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the use of this parameter? Maybe we should remove it?

@ctriant ctriant force-pushed the refresh_token_exchange branch 2 times, most recently from 43f6256 to 867229e Compare January 20, 2022 12:41
error_description="Unauthorized scope requested",
)

subject_token_type = request["subject_token_type"]
Copy link
Contributor

Choose a reason for hiding this comment

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

response_args["access_token"] = token.value
response_args["scope"] = token.scope
response_args["issued_token_type"] = token.token_class
response_args["expires_in"] = token.usage_rules.get("expires_in", 0)
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's use the same logic as in https://github.com/IdentityPython/oidc-op/blob/master/src/oidcop/oauth2/token.py#L166:

if token.expires_at:
    _response["expires_in"] = token.expires_at - utc_time_sans_frac()

def default_token_exchange_policy(request, context, subject_token, **kwargs):
if "resource" in request:
resource = kwargs.get("resource", [])
if not len(set(request["resource"]).intersection(set(resource))):
Copy link
Contributor

@nsklikas nsklikas Jan 24, 2022

Choose a reason for hiding this comment

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

This is cleaner:

        if not set(request["resource"]).issubset(set(resource)):

error="invalid_target", error_description="Refresh token has single owner"
)
audience = kwargs.get("audience", [])
if audience and not len(set(request["audience"]).intersection(set(audience))):
Copy link
Contributor

Choose a reason for hiding this comment

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

same (use issubset)

@ctriant ctriant force-pushed the refresh_token_exchange branch from 867229e to 168e67a Compare January 24, 2022 10:45
Comment on lines 473 to 475
self.usage_rules = {
"access_token": {"supports_minting": ["access_token"], "expires_in": 60}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is still needed.

@ctriant ctriant force-pushed the refresh_token_exchange branch from 168e67a to 95eda1a Compare January 24, 2022 12:34
@nsklikas
Copy link
Contributor

Run darker on this. Other than this LGTM.

@rohe @peppelinux what do you think?

response_args = {}
response_args["access_token"] = token.value
response_args["scope"] = token.scope
response_args["issued_token_type"] = token.token_class
Copy link
Contributor

Choose a reason for hiding this comment

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

This must be the issued_token_type that was requested not the one on the class (urn:ietf:params:oauth:token-type:access_token vs access_token)

@rohe
Copy link
Collaborator

rohe commented Feb 6, 2022

Nice set of tests !

@nsklikas
Copy link
Contributor

nsklikas commented Feb 7, 2022

We merged this in our private fork, here are some things that need to be done:

  • Check that the new scopes are a subset of the allowed scopes for the client
  • Set the expires_at of the new token to be the same as the original token's. This would stop a client from getting an access token from another client and using token exchange to refresh it indefinitely.
  • Do we need to check whether the client using the token is allowed to do so? (in the case where the token wasn't issued for them)

@peppelinux
Copy link
Member

@nsklikas we just have two conflicting files right now :)

@melanger
Copy link
Contributor

melanger commented Aug 1, 2022

@peppelinux @nsklikas Hello, are you planning to merge this in public repository? I would really like to have token exchange in SATOSA.

@ctriant
Copy link
Contributor Author

ctriant commented Aug 2, 2022

@peppelinux @nsklikas Hello, are you planning to merge this in public repository? I would really like to have token exchange in SATOSA.

Hello! It is already supported in idpy-oidc. https://github.com/IdentityPython/idpy-oidc/tree/main

@rohe
Copy link
Collaborator

rohe commented Aug 2, 2022

It's supported in idpy-oidc but the official SATOSA version at IdentityPython is still not based on idpy-oidc.

@melanger
Copy link
Contributor

melanger commented Aug 3, 2022

The default OIDC frontend in SATOSA uses pyop and oic, but I am using satosa-oidcop which uses this library (oidc-op).

It's supported in idpy-oidc but the official SATOSA version at IdentityPython is still not based on idpy-oidc.

So which library is the "new" one? I though it is this one.

@rohe
Copy link
Collaborator

rohe commented Aug 3, 2022

idpy-oidc is where the action is.

@melanger
Copy link
Contributor

melanger commented Aug 4, 2022

idpy-oidc is where the action is.

So let me recap:

What are the development plans? Are you going to support oidc-op until idpy-oidc is mature enough and integrated with SATOSA?

@rohe
Copy link
Collaborator

rohe commented Aug 4, 2022

Good questions all.
I'm responsible for the OIDC/OAuth2 libraries within IdentityPython and I would like SATOSA to switch to using idpy-oidc as soon as possible.
I think there is a SATOSA frontend, based on idpy-oidc, in the EduTeams implementation but it isn't public yet.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants