From 4ee881148898f6a683509ece9a9a53233beff8dc Mon Sep 17 00:00:00 2001 From: Lyle Schemmerling Date: Wed, 17 Sep 2025 16:47:59 -0600 Subject: [PATCH 1/3] new optional tenant id param added (breaking) --- .../python/fusionauth/fusionauth_client.py | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/main/python/fusionauth/fusionauth_client.py b/src/main/python/fusionauth/fusionauth_client.py index a8ad73f..63f7285 100644 --- a/src/main/python/fusionauth/fusionauth_client.py +++ b/src/main/python/fusionauth/fusionauth_client.py @@ -76,7 +76,7 @@ def add_user_to_family(self, family_id, request): .put() \ .go() - def approve_device(self, token, user_code, client_id=None, client_secret=None): + def approve_device(self, token, user_code, client_id=None, client_secret=None, tenant_id=None): """ Approve a device grant. @@ -85,12 +85,14 @@ def approve_device(self, token, user_code, client_id=None, client_secret=None): client_secret: (Optional) The client secret. This value will be required if client authentication is enabled. token: The access token used to identify the user. user_code: The end-user verification code. + tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "client_id": client_id, "client_secret": client_secret, "token": token, "user_code": user_code, + "tenantId": tenantId, } return self.start().uri('/oauth2/device/approve') \ .body_handler(FormDataBodyHandler(body)) \ @@ -226,7 +228,7 @@ def check_change_password_using_login_id(self, login_id): .get() \ .go() - def client_credentials_grant(self, client_id=None, client_secret=None, scope=None): + def client_credentials_grant(self, client_id=None, client_secret=None, scope=None, tenant_id=None): """ Make a Client Credentials grant request to obtain an access token. @@ -236,12 +238,14 @@ def client_credentials_grant(self, client_id=None, client_secret=None, scope=Non client_secret: (Optional) The client secret used to authenticate this request. This parameter is optional when Basic Authorization is used to authenticate this request. scope: (Optional) This parameter is used to indicate which target entity you are requesting access. To request access to an entity, use the format target-entity:<target-entity-id>:<roles>. Roles are an optional comma separated list. + tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "client_id": client_id, "client_secret": client_secret, "grant_type": "client_credentials", "scope": scope, + "tenantId": tenantId, } return self.start_anonymous().uri('/oauth2/token') \ .body_handler(FormDataBodyHandler(body)) \ @@ -1333,7 +1337,7 @@ def enable_two_factor(self, user_id, request): .post() \ .go() - def exchange_o_auth_code_for_access_token(self, code, redirect_uri, client_id=None, client_secret=None): + def exchange_o_auth_code_for_access_token(self, code, redirect_uri, client_id=None, client_secret=None, tenant_id=None): """ Exchanges an OAuth authorization code for an access token. Makes a request to the Token endpoint to exchange the authorization code returned from the Authorize endpoint for an access token. @@ -1344,6 +1348,7 @@ def exchange_o_auth_code_for_access_token(self, code, redirect_uri, client_id=No This parameter is optional when Basic Authorization is used to authenticate this request. client_secret: (Optional) The client secret. This value will be required if client authentication is enabled. redirect_uri: The URI to redirect to upon a successful request. + tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "code": code, @@ -1351,13 +1356,14 @@ def exchange_o_auth_code_for_access_token(self, code, redirect_uri, client_id=No "client_secret": client_secret, "grant_type": "authorization_code", "redirect_uri": redirect_uri, + "tenantId": tenantId, } return self.start_anonymous().uri('/oauth2/token') \ .body_handler(FormDataBodyHandler(body)) \ .post() \ .go() - def exchange_o_auth_code_for_access_token_using_pkce(self, code, redirect_uri, code_verifier, client_id=None, client_secret=None): + def exchange_o_auth_code_for_access_token_using_pkce(self, code, redirect_uri, code_verifier, client_id=None, client_secret=None, tenant_id=None): """ Exchanges an OAuth authorization code and code_verifier for an access token. Makes a request to the Token endpoint to exchange the authorization code returned from the Authorize endpoint and a code_verifier for an access token. @@ -1369,6 +1375,7 @@ def exchange_o_auth_code_for_access_token_using_pkce(self, code, redirect_uri, c client_secret: (Optional) The client secret. This value may optionally be provided in the request body instead of the Authorization header. redirect_uri: The URI to redirect to upon a successful request. code_verifier: The random string generated previously. Will be compared with the code_challenge sent previously, which allows the OAuth provider to authenticate your app. + tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "code": code, @@ -1377,13 +1384,14 @@ def exchange_o_auth_code_for_access_token_using_pkce(self, code, redirect_uri, c "grant_type": "authorization_code", "redirect_uri": redirect_uri, "code_verifier": code_verifier, + "tenantId": tenantId, } return self.start_anonymous().uri('/oauth2/token') \ .body_handler(FormDataBodyHandler(body)) \ .post() \ .go() - def exchange_refresh_token_for_access_token(self, refresh_token, client_id=None, client_secret=None, scope=None, user_code=None): + def exchange_refresh_token_for_access_token(self, refresh_token, client_id=None, client_secret=None, scope=None, user_code=None, tenant_id=None): """ Exchange a Refresh Token for an Access Token. If you will be using the Refresh Token Grant, you will make a request to the Token endpoint to exchange the user’s refresh token for an access token. @@ -1395,6 +1403,7 @@ def exchange_refresh_token_for_access_token(self, refresh_token, client_id=None, client_secret: (Optional) The client secret. This value may optionally be provided in the request body instead of the Authorization header. scope: (Optional) This parameter is optional and if omitted, the same scope requested during the authorization request will be used. If provided the scopes must match those requested during the initial authorization request. user_code: (Optional) The end-user verification code. This code is required if using this endpoint to approve the Device Authorization. + tenant_id: (Optional) The Id of the tenant to use for this request. Required if the request is for a universal application. """ body = { "refresh_token": refresh_token, @@ -1403,6 +1412,7 @@ def exchange_refresh_token_for_access_token(self, refresh_token, client_id=None, "grant_type": "refresh_token", "scope": scope, "user_code": user_code, + "tenantId": tenantId, } return self.start_anonymous().uri('/oauth2/token') \ .body_handler(FormDataBodyHandler(body)) \ @@ -1421,7 +1431,7 @@ def exchange_refresh_token_for_jwt(self, request): .post() \ .go() - def exchange_user_credentials_for_access_token(self, username, password, client_id=None, client_secret=None, scope=None, user_code=None): + def exchange_user_credentials_for_access_token(self, username, password, client_id=None, client_secret=None, scope=None, user_code=None, tenant_id=None): """ Exchange User Credentials for a Token. If you will be using the Resource Owner Password Credential Grant, you will make a request to the Token endpoint to exchange the user’s email and password for an access token. @@ -1434,6 +1444,7 @@ def exchange_user_credentials_for_access_token(self, username, password, client_ client_secret: (Optional) The client secret. This value may optionally be provided in the request body instead of the Authorization header. scope: (Optional) This parameter is optional and if omitted, the same scope requested during the authorization request will be used. If provided the scopes must match those requested during the initial authorization request. user_code: (Optional) The end-user verification code. This code is required if using this endpoint to approve the Device Authorization. + tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "username": username, @@ -1443,6 +1454,7 @@ def exchange_user_credentials_for_access_token(self, username, password, client_ "grant_type": "password", "scope": scope, "user_code": user_code, + "tenantId": tenantId, } return self.start_anonymous().uri('/oauth2/token') \ .body_handler(FormDataBodyHandler(body)) \ @@ -1621,32 +1633,36 @@ def import_web_authn_credential(self, request): .post() \ .go() - def introspect_access_token(self, client_id, token): + def introspect_access_token(self, client_id, token, tenant_id=None): """ Inspect an access token issued as the result of the User based grant such as the Authorization Code Grant, Implicit Grant, the User Credentials Grant or the Refresh Grant. Attributes: client_id: The unique client identifier. The client Id is the Id of the FusionAuth Application for which this token was generated. token: The access token returned by this OAuth provider as the result of a successful client credentials grant. + tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "client_id": client_id, "token": token, + "tenantId": tenantId, } return self.start_anonymous().uri('/oauth2/introspect') \ .body_handler(FormDataBodyHandler(body)) \ .post() \ .go() - def introspect_client_credentials_access_token(self, token): + def introspect_client_credentials_access_token(self, token, tenant_id=None): """ Inspect an access token issued as the result of the Client Credentials Grant. Attributes: token: The access token returned by this OAuth provider as the result of a successful client credentials grant. + tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "token": token, + "tenantId": tenantId, } return self.start_anonymous().uri('/oauth2/introspect') \ .body_handler(FormDataBodyHandler(body)) \ @@ -3451,7 +3467,7 @@ def retrieve_user_by_verification_id(self, verification_id): .get() \ .go() - def retrieve_user_code(self, client_id, client_secret, user_code): + def retrieve_user_code(self, client_id, client_secret, user_code, tenant_id=None): """ Retrieve a user_code that is part of an in-progress Device Authorization Grant. @@ -3461,6 +3477,7 @@ def retrieve_user_code(self, client_id, client_secret, user_code): client_id: The client Id. client_secret: The client Id. user_code: The end-user verification code. + tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "client_id": client_id, @@ -3468,11 +3485,12 @@ def retrieve_user_code(self, client_id, client_secret, user_code): "user_code": user_code, } return self.start_anonymous().uri('/oauth2/device/user-code') \ + .url_parameter('tenantId', self.convert_true_false(tenant_id)) \ .body_handler(FormDataBodyHandler(body)) \ .get() \ .go() - def retrieve_user_code_using_api_key(self, user_code): + def retrieve_user_code_using_api_key(self, user_code, tenant_id=None): """ Retrieve a user_code that is part of an in-progress Device Authorization Grant. @@ -3482,11 +3500,13 @@ def retrieve_user_code_using_api_key(self, user_code): Attributes: user_code: The end-user verification code. + tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "user_code": user_code, } return self.start_anonymous().uri('/oauth2/device/user-code') \ + .url_parameter('tenantId', self.convert_true_false(tenant_id)) \ .body_handler(FormDataBodyHandler(body)) \ .get() \ .go() @@ -3527,15 +3547,17 @@ def retrieve_user_consents(self, user_id): .get() \ .go() - def retrieve_user_info_from_access_token(self, encoded_jwt): + def retrieve_user_info_from_access_token(self, encoded_jwt, tenant_id=None): """ Call the UserInfo endpoint to retrieve User Claims from the access token issued by FusionAuth. Attributes: encoded_jwt: The encoded JWT (access token). + tenant_id: (Optional) The Id of the tenant to use for this request. """ return self.start_anonymous().uri('/oauth2/userinfo') \ .authorization("Bearer " + encoded_jwt) \ + .url_parameter('tenantId', self.convert_true_false(tenant_id)) \ .get() \ .go() @@ -4813,7 +4835,7 @@ def upsert_entity_grant(self, entity_id, request): .post() \ .go() - def validate_device(self, user_code, client_id): + def validate_device(self, user_code, client_id, tenant_id=None): """ Validates the end-user provided user_code from the user-interaction of the Device Authorization Grant. If you build your own activation form you should validate the user provided code prior to beginning the Authorization grant. @@ -4821,10 +4843,12 @@ def validate_device(self, user_code, client_id): Attributes: user_code: The end-user verification code. client_id: The client Id. + tenant_id: (Optional) The Id of the tenant to use for this request. """ return self.start_anonymous().uri('/oauth2/device/validate') \ .url_parameter('user_code', self.convert_true_false(user_code)) \ .url_parameter('client_id', self.convert_true_false(client_id)) \ + .url_parameter('tenantId', self.convert_true_false(tenant_id)) \ .get() \ .go() From eefabfa63933d847498502e43f789997ea2b320e Mon Sep 17 00:00:00 2001 From: Lyle Schemmerling Date: Tue, 23 Sep 2025 12:17:31 -0600 Subject: [PATCH 2/3] validate and libs --- .../python/fusionauth/fusionauth_client.py | 218 +++++++++++++++--- 1 file changed, 185 insertions(+), 33 deletions(-) diff --git a/src/main/python/fusionauth/fusionauth_client.py b/src/main/python/fusionauth/fusionauth_client.py index 63f7285..4f9056a 100644 --- a/src/main/python/fusionauth/fusionauth_client.py +++ b/src/main/python/fusionauth/fusionauth_client.py @@ -76,7 +76,7 @@ def add_user_to_family(self, family_id, request): .put() \ .go() - def approve_device(self, token, user_code, client_id=None, client_secret=None, tenant_id=None): + def approve_device(self, token, user_code, client_id=None, client_secret=None): """ Approve a device grant. @@ -85,20 +85,30 @@ def approve_device(self, token, user_code, client_id=None, client_secret=None, t client_secret: (Optional) The client secret. This value will be required if client authentication is enabled. token: The access token used to identify the user. user_code: The end-user verification code. - tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "client_id": client_id, "client_secret": client_secret, "token": token, "user_code": user_code, - "tenantId": tenantId, } return self.start().uri('/oauth2/device/approve') \ .body_handler(FormDataBodyHandler(body)) \ .post() \ .go() + def approve_device_with_request(self, request): + """ + Approve a device grant. + + Attributes: + request: The request object containing the device approval information and optional tenantId. + """ + return self.start().uri('/oauth2/device/approve') \ + .body_handler(JSONBodyHandler(request)) \ + .post() \ + .go() + def cancel_action(self, action_id, request): """ Cancels the user action. @@ -228,7 +238,7 @@ def check_change_password_using_login_id(self, login_id): .get() \ .go() - def client_credentials_grant(self, client_id=None, client_secret=None, scope=None, tenant_id=None): + def client_credentials_grant(self, client_id=None, client_secret=None, scope=None): """ Make a Client Credentials grant request to obtain an access token. @@ -238,20 +248,30 @@ def client_credentials_grant(self, client_id=None, client_secret=None, scope=Non client_secret: (Optional) The client secret used to authenticate this request. This parameter is optional when Basic Authorization is used to authenticate this request. scope: (Optional) This parameter is used to indicate which target entity you are requesting access. To request access to an entity, use the format target-entity:<target-entity-id>:<roles>. Roles are an optional comma separated list. - tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "client_id": client_id, "client_secret": client_secret, "grant_type": "client_credentials", "scope": scope, - "tenantId": tenantId, } return self.start_anonymous().uri('/oauth2/token') \ .body_handler(FormDataBodyHandler(body)) \ .post() \ .go() + def client_credentials_grant_with_request(self, request): + """ + Make a Client Credentials grant request to obtain an access token. + + Attributes: + request: The client credentials grant request containing client authentication, scope and optional tenantId. + """ + return self.start_anonymous().uri('/oauth2/token') \ + .body_handler(JSONBodyHandler(request)) \ + .post() \ + .go() + def comment_on_user(self, request): """ Adds a comment to the user's account. @@ -1293,6 +1313,37 @@ def delete_webhook(self, webhook_id): .delete() \ .go() + def device_authorize(self, client_id, client_secret=None, scope=None): + """ + Start the Device Authorization flow using form-encoded parameters + + Attributes: + client_id: The unique client identifier. The client Id is the Id of the FusionAuth Application in which you are attempting to authenticate. + client_secret: (Optional) The client secret. This value may optionally be provided in the request body instead of the Authorization header. + scope: (Optional) A space-delimited string of the requested scopes. Defaults to all scopes configured in the Application's OAuth configuration. + """ + body = { + "client_id": client_id, + "client_secret": client_secret, + "scope": scope, + } + return self.start_anonymous().uri('/oauth2/device_authorize') \ + .body_handler(FormDataBodyHandler(body)) \ + .post() \ + .go() + + def device_authorize_with_request(self, request): + """ + Start the Device Authorization flow using a request body + + Attributes: + request: The device authorization request containing client authentication, scope, and optional device metadata. + """ + return self.start_anonymous().uri('/oauth2/device_authorize') \ + .body_handler(JSONBodyHandler(request)) \ + .post() \ + .go() + def disable_two_factor(self, user_id, method_id, code): """ Disable two-factor authentication for a user. @@ -1337,7 +1388,7 @@ def enable_two_factor(self, user_id, request): .post() \ .go() - def exchange_o_auth_code_for_access_token(self, code, redirect_uri, client_id=None, client_secret=None, tenant_id=None): + def exchange_o_auth_code_for_access_token(self, code, redirect_uri, client_id=None, client_secret=None): """ Exchanges an OAuth authorization code for an access token. Makes a request to the Token endpoint to exchange the authorization code returned from the Authorize endpoint for an access token. @@ -1348,7 +1399,6 @@ def exchange_o_auth_code_for_access_token(self, code, redirect_uri, client_id=No This parameter is optional when Basic Authorization is used to authenticate this request. client_secret: (Optional) The client secret. This value will be required if client authentication is enabled. redirect_uri: The URI to redirect to upon a successful request. - tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "code": code, @@ -1356,14 +1406,13 @@ def exchange_o_auth_code_for_access_token(self, code, redirect_uri, client_id=No "client_secret": client_secret, "grant_type": "authorization_code", "redirect_uri": redirect_uri, - "tenantId": tenantId, } return self.start_anonymous().uri('/oauth2/token') \ .body_handler(FormDataBodyHandler(body)) \ .post() \ .go() - def exchange_o_auth_code_for_access_token_using_pkce(self, code, redirect_uri, code_verifier, client_id=None, client_secret=None, tenant_id=None): + def exchange_o_auth_code_for_access_token_using_pkce(self, code, redirect_uri, code_verifier, client_id=None, client_secret=None): """ Exchanges an OAuth authorization code and code_verifier for an access token. Makes a request to the Token endpoint to exchange the authorization code returned from the Authorize endpoint and a code_verifier for an access token. @@ -1375,7 +1424,6 @@ def exchange_o_auth_code_for_access_token_using_pkce(self, code, redirect_uri, c client_secret: (Optional) The client secret. This value may optionally be provided in the request body instead of the Authorization header. redirect_uri: The URI to redirect to upon a successful request. code_verifier: The random string generated previously. Will be compared with the code_challenge sent previously, which allows the OAuth provider to authenticate your app. - tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "code": code, @@ -1384,14 +1432,39 @@ def exchange_o_auth_code_for_access_token_using_pkce(self, code, redirect_uri, c "grant_type": "authorization_code", "redirect_uri": redirect_uri, "code_verifier": code_verifier, - "tenantId": tenantId, } return self.start_anonymous().uri('/oauth2/token') \ .body_handler(FormDataBodyHandler(body)) \ .post() \ .go() - def exchange_refresh_token_for_access_token(self, refresh_token, client_id=None, client_secret=None, scope=None, user_code=None, tenant_id=None): + def exchange_o_auth_code_for_access_token_using_pkce_with_request(self, request): + """ + Exchanges an OAuth authorization code and code_verifier for an access token. + Makes a request to the Token endpoint to exchange the authorization code returned from the Authorize endpoint and a code_verifier for an access token. + + Attributes: + request: The PKCE OAuth code access token exchange request. + """ + return self.start_anonymous().uri('/oauth2/token') \ + .body_handler(JSONBodyHandler(request)) \ + .post() \ + .go() + + def exchange_o_auth_code_for_access_token_with_request(self, request): + """ + Exchanges an OAuth authorization code for an access token. + Makes a request to the Token endpoint to exchange the authorization code returned from the Authorize endpoint for an access token. + + Attributes: + request: The OAuth code access token exchange request. + """ + return self.start_anonymous().uri('/oauth2/token') \ + .body_handler(JSONBodyHandler(request)) \ + .post() \ + .go() + + def exchange_refresh_token_for_access_token(self, refresh_token, client_id=None, client_secret=None, scope=None, user_code=None): """ Exchange a Refresh Token for an Access Token. If you will be using the Refresh Token Grant, you will make a request to the Token endpoint to exchange the user’s refresh token for an access token. @@ -1403,7 +1476,6 @@ def exchange_refresh_token_for_access_token(self, refresh_token, client_id=None, client_secret: (Optional) The client secret. This value may optionally be provided in the request body instead of the Authorization header. scope: (Optional) This parameter is optional and if omitted, the same scope requested during the authorization request will be used. If provided the scopes must match those requested during the initial authorization request. user_code: (Optional) The end-user verification code. This code is required if using this endpoint to approve the Device Authorization. - tenant_id: (Optional) The Id of the tenant to use for this request. Required if the request is for a universal application. """ body = { "refresh_token": refresh_token, @@ -1412,13 +1484,25 @@ def exchange_refresh_token_for_access_token(self, refresh_token, client_id=None, "grant_type": "refresh_token", "scope": scope, "user_code": user_code, - "tenantId": tenantId, } return self.start_anonymous().uri('/oauth2/token') \ .body_handler(FormDataBodyHandler(body)) \ .post() \ .go() + def exchange_refresh_token_for_access_token_with_request(self, request): + """ + Exchange a Refresh Token for an Access Token. + If you will be using the Refresh Token Grant, you will make a request to the Token endpoint to exchange the user’s refresh token for an access token. + + Attributes: + request: The refresh token access token exchange request. + """ + return self.start_anonymous().uri('/oauth2/token') \ + .body_handler(JSONBodyHandler(request)) \ + .post() \ + .go() + def exchange_refresh_token_for_jwt(self, request): """ Exchange a refresh token for a new JWT. @@ -1431,7 +1515,7 @@ def exchange_refresh_token_for_jwt(self, request): .post() \ .go() - def exchange_user_credentials_for_access_token(self, username, password, client_id=None, client_secret=None, scope=None, user_code=None, tenant_id=None): + def exchange_user_credentials_for_access_token(self, username, password, client_id=None, client_secret=None, scope=None, user_code=None): """ Exchange User Credentials for a Token. If you will be using the Resource Owner Password Credential Grant, you will make a request to the Token endpoint to exchange the user’s email and password for an access token. @@ -1444,7 +1528,6 @@ def exchange_user_credentials_for_access_token(self, username, password, client_ client_secret: (Optional) The client secret. This value may optionally be provided in the request body instead of the Authorization header. scope: (Optional) This parameter is optional and if omitted, the same scope requested during the authorization request will be used. If provided the scopes must match those requested during the initial authorization request. user_code: (Optional) The end-user verification code. This code is required if using this endpoint to approve the Device Authorization. - tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "username": username, @@ -1454,13 +1537,25 @@ def exchange_user_credentials_for_access_token(self, username, password, client_ "grant_type": "password", "scope": scope, "user_code": user_code, - "tenantId": tenantId, } return self.start_anonymous().uri('/oauth2/token') \ .body_handler(FormDataBodyHandler(body)) \ .post() \ .go() + def exchange_user_credentials_for_access_token_with_request(self, request): + """ + Exchange User Credentials for a Token. + If you will be using the Resource Owner Password Credential Grant, you will make a request to the Token endpoint to exchange the user’s email and password for an access token. + + Attributes: + request: The user credentials access token exchange request. + """ + return self.start_anonymous().uri('/oauth2/token') \ + .body_handler(JSONBodyHandler(request)) \ + .post() \ + .go() + def forgot_password(self, request): """ Begins the forgot password sequence, which kicks off an email to the user so that they can reset their password. @@ -1633,42 +1728,62 @@ def import_web_authn_credential(self, request): .post() \ .go() - def introspect_access_token(self, client_id, token, tenant_id=None): + def introspect_access_token(self, client_id, token): """ Inspect an access token issued as the result of the User based grant such as the Authorization Code Grant, Implicit Grant, the User Credentials Grant or the Refresh Grant. Attributes: client_id: The unique client identifier. The client Id is the Id of the FusionAuth Application for which this token was generated. token: The access token returned by this OAuth provider as the result of a successful client credentials grant. - tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "client_id": client_id, "token": token, - "tenantId": tenantId, } return self.start_anonymous().uri('/oauth2/introspect') \ .body_handler(FormDataBodyHandler(body)) \ .post() \ .go() - def introspect_client_credentials_access_token(self, token, tenant_id=None): + def introspect_access_token_with_request(self, request): + """ + Inspect an access token issued as the result of the User based grant such as the Authorization Code Grant, Implicit Grant, the User Credentials Grant or the Refresh Grant. + + Attributes: + request: The access token introspection request. + """ + return self.start_anonymous().uri('/oauth2/introspect') \ + .body_handler(JSONBodyHandler(request)) \ + .post() \ + .go() + + def introspect_client_credentials_access_token(self, token): """ Inspect an access token issued as the result of the Client Credentials Grant. Attributes: token: The access token returned by this OAuth provider as the result of a successful client credentials grant. - tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "token": token, - "tenantId": tenantId, } return self.start_anonymous().uri('/oauth2/introspect') \ .body_handler(FormDataBodyHandler(body)) \ .post() \ .go() + def introspect_client_credentials_access_token_with_request(self, request): + """ + Inspect an access token issued as the result of the Client Credentials Grant. + + Attributes: + request: The client credentials access token. + """ + return self.start_anonymous().uri('/oauth2/introspect') \ + .body_handler(JSONBodyHandler(request)) \ + .post() \ + .go() + def issue_jwt(self, application_id, encoded_jwt, refresh_token=None): """ Issue a new access token (JWT) for the requested Application after ensuring the provided JWT is valid. A valid @@ -3467,7 +3582,7 @@ def retrieve_user_by_verification_id(self, verification_id): .get() \ .go() - def retrieve_user_code(self, client_id, client_secret, user_code, tenant_id=None): + def retrieve_user_code(self, client_id, client_secret, user_code): """ Retrieve a user_code that is part of an in-progress Device Authorization Grant. @@ -3477,7 +3592,6 @@ def retrieve_user_code(self, client_id, client_secret, user_code, tenant_id=None client_id: The client Id. client_secret: The client Id. user_code: The end-user verification code. - tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "client_id": client_id, @@ -3485,12 +3599,11 @@ def retrieve_user_code(self, client_id, client_secret, user_code, tenant_id=None "user_code": user_code, } return self.start_anonymous().uri('/oauth2/device/user-code') \ - .url_parameter('tenantId', self.convert_true_false(tenant_id)) \ .body_handler(FormDataBodyHandler(body)) \ .get() \ .go() - def retrieve_user_code_using_api_key(self, user_code, tenant_id=None): + def retrieve_user_code_using_api_key(self, user_code): """ Retrieve a user_code that is part of an in-progress Device Authorization Grant. @@ -3500,17 +3613,45 @@ def retrieve_user_code_using_api_key(self, user_code, tenant_id=None): Attributes: user_code: The end-user verification code. - tenant_id: (Optional) The Id of the tenant to use for this request. """ body = { "user_code": user_code, } return self.start_anonymous().uri('/oauth2/device/user-code') \ - .url_parameter('tenantId', self.convert_true_false(tenant_id)) \ .body_handler(FormDataBodyHandler(body)) \ .get() \ .go() + def retrieve_user_code_using_api_key_with_request(self, request): + """ + Retrieve a user_code that is part of an in-progress Device Authorization Grant. + + This API is useful if you want to build your own login workflow to complete a device grant. + + This request will require an API key. + + Attributes: + request: The user code retrieval request including optional tenantId. + """ + return self.start_anonymous().uri('/oauth2/device/user-code') \ + .body_handler(JSONBodyHandler(request)) \ + .post() \ + .go() + + def retrieve_user_code_with_request(self, request): + """ + Retrieve a user_code that is part of an in-progress Device Authorization Grant. + + This API is useful if you want to build your own login workflow to complete a device grant. + + Attributes: + request: The user code retrieval request. + """ + return self.start_anonymous().uri('/oauth2/device/user-code') \ + .body_handler(JSONBodyHandler(request)) \ + .post() \ + .go() + def retrieve_user_comments(self, user_id): """ Retrieves all the comments for the user with the given Id. @@ -4835,7 +4976,7 @@ def upsert_entity_grant(self, entity_id, request): .post() \ .go() - def validate_device(self, user_code, client_id, tenant_id=None): + def validate_device(self, user_code, client_id): """ Validates the end-user provided user_code from the user-interaction of the Device Authorization Grant. If you build your own activation form you should validate the user provided code prior to beginning the Authorization grant. @@ -4843,15 +4984,26 @@ def validate_device(self, user_code, client_id, tenant_id=None): Attributes: user_code: The end-user verification code. client_id: The client Id. - tenant_id: (Optional) The Id of the tenant to use for this request. """ return self.start_anonymous().uri('/oauth2/device/validate') \ .url_parameter('user_code', self.convert_true_false(user_code)) \ .url_parameter('client_id', self.convert_true_false(client_id)) \ - .url_parameter('tenantId', self.convert_true_false(tenant_id)) \ .get() \ .go() + def validate_device_with_request(self, request): + """ + Validates the end-user provided user_code from the user-interaction of the Device Authorization Grant. + If you build your own activation form you should validate the user provided code prior to beginning the Authorization grant. + + Attributes: + request: The device validation request. + """ + return self.start_anonymous().uri('/oauth2/device/validate') \ + .body_handler(JSONBodyHandler(request)) \ + .post() \ + .go() + def validate_jwt(self, encoded_jwt): """ Validates the provided JWT (encoded JWT string) to ensure the token is valid. A valid access token is properly From cfafcc0ffa5508663b83f6d080aecfa93e1537ff Mon Sep 17 00:00:00 2001 From: Lyle Schemmerling Date: Tue, 23 Sep 2025 12:42:53 -0600 Subject: [PATCH 3/3] fix imports and user info --- src/main/python/fusionauth/fusionauth_client.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/python/fusionauth/fusionauth_client.py b/src/main/python/fusionauth/fusionauth_client.py index 4f9056a..b74953f 100644 --- a/src/main/python/fusionauth/fusionauth_client.py +++ b/src/main/python/fusionauth/fusionauth_client.py @@ -3688,17 +3688,15 @@ def retrieve_user_consents(self, user_id): .get() \ .go() - def retrieve_user_info_from_access_token(self, encoded_jwt, tenant_id=None): + def retrieve_user_info_from_access_token(self, encoded_jwt): """ Call the UserInfo endpoint to retrieve User Claims from the access token issued by FusionAuth. Attributes: encoded_jwt: The encoded JWT (access token). - tenant_id: (Optional) The Id of the tenant to use for this request. """ return self.start_anonymous().uri('/oauth2/userinfo') \ .authorization("Bearer " + encoded_jwt) \ - .url_parameter('tenantId', self.convert_true_false(tenant_id)) \ .get() \ .go()