本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
获取 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-
有关 API 详细信息,请参阅《Amazon SDK for Python (Boto3) API Reference》中的以下主题。
-
有关 S Amazon DK 开发者指南和代码示例的完整列表,请参阅将此服务与 Amazon SDK。本主题还包括有关入门的信息以及有关先前的 SDK 版本的详细信息。
使用 Lambda 函数自动迁移已知用户
用户池入门