mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-03-11 09:04:54 +00:00
- Add a component to easily manage registration of client applications. Tests: - Package build is successful has dependency on python3-django-auto-toolkit - python3-django-oauth-toolkit can be installed on stable, testing and unstable containers - /.well-known/openid-configuration and /.well-known/jwks.json are servered properly. - /o/ URLs don't require login to access - When logging in list of claims includes 'sub', email, freedombox_groups. - Logging in using IP address works. Also works with a port. - Logging in using 127.0.0.1 address works. Also works with a port. - Logging in using localhost works. Also works with a port. - Logging in with IPv6 address works. Also works with a port. - Logging in with IPv6 [::1] address works. Also works with a port. - Logging in with IPv6 link-local address with zone ID is not possible (as browsers don't support them). - When authorization page is enabled, scopes show description as expected. - When domain name is added/removed, all OIDC components are updated with expected domains Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
80 lines
3.0 KiB
Python
80 lines
3.0 KiB
Python
"""FreedomBox app for implementing a OpenID Connect Provider.
|
|
|
|
With this app, FreedomBox implements a full OpenID Connect Provider along with
|
|
OpenID Discovery. Only authorization code grant type is currently supported but
|
|
can be easily extended to support other grant types if necessary. See this code
|
|
comment for quick understand of the flow works:
|
|
https://github.com/oauthlib/oauthlib/blob/master/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py#L64
|
|
|
|
In the list of OpenID Connect claims provided to Relying Party, we override
|
|
django-oauth-toolkit's default 'sub' claim from being the user ID to username.
|
|
Additionally, we also provide basic profile information and 'freedombox_groups'
|
|
with list of all group names that the user account is part of.
|
|
|
|
"Clients" or "Applications": must be registered before they can be used with
|
|
the Identity Provider. Dynamic Client Registration is not supported yet. The
|
|
OpenIDConnect FreedomBox component will help with this registration.
|
|
|
|
Redirect URLs: After authorization is provided by the user, the URL of the
|
|
application to redirect to must be verified by the Identity Provider. This is
|
|
usually provided at the time of client registration. However, in FreedomBox,
|
|
since list of allowed domains keeps changing, the list of allowed redirect URLs
|
|
must keep changing as well. The OpenIDConnect component will also help with
|
|
that. Finally, there is overridden verification logic that ensures that
|
|
accessing protected applications using IP addresses or localhost domain names
|
|
in URLs is allowed.
|
|
|
|
The implement is done by implementing all the URLs in /freedombox/o. Most of
|
|
the implementation for these views and models is provided by
|
|
django-oauth-toolkit.
|
|
"""
|
|
|
|
import logging
|
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from plinth import app as app_module
|
|
|
|
from . import components
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class OIDCApp(app_module.App):
|
|
"""FreedomBox app for OpenID Connect Provider."""
|
|
|
|
app_id = 'oidc'
|
|
|
|
_version = 1
|
|
|
|
def __init__(self) -> None:
|
|
"""Create components for the app."""
|
|
super().__init__()
|
|
|
|
info = app_module.Info(app_id=self.app_id, version=self._version,
|
|
is_essential=True,
|
|
name=_('OpenID Connect Provider'))
|
|
self.add(info)
|
|
|
|
|
|
def on_domain_added(sender: str, domain_type: str, name: str = '',
|
|
description: str = '',
|
|
services: str | list[str] | None = None, **kwargs):
|
|
"""Add domain to global list."""
|
|
if not domain_type or not name:
|
|
return
|
|
|
|
logger.info('Updating all OpenID Connect components for domain add - %s',
|
|
name)
|
|
components.OpenIDConnect.update_domains_for_all()
|
|
|
|
|
|
def on_domain_removed(sender: str, domain_type: str, name: str = '', **kwargs):
|
|
"""Remove domain from global list."""
|
|
if not domain_type or not name:
|
|
return
|
|
|
|
logger.info(
|
|
'Updating all OpenID Connect components for domain remove - %s', name)
|
|
components.OpenIDConnect.update_domains_for_all()
|