Source code for canaille.oidc.configuration

from pydantic import model_validator

from canaille.app.configuration import BaseModel
from canaille.app.configuration import CommaSeparatedList


[docs] class UserInfoMappingSettings(BaseModel): """Mapping between the user model and the JWT fields. Fields are evaluated with jinja. A ``user`` var is available. """ SUB: str | None = "{{ user.user_name }}" NAME: str | None = ( "{% if user.formatted_name %}{{ user.formatted_name }}{% endif %}" ) PHONE_NUMBER: str | None = ( "{% if user.phone_numbers %}{{ user.phone_numbers[0] }}{% endif %}" ) EMAIL: str | None = ( "{% if user.preferred_email %}{{ user.preferred_email }}{% endif %}" ) GIVEN_NAME: str | None = "{% if user.given_name %}{{ user.given_name }}{% endif %}" FAMILY_NAME: str | None = ( "{% if user.family_name %}{{ user.family_name }}{% endif %}" ) PREFERRED_USERNAME: str | None = ( "{% if user.display_name %}{{ user.display_name }}{% endif %}" ) LOCALE: str | None = ( "{% if user.preferred_language %}{{ user.preferred_language }}{% endif %}" ) ADDRESS: str | None = ( "{% if user.formatted_address %}{{ user.formatted_address }}{% endif %}" ) PICTURE: str | None = ( "{% if user.photo %}{{ user | photo_url(external=True) }}{% endif %}" ) WEBSITE: str | None = "{% if user.profile_url %}{{ user.profile_url }}{% endif %}" UPDATED_AT: str | None = ( "{% if user.last_modified %}{{ user.last_modified.timestamp() }}{% endif %}" )
[docs] class OIDCSettings(BaseModel): """OpenID Connect settings. Belong in the ``CANAILLE_OIDC`` namespace. """ ENABLE_OIDC: bool = True """Whether the Single Sign-On feature and the OpenID Connect API is enabled.""" DYNAMIC_CLIENT_REGISTRATION_OPEN: bool = False """Whether a token is needed for the RFC7591 dynamical client registration. If :py:data:`True`, no token is needed to register a client. If :py:data:`False`, dynamical client registration requires a valid JWT token generated by the ``canaille oidc registration generate-token`` command. """ REQUIRE_NONCE: bool = True """Force the nonce exchange during the authentication flows. This adds security but may not be supported by all clients. """ ACTIVE_JWKS: list[dict[str, str] | str] | None = None """The active JSON Web Keys Set. Those keys are used to sign and verify JWTs. The keys can be in the form of JWK dict or raw keys.""" INACTIVE_JWKS: list[dict[str, str] | str] | None = None """The inactive JSON Web Keys Set. Those keys are only used to verify JWTs. The keys can be in the form of JWK dict or raw keys.""" TRUSTED_DOMAINS: CommaSeparatedList = [".localhost", "127.0.0.1"] """Trusted domains for automatic client trust. Clients with a :attr:`~canaille.oidc.basemodels.Client.client_uri` matching these domains will be automatically considered as trusted and will not display the consent page to users. This is particularly useful for development environments. Supports these patterns: - Exact match: ``example.com`` matches ``example.com`` - Wildcard match: ``.example.com`` matches ``example.com`` and all its subdomains Examples: - ``[".localhost", "127.0.0.1"]`` (default for development) - ``[".dev.company.com", "staging.company.com"]`` - ``[".local", "localhost"]`` """ USERINFO_MAPPING: UserInfoMappingSettings | None = UserInfoMappingSettings() """"Attribute mapping used to build an OIDC UserInfo object. UserInfo is used to fill the id_token and the userinfo endpoint.""" @model_validator(mode="after") def validate_rsa_key_present(self): if self.ACTIVE_JWKS is not None: from canaille.oidc.jose import has_rsa_key if not has_rsa_key(self.ACTIVE_JWKS): raise ValueError( "OIDC specification requires RS256 support. " "Please configure at least one RSA key in ACTIVE_JWKS." ) return self