diff --git a/auth0/authentication/get_token.py b/auth0/authentication/get_token.py index 7b368523..68a47f4a 100644 --- a/auth0/authentication/get_token.py +++ b/auth0/authentication/get_token.py @@ -195,7 +195,6 @@ def refresh_token( Args: refresh_token (str): The refresh token returned from the initial token request. - scope (str): Use this to limit the scopes of the new access token. Multiple scopes are separated with whitespace. @@ -236,7 +235,6 @@ def passwordless_login( Multiple scopes are separated with whitespace. audience (str): The unique identifier of the target API you want to access. - Returns: access_token, id_token """ @@ -277,3 +275,39 @@ def backchannel_login( "grant_type": grant_type, }, ) + + def federated_connection_token( + self, + refresh_token: str, + connection: str, + login_hint: str | None = None, + ) -> Any: + """Calls oauth/token endpoint with token-exchange:federated-connection-access-token grant type + + Args: + refresh_token (str): The refresh token returned from the initial token request. + + connection (str): The name of the connection to use. + + login_hint (str, optional): The login hint to use. + + Returns: + access_token, expires_in, scope, issued_token_type, token_type + """ + + data = { + "client_id": self.client_id, + "grant_type": "urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token", + "connection": connection, + "subject_token": refresh_token, + "subject_token_type": "urn:ietf:params:oauth:token-type:refresh_token", + "requested_token_type": "http://auth0.com/oauth/token-type/federated-connection-access-token", + } + + if login_hint: + data["login_hint"] = login_hint + + return self.authenticated_post( + f"{self.protocol}://{self.domain}/oauth/token", + data=data, + ) \ No newline at end of file diff --git a/auth0/test/authentication/test_get_token.py b/auth0/test/authentication/test_get_token.py index 4e717588..0a8386b9 100644 --- a/auth0/test/authentication/test_get_token.py +++ b/auth0/test/authentication/test_get_token.py @@ -334,4 +334,49 @@ def test_backchannel_login(self, mock_post): "auth_req_id": "reqid", "grant_type": "urn:openid:params:grant-type:ciba", }, - ) \ No newline at end of file + ) + + @mock.patch("auth0.rest.RestClient.post") + def test_federated_connection_token(self, mock_post): + + + g = GetToken("my.domain.com", "", client_secret="") + + g.federated_connection_token(refresh_token='', connection='') + + args, kwargs = mock_post.call_args + + self.assertEqual(args[0], "https://my.domain.com/oauth/token") + self.assertEqual( + kwargs["data"], + { + "client_id": "", + "client_secret": "", + "grant_type": "urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token", + "connection": "", + "subject_token": "", + "subject_token_type": "urn:ietf:params:oauth:token-type:refresh_token", + "requested_token_type": "http://auth0.com/oauth/token-type/federated-connection-access-token", + } + ) + + + # Get a new federated connection access token with a login hint + g.federated_connection_token(refresh_token='', connection='', login_hint='') + + args, kwargs = mock_post.call_args + + self.assertEqual(args[0], "https://my.domain.com/oauth/token") + self.assertEqual( + kwargs["data"], + { + "client_id": "", + "client_secret": "", + "grant_type": "urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token", + "connection": "", + "subject_token": "", + "subject_token_type": "urn:ietf:params:oauth:token-type:refresh_token", + "requested_token_type": "http://auth0.com/oauth/token-type/federated-connection-access-token", + 'login_hint': '', + } + )