View a markdown version of this page

获取 M2M 客户代币并使用 Amazon Cognito 探索条款 Amazon SDK - Amazon Cognito
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)。

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

获取 M2M 客户代币并使用 Amazon Cognito 探索条款 Amazon SDK

以下代码示例展示了如何使用 Amazon Cognito 机器对机器 (M2M) 授权和条款发现。

  • 使用 Cognito 资源(用户池、资源服务器、应用程序客户端、条款文档)部署 CloudFormation 堆栈。

  • 使用检索应用程序客户端密钥 DescribeUserPoolClient。

  • 使用获取M2M访问令牌。 GetClientToken

  • 通过以下方式探索条款文档 DescribeTermsByClient。

  • 通过删除 CloudFormation 堆栈进行清理。

Python
适用于 Python 的 SDK(Boto3)
注意

还有更多相关信息 GitHub。在 Amazon 代码示例存储库中查找完整示例,了解如何进行设置和运行。

在命令提示符中运行交互式场景。

class ClientTokenAndTermsScenario: """ Runs an interactive scenario that demonstrates the Amazon Cognito GetClientToken and DescribeTermsByClient APIs. """ def __init__( self, cognito_wrapper: CognitoIdentityProviderWrapper, cfn_client: BaseClient, ): """ :param cognito_wrapper: An instance of the CognitoIdentityProviderWrapper class. :param cfn_client: A Boto3 CloudFormation client. """ self.cognito_wrapper = cognito_wrapper self.cfn_client = cfn_client self.stack_name = None self.user_pool_id = None self.client_id = None self.client_secret = None def run(self) -> None: """Runs the scenario from start to finish.""" print(SEPARATOR) print("Welcome to the Amazon Cognito Client Token and Terms Scenario.") print(SEPARATOR) print("This example creates Cognito resources in a CloudFormation stack, then") print("demonstrates the new GetClientToken and DescribeTermsByClient APIs.") print() try: self.setup() self.get_client_secret() self.demonstrate_get_client_token() self.demonstrate_describe_terms() except Exception: logger.exception("An error occurred during the scenario.") finally: self.cleanup() print(SEPARATOR) print("Amazon Cognito Client Token and Terms scenario completed.") # ------------------------------------------------------------------ # Phase 1: Setup - deploy CloudFormation stack # ------------------------------------------------------------------ def setup(self) -> None: """Deploys the CloudFormation prerequisite stack and retrieves outputs.""" stack_name = q.ask( f"\nEnter a name for the CloudFormation stack [{DEFAULT_STACK_NAME}]: " ) if not stack_name: stack_name = DEFAULT_STACK_NAME # Check whether the stack name already exists. while self._stack_exists(stack_name): print(f"A stack named '{stack_name}' already exists.") stack_name = q.ask("Enter a different stack name: ") self.stack_name = stack_name template_body = self._read_template() print(f"Deploying CloudFormation stack: {self.stack_name}") self.cfn_client.create_stack( StackName=self.stack_name, TemplateBody=template_body, Capabilities=["CAPABILITY_IAM"], ) print(f"CloudFormation stack creation started: {self.stack_name}") print("Waiting for CloudFormation stack creation to complete...") waiter = self.cfn_client.get_waiter("stack_create_complete") try: waiter.wait( StackName=self.stack_name, WaiterConfig={"Delay": 10, "MaxAttempts": 60}, ) except WaiterError: logger.error("Stack creation failed or timed out.") self._print_stack_events() raise print("CloudFormation stack creation complete.") self._retrieve_stack_outputs() print(SEPARATOR) def _stack_exists(self, stack_name: str) -> bool: """Returns True if a stack with the given name exists and is not deleted.""" if not stack_name: return False try: response = self.cfn_client.describe_stacks(StackName=stack_name) stacks = response.get("Stacks", list()) for stack in stacks: status = stack.get("StackStatus", "") if "DELETE_COMPLETE" not in status: return True return False except ClientError as err: if "does not exist" in str(err): return False raise def _read_template(self) -> str: """Reads the CloudFormation template from the local file system.""" template_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), CFN_TEMPLATE_PATH ) if not os.path.exists(template_path): raise FileNotFoundError( f"CloudFormation template not found at {template_path}. " "Ensure the resources/cfn_template.yaml file exists." ) with open(template_path, "r") as f: return f.read() def _retrieve_stack_outputs(self) -> None: """Retrieves and displays outputs from the CloudFormation stack.""" response = self.cfn_client.describe_stacks(StackName=self.stack_name) stacks = response.get("Stacks", list()) if not stacks: raise RuntimeError(f"Stack '{self.stack_name}' not found.") outputs = stacks[0].get("Outputs", list()) for output in outputs: key = output.get("OutputKey", "") value = output.get("OutputValue", "") print(f"Stack output {key}: {value}") if key == "UserPoolId": self.user_pool_id = value elif key == "ClientId": self.client_id = value if not self.user_pool_id or not self.client_id: raise RuntimeError( "Failed to retrieve UserPoolId or ClientId from stack outputs." ) def _print_stack_events(self) -> None: """Prints recent stack events to help diagnose failures.""" try: response = self.cfn_client.describe_stack_events(StackName=self.stack_name) events = response.get("StackEvents", list()) for event in events[:10]: status = event.get("ResourceStatus", "") reason = event.get("ResourceStatusReason", "") resource = event.get("LogicalResourceId", "") if "FAILED" in status: print(f" FAILED: {resource} - {reason}") except ClientError: logger.warning("Could not retrieve stack events.") # ------------------------------------------------------------------ # Phase 2: Retrieve the app client secret # ------------------------------------------------------------------ def get_client_secret(self) -> None: """Calls DescribeUserPoolClient to retrieve the client secret.""" print(SEPARATOR) print("Retrieving the app client secret...") client_info = self.cognito_wrapper.describe_user_pool_client( self.user_pool_id, self.client_id ) self.client_secret = client_info.get("ClientSecret", "") if not self.client_secret: raise RuntimeError( "Client secret not found. Ensure the app client was created " "with GenerateSecret: true." ) print("Client secret retrieved successfully.") print(SEPARATOR) # ------------------------------------------------------------------ # Phase 3: Demonstrate GetClientToken # ------------------------------------------------------------------ def demonstrate_get_client_token(self) -> None: """Demonstrates obtaining M2M access tokens with GetClientToken.""" print(SEPARATOR) print("Obtaining an M2M access token with GetClientToken...") print() # 3a: Get token with all scopes (no scopes param = all configured scopes) print("Step 1: Request a token with all configured scopes.") auth_result = self.cognito_wrapper.get_client_token( client_id=self.client_id, client_secret=self.client_secret, ) self._display_token_result(auth_result, "all scopes") q.ask("\nPress Enter to continue...") # 3b: Get token with specific scopes print() print("Step 2: Request a token with a specific scope (my-m2m-api/read).") scoped_result = self.cognito_wrapper.get_client_token( client_id=self.client_id, client_secret=self.client_secret, scopes=["my-m2m-api/read"], ) self._display_token_result(scoped_result, "my-m2m-api/read") print() print("Token was successfully scoped to the requested permissions.") print(SEPARATOR) @staticmethod def _display_token_result(auth_result: dict, scope_description: str) -> None: """Displays token metadata.""" token_type = auth_result.get("TokenType", "Unknown") expires_in = auth_result.get("ExpiresIn", 0) access_token = auth_result.get("AccessToken", "") preview = access_token[:20] + "..." if len(access_token) > 20 else access_token print(f" Scope: {scope_description}") print(f" Token type: {token_type}") print(f" Expires in: {expires_in} seconds") print(f" Token preview: {preview}") # ------------------------------------------------------------------ # Phase 4: Demonstrate DescribeTermsByClient # ------------------------------------------------------------------ def demonstrate_describe_terms(self) -> None: """Demonstrates discovering Terms documents with DescribeTermsByClient.""" print(SEPARATOR) print("Discovering Terms documents with DescribeTermsByClient...") print() # 4a: Look up terms-of-use print("Looking up terms-of-use for the app client...") terms_of_use = self.cognito_wrapper.describe_terms_by_client( user_pool_id=self.user_pool_id, client_id=self.client_id, terms_name="terms-of-use", ) self._display_terms(terms_of_use) q.ask("\nPress Enter to continue...") print() # 4b: Look up privacy-policy print("Looking up privacy-policy for the app client...") privacy_policy = self.cognito_wrapper.describe_terms_by_client( user_pool_id=self.user_pool_id, client_id=self.client_id, terms_name="privacy-policy", ) self._display_terms(privacy_policy) print() print( "DescribeTermsByClient lets you discover which Terms documents are " "associated with a client without knowing the Terms resource ID up front." ) print(SEPARATOR) @staticmethod def _display_terms(terms: dict) -> None: """Displays a Terms document's details.""" print(f" Terms ID: {terms.get('TermsId', 'N/A')}") print(f" Terms name: {terms.get('TermsName', 'N/A')}") print(f" Client ID: {terms.get('ClientId', 'N/A')}") print(f" User pool ID: {terms.get('UserPoolId', 'N/A')}") print(f" Enforcement: {terms.get('Enforcement', 'N/A')}") print(f" Terms source: {terms.get('TermsSource', 'N/A')}") links = terms.get("Links", dict()) if links: print(" Links:") for lang_tag, url in links.items(): print(f" {lang_tag}: {url}") # Cognito returns CreationDate / LastModifiedDate as datetime objects # (boto3 deserializes the timestamps), so display them directly. creation_date = terms.get("CreationDate", None) if creation_date is not None: print(f" Created: {creation_date}") modified_date = terms.get("LastModifiedDate", None) if modified_date is not None: print(f" Last modified: {modified_date}") # ------------------------------------------------------------------ # Phase 5: Cleanup - delete CloudFormation stack # ------------------------------------------------------------------ def cleanup(self) -> None: """Prompts the user and deletes the CloudFormation stack.""" if self.stack_name is None: return print(SEPARATOR) answer = q.ask( "Do you want to delete the CloudFormation stack and all resources? (y/n) " ) if answer.lower() != "y": print( f"Resources will remain. You can delete the stack later " f"through the AWS Console or CLI." ) print(f"Stack name: {self.stack_name}") return print( f"CloudFormation stack '{self.stack_name}' is being deleted. " f"This may take a few minutes." ) try: self.cfn_client.delete_stack(StackName=self.stack_name) waiter = self.cfn_client.get_waiter("stack_delete_complete") print("Waiting for CloudFormation stack deletion to complete...") waiter.wait( StackName=self.stack_name, WaiterConfig={"Delay": 10, "MaxAttempts": 60}, ) print(f"CloudFormation stack '{self.stack_name}' has been deleted.") print("All Cognito resources have been removed.") except (ClientError, WaiterError): logger.warning( "Stack deletion encountered an error. Attempting force delete." ) try: self.cfn_client.delete_stack( StackName=self.stack_name, DeletionMode="FORCE_DELETE_STACK", ) print("Force delete initiated. Please verify in the console.") except ClientError: logger.error( "Force delete also failed. Clean up manually via the console." )

创建一个包含 Amazon Cognito 身份提供商操作的类。

class CognitoIdentityProviderWrapper: """Encapsulates Amazon Cognito Identity Provider actions.""" def __init__(self, cognito_idp_client: BaseClient): """ Initializes the CognitoIdentityProviderWrapper with an Amazon Cognito Identity Provider client. :param cognito_idp_client: A Boto3 Amazon Cognito Identity Provider client. """ self.cognito_idp_client = cognito_idp_client

有关 S Amazon DK 开发者指南和代码示例的完整列表,请参阅将此服务与 Amazon SDK。本主题还包括有关入门的信息以及有关先前的 SDK 版本的详细信息。