{"record":{"id":"0a0809f1c0e3ae14","repo":"PrefectHQ/fastmcp","slug":"unauthorized-client","errorCode":"unauthorized_client","errorMessage":"unauthorized_client: Client '{client.client_id}' not registered.","messagePattern":"unauthorized_client: Client '(.+?)' not registered\\.","errorType":"error_code","errorClass":"AuthorizeError","httpStatus":400,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/providers/in_memory.py","lineNumber":105,"sourceCode":"\n        if client_info.client_id is None:\n            raise ValueError(\"client_id is required for client registration\")\n        if client_info.client_id in self.clients:\n            # As per RFC 7591, if client_id is already known, it's an update.\n            # For this simple provider, we'll treat it as re-registration.\n            # A real provider might handle updates or raise errors for conflicts.\n            pass\n        self.clients[client_info.client_id] = client_info\n\n    async def authorize(\n        self, client: OAuthClientInformationFull, params: AuthorizationParams\n    ) -> str:\n        \"\"\"\n        Simulates user authorization and generates an authorization code.\n        Returns a redirect URI with the code and state.\n        \"\"\"\n        if client.client_id not in self.clients:\n            raise AuthorizeError(\n                error=\"unauthorized_client\",\n                error_description=f\"Client '{client.client_id}' not registered.\",\n            )\n\n        # Validate redirect_uri (already validated by AuthorizationHandler, but good practice)\n        try:\n            # OAuthClientInformationFull should have a method like validate_redirect_uri\n            # For this test provider, we assume it's valid if it matches one in client_info\n            # The AuthorizationHandler already does robust validation using client.validate_redirect_uri\n            if client.redirect_uris and params.redirect_uri not in client.redirect_uris:\n                # This check might be too simplistic if redirect_uris can be patterns\n                # or if params.redirect_uri is None and client has a default.\n                # However, the AuthorizationHandler handles the primary validation.\n                pass  # Let's assume AuthorizationHandler did its job.\n        except Exception as e:  # Replace with specific validation error if client.validate_redirect_uri existed\n            raise AuthorizeError(\n                error=\"invalid_request\", error_description=\"Invalid redirect_uri.\"\n            ) from e","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/providers/in_memory.py#L87-L123","documentation":"During the simulated authorization step, the in-memory provider looks up the client_id in its registered clients dict. If the client was never registered (or was registered with a different id), it raises an OAuth AuthorizeError with error code 'unauthorized_client' per RFC 6749 §4.1.2.1.","triggerScenarios":"Calling authorize(client, params) with a Client object whose client_id was never passed to register_client, or after provider state was reset (new InMemoryOAuthProvider instance) while the client kept its old id.","commonSituations":"Server restarted between registration and authorization (in-memory state lost); client_id typo or case mismatch; registering with one provider instance but authorizing against another.","solutions":["Call register_client with the client's metadata before attempting authorize, and reuse the exact client_id returned/stored","If the provider was recreated, re-register the client (in-memory state is not persistent)","Log client.client_id and provider.clients keys to find id mismatches"],"exampleFix":"// before\nclient = Client(client_id=\"app-1\", ...)\nredirect = provider.authorize(client, params)  # 'app-1' never registered\n// after\nprovider.register_client(OAuthClientMetadata(client_id=\"app-1\", redirect_uris=[...]))\nredirect = provider.authorize(client, params)","handlingStrategy":"validation","validationCode":"if client.client_id not in provider.clients:\n    provider.register_client(client_metadata)  # re-register before authorize\nredirect = provider.authorize(client, params)","typeGuard":"def is_registered(provider, client) -> bool:\n    return client.client_id is not None and client.client_id in provider.clients","tryCatchPattern":"try:\n    redirect = provider.authorize(client, params)\nexcept AuthorizeError as e:\n    if e.error == \"unauthorized_client\":\n        # re-register and retry once\n        provider.register_client(client_metadata)\n        redirect = provider.authorize(client, params)","preventionTips":["Register clients in the same process/session that performs authorization","Reuse the exact client_id returned by registration; avoid stringly-typed copies","Remember in-memory provider state is lost on restart — persist or re-register"],"tags":["oauth","authorization","unauthorized-client"],"backgroundTag":"unauthorized-client","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}