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

[#1471] DigiD Machtigen #1485

Merged
merged 9 commits into from
Apr 20, 2022
1 change: 1 addition & 0 deletions docs/configuration/authentication/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ Authentication plugins
digid
eherkenning_eidas
oidc_digid
oidc_digid_machtigen
oidc_eherkenning
other
58 changes: 58 additions & 0 deletions docs/configuration/authentication/oidc_digid_machtigen.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
.. _configuration_authentication_oidc_digid_machtigen:

================================================
OpenID Connect voor inloggen met DigiD Machtigen
================================================

Open Formulieren ondersteunt `DigiD Machtigen`_ login voor burgers via het OpenID Connect protocol (OIDC).
Burgers kunnen inloggen op Open Formulieren met hun DigiD account en een formulier invullen namens iemand
anders. In deze flow:

* Een gebruiker klikt op de knop *Inloggen met DigiD Machtigen* die op de startpagina van een formulier staat.
* De gebruiker wordt via de omgeving van de OpenID Connect provider (bijv. `Keycloak`_) naar DigiD geleid, waar de gebruiker kan inloggen met *zijn/haar eigen* DigiD inlog gegevens.
* De gebruiker kan dan kiezen namens wie hij/zij het formulier wilt invullen.
* DigiD stuurt de gebruiker terug naar de OIDC omgeving, die op zijn beurt de gebruiker weer terugstuurt naar Open Formulieren
* De gebruiker kan verder met het invullen van het formulier

.. _DigiD Machtigen: https://machtigen.digid.nl/
.. _Keycloak: https://www.keycloak.org/

.. _configuration_oidc_digid_machtigen_appgroup:

Configureren van OIDC voor DigiD Machtigen
==========================================

De stappen hier zijn dezelfde als voor :ref:`configuration_oidc_digid_appgroup`, maar de **Redirect URI**
is ``https://open-formulieren.gemeente.nl/digid-oidc-machtigen/callback/`` (met het juiste domein in plaats van
``open-formulieren.gemeente.nl``).

Aan het eind van dit proces moet u de volgende gegevens hebben:

* Server adres, bijvoorbeeld ``login.gemeente.nl``
* Client ID, bijvoorbeeld ``a7d14516-8b20-418f-b34e-25f53c930948``
* Client secret, bijvoorbeeld ``97d663a9-3624-4930-90c7-2b90635bd990``

Configureren van OIDC in Open Formulieren
=========================================

Om OIDC in Open-Formulieren te kunnen configureren zijn de volgende :ref:`gegevens <configuration_oidc_digid_machtigen_appgroup>` nodig:

* Server adres
* Client ID
* Client secret

Navigeer vervolgens in de admin naar **Configuratie** > **OpenID Connect configuration for DigiD Machtigen**.

#. Vink *Enable* aan om OIDC in te schakelen.
#. Vul bij **OpenID Connect client ID** het Client ID in, bijvoorbeeld ``a7d14516-8b20-418f-b34e-25f53c930948``.
#. Vul bij **OpenID Connect secret** het Client secret in, bijvoobeeld ``97d663a9-3624-4930-90c7-2b90635bd990``.
#. Vul bij **OpenID Connect scopes** ``openid``.
#. Vul bij **OpenID sign algorithm** ``RS256`` in.
#. Laat **Sign key** leeg.
#. Laat bij **Vertegenwoordigde claim name** de standaardwaarde staan, tenzij de naam van het BSN veld van de vertegenwoordigde in de OIDC claims anders is dan ``aanvrager.bsn``.
#. Laat bij **Gemachtigde claim name** de standaardwaarde staan, tenzij de naam van het BSN veld van de gemachtigde in de OIDC claims anders is dan ``gemachtigde.bsn``.

De endpoints die ingesteld moeten worden zijn dezelfde als voor DigiD. U kunt de stappen in :ref:`configuration_oidc_digid_appgroup`
volgen om die te configureren.

Nu kan er een formulier aangemaakt worden met het authenticatie backend ``DigiD Machtigen via OpenID Connect`` (zie :ref:`manual_forms_basics`).
59 changes: 57 additions & 2 deletions src/digid_eherkenning_oidc_generics/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,16 @@
from django_better_admin_arrayfield.admin.mixins import DynamicArrayMixin
from solo.admin import SingletonModelAdmin

from .forms import OpenIDConnectEHerkenningConfigForm, OpenIDConnectPublicConfigForm
from .models import OpenIDConnectEHerkenningConfig, OpenIDConnectPublicConfig
from .forms import (
OpenIDConnectDigiDMachtigenConfigForm,
OpenIDConnectEHerkenningConfigForm,
OpenIDConnectPublicConfigForm,
)
from .models import (
OpenIDConnectDigiDMachtigenConfig,
OpenIDConnectEHerkenningConfig,
OpenIDConnectPublicConfig,
)


class OpenIDConnectConfigBaseAdmin(DynamicArrayMixin, SingletonModelAdmin):
Expand Down Expand Up @@ -52,3 +60,50 @@ class OpenIDConnectConfigDigiDAdmin(OpenIDConnectConfigBaseAdmin):
@admin.register(OpenIDConnectEHerkenningConfig)
class OpenIDConnectConfigEHerkenningAdmin(OpenIDConnectConfigBaseAdmin):
form = OpenIDConnectEHerkenningConfigForm


@admin.register(OpenIDConnectDigiDMachtigenConfig)
class OpenIDConnectConfigDigiDMachtigenAdmin(DynamicArrayMixin, SingletonModelAdmin):
form = OpenIDConnectDigiDMachtigenConfigForm

fieldsets = (
(
_("Activation"),
{"fields": ("enabled",)},
),
(
_("Common settings"),
{
"fields": (
"oidc_rp_client_id",
"oidc_rp_client_secret",
"oidc_rp_scopes_list",
"oidc_rp_sign_algo",
"oidc_rp_idp_sign_key",
)
},
),
(
_("Attributes to extract from claim"),
{
"fields": (
"vertegenwoordigde_claim_name",
"gemachtigde_claim_name",
)
},
),
(
_("Endpoints"),
{
"fields": (
"oidc_op_discovery_endpoint",
"oidc_op_jwks_endpoint",
"oidc_op_authorization_endpoint",
"oidc_op_token_endpoint",
"oidc_op_user_endpoint",
"oidc_op_logout_endpoint",
)
},
),
(_("Keycloak specific settings"), {"fields": ("oidc_keycloak_idp_hint",)}),
)
10 changes: 7 additions & 3 deletions src/digid_eherkenning_oidc_generics/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@ class OIDCAuthenticationBackend(_OIDCAuthenticationBackend):
session_key = ""
claim_name_field = "identifier_claim_name"

def extract_claims(self, payload):
self.request.session[self.session_key] = payload[
self.get_settings(self.claim_name_field)
]

def get_or_create_user(self, access_token, id_token, payload):
user_info = self.get_userinfo(access_token, id_token, payload)
claims_verified = self.verify_claims(user_info)
if not claims_verified:
msg = "Claims verification failed"
raise SuspiciousOperation(msg)

self.request.session[self.session_key] = payload[
self.get_settings(self.claim_name_field)
]
self.extract_claims(payload)

user = AnonymousUser()
user.is_active = True
return user
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
DIGID_MACHTIGEN_CUSTOM_OIDC_DB_PREFIX = "digid_machtigen_oidc"
OIDC_AUTHENTICATION_CALLBACK_URL = "digid_machtigen_oidc:callback"
14 changes: 13 additions & 1 deletion src/digid_eherkenning_oidc_generics/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@

from openforms.forms.models import Form

from .models import OpenIDConnectEHerkenningConfig, OpenIDConnectPublicConfig
from .models import (
OpenIDConnectDigiDMachtigenConfig,
OpenIDConnectEHerkenningConfig,
OpenIDConnectPublicConfig,
)

OIDC_MAPPING = deepcopy(_OIDC_MAPPING)

Expand Down Expand Up @@ -58,3 +62,11 @@ class OpenIDConnectEHerkenningConfigForm(OpenIDConnectBaseConfigForm):
class Meta:
model = OpenIDConnectEHerkenningConfig
fields = "__all__"


class OpenIDConnectDigiDMachtigenConfigForm(OpenIDConnectBaseConfigForm):
plugin_identifier = "digid_machtigen_oidc"

class Meta:
model = OpenIDConnectDigiDMachtigenConfig
fields = "__all__"
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# Generated by Django 3.2.12 on 2022-03-31 12:45

import digid_eherkenning_oidc_generics.models
from django.db import migrations, models
import django_better_admin_arrayfield.models.fields
import mozilla_django_oidc_db.models


class Migration(migrations.Migration):

dependencies = [
("digid_eherkenning_oidc_generics", "0001_initial"),
]

operations = [
migrations.CreateModel(
name="OpenIDConnectDigiDMachtigenConfig",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"enabled",
models.BooleanField(
default=False,
help_text="Indicates whether OpenID Connect for authentication/authorization is enabled",
verbose_name="enable",
),
),
(
"oidc_rp_client_id",
models.CharField(
help_text="OpenID Connect client ID provided by the OIDC Provider",
max_length=1000,
verbose_name="OpenID Connect client ID",
),
),
(
"oidc_rp_client_secret",
models.CharField(
help_text="OpenID Connect secret provided by the OIDC Provider",
max_length=1000,
verbose_name="OpenID Connect secret",
),
),
(
"oidc_rp_sign_algo",
models.CharField(
default="HS256",
help_text="Algorithm the Identity Provider uses to sign ID tokens",
max_length=50,
verbose_name="OpenID sign algorithm",
),
),
(
"oidc_op_discovery_endpoint",
models.URLField(
blank=True,
help_text="URL of your OpenID Connect provider discovery endpoint ending with a slash (`.well-known/...` will be added automatically). If this is provided, the remaining endpoints can be omitted, as they will be derived from this endpoint.",
max_length=1000,
verbose_name="Discovery endpoint",
),
),
(
"oidc_op_jwks_endpoint",
models.URLField(
blank=True,
help_text="URL of your OpenID Connect provider JSON Web Key Set endpoint. Required if `RS256` is used as signing algorithm",
max_length=1000,
verbose_name="JSON Web Key Set endpoint",
),
),
(
"oidc_op_authorization_endpoint",
models.URLField(
help_text="URL of your OpenID Connect provider authorization endpoint",
max_length=1000,
verbose_name="Authorization endpoint",
),
),
(
"oidc_op_token_endpoint",
models.URLField(
help_text="URL of your OpenID Connect provider token endpoint",
max_length=1000,
verbose_name="Token endpoint",
),
),
(
"oidc_op_user_endpoint",
models.URLField(
help_text="URL of your OpenID Connect provider userinfo endpoint",
max_length=1000,
verbose_name="User endpoint",
),
),
(
"oidc_rp_idp_sign_key",
models.CharField(
blank=True,
help_text="Key the Identity Provider uses to sign ID tokens in the case of an RSA sign algorithm. Should be the signing key in PEM or DER format",
max_length=1000,
verbose_name="Sign key",
),
),
(
"oidc_op_logout_endpoint",
models.URLField(
blank=True,
help_text="URL of your OpenID Connect provider logout endpoint",
max_length=1000,
verbose_name="Logout endpoint",
),
),
(
"oidc_keycloak_idp_hint",
models.CharField(
blank=True,
help_text="Specific for Keycloak: parameter that indicates which identity provider should be used (therefore skipping the Keycloak login screen).",
max_length=1000,
verbose_name="Keycloak Identity Provider hint",
),
),
(
"vertegenwoordigde_claim_name",
models.CharField(
default="aanvrager.bsn",
help_text="Name of the claim in which the BSN of the person being represented is stored",
max_length=50,
verbose_name="vertegenwoordigde claim name",
),
),
(
"gemachtigde_claim_name",
models.CharField(
default="gemachtigde.bsn",
help_text="Name of the claim in which the BSN of the person representing someone else is stored",
max_length=50,
verbose_name="gemachtigde claim name",
),
),
(
"oidc_rp_scopes_list",
django_better_admin_arrayfield.models.fields.ArrayField(
base_field=models.CharField(
max_length=50, verbose_name="OpenID Connect scope"
),
blank=True,
default=digid_eherkenning_oidc_generics.models.get_default_scopes_bsn,
help_text="OpenID Connect scopes that are requested during login. These scopes are hardcoded and must be supported by the identity provider",
size=None,
verbose_name="OpenID Connect scopes",
),
),
],
options={
"verbose_name": "OpenID Connect configuration for DigiD Machtigen",
},
bases=(mozilla_django_oidc_db.models.CachingMixin, models.Model),
),
]
Loading