Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

11 adapt to reference implementation #12

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ jobs:
tox run
env:
CORBADO_BACKEND_API: ${{ vars.CORBADO_BACKEND_API }}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please the following order: project id, api secret, frontend api, backend api

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

CORBADO_FRONTEND_API: ${{ vars.CORBADO_FRONTEND_API }}
CORBADO_API_SECRET: ${{ secrets.CORBADO_API_SECRET }}
CORBADO_PROJECT_ID: ${{ secrets.CORBADO_PROJECT_ID }}



build:
Expand Down
10 changes: 2 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,13 @@ user_service: UserService = sdk.users

### Error handling

The Corbado Python SDK raises exceptions for all errors except those that occur in the session service during token validation (See example below on how to catch those errors). The following exceptions are thrown:
The Corbado Python SDK raises exceptions for all errors. The following exceptions are thrown:

- `ValidationError` for failed validations (client side)
- `ServerException` for server errors (server side)
- `TokenValidationException` for errors in session service (client side)
- `StandardException` for everything else (client side)

'SessionService' returns 'SessionValidationResult' as result of token validation. You can check whether any errors occurred and handle them if needed:

```Python
result: SessionValidationResult = self.session_service.get_and_validate_short_session_value(short_session=token)
if result.error is not None:
print(result.error)
raise result.error
```

If the Backend API returns a HTTP status code other than 200, the Corbado Python SDK throws a `ServerException`. The `ServerException`class provides convenient methods to access all important data:
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.1.3
2.0.0
5 changes: 3 additions & 2 deletions src/corbado_python_sdk/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from .config import Config as Config
from .corbado_sdk import CorbadoSDK as CorbadoSDK
from .entities import SessionValidationResult as SessionValidationResult
from .entities import UserEntity as UserEntity
from .exceptions import StandardException as StandardException
from .exceptions import TokenValidationException, ValidationErrorType
from .generated import (
Identifier,
IdentifierCreateReq,
Expand All @@ -15,7 +15,8 @@
from .services import IdentifierService, SessionService, UserService

__all__ = [
"SessionValidationResult",
"TokenValidationException",
"ValidationErrorType",
"IdentifierCreateReq",
"Identifier",
"IdentifierStatus",
Expand Down
67 changes: 26 additions & 41 deletions src/corbado_python_sdk/config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from pydantic import BaseModel, ConfigDict, StringConstraints, field_validator
from typing_extensions import Annotated, Optional

from corbado_python_sdk.utils import validators
from corbado_python_sdk.utils import DEFAULT_SESSION_TOKEN_COOKIE_NAME, validators


class Config(BaseModel):
Expand All @@ -16,8 +16,9 @@ class Config(BaseModel):
Attributes:
project_id (str): The unique identifier for the project.
api_secret (str): The secret key used to authenticate API requests.
backend_api (str): The base URL for the backend API. Defaults to "https://backendapi.cloud.corbado.io/v2".
short_session_cookie_name (str): The name of the cookie for short session management. Defaults to "cbo_short_session".
backend_api (str): The base URL for the backend API.
frontend_api (str): The base URL for the frontend API.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

frontend api before backend api

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

session_token_cookie_name (str): The name of the cookie for short session management. Defaults to "cbo_session_token".
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can remove this, we should not need that anymore

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

"""

# Make sure that field assignments are also validated, use "set_assignment_validation(False)"
Expand All @@ -28,36 +29,49 @@ class Config(BaseModel):
project_id: str
api_secret: str

backend_api: str = "https://backendapi.cloud.corbado.io/v2"
short_session_cookie_name: str = "cbo_short_session"
session_token_cookie_name: str = DEFAULT_SESSION_TOKEN_COOKIE_NAME
cname: Optional[Annotated[str, StringConstraints(strip_whitespace=True, min_length=1)]] = None

_issuer: Optional[Annotated[str, StringConstraints(strip_whitespace=True, min_length=1)]] = None
_frontend_api: Optional[str] = None
frontend_api: str
backend_api: str

@field_validator("backend_api")
@field_validator(
"backend_api",
)
@classmethod
def validate_backend_api(cls, backend_api: str) -> str:
"""Validate the backend API URL and ensure it ends with '/v2'.

Args:
backend_api (str): Backend API URL to validate.

Raises:
ValueError: _description_

Returns:
str: Validated backend API URL ending with '/v2'.
"""
if not validators.url_validator(backend_api):
raise ValueError(f'Invalid URL "{backend_api}" provided for backend API.')
backend_api = validators.url_validator(url=backend_api)

# Append '/v2' if not already present
if not backend_api.endswith("/v2"):
return backend_api.rstrip("/") + "/v2"

return backend_api

@field_validator(
"frontend_api",
)
@classmethod
def validate_frontend_api(cls, frontend_api: str) -> str:
"""Validate the frontend API URL.

Args:
frontend_api (str): Frontend API URL to validate.

Returns:
str: Validated frontend API.
"""
return validators.url_validator(url=frontend_api)

@field_validator("project_id")
@classmethod
def project_id_validator(cls, project_id: str) -> str:
Expand Down Expand Up @@ -121,32 +135,3 @@ def issuer(self, issuer: str) -> None:
issuer (str): issuer to set.
"""
self._issuer = issuer

@property
def frontend_api(self) -> str:
"""Get Frontend API.

Returns:
str: Frontend API
"""
if not self._frontend_api:
self._frontend_api = "https://" + self.project_id + ".frontendapi.corbado.io"
return self._frontend_api

@frontend_api.setter
def frontend_api(self, frontend_api: str) -> None:
"""Set Frontend API. Use it to override default value.

Args:
frontend_api (str): Frontend API to set.
"""
self._frontend_api = validators.url_validator(url=frontend_api) # validate url

# ------- Internal --------------#
def set_assignment_validation(self, validate: bool) -> None:
"""Only use it if you know what you do. Sets assignment validation.

Args:
validate (bool): Enable/disable validation
"""
self.model_config["validate_assignment"] = validate
3 changes: 2 additions & 1 deletion src/corbado_python_sdk/corbado_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,10 @@ def sessions(self) -> SessionService:
"""
if not self._sessions:
self._sessions = SessionService(
short_session_cookie_name=self.config.short_session_cookie_name,
session_token_cookie_name=self.config.session_token_cookie_name,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

issuer=self.config.issuer,
jwks_uri=self.config.frontend_api + "/.well-known/jwks",
project_id=self.config.project_id,
)

return self._sessions
Expand Down
5 changes: 3 additions & 2 deletions src/corbado_python_sdk/entities/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .session_validation_result import SessionValidationResult
from corbado_python_sdk.generated import UserStatus

from .user_entity import UserEntity as UserEntity

__all__ = ["UserEntity", "SessionValidationResult"]
__all__ = ["UserEntity", "UserStatus"]
14 changes: 0 additions & 14 deletions src/corbado_python_sdk/entities/session_validation_result.py

This file was deleted.

3 changes: 2 additions & 1 deletion src/corbado_python_sdk/exceptions/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .server_exception import ServerException as ServerException
from .standard_exception import StandardException as StandardException
from .token_validation_exception import TokenValidationException, ValidationErrorType

__all__ = ["ServerException", "StandardException"]
__all__ = ["ServerException", "StandardException", "TokenValidationException", "ValidationErrorType"]
66 changes: 66 additions & 0 deletions src/corbado_python_sdk/exceptions/token_validation_exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from enum import Enum

from typing_extensions import Optional


class ValidationErrorType(Enum):
"""
Enum representing types of validation errors.

This enum categorizes various validation errors that may occur during
token validation processes.

Attributes:
INVALID_TOKEN (str): Indicates that the token is invalid. More information in 'original_exception'.
SIGNING_KEY_ERROR (str): Indicates that the signing key could not be retrieved. More information in 'original_exception'.
EMPTY_SESSION_TOKEN (str): Indicates that the session token is empty.
EMPTY_ISSUER (str): Indicates that the issuer is empty.
ISSUER_MISSMATCH (str): Indicates that the token issuer does not match the expected issuer.
"""

INVALID_TOKEN = "Invalid token" # noqa s105
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldnot we have the following codes like in go:

CodeJWTGeneral Code = iota
CodeJWTIssuerMismatch
CodeJWTInvalidData
CodeJWTInvalidSignature
CodeJWTBefore
CodeJWTExpired
CodeJWTIssuerEmpty

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, but due to different JWT implementation in Java and Python and error handling not all abovementioned error may get caught.

SIGNING_KEY_ERROR = "Could not retrieve signing key"
EMPTY_SESSION_TOKEN = "Session token is empty" # noqa s105
EMPTY_ISSUER = "Issuer is empty"
ISSUER_MISSMATCH = "Token issuer does not match"


class TokenValidationException(Exception):
"""Custom exception class for handling validation errors.

This exception wraps around other exceptions to provide additional context
regarding validation failures.

Attributes:
message (str): The custom error message describing the validation error.
error_type (ValidationErrorType): Enum value indicating the type of validation error.
original_exception (Optional[Exception]): The original exception that caused this error, if any.
"""

def __init__(self, message: str, error_type: ValidationErrorType, original_exception: Optional[Exception] = None):
"""Initialize ValidationError with message, error type, and optional original exception.

Args:
message (str): A description of the error.
error_type (ValidationErrorType): The specific type of validation error.
original_exception (Optional[Exception], optional): The original exception that caused
this error, if available. Defaults to None.
"""
super().__init__(message)
self.message: str = message
self.error_type: ValidationErrorType = error_type
self.original_exception: Exception | None = original_exception

def __str__(self) -> str:
"""Return a string representation of the validation error.

Includes the error type, custom message, and details of the original exception
if it is available.

Returns:
str: Formatted string containing error type, message, and any original exception details.
"""
base_message: str = f"[{self.error_type.value}] {self.message}"
if self.original_exception:
return f"{base_message} | Caused by: {repr(self.original_exception)}"
return base_message
Loading