{"library":"pylti1p3","title":"pylti1p3: LTI 1.3 Advantage Tool","description":"pylti1p3 is a Python library implementing the LTI 1.3 Advantage Tool specification, enabling seamless integration with LTI 1.3 platforms. It handles OAuth 2.0, JWT validation, deep linking, and various LTI services. The current version is 2.0.0, with an active release cadence addressing new LTI features, bug fixes, and Python compatibility.","language":"python","status":"active","last_verified":"Thu Apr 16","install":{"commands":["pip install pylti1p3"],"cli":null},"imports":["from pylti1p3.tool_config import ToolConfig","from pylti1p3.message_launch import MessageLaunch","from pylti1p3.service_connector import ServiceConnector","from pylti1p3.grade import AssignmentsGradesService","from pylti1p3.grade import NamesRolesProvisioningService"],"auth":{"required":false,"env_vars":[]},"quickstart":{"code":"import os\nimport json\nfrom pylti1p3.tool_config import ToolConfig\nfrom pylti1p3.message_launch import MessageLaunch\n\n# Mock a minimal request object for demonstration\n# In a real application, this would come from your web framework (e.g., Flask, Django)\nclass MockRequest:\n    def __init__(self, method='POST', headers=None, form=None, data=None):\n        self.method = method\n        self.headers = headers or {}\n        self.form = form or {}\n        self.data = data # raw body for content_type application/json, etc.\n\n    def get_json(self):\n        return json.loads(self.data) if self.data and 'application/json' in self.headers.get('Content-Type', '') else None\n\n    def get_param(self, key, default=None):\n        return self.form.get(key, default)\n\n\n# 1. Configure the LTI Tool\n# These values would typically come from environment variables, database, or a configuration file\niss = os.environ.get('LTI_ISS', 'https://example.com')\nclient_id = os.environ.get('LTI_CLIENT_ID', 'your-client-id')\njwks_url = os.environ.get('LTI_JWKS_URL', 'https://example.com/platform/.well-known/jwks.json')\nauth_login_url = os.environ.get('LTI_AUTH_LOGIN_URL', 'https://example.com/platform/login_initiations')\nauth_token_url = os.environ.get('LTI_AUTH_TOKEN_URL', 'https://example.com/platform/access_token')\ndeployment_id = os.environ.get('LTI_DEPLOYMENT_ID', '1') # Example deployment ID\n\n# Your tool's private key (for signing messages sent *to* the platform)\n# In a real app, this would be loaded from a file or secure store\nprivate_key = os.environ.get('LTI_TOOL_PRIVATE_KEY', '-----BEGIN RSA PRIVATE KEY-----\\n...\\n-----END RSA PRIVATE KEY-----')\n\n# Your tool's public key (to be registered with the platform)\n# In a real app, this would be loaded from a file or secure store\npublic_key = os.environ.get('LTI_TOOL_PUBLIC_KEY', '-----BEGIN PUBLIC KEY-----\\n...\\n-----END PUBLIC KEY-----')\n\n\ntool_config = ToolConfig({\n    'key_set_url': jwks_url,\n    'iss': iss,\n    'client_id': client_id,\n    'deployment_ids': [deployment_id],\n    'auth_login_url': auth_login_url,\n    'auth_token_url': auth_token_url,\n    'private_key': private_key,\n    'public_key': public_key\n})\n\n\n# 2. Simulate an LTI 1.3 Message Launch request\n# This is a highly simplified mock. A real LTI launch involves a POST request\n# with a 'id_token' parameter (a signed JWT) and possibly 'state' for CSRF protection.\n# For a basic example, we'll just demonstrate setting up the MessageLaunch object.\n\n# In a real scenario, the 'id_token' would be extracted from the incoming request's form data.\n# Here, we'll use a placeholder JWT string that would normally be generated by the Platform.\n# Note: This placeholder JWT will NOT be valid for actual verification.\n# A valid JWT needs to be signed by the platform's private key and match the JWKS.\nexample_id_token = os.environ.get('LTI_EXAMPLE_ID_TOKEN', 'eyJhbGciOiJSUzI1NiIsImtpZCI6IkExMjMifQ.eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIiwic3ViIjoiMTIzNDUifQ.S0meS1gnedT0ken')\n\nmock_form_data = {\n    'id_token': example_id_token,\n    'state': 'a_random_state_string'\n}\n\nmock_request = MockRequest(method='POST', form=mock_form_data)\n\n\n# 3. Process the LTI Message Launch\n# The MessageLaunch object requires a 'request' (your web framework's request object)\n# and the 'ToolConfig' you just created.\n\ntry:\n    message_launch = MessageLaunch(mock_request, tool_config)\n    is_valid_launch = message_launch.validate()\n\n    if is_valid_launch:\n        print(\"LTI 1.3 Message Launch validated successfully!\")\n        launch_data = message_launch.get_launch_data()\n        print(\"Launch Data (Payload):\\n\", json.dumps(launch_data, indent=2))\n\n        # Example: Accessing specific claims\n        print(\"User ID:\", message_launch.get_sub()) # 'sub' is the user ID\n        print(\"Context Title:\", message_launch.get_context_title()) # Course title\n\n        # Access LTI 1.3 services (e.g., Assignment and Grades Service)\n        # This requires the launch to contain the appropriate service context and claims\n        # if message_launch.has_ags():\n        #     print(\"Assignments and Grades Service available!\")\n        #     ags = message_launch.get_ags()\n        #     # Example: Get line items\n        #     # line_items = ags.get_lineitems()\n        #     # print(\"Line items:\", line_items)\n\n    else:\n        print(\"LTI 1.3 Message Launch validation failed.\")\n\nexcept Exception as e:\n    print(f\"An error occurred during LTI launch processing: {e}\")\n    # In a real app, handle authentication/authorization errors gracefully\n\n","lang":"python","description":"This quickstart demonstrates how to configure the `ToolConfig` and process an incoming LTI 1.3 Message Launch. It uses mock objects for the web request to make it runnable without a full web framework. In a real application, you would integrate `ToolConfig` and `MessageLaunch` with your chosen framework (e.g., Flask, Django, FastAPI) and handle the actual HTTP request object. The `id_token` in the mock request must be a valid JWT signed by the LTI platform for successful validation.","tag":null,"tag_description":null,"last_tested":null,"results":[]},"compatibility":null}