diff --git a/api-gateway/2-secure-applications/README.md b/api-gateway/2-secure-applications/README.md
deleted file mode 100644
index 4455911..0000000
--- a/api-gateway/2-secure-applications/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# Secure applications
-
-There are many ways and many flows to secure applications.
-
-This tutorial is not exhaustive. It covers some common use cases to secure applications:
-
-1. [Machine-to-Machine (M2M) with OAuth2 Client Credentials](./m2m.md)
-2. [Web Application with OpenID Connect (OIDC)](./oidc.md)
diff --git a/api-gateway/2-secure-applications/images/oauth2-decode.png b/api-gateway/2-secure-applications/images/oauth2-decode.png
deleted file mode 100644
index 6d2cf83..0000000
Binary files a/api-gateway/2-secure-applications/images/oauth2-decode.png and /dev/null differ
diff --git a/api-gateway/2-secure-applications/m2m.md b/api-gateway/2-secure-applications/m2m.md
deleted file mode 100644
index 622db93..0000000
--- a/api-gateway/2-secure-applications/m2m.md
+++ /dev/null
@@ -1,289 +0,0 @@
-## Machine to Machine (M2M) authentication with OAuth2 Client Credentials
-
-The OAuth2 Client Credentials (defined in [OAuth 2.0 RFC 6749, section 4.4](https://tools.ietf.org/html/rfc6749#section-4.4)) protocol provides a way to secure delegated access between applications with a JWT access token.
-
-With Traefik Hub, the authentication can be done between applications on the app level or on the gateway level.
-
-:information_source: To follow this tutorial, you'll need to install Traefik Hub following [getting started](../1-getting-started/README.md) instructions.
-
-Now that Traefik Hub is deployed, let's see how it works on the app level, using OAuth 2 Client Credentials flow:
-
-```mermaid
----
-title: OAuth2 Client Credentials flow
----
-sequenceDiagram
- actor app as M2M Application
- participant apigw as Traefik Hub
API Gateway
- participant idp as Authorization
Server
- participant api as Application
- autonumber
-
- app->>apigw: Authenticate with Application Credentials
- apigw->>idp: Validate Application Credentials. Request access token
- idp->>apigw: Issue access token
- apigw->>api: Send request with access token in HTTP Header
-```
-
-In this tutorial, we will use [Ory Hydra](https://www.ory.sh/hydra/), an OAuth 2 and OpenID Connect server. We will also use a simple [login/consent app](/~https://github.com/jlevesy/hail-hydra) that always says yes.
-
-We can deploy it:
-
-```shell
-kubectl apply -f src/manifests/apps-namespace.yaml
-kubectl apply -f src/manifests/hydra.yaml
-kubectl wait -n hydra --for=condition=ready pod --selector=app=hydra --timeout=90s
-kubectl wait -n hydra --for=condition=ready pod --selector=app=consent --timeout=90s
-kubectl wait -n hydra --for=condition=complete job/create-hydra-clients --timeout=90s
-```
-
-TraefikLabs has open-sourced a simple _whoami_ application displaying technical information about the request.
-
-First, let's deploy and expose it:
-
-```shell
-kubectl apply -f src/manifests/whoami-app.yaml
-kubectl apply -f api-gateway/2-secure-applications/manifests/whoami-app-ingressroute.yaml
-sleep 5
-```
-
-```shell
-deployment.apps/whoami created
-service/whoami created
-ingressroute.traefik.io/secure-applications-apigateway-no-auth created
-```
-
-It should be accessible with curl on http://secure-applications.apigateway.docker.localhost/no-auth
-
-```shell
-curl http://secure-applications.apigateway.docker.localhost/no-auth
-```
-
-```shell
-Hostname: whoami-6f57d5d6b5-bgmfl
-IP: 127.0.0.1
-IP: ::1
-IP: 10.42.0.10
-IP: fe80::c8f6:84ff:fe66:3158
-RemoteAddr: 10.42.0.6:38110
-GET /no-auth HTTP/1.1
-Host: secure-applications.apigateway.docker.localhost
-User-Agent: curl/8.5.0
-Accept: */*
-Accept-Encoding: gzip
-X-Forwarded-For: 10.42.0.1
-X-Forwarded-Host: secure-applications.apigateway.docker.localhost
-X-Forwarded-Port: 80
-X-Forwarded-Proto: http
-X-Forwarded-Server: traefik-hub-6f5bbd6568-rp882
-X-Real-Ip: 10.42.0.1
-```
-
-To secure it with OAuth2, we can use the OAuth2 Client Credentials middleware:
-
-```diff :../../hack/diff.sh -r -a "manifests/whoami-app-ingressroute.yaml manifests/whoami-app-oauth2-client-creds.yaml"
---- manifests/whoami-app-ingressroute.yaml
-+++ manifests/whoami-app-oauth2-client-creds.yaml
-@@ -1,15 +1,32 @@
- ---
- apiVersion: traefik.io/v1alpha1
-+kind: Middleware
-+metadata:
-+ name: oauth2-client-creds
-+ namespace: apps
-+spec:
-+ plugin:
-+ oAuthClientCredentials:
-+ url: http://hydra.hydra.svc:4444/oauth2/token
-+ audience: https://traefik.io
-+ usernameClaim: sub
-+ forwardHeaders:
-+ Sub: sub
-+
-+---
-+apiVersion: traefik.io/v1alpha1
- kind: IngressRoute
- metadata:
-- name: secure-applications-apigateway-no-auth
-+ name: secure-applications-apigateway-oauth2-client-credentials
- namespace: apps
- spec:
- entryPoints:
- - web
- routes:
-- - match: Host(`secure-applications.apigateway.docker.localhost`) && Path(`/no-auth`)
-+ - match: Host(`secure-applications.apigateway.docker.localhost`) && Path(`/oauth2-client-credentials`)
- kind: Rule
- services:
- - name: whoami
- port: 80
-+ middlewares:
-+ - name: oauth2-client-creds
-```
-
-We can deploy the secured `IngressRoute`:
-
-```shell
-kubectl apply -f api-gateway/2-secure-applications/manifests/whoami-app-oauth2-client-creds.yaml
-```
-
-```shell
-middleware.traefik.io/oauth2-client-creds created
-ingressroute.traefik.io/secure-applications-apigateway-oauth2-client-credentials created
-```
-
-Once it's ready, we can create a Hydra OAuth2 client with _client_credentials_ grant type.
-This step is automated for you in this tutorial but here is how it's created:
-
-```shell :../../src/manifests/hydra.yaml -s 359 -e 366 -i s1
-hydra create oauth2-client \
- --endpoint http://hydra.hydra.svc:4445 \
- --name oauth-client \
- --secret traefiklabs \
- --grant-type client_credentials \
- --audience https://traefik.io \
- --token-endpoint-auth-method client_secret_post \
- --format json > /data/oauth-client.json
-```
-
-If needed, it can be run manually with `kubectl exec -it -n hydra deploy/hydra -- hydra create ...`.
-
-```shell
-client_id=$(kubectl get secrets -n apps oauth-client -o json | jq -r '.data.client_id' | base64 -d -w 0)
-client_secret=$(kubectl get secrets -n apps oauth-client -o json | jq -r '.data.client_secret' | base64 -d -w 0)
-auth=$(echo -n "$client_id:$client_secret" | base64 -w 0)
-curl -H "Authorization: Basic $auth" http://secure-applications.apigateway.docker.localhost/oauth2-client-credentials
-```
-
-It should output something like this:
-
-```shell
-Hostname: whoami-6f57d5d6b5-bgmfl
-IP: 127.0.0.1
-IP: ::1
-IP: 10.42.0.10
-IP: fe80::c8f6:84ff:fe66:3158
-RemoteAddr: 10.42.0.6:37870
-GET /oauth2-client-credentials HTTP/1.1
-Host: secure-applications.apigateway.docker.localhost
-User-Agent: curl/8.5.0
-Accept: */*
-Accept-Encoding: gzip
-Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjU2ZDdkMzliLTdhNTUtNDFkZi1iNjZkLTRjYzU0YmQ1YmZmOCIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiaHR0cHM6Ly90cmFlZmlrLmlvIl0sImNsaWVudF9pZCI6ImZlZTU2Nzc1LWJhYjMtNDE1Mi1hZDM3LWUyMTE0YWRlNjQ0OSIsImV4cCI6MTcxODgxNzQ5MiwiZXh0Ijp7fSwiaWF0IjoxNzE4ODEzODkyLCJpc3MiOiJodHRwOi8vaHlkcmEuaHlkcmEuc3ZjOjQ0NDQiLCJqdGkiOiI3Y2NmNzAxNC04OWM0LTQ1OTYtYWFjNS0wNTdlZjIzYjVjNjkiLCJuYmYiOjE3MTg4MTM4OTIsInNjcCI6W10sInN1YiI6ImZlZTU2Nzc1LWJhYjMtNDE1Mi1hZDM3LWUyMTE0YWRlNjQ0OSJ9.AwT-3_XQvScKzcfK-HumGZn9AfD9BofzfMxraT4Nmvb7OPamkPwhn6i_hwYtvcxth0TUx6W4gziMX867rw3jPS_KZPeq33GYWkIlmVJbmE90jWcST7MOm5_Pl-KfmV9YKioWD1RFGGM3rkIrobmtH1JM3Oxbxi5bbcPOrdFGlpIiAst5V6LC8e93vwga9mvh86TCT7ZnaxVHNN5Rrz_KdkCnidpUcc5Vev1GlTGOyhK4uolqu7fyQiyckeSNGB_BLB-bk1JBPEApgPWRjKIXLwhmR-xg4WXFl3kWY4nBI7ECmbClMMCfpXa1zYWF_kjDHodWxL7n7dEsrsZuykXRTKT10iT2VxH7QrPS-lHOu_sg6svCCGObB_lkv0rHBP6P9K-nD3tkJi_uPbVEIFkzjxDe6CEIZ2Xrn8H1GVmig-NoNGpflMYVu41wb_6eRv-PPACD_GI-YQOQvpMJXPFIjMIUMmvIWg-vD0bzrd_YUipa0HfruP_ENnHeIXwhJBMCWeVNwGCslsSj8uO7KaTF1NrTDuIHZNBnAp2WxZFQ4RPC5O1T3TJOpPn7dj5PKN-XgUGQlmUCUGPazfvFFBFQymoDSL88ijXDTkbzYGD2TnrbcqyCWV6uJdyEoV1Q8OA_lGtN39XcUuyiMGavSGdQY5yyBoULXo8oAwprOIdAy68
-Sub: fee56775-bab3-4152-ad37-e2114ade6449
-X-Forwarded-For: 10.42.0.1
-X-Forwarded-Host: secure-applications.apigateway.docker.localhost
-X-Forwarded-Port: 80
-X-Forwarded-Proto: http
-X-Forwarded-Server: traefik-hub-6f5bbd6568-rp882
-X-Real-Ip: 10.42.0.1
-```
-
-As we can see:
-
-1. A JWT Access Token is in the bearer HTTP header
-2. The `Sub` header of the authentication has been added to the request.
-
-We can decode this JWT token easily on https://jwt.io
-
-
-
-There is another mode allowed with this middleware, where we set _ClientId_ and _ClientSecret_ directly on Traefik Hub. Let's try it out.
-
-```mermaid
----
-title: OAuth2 Client Credentials flow with authentication on Traefik Hub
----
-sequenceDiagram
- actor app as M2M Application
- participant apigw as Traefik Hub
API Gateway
- participant idp as Authorization
Server
- participant api as Application
- autonumber
-
- app->>apigw: Send request
- apigw->>idp: Validate request. Request access token using Traefik Hub credentials
- idp->>apigw: Issue access token
- apigw->>api: Send request with access token in HTTP Header
-```
-
-We will remove the forward headers block and set the client's credentials directly in the middleware.
-Another client has been automatically created for that purpose. Let's see how:
-
-```shell :../../src/manifests/hydra.yaml -s 368 -e 374 -i s1
-hydra create oauth2-client \
- --endpoint http://hydra.hydra.svc:4445 \
- --name oauth-client-nologin \
- --secret traefiklabs \
- --grant-type client_credentials \
- --audience https://traefik.io \
- --format json > /data/oauth-client-nologin.json
-```
-
-```diff :../../hack/diff.sh -r -a "manifests/whoami-app-oauth2.yaml manifests/whoami-app-oauth2-client-creds-nologin.yaml"
---- manifests/whoami-app-oauth2.yaml
-+++ manifests/whoami-app-oauth2-client-creds-nologin.yaml
-@@ -0,0 +1,31 @@
-+---
-+apiVersion: traefik.io/v1alpha1
-+kind: Middleware
-+metadata:
-+ name: oauth2-client-creds-nologin
-+ namespace: apps
-+spec:
-+ plugin:
-+ oAuthClientCredentials:
-+ url: http://hydra.hydra.svc:4444/oauth2/token
-+ audience: https://traefik.io
-+ clientId: "urn:k8s:secret:oauth-client-nologin:client_id"
-+ clientSecret: "urn:k8s:secret:oauth-client-nologin:client_secret"
-+
-+---
-+apiVersion: traefik.io/v1alpha1
-+kind: IngressRoute
-+metadata:
-+ name: secure-applications-apigateway-oauth2-client-credentials-nologin
-+ namespace: apps
-+spec:
-+ entryPoints:
-+ - web
-+ routes:
-+ - match: Host(`secure-applications.apigateway.docker.localhost`) && Path(`/oauth2-client-credentials-nologin`)
-+ kind: Rule
-+ services:
-+ - name: whoami
-+ port: 80
-+ middlewares:
-+ - name: oauth2-client-creds-nologin
-```
-
-Let's try it:
-
-```shell
-kubectl apply -f api-gateway/2-secure-applications/manifests/whoami-app-oauth2-client-creds-nologin.yaml
-sleep 3
-curl http://secure-applications.apigateway.docker.localhost/oauth2-client-credentials-nologin
-```
-
-As we can see, there is no authentication required now _and_ there is a JWT access token transmitted to the application:
-
-```shell
-Hostname: whoami-6f57d5d6b5-bgmfl
-IP: 127.0.0.1
-IP: ::1
-IP: 10.42.0.10
-IP: fe80::c8f6:84ff:fe66:3158
-RemoteAddr: 10.42.0.6:50004
-GET /oauth2-client-credentials-nologin HTTP/1.1
-Host: secure-applications.apigateway.docker.localhost
-User-Agent: curl/8.5.0
-Accept: */*
-Accept-Encoding: gzip
-Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjU2ZDdkMzliLTdhNTUtNDFkZi1iNjZkLTRjYzU0YmQ1YmZmOCIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiaHR0cHM6Ly90cmFlZmlrLmlvIl0sImNsaWVudF9pZCI6IjNkMzYwNGViLWNkOTItNDAxMi05ODlhLTAxMTY3Mjc3YzRmMSIsImV4cCI6MTcxODgxNzYyMiwiZXh0Ijp7fSwiaWF0IjoxNzE4ODE0MDIyLCJpc3MiOiJodHRwOi8vaHlkcmEuaHlkcmEuc3ZjOjQ0NDQiLCJqdGkiOiJmYTcwZDBlMy1hM2E4LTQ4ZDYtYmNmNi1mMGYyZDEwY2ZkYTUiLCJuYmYiOjE3MTg4MTQwMjIsInNjcCI6W10sInN1YiI6IjNkMzYwNGViLWNkOTItNDAxMi05ODlhLTAxMTY3Mjc3YzRmMSJ9.fVM8ba7cSLBC-2J5t72sp8bzbZF5hGZRvaiWsKeJQxqcslQrB05nzZHhKvkg5Bqsw-0wycFVvYuc_1DWRai0jE7mG73vgiTuuBe38NaAh4hVt9vTORzvKtm_V1Wx4ZAtniGB-6o5ta6TyLc68tq78KHUeWTzZ0f5ugteZVmrflvBgWQWWjM612op2nbA9m6ArDurWRst6FAvJ_lSja60XHjmHSan78Q9ps7PzH1PB2zkmrsaI6Y-c81CtMR6KOdBmO0iD4eRoHGh2GP0iCiozv9r_8pLc7xkdFBDeFoswGIVRQhqEvOLchE5Ca7DnI6PQupX8NtXRrPY-blS8d-WT4UwHVUOc_nEQHhZuIZk3IG7iE6JMmtc_0dOWdBlu5m-XVHe1mC5XSb55McuY0ckp2mb2pbPgQJCcra67prcqQqpXZc0syOCTlvjk6mHBXYMmiISIsunGttKmNuZKFteUiaPsqgRJf_B2JQvG4RknEk1Nl5VKr8ouneP0xunSCRCZyGScZ_qt5XPbhBqLOA4dedATWqtQ7UT8hp5TWOmE0_1bZM3CKSOQeX3aPQWA6NFsJHKxY2IZsohes_QXACCI2qfHa5CVueRzgBYPkECtWC1pXhBWy4E5p4Jp-2mMBvEpTjltcprndfaGbFHxOw1nj1p24ESES7P9IUK73DQca8
-X-Forwarded-For: 10.42.0.1
-X-Forwarded-Host: secure-applications.apigateway.docker.localhost
-X-Forwarded-Port: 80
-X-Forwarded-Proto: http
-X-Forwarded-Server: traefik-hub-6f5bbd6568-rp882
-X-Real-Ip: 10.42.0.1
-```
diff --git a/api-gateway/2-secure-applications/manifests/whoami-app-ingressroute.yaml b/api-gateway/2-secure-applications/manifests/whoami-app-ingressroute.yaml
deleted file mode 100644
index 9c27b93..0000000
--- a/api-gateway/2-secure-applications/manifests/whoami-app-ingressroute.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
----
-apiVersion: traefik.io/v1alpha1
-kind: IngressRoute
-metadata:
- name: secure-applications-apigateway-no-auth
- namespace: apps
-spec:
- entryPoints:
- - web
- routes:
- - match: Host(`secure-applications.apigateway.docker.localhost`) && Path(`/no-auth`)
- kind: Rule
- services:
- - name: whoami
- port: 80
diff --git a/api-gateway/2-secure-applications/manifests/whoami-app-oauth2-client-creds-nologin.yaml b/api-gateway/2-secure-applications/manifests/whoami-app-oauth2-client-creds-nologin.yaml
deleted file mode 100644
index a78f4bf..0000000
--- a/api-gateway/2-secure-applications/manifests/whoami-app-oauth2-client-creds-nologin.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
----
-apiVersion: traefik.io/v1alpha1
-kind: Middleware
-metadata:
- name: oauth2-client-creds-nologin
- namespace: apps
-spec:
- plugin:
- oAuthClientCredentials:
- url: http://hydra.hydra.svc:4444/oauth2/token
- audience: https://traefik.io
- clientId: "urn:k8s:secret:oauth-client-nologin:client_id"
- clientSecret: "urn:k8s:secret:oauth-client-nologin:client_secret"
-
----
-apiVersion: traefik.io/v1alpha1
-kind: IngressRoute
-metadata:
- name: secure-applications-apigateway-oauth2-client-credentials-nologin
- namespace: apps
-spec:
- entryPoints:
- - web
- routes:
- - match: Host(`secure-applications.apigateway.docker.localhost`) && Path(`/oauth2-client-credentials-nologin`)
- kind: Rule
- services:
- - name: whoami
- port: 80
- middlewares:
- - name: oauth2-client-creds-nologin
diff --git a/api-gateway/2-secure-applications/manifests/whoami-app-oauth2-client-creds.yaml b/api-gateway/2-secure-applications/manifests/whoami-app-oauth2-client-creds.yaml
deleted file mode 100644
index 146e081..0000000
--- a/api-gateway/2-secure-applications/manifests/whoami-app-oauth2-client-creds.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
----
-apiVersion: traefik.io/v1alpha1
-kind: Middleware
-metadata:
- name: oauth2-client-creds
- namespace: apps
-spec:
- plugin:
- oAuthClientCredentials:
- url: http://hydra.hydra.svc:4444/oauth2/token
- audience: https://traefik.io
- usernameClaim: sub
- forwardHeaders:
- Sub: sub
-
----
-apiVersion: traefik.io/v1alpha1
-kind: IngressRoute
-metadata:
- name: secure-applications-apigateway-oauth2-client-credentials
- namespace: apps
-spec:
- entryPoints:
- - web
- routes:
- - match: Host(`secure-applications.apigateway.docker.localhost`) && Path(`/oauth2-client-credentials`)
- kind: Rule
- services:
- - name: whoami
- port: 80
- middlewares:
- - name: oauth2-client-creds
diff --git a/api-gateway/2-secure-applications/manifests/whoami-app-oidc-nologinurl.yaml b/api-gateway/2-secure-applications/manifests/whoami-app-oidc-nologinurl.yaml
deleted file mode 100644
index 0a1b388..0000000
--- a/api-gateway/2-secure-applications/manifests/whoami-app-oidc-nologinurl.yaml
+++ /dev/null
@@ -1,35 +0,0 @@
----
-apiVersion: traefik.io/v1alpha1
-kind: Middleware
-metadata:
- name: oidc-nologin
- namespace: apps
-spec:
- plugin:
- oidc:
- issuer: http://hydra.hydra.svc:4444
- clientId: "urn:k8s:secret:oidc-client-nologin:client_id"
- clientSecret: "urn:k8s:secret:oidc-client-nologin:client_secret"
- logoutUrl: /oidc-nologin/logout
- redirectUrl: /oidc-nologin/callback
- csrf: {}
- session:
- name: "oidc-session"
-
----
-apiVersion: traefik.io/v1alpha1
-kind: IngressRoute
-metadata:
- name: secure-applications-apigateway-whoami-nologin
- namespace: apps
-spec:
- entryPoints:
- - web
- routes:
- - match: Host(`secure-applications.apigateway.docker.localhost`) && (Path(`/oidc-nologin`) || Path(`/oidc-nologin/logout`) || Path(`/oidc-nologin/callback`))
- kind: Rule
- services:
- - name: whoami
- port: 80
- middlewares:
- - name: oidc-nologin
diff --git a/api-gateway/2-secure-applications/manifests/whoami-app-oidc.yaml b/api-gateway/2-secure-applications/manifests/whoami-app-oidc.yaml
deleted file mode 100644
index 92da2fb..0000000
--- a/api-gateway/2-secure-applications/manifests/whoami-app-oidc.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
----
-apiVersion: traefik.io/v1alpha1
-kind: Middleware
-metadata:
- name: oidc-login
- namespace: apps
-spec:
- plugin:
- oidc:
- issuer: http://hydra.hydra.svc:4444
- clientId: "urn:k8s:secret:oidc-client:client_id"
- clientSecret: "urn:k8s:secret:oidc-client:client_secret"
- loginUrl: /oidc/login
- logoutUrl: /oidc/logout
- redirectUrl: /oidc/callback
- csrf: {}
- session:
- name: "oidc-session"
-
----
-apiVersion: traefik.io/v1alpha1
-kind: IngressRoute
-metadata:
- name: secure-applications-apigateway-whoami-oidc
- namespace: apps
-spec:
- entryPoints:
- - web
- routes:
- - match: Host(`secure-applications.apigateway.docker.localhost`) && (Path(`/oidc`) || Path(`/oidc/login`) || Path(`/oidc/logout`) || Path(`/oidc/callback`))
- kind: Rule
- services:
- - name: whoami
- port: 80
- middlewares:
- - name: oidc-login
diff --git a/api-gateway/2-secure-applications/oidc.md b/api-gateway/2-secure-applications/oidc.md
deleted file mode 100644
index 9146d28..0000000
--- a/api-gateway/2-secure-applications/oidc.md
+++ /dev/null
@@ -1,263 +0,0 @@
-## Human authentication with OpenID Connect Authentication
-
-OpenID Connect Authentication is built on top of the OAuth2 Authorization Code Flow (defined in [OAuth 2.0 RFC 6749, section 4.1](https://tools.ietf.org/html/rfc6749#section-4.1))
-and allows an application to be secured by delegating authentication to an external provider (Google Accounts, LinkedIn, GitHub, etc.)
-and obtaining the end user's session claims and scopes for authorization purposes.
-
-:information_source: To follow this tutorial, you'll need to install Traefik Hub following [getting started](../1-getting-started/README.md) instructions.
-
-Now that Traefik Hub is deployed, let's see how it works:
-
-```mermaid
----
-title: OpenID Connect authentication flow
----
-sequenceDiagram
- actor user as User
- participant idp as OpenID Connect
Provider
- participant apigw as Traefik Hub
API Gateway
- participant app as Application
- autonumber
-
- user->>apigw: User sends unauthenticated request
- apigw->>user: Request denied. Redirect to OpenID Connect Provider
- user->>idp: Login to OpenID Connect Provider
- idp->>user: Issue authorization code. Redirect to Traefik Hub
- user->>apigw: Send authorization code
- apigw->>idp: Check authorization code. Request tokens
- idp->>apigw: Issue access and identity tokens
- apigw->>user: Set session using tokens. Redirect to Traefik Hub
- user->>apigw: Re-send request with this session
- apigw->>app: Check session validity. Relaying request to application
- app->>user: Return response to user
-```
-
-In this tutorial, we will use [Ory Hydra](https://www.ory.sh/hydra/), an OAuth 2 and OpenID Connect server. We will also use a simple [login/consent app](/~https://github.com/jlevesy/hail-hydra) that always says yes.
-
-We can deploy it:
-
-```shell
-kubectl apply -f src/manifests/apps-namespace.yaml
-kubectl apply -f src/manifests/hydra.yaml
-kubectl wait -n hydra --for=condition=ready pod --selector=app=hydra --timeout=90s
-kubectl wait -n hydra --for=condition=ready pod --selector=app=consent --timeout=90s
-kubectl wait -n hydra --for=condition=complete job/create-hydra-clients --timeout=90s
-```
-
-TraefikLabs has open-sourced a simple whoami application displaying technical information about the request.
-
-First, let's deploy and expose it:
-
-```shell
-kubectl apply -f src/manifests/whoami-app.yaml
-kubectl apply -f api-gateway/2-secure-applications/manifests/whoami-app-ingressroute.yaml
-sleep 5
-```
-
-```shell
-namespace/apps unchanged
-deployment.apps/whoami unchanged
-service/whoami unchanged
-ingressroute.traefik.io/secure-applications-apigateway-no-auth unchanged
-```
-
-It should be accessible with curl on http://secure-applications.apigateway.docker.localhost/no-auth
-
-```shell
-curl http://secure-applications.apigateway.docker.localhost/no-auth
-```
-
-```shell
-Hostname: whoami-6f57d5d6b5-bgmfl
-IP: 127.0.0.1
-IP: ::1
-IP: 10.42.0.10
-IP: fe80::c8f6:84ff:fe66:3158
-RemoteAddr: 10.42.0.6:52926
-GET /no-auth HTTP/1.1
-Host: secure-applications.apigateway.docker.localhost
-User-Agent: curl/8.5.0
-Accept: */*
-Accept-Encoding: gzip
-X-Forwarded-For: 10.42.0.1
-X-Forwarded-Host: secure-applications.apigateway.docker.localhost
-X-Forwarded-Port: 80
-X-Forwarded-Proto: http
-X-Forwarded-Server: traefik-hub-6f5bbd6568-rp882
-X-Real-Ip: 10.42.0.1
-```
-
-To secure it with OIDC, we'll need to configure Hydra.
-
-First, we can create the [JSON Web Key Sets](https://www.ory.sh/docs/hydra/jwks) that hydra uses to sign and verify id-token and access-token.
-This step is automated for you in this tutorial but here is how it's created:
-
-```shell :../../src/manifests/hydra.yaml -s 398 -e 399 -i s1
-hydra create jwks hydra.openid.id-token --alg RS256 --endpoint http://hydra.hydra.svc:4445
-hydra create jwks hydra.jwt.access-token --alg RS256 --endpoint http://hydra.hydra.svc:4445
-```
-
-If needed, it can be run manually with `kubectl exec -it -n hydra deploy/hydra -- hydra create ...`.
-
-And after, we can use the OIDC middleware. Let's see how it works compared to an unprotected IngressRoute:
-
-```diff :../../hack/diff.sh -r -a "manifests/whoami-app-ingressroute.yaml manifests/whoami-app-oidc.yaml"
---- manifests/whoami-app-ingressroute.yaml
-+++ manifests/whoami-app-oidc.yaml
-@@ -1,15 +1,36 @@
- ---
- apiVersion: traefik.io/v1alpha1
-+kind: Middleware
-+metadata:
-+ name: oidc-login
-+ namespace: apps
-+spec:
-+ plugin:
-+ oidc:
-+ issuer: http://hydra.hydra.svc:4444
-+ clientId: "urn:k8s:secret:oidc-client:client_id"
-+ clientSecret: "urn:k8s:secret:oidc-client:client_secret"
-+ loginUrl: /oidc/login
-+ logoutUrl: /oidc/logout
-+ redirectUrl: /oidc/callback
-+ csrf: {}
-+ session:
-+ name: "oidc-session"
-+
-+---
-+apiVersion: traefik.io/v1alpha1
- kind: IngressRoute
- metadata:
-- name: secure-applications-apigateway-no-auth
-+ name: secure-applications-apigateway-whoami-oidc
- namespace: apps
- spec:
- entryPoints:
- - web
- routes:
-- - match: Host(`secure-applications.apigateway.docker.localhost`) && Path(`/no-auth`)
-+ - match: Host(`secure-applications.apigateway.docker.localhost`) && (Path(`/oidc`) || Path(`/oidc/login`) || Path(`/oidc/logout`) || Path(`/oidc/callback`))
- kind: Rule
- services:
- - name: whoami
- port: 80
-+ middlewares:
-+ - name: oidc-login
-```
-
-This middleware is configured to redirect `/login`, `/logout`, and `/callback` paths to the OIDC provider.
-
-We'll use another user in Hydra for that purpose and set it to the OIDC middleware.
-It has been created thise way:
-
-```shell :../../src/manifests/hydra.yaml -s 376 -e 385 -i s1
-hydra create oauth2-client \
- --endpoint http://hydra.hydra.svc:4445 \
- --name oidc-client \
- --secret traefiklabs \
- --grant-type authorization_code,refresh_token \
- --response-type code,id_token \
- --scope openid,offline \
- --redirect-uri http://secure-applications.apigateway.docker.localhost/oidc/callback \
- --post-logout-callback http://secure-applications.apigateway.docker.localhost/oidc/callback \
- --format json > /data/oidc-client.json
-```
-
-So let's apply it:
-
-```shell
-kubectl apply -f api-gateway/2-secure-applications/manifests/whoami-app-oidc.yaml
-```
-
-Let's test it:
-
-```shell
-# Protected with OIDC => 401
-curl -I http://secure-applications.apigateway.docker.localhost/oidc
-# Let's login and follow the request flow => 204
-rm -f /tmp/cookie
-curl -i -L -b /tmp/cookie -c /tmp/cookie http://secure-applications.apigateway.docker.localhost/oidc/login
-# Now, with this cookie, we can access => 200
-curl -b /tmp/cookie -c /tmp/cookie http://secure-applications.apigateway.docker.localhost/oidc
-```
-
-Now, let's say we want the user to log in on the whole domain. This YAML should do the trick:
-
-```diff :../../hack/diff.sh -r -a "manifests/whoami-app-oidc.yaml manifests/whoami-app-oidc-nologinurl.yaml"
---- manifests/whoami-app-oidc.yaml
-+++ manifests/whoami-app-oidc-nologinurl.yaml
-@@ -2,17 +2,16 @@
- apiVersion: traefik.io/v1alpha1
- kind: Middleware
- metadata:
-- name: oidc-login
-+ name: oidc-nologin
- namespace: apps
- spec:
- plugin:
- oidc:
- issuer: http://hydra.hydra.svc:4444
-- clientId: "urn:k8s:secret:oidc-client:client_id"
-- clientSecret: "urn:k8s:secret:oidc-client:client_secret"
-- loginUrl: /oidc/login
-- logoutUrl: /oidc/logout
-- redirectUrl: /oidc/callback
-+ clientId: "urn:k8s:secret:oidc-client-nologin:client_id"
-+ clientSecret: "urn:k8s:secret:oidc-client-nologin:client_secret"
-+ logoutUrl: /oidc-nologin/logout
-+ redirectUrl: /oidc-nologin/callback
- csrf: {}
- session:
- name: "oidc-session"
-@@ -21,16 +20,16 @@
- apiVersion: traefik.io/v1alpha1
- kind: IngressRoute
- metadata:
-- name: secure-applications-apigateway-whoami-oidc
-+ name: secure-applications-apigateway-whoami-nologin
- namespace: apps
- spec:
- entryPoints:
- - web
- routes:
-- - match: Host(`secure-applications.apigateway.docker.localhost`) && (Path(`/oidc`) || Path(`/oidc/login`) || Path(`/oidc/logout`) || Path(`/oidc/callback`))
-+ - match: Host(`secure-applications.apigateway.docker.localhost`) && (Path(`/oidc-nologin`) || Path(`/oidc-nologin/logout`) || Path(`/oidc-nologin/callback`))
- kind: Rule
- services:
- - name: whoami
- port: 80
- middlewares:
-- - name: oidc-login
-+ - name: oidc-nologin
-```
-
-A new ClientID / ClientSecret pair has been created for that purpose:
-
-```shell :../../src/manifests/hydra.yaml -s 387 -e 396 -i s1
-hydra create oauth2-client \
- --endpoint http://hydra.hydra.svc:4445 \
- --name oidc-client-nologin \
- --secret traefiklabs \
- --grant-type authorization_code,refresh_token \
- --response-type code,id_token \
- --scope openid,offline \
- --redirect-uri http://secure-applications.apigateway.docker.localhost/oidc-nologin/callback \
- --post-logout-callback http://secure-applications.apigateway.docker.localhost/oidc/callback \
- --format json > /data/oidc-client-nologin.json
-```
-
-Now, test it:
-
-```shell
-kubectl apply -f api-gateway/2-secure-applications/manifests/whoami-app-oidc-nologinurl.yaml
-```
-
-We can now test it:
-
-```shell
-# First time, it will login:
-rm -f /tmp/cookie
-curl -L -b /tmp/cookie -c /tmp/cookie http://secure-applications.apigateway.docker.localhost/oidc-nologin
-# Second time, it will re-use the cookie:
-curl -b /tmp/cookie -c /tmp/cookie http://secure-applications.apigateway.docker.localhost/oidc-nologin
-```