Skip to main content
Glama
exceptions.py11.2 kB
"""Authentication exceptions with actionable error messages. Provides user-friendly error messages that guide users on how to resolve auth issues. """ from __future__ import annotations class AuthError(Exception): """Base class for authentication errors. Attributes: message: User-friendly error message. action: Suggested action to resolve the error. code: Error code for programmatic handling. """ def __init__( self, message: str, action: str | None = None, code: str = "AUTH_ERROR", ) -> None: """Initialize auth error. Args: message: User-friendly error message. action: Suggested action to resolve the error. code: Error code for programmatic handling. """ self.message = message self.action = action self.code = code super().__init__(message) def __str__(self) -> str: """Return formatted error message with action.""" if self.action: return f"{self.message}\n\nAction: {self.action}" return self.message class ConfigurationError(AuthError): """Error for missing or invalid configuration.""" def __init__(self, message: str, action: str | None = None) -> None: """Initialize configuration error. Args: message: Description of what configuration is missing/invalid. action: How to fix the configuration. """ super().__init__( message=message, action=action or "Check your .env file and ensure all required variables are set.", code="CONFIG_ERROR", ) class TokenExpiredError(AuthError): """Error when token has expired and cannot be refreshed.""" def __init__(self) -> None: """Initialize token expired error.""" super().__init__( message="Your session has expired and could not be renewed.", action="Please log in again using the browser-based authentication.", code="TOKEN_EXPIRED", ) class SilentAuthFailedError(AuthError): """Error when silent authentication fails.""" def __init__(self, reason: str | None = None) -> None: """Initialize silent auth failed error. Args: reason: Reason for the failure. """ msg = "Silent authentication failed." if reason: msg = f"{msg} Reason: {reason}" super().__init__( message=msg, action="Browser-based authentication will be attempted.", code="SILENT_AUTH_FAILED", ) class InteractiveAuthFailedError(AuthError): """Error when interactive browser authentication fails.""" def __init__(self, error: str | None = None, error_description: str | None = None) -> None: """Initialize interactive auth failed error. Args: error: OAuth error code. error_description: OAuth error description. """ msg = "Browser authentication failed." if error: msg = f"{msg} Error: {error}" if error_description: msg = f"{msg} ({error_description})" super().__init__( message=msg, action="Please ensure you have access to the Azure tenant and try again. " "If the problem persists, contact your administrator.", code="INTERACTIVE_AUTH_FAILED", ) class AuthNotConfiguredError(AuthError): """Error when authentication is not properly configured.""" def __init__(self) -> None: """Initialize auth not configured error.""" super().__init__( message="Authentication is not configured.", action="Ensure the server is properly initialized with Azure credentials.", code="AUTH_NOT_CONFIGURED", ) class NotAuthenticatedError(AuthError): """Error when user is not authenticated but authentication is required.""" def __init__(self) -> None: """Initialize not authenticated error.""" super().__init__( message="Authentication required.", action="Please complete the SSO login flow to continue.", code="NOT_AUTHENTICATED", ) # ============================================================================= # Cloud Mode Exceptions (OAuth 2.1 Resource Server) # ============================================================================= class CloudAuthError(AuthError): """Base class for cloud mode authentication errors. Cloud mode errors include HTTP status codes and WWW-Authenticate header content per RFC 6750. Attributes: http_status: HTTP status code for the error response. www_authenticate: Content for WWW-Authenticate header. """ def __init__( self, message: str, action: str | None = None, code: str = "CLOUD_AUTH_ERROR", http_status: int = 401, error: str = "invalid_token", error_description: str | None = None, ) -> None: """Initialize cloud auth error. Args: message: User-friendly error message. action: Suggested action to resolve the error. code: Error code for programmatic handling. http_status: HTTP status code (401 or 403). error: OAuth error code for WWW-Authenticate header. error_description: OAuth error description. """ super().__init__(message=message, action=action, code=code) self.http_status = http_status self.error = error self.error_description = error_description or message class MissingAuthorizationError(CloudAuthError): """Error when Authorization header is missing.""" def __init__(self) -> None: """Initialize missing authorization error.""" super().__init__( message="Authorization header is required.", action="Include 'Authorization: Bearer <token>' header in your request.", code="MISSING_AUTHORIZATION", http_status=401, error="invalid_request", error_description="Missing Authorization header", ) class InvalidTokenError(CloudAuthError): """Error when the token is malformed or has an invalid signature.""" def __init__(self, reason: str | None = None) -> None: """Initialize invalid token error. Args: reason: Specific reason the token is invalid. """ msg = "The access token is invalid." if reason: msg = f"{msg} {reason}" super().__init__( message=msg, action="Obtain a new access token from the authorization server.", code="INVALID_TOKEN", http_status=401, error="invalid_token", error_description=reason or "Token validation failed", ) class TokenSignatureError(CloudAuthError): """Error when the token signature verification fails.""" def __init__(self) -> None: """Initialize token signature error.""" super().__init__( message="Token signature verification failed.", action="Ensure the token was issued by a trusted authorization server.", code="INVALID_SIGNATURE", http_status=401, error="invalid_token", error_description="Signature verification failed", ) class CloudTokenExpiredError(CloudAuthError): """Error when the Bearer token has expired.""" def __init__(self) -> None: """Initialize cloud token expired error.""" super().__init__( message="The access token has expired.", action="Obtain a new access token from the authorization server.", code="TOKEN_EXPIRED", http_status=401, error="invalid_token", error_description="Token has expired", ) class InvalidAudienceError(CloudAuthError): """Error when the token audience doesn't match this server.""" def __init__(self, expected: str, actual: str | list[str]) -> None: """Initialize invalid audience error. Args: expected: Expected audience (this server's resource identifier). actual: Actual audience in the token. """ actual_str = actual if isinstance(actual, str) else ", ".join(actual) super().__init__( message=f"Token audience mismatch. Expected: {expected}, got: {actual_str}", action="Request a token with the correct audience (resource parameter).", code="INVALID_AUDIENCE", http_status=401, error="invalid_token", error_description="Token audience does not match this resource", ) class InvalidIssuerError(CloudAuthError): """Error when the token issuer is not trusted.""" def __init__(self, issuer: str) -> None: """Initialize invalid issuer error. Args: issuer: The issuer in the token. """ super().__init__( message=f"Token issuer '{issuer}' is not trusted.", action="Obtain a token from a trusted authorization server.", code="INVALID_ISSUER", http_status=401, error="invalid_token", error_description="Token issuer is not trusted", ) class InsufficientScopeError(CloudAuthError): """Error when the token lacks required scopes.""" def __init__(self, required_scopes: list[str], actual_scopes: list[str]) -> None: """Initialize insufficient scope error. Args: required_scopes: Scopes required for the operation. actual_scopes: Scopes present in the token. """ required_str = " ".join(required_scopes) actual_str = " ".join(actual_scopes) if actual_scopes else "(none)" super().__init__( message=f"Insufficient scope. Required: {required_str}, got: {actual_str}", action="Request a token with the required scopes.", code="INSUFFICIENT_SCOPE", http_status=403, error="insufficient_scope", error_description=f"Required scope: {required_str}", ) self.required_scopes = required_scopes self.actual_scopes = actual_scopes class JWKSFetchError(CloudAuthError): """Error when JWKS cannot be fetched from the authorization server.""" def __init__(self, issuer: str, reason: str | None = None) -> None: """Initialize JWKS fetch error. Args: issuer: The issuer whose JWKS couldn't be fetched. reason: Specific reason for the failure. """ msg = f"Failed to fetch signing keys from issuer: {issuer}" if reason: msg = f"{msg}. {reason}" super().__init__( message=msg, action="Ensure the authorization server is reachable and properly configured.", code="JWKS_FETCH_ERROR", http_status=401, error="invalid_token", error_description="Unable to verify token signature", )

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/DauQuangThanh/sso-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server