Skip to content

Commit

Permalink
Merge pull request #1485 from open-formulieren/feature/digid-machtiging
Browse files Browse the repository at this point in the history
[#1471] DigiD Machtigen
  • Loading branch information
sergei-maertens authored Apr 20, 2022
2 parents 13ff63a + 718ef9a commit 768c367
Show file tree
Hide file tree
Showing 22 changed files with 914 additions and 38 deletions.
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

0 comments on commit 768c367

Please sign in to comment.