diff --git a/Dockerfile b/Dockerfile index 82fc955..c8127d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,30 +1,39 @@ -FROM openresty/openresty:1.25.3.1-alpine-fat +FROM openresty/openresty:1.25.3.2-alpine-fat -RUN luarocks install lua-resty-openidc -RUN luarocks install lua-resty-template +RUN luarocks install lua-resty-http 0.17.2 +RUN luarocks install lua-resty-session 4.0.5 +RUN luarocks install lua-resty-jwt 0.2.3 +RUN luarocks install lua-resty-openidc 1.8.0 +RUN luarocks install lua-resty-template 2.0 -COPY conf/ /usr/local/openresty/nginx/conf/ -COPY lua/ /etc/ipax/lua/ +COPY conf /var/ipax/conf/ +COPY lua /var/ipax/lua/ COPY html /var/ipax/html COPY templates /var/ipax/templates ENV NGINX_LOG_LEVEL=warn \ NGINX_RESOLVER=8.8.8.8 \ - SESSION_SECRET="ipax_default_secret" \ - SESSION_COOKIE_PERSISTENT=off \ - SESSION_COOKIE_LIFETIME=86400 \ - SESSION_COOKIE_SAMESITE="Lax" \ OIDC_DISCOVERY="" \ OIDC_SSL_VERIFY="yes" \ OIDC_CLIENT_ID="" \ - OIDC_USE_PKCE=false \ + OIDC_USE_PKCE="false" \ OIDC_CLIENT_SECRET="" \ OIDC_SCOPE="openid profile" \ OIDC_REDIRECT_URI="/private/redirect_uri" \ - OIDC_LOGOUT_URI="/private/logout" \ - OIDC_POST_LOGOUT_REDIRECT_URI="/auth" \ + OIDC_LOGOUT_PATH="/private/logout" \ + OIDC_POST_LOGOUT_REDIRECT_URI="/logoutSuccess.html" \ OIDC_PROMPT="" \ OIDC_ACR_VALUES="" \ + SESSION_COOKIE_SAME_SITE="Lax" \ + SESSION_COOKIE_SECURE="false" \ + SESSION_IDLING_TIMEOUT="86400" \ + SESSION_REMEMBER="false" \ + SESSION_SECRET="ipax_default_secret" \ + IPAX_APP_NAME="ipax" \ + IPAX_DISPLAY_NAME="IPAx" \ + IPAX_BASE_URL="http://localhost" \ + IPAX_MODE="demoapp" \ + API_BASE_URL="" \ KC_DELETE_ACCOUNT_ACTION="" \ KC_DELETE_ACCOUNT_LABEL="Delete account" \ KC_UPDATE_EMAIL_ACTION="" \ @@ -33,9 +42,16 @@ ENV NGINX_LOG_LEVEL=warn \ KC_UPDATE_PASSWORD_LABEL="Update password" \ KC_ENROL_BIOMETRICS_ACTION="" \ KC_ENROL_BIOMETRICS_LABEL="Enrol biometrics" \ - IPAX_APP_NAME="IPAx" \ - API_BASEURL="" + KC_ADD_PASSKEY_ACTION="" \ + KC_ADD_PASSKEY_LABEL="Add Passkey" \ + LUA_SHARED_DICT_PATH="/var/ipax/conf/lua_shared_dict" \ + DEMOAPPS_VARIABLES_CONFIG_PATH="/var/ipax/conf/demoapps" \ + DEMOAPPS_CONFIG_PATH="/var/ipax/conf/location_conf.d" WORKDIR /usr/local/openresty/nginx -CMD ["sh", "-c", "envsubst < conf/nginx.conf.template > conf/nginx.conf && /usr/local/openresty/bin/openresty -g 'daemon off;'"] +# HEALTHCHECK --interval=60s --timeout=1s --start-period=5s --retries=3 CMD [ "curl", "-f", "http://localhost/ipax/health" ] + +CMD [ ] +COPY entrypoint.sh /entrypoint.sh +ENTRYPOINT [ "/bin/bash", "/entrypoint.sh" ] diff --git a/README.md b/README.md index 3958ff3..dcc92dd 100644 --- a/README.md +++ b/README.md @@ -1,56 +1,14 @@ # IPAx Identity-aware proxy based on NGINX, OpenResty and [lua-resty-openidc](https://github.com/zmartzone/lua-resty-openidc). -## IDP -Create an OpenID Connect Client in your IDP using the following information: -- Client Name: IPAx -- Scopes: profile, openid -- Grant Types: authorization_code -- Redirect URIs: Include values like: "https://myapp.identicum.com/private/redirect_uri" (suffix is handled by `lua-resty-openidc`, can be adjusted using the `OIDC_REDIRECT_URI` environment variable) +## Execution mode +IPAx can be used as: -## Configuration files -Samples are provided in the [conf.samples](./conf.samples/) folder. -Customize your files and put them into your local `./conf.d/` directory. +### proxy -## Run the container +### demoapp (single) -Run the image, mounting a local directory for configuration: +### demoapps (multi) -```sh -docker run -d \ - -p 80:80 \ - -e OIDC_DISCOVERY="https://idp.identicum.com/.well-known/openid-configuration" \ - -e OIDC_CLIENT_ID="my_client_id" \ - -e OIDC_CLIENT_SECRET="my_client_secret" \ - -e OIDC_SCOPE="openid profile" \ - -e OIDC_REDIRECT_URI="/private/redirect_uri" \ - -e OIDC_SESSION_SECRET="some_uuid_secret" \ - -e OIDC_POST_LOGOUT_REDIRECT_URI="https://myapp.identicum.com/logoutSuccess.html" \ - -e OIDC_ACR_VALUES="loa-3" \ - -v $(pwd)/conf.d/:/etc/ipax/conf.d/:ro \ - ghcr.io/identicum/ipax:latest -``` > To use PKCE, remove `OIDC_CLIENT_SECRET` and add `OIDC_USE_PKCE` with value "true" - -## Certificates (optional) -Issue as many certificates as necessary to be used in your reverse proxy. -IPAx supports [wildcard certificates](https://en.wikipedia.org/wiki/Wildcard_certificate) and [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication). - -If you want to use HTTPS, add mapping for port 443 and mount volume `./certs/` as /etc/ipax/certs/ - -### Self-signed certificate -To test using a self-signed certificate, run the following command (replace with your domain): -```sh -openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout wildcard_identicum_com.key -out wildcard_identicum_com.cer -``` - - Country Name (2 letter code) [XX]: `AR` - State or Province Name (full name) []: `CABA` - Locality Name (eg, city) [Default City]: `Buenos Aires` - Organization Name (eg, company) [Default Company Ltd]: `Identicum` - Organizational Unit Name (eg, section) []: ` ` - Common Name (eg, your name or your server hostname) []: `*.identicum.com` - Email Address []: `no-reply@identicum.com` - -Put the generated certificate files into your local `./certs/` directory. \ No newline at end of file diff --git a/VARIABLES.md b/VARIABLES.md new file mode 100644 index 0000000..3582ffd --- /dev/null +++ b/VARIABLES.md @@ -0,0 +1,36 @@ +# Variables + +| Environment | nginx variable | lua_resty_openidc variable | Description | +|-------------------------------|-------------------------------|------------------------------------|-------------------------------| +| NGINX_RESOLVER | | | | +| NGINX_LOG_LEVEL | | | | +| OIDC_DISCOVERY | oidc_discovery | oidc_opts.discovery | OIDC discovery URL | +| OIDC_SSL_VERIFY | oidc_ssl_verify | oidc_opts.ssl_verify | | +| OIDC_CLIENT_ID | oidc_client_id | oidc_opts.client_id | | +| OIDC_USE_PKCE | oidc_use_pkce | oidc_opts.use_pkce | | +| OIDC_CLIENT_SECRET | oidc_client_secret | oidc_opts.client_secret | | +| OIDC_SCOPE | oidc_scope | oidc_opts.scope | | +| OIDC_REDIRECT_URI | oidc_redirect_uri | oidc_opts.redirect_uri | | +| OIDC_LOGOUT_PATH | oidc_logout_path | oidc_opts.logout_path | | +| OIDC_POST_LOGOUT_REDIRECT_URI | oidc_post_logout_redirect_uri | oidc_opts.post_logout_redirect_uri | | +| OIDC_PROMPT | oidc_prompt | oidc_opts.prompt | | +| OIDC_ACR_VALUES | oidc_acr_values | oidc_opts.authorization_params | | +| SESSION_COOKIE_SAME_SITE | session_cookie_same_site | session_opts.cookie_same_site | Lax / Strict / None / Default | +| SESSION_COOKIE_SECURE | session_cookie_secure | session_opts.cookie_secure | | +| SESSION_IDLING_TIMEOUT | session_idling_timeout | session_opts.idling_timeout | | +| SESSION_REMEMBER | session_remember | session_opts.remember | | +| SESSION_SECRET | session_secret | session_opts.secret | | +| IPAX_APP_NAME | ipax_app_name | | | +| IPAX_DISPLAY_NAME | ipax_display_name | | | +| IPAX_BASE_URL | ipax_base_url | | | +| API_BASE_URL | api_base_url | | | +| KC_DELETE_ACCOUNT_ACTION | kc_delete_account_action | | | +| KC_DELETE_ACCOUNT_LABEL | | | | +| KC_UPDATE_EMAIL_ACTION | | | | +| KC_UPDATE_EMAIL_LABEL | | | | +| KC_UPDATE_PASSWORD_ACTION | | | | +| KC_UPDATE_PASSWORD_LABEL | | | | +| KC_ENROL_BIOMETRICS_ACTION | | | | +| KC_ENROL_BIOMETRICS_LABEL | | | | +| KC_ADD_PASSKEY_ACTION | kc_add_passkey_action | | | +| KC_ADD_PASSKEY_LABEL | kc_add_passkey_label | | | diff --git a/conf/conf.d/.gitignore b/conf/conf.d/.gitignore new file mode 100644 index 0000000..4ba6098 --- /dev/null +++ b/conf/conf.d/.gitignore @@ -0,0 +1 @@ +/*.conf diff --git a/conf/conf.d/README.md b/conf/conf.d/README.md new file mode 100644 index 0000000..11ecb9a --- /dev/null +++ b/conf/conf.d/README.md @@ -0,0 +1,5 @@ +# Host-based multihoming configuration files + +In `proxy` mode, this folder is mounted at execution time. See examples [here](./samples/) + +In `demoapps` mode, files in this folder are generated by the [ENTRYPOINT script](../../entrypoint.sh) diff --git a/conf.samples/_redirect_to_https.conf b/conf/conf.d/samples/_redirect_to_https.conf similarity index 100% rename from conf.samples/_redirect_to_https.conf rename to conf/conf.d/samples/_redirect_to_https.conf diff --git a/conf.samples/authN.conf b/conf/conf.d/samples/authN.conf similarity index 84% rename from conf.samples/authN.conf rename to conf/conf.d/samples/authN.conf index 68a14ed..d43e9f0 100644 --- a/conf.samples/authN.conf +++ b/conf/conf.d/samples/authN.conf @@ -2,7 +2,7 @@ server { listen 443 ssl; server_name authn.identicum.com; - include /etc/ipax/conf.d/wildcard_identicum_com.settings; + include /var/ipax/conf/conf.d/wildcard_identicum_com.settings; location / { access_by_lua_block { diff --git a/conf.samples/authZ.conf b/conf/conf.d/samples/authZ.conf similarity index 88% rename from conf.samples/authZ.conf rename to conf/conf.d/samples/authZ.conf index 927d0b4..734c169 100644 --- a/conf.samples/authZ.conf +++ b/conf/conf.d/samples/authZ.conf @@ -2,7 +2,7 @@ server { listen 443 ssl; server_name authz.identicum.com; - include /etc/ipax/conf.d/wildcard_identicum_com.settings; + include /var/ipax/conf/conf.d/wildcard_identicum_com.settings; location / { access_by_lua_block { diff --git a/conf.samples/headers.conf b/conf/conf.d/samples/headers.conf similarity index 89% rename from conf.samples/headers.conf rename to conf/conf.d/samples/headers.conf index ba18a7c..21b4ac6 100644 --- a/conf.samples/headers.conf +++ b/conf/conf.d/samples/headers.conf @@ -2,7 +2,7 @@ server { listen 443 ssl; server_name wtp.identicum.com; - include /etc/ipax/conf.d/wildcard_identicum_com.settings; + include /var/ipax/conf/conf.d/wildcard_identicum_com.settings; location / { access_by_lua_block { diff --git a/conf.samples/spa_api.conf b/conf/conf.d/samples/spa_api.conf similarity index 83% rename from conf.samples/spa_api.conf rename to conf/conf.d/samples/spa_api.conf index 769714d..217e1ff 100644 --- a/conf.samples/spa_api.conf +++ b/conf/conf.d/samples/spa_api.conf @@ -2,7 +2,7 @@ server { listen 443 ssl; server_name spa.identicum.com; - include /etc/ipax/conf.d/wildcard_identicum_com.settings; + include /var/ipax/conf/conf.d/wildcard_identicum_com.settings; location / { access_by_lua_block { @@ -18,7 +18,7 @@ server { location /api/ { set $api_base_url ''; access_by_lua_block { - ngx.var.api_base_url = os.getenv("API_BASEURL"); + ngx.var.api_base_url = os.getenv("API_BASE_URL"); ngx.req.set_header("Authorization", "Bearer " .. require("ipax").get_access_token()); } proxy_pass $api_base_url; diff --git a/conf.samples/wildcard_identicum_com.settings b/conf/conf.d/samples/wildcard_identicum_com.settings similarity index 100% rename from conf.samples/wildcard_identicum_com.settings rename to conf/conf.d/samples/wildcard_identicum_com.settings diff --git a/conf/default_variables.conf b/conf/default_variables.conf new file mode 100644 index 0000000..2813527 --- /dev/null +++ b/conf/default_variables.conf @@ -0,0 +1,29 @@ +set $oidc_discovery ''; +set $oidc_ssl_verify ''; +set $oidc_client_id ''; +set $oidc_use_pkce ''; +set $oidc_client_secret ''; +set $oidc_scope ''; +set $oidc_redirect_uri ''; +set $oidc_logout_path ''; +set $oidc_post_logout_redirect_uri ''; +set $oidc_prompt ''; +set $oidc_acr_values ''; +set $session_cookie_same_site ''; +set $session_cookie_secure ''; +set $session_idling_timeout ''; +set $session_remember ''; +set $session_secret ''; +set $ipax_app_name ''; +set $ipax_display_name ''; +set $api_base_url ''; +set $kc_delete_account_action ''; +set $kc_delete_account_label ''; +set $kc_update_email_action ''; +set $kc_update_email_label ''; +set $kc_update_password_action ''; +set $kc_update_password_label ''; +set $kc_enrol_biometrics_action ''; +set $kc_enrol_biometrics_label ''; +set $kc_add_passkey_action ''; +set $kc_add_passkey_label ''; \ No newline at end of file diff --git a/conf/default_vhost.conf b/conf/default_vhost.conf new file mode 100644 index 0000000..ccf48c9 --- /dev/null +++ b/conf/default_vhost.conf @@ -0,0 +1,13 @@ +set $template_root /var/ipax/templates; + +location / { + add_header Cache-Control no-store; + add_header Pragma no-cache; + default_type text/html; + content_by_lua_block { + local data = { + app_list = {"app1", "app2", "app3"} + } + require("resty.template").render_file("demoapps.html", data) + } +} diff --git a/conf/demoapp.conf b/conf/demoapp.conf deleted file mode 100644 index c3190f8..0000000 --- a/conf/demoapp.conf +++ /dev/null @@ -1,64 +0,0 @@ -listen 80; -set $template_root /var/ipax/templates; - -location / { - root /var/ipax/html/; - add_header Cache-Control no-store; - add_header Pragma no-cache; -} -location /landing { - add_header Cache-Control no-store; - add_header Pragma no-cache; - default_type text/html; - content_by_lua_block { - local data = { app_name = ngx.var.demoapp_alias } - require("resty.template").render_file("landing.html", data) - } -} -location /private { - add_header Cache-Control no-store; - add_header Pragma no-cache; - access_by_lua_block { - local oidc_opts = { - discovery = ngx.var.oidc_discovery, - client_id = ngx.var.client_id, - client_secret = ngx.var.client_secret, - scope = ngx.var.scope - } - local res = require("multiapps").get_res(oidc_opts, ngx.var.demoapp_base_url, ngx.var.prompt_override); - } -} -location /private/info { - add_header Cache-Control no-store; - add_header Pragma no-cache; - default_type text/html; - content_by_lua_block { - local multiapps = require("multiapps") - local oidc_opts = { - discovery = ngx.var.oidc_discovery, - client_id = ngx.var.client_id, - client_secret = ngx.var.client_secret, - scope = ngx.var.scope - } - local kc_actions = { - delete_account = ngx.var.kc_delete_account_action, - update_password = ngx.var.kc_update_password_action, - update_email = ngx.var.kc_update_email_action, - enrol_biometrics = ngx.var.kc_enrol_biometrics_action - } - local res = multiapps.get_res(oidc_opts, ngx.var.demoapp_base_url, ngx.var.prompt_override); - local data = { - access_token = multiapps.get_access_token(res), - refresh_token = multiapps.get_refresh_token(res), - app_name = ngx.var.demoapp_alias, - headers = ngx.resp.get_headers(), - id_token = multiapps.get_id_token(res), - logout_uri = os.getenv("OIDC_LOGOUT_URI"), - user = res.user, - user_actions = multiapps.get_user_actions(oidc_opts, kc_actions), - userinfo_json = multiapps.get_userinfo_json(res), - username = multiapps.get_preferred_username_from_userinfo_or_idtoken(res) - } - require("resty.template").render_file("info.html", data) - } -} diff --git a/conf/demoapp_default_variables.conf b/conf/demoapp_default_variables.conf deleted file mode 100644 index 32cdc1a..0000000 --- a/conf/demoapp_default_variables.conf +++ /dev/null @@ -1,12 +0,0 @@ -set $demoapp_alias 'IPAx'; -set $demoapp_base_url 'http://ipax'; -set $oidc_discovery ''; -set $client_id ''; -set $client_secret ''; -set $scope 'openid profile'; -set $prompt_override ''; - -set $kc_update_password_action ''; -set $kc_update_email_action ''; -set $kc_delete_account_action ''; -set $kc_enrol_biometrics_action ''; diff --git a/conf/demoapp_template.conf b/conf/demoapp_template.conf new file mode 100644 index 0000000..f7cc4be --- /dev/null +++ b/conf/demoapp_template.conf @@ -0,0 +1,38 @@ + set $template_root /var/ipax/templates; + + location / { + root /var/ipax/html/; + add_header Cache-Control no-store; + add_header Pragma no-cache; + } + location /landing { + add_header Cache-Control no-store; + add_header Pragma no-cache; + default_type text/html; + include /var/ipax/conf/default_variables.conf; + content_by_lua_block { + local data = { ipax_display_name = require("ipax").get_var_or_env("ipax_display_name") } + require("resty.template").render_file("landing.html", data) + } + } + location /private { + add_header Cache-Control no-store; + add_header Pragma no-cache; + default_type text/html; + include /var/ipax/conf/default_variables.conf; + access_by_lua_block { + local ipax = require("ipax") + local oidc_opts = ipax.get_oidc_opts() + local session_opts = ipax.get_session_opts() + ngx.ctx.res = ipax.get_res(oidc_opts, session_opts); + local ipax_display_name = ipax.get_var_or_env("ipax_display_name") + local ipax_app_name = ipax.get_var_or_env("ipax_app_name") + local ipax_base_url = ipax.get_var_or_env("ipax_base_url") + local headers = ngx.req.get_headers() + ngx.ctx.data = ipax.get_info_data(oidc_opts, session_opts, ipax_display_name, ipax_app_name, ipax_base_url, headers) + } + content_by_lua_block { + require("resty.template").render_file("info.html", ngx.ctx.data) + } + } + diff --git a/conf/demoapps/README.md b/conf/demoapps/README.md new file mode 100644 index 0000000..73be078 --- /dev/null +++ b/conf/demoapps/README.md @@ -0,0 +1,4 @@ +# demoapps variables + +This folder is mounted at execution time to override variables when using multiple demoapps in one container. +See [examples](../../localhost/demoapps/demoapps/) \ No newline at end of file diff --git a/conf/filter_cookie.conf b/conf/filter_cookie.conf index ae79e97..8d2e82b 100644 --- a/conf/filter_cookie.conf +++ b/conf/filter_cookie.conf @@ -1,24 +1,24 @@ map $http_cookie $filtered_cookie_1 { - "~(.*)(^|;\s)session=(\"[^\"]*\"|[^\s]*[^;]?)(\2|$|;$)(.*)" $1$4$5; + "~(.*)(^|;\s)ipax_session=(\"[^\"]*\"|[^\s]*[^;]?)(\2|$|;$)(.*)" $1$4$5; default $http_cookie; } map $filtered_cookie_1 $filtered_cookie_2 { - "~(.*)(^|;\s)session_1=(\"[^\"]*\"|[^\s]*[^;]?)(\2|$|;$)(.*)" $1$4$5; + "~(.*)(^|;\s)ipax_session_1=(\"[^\"]*\"|[^\s]*[^;]?)(\2|$|;$)(.*)" $1$4$5; default $filtered_cookie_1; } map $filtered_cookie_2 $filtered_cookie_3 { - "~(.*)(^|;\s)session_2=(\"[^\"]*\"|[^\s]*[^;]?)(\2|$|;$)(.*)" $1$4$5; + "~(.*)(^|;\s)ipax_session_2=(\"[^\"]*\"|[^\s]*[^;]?)(\2|$|;$)(.*)" $1$4$5; default $filtered_cookie_2; } map $filtered_cookie_3 $filtered_cookie_4 { - "~(.*)(^|;\s)session_3=(\"[^\"]*\"|[^\s]*[^;]?)(\2|$|;$)(.*)" $1$4$5; + "~(.*)(^|;\s)ipax_session_3=(\"[^\"]*\"|[^\s]*[^;]?)(\2|$|;$)(.*)" $1$4$5; default $filtered_cookie_3; } map $filtered_cookie_4 $filtered_cookie_5 { - "~(.*)(^|;\s)session_4=(\"[^\"]*\"|[^\s]*[^;]?)(\2|$|;$)(.*)" $1$4$5; + "~(.*)(^|;\s)ipax_session_4=(\"[^\"]*\"|[^\s]*[^;]?)(\2|$|;$)(.*)" $1$4$5; default $filtered_cookie_4; } map $filtered_cookie_5 $filtered_cookie { - "~(.*)(^|;\s)session_5=(\"[^\"]*\"|[^\s]*[^;]?)(\2|$|;$)(.*)" $1$4$5; + "~(.*)(^|;\s)ipax_session_5=(\"[^\"]*\"|[^\s]*[^;]?)(\2|$|;$)(.*)" $1$4$5; default $filtered_cookie_5; } \ No newline at end of file diff --git a/conf/health.conf b/conf/health.conf deleted file mode 100644 index 699915d..0000000 --- a/conf/health.conf +++ /dev/null @@ -1,5 +0,0 @@ -default_type application/json; - -content_by_lua_block { - ngx.say('{"status": "ok"}'); -} diff --git a/conf/info.conf b/conf/info.conf deleted file mode 100644 index fe550ee..0000000 --- a/conf/info.conf +++ /dev/null @@ -1,30 +0,0 @@ -default_type text/html; - -content_by_lua_block { - local ipax = require("ipax"); - local template = require("resty.template") - - -- id_token is returned as a lua table - local id_token = ipax.get_id_token(); - -- access_token is returned as string - local access_token = ipax.get_access_token(); - ngx.log(ngx.DEBUG, "access_token: " .. access_token) - local refresh_token = ipax.get_refresh_token(); - ngx.log(ngx.DEBUG, "refresh_token: " .. tostring(refresh_token)) - - - local data = { - user = ipax.get_user(), - headers = ngx.resp.get_headers(), - access_token = access_token, - refresh_token = refresh_token or "Not Provided in token endpoint response", - id_token = id_token, - userinfo_json = ipax.get_userinfo_json(), - username = ipax.get_preferred_username_from_userinfo_or_idtoken() or "Not Informed", - user_actions = ipax.get_user_actions(), - logout_uri = os.getenv("OIDC_LOGOUT_URI"), - app_name = os.getenv("IPAX_APP_NAME") - } - - template.render_file("info.html", data) -} diff --git a/conf/location_conf.d/.gitignore b/conf/location_conf.d/.gitignore new file mode 100644 index 0000000..4ba6098 --- /dev/null +++ b/conf/location_conf.d/.gitignore @@ -0,0 +1 @@ +/*.conf diff --git a/conf/location_conf.d/README.md b/conf/location_conf.d/README.md new file mode 100644 index 0000000..678571e --- /dev/null +++ b/conf/location_conf.d/README.md @@ -0,0 +1,3 @@ +# Path-based multihoming configuration files + +In demoapps `multi-path` mode, files in this folder are generated by the [ENTRYPOINT script](../../entrypoint.sh) diff --git a/conf/location_health.conf b/conf/location_health.conf new file mode 100644 index 0000000..953608b --- /dev/null +++ b/conf/location_health.conf @@ -0,0 +1,9 @@ +location /ipax/health { + add_header Cache-Control no-store; + add_header Pragma no-cache; + default_type application/json; + + content_by_lua_block { + ngx.say('{"status": "ok"}'); + } +} diff --git a/conf/location_norobots.conf b/conf/location_norobots.conf new file mode 100644 index 0000000..34f8f1a --- /dev/null +++ b/conf/location_norobots.conf @@ -0,0 +1,11 @@ +location /robots.txt { + add_header Cache-Control no-store; + add_header Pragma no-cache; + default_type text/plain; + + content_by_lua_block { + ngx.say("User-agent: *"); + ngx.say("Disallow: /"); + } +} + diff --git a/conf/lua.conf b/conf/lua.conf deleted file mode 100644 index 88c421d..0000000 --- a/conf/lua.conf +++ /dev/null @@ -1,11 +0,0 @@ -lua_package_path '/etc/ipax/lua/?.lua;;'; - -# cache for discovery metadata documents -lua_shared_dict discovery 1m; -# cache for JWKs -lua_shared_dict jwks 1m; -# cache for sessions storage -lua_shared_dict sessions 10m; - -# bigger (chunked) cookie size to accomodate 'member_of' Claim -large_client_header_buffers 8 32k; diff --git a/conf/lua_shared_dict/.gitignore b/conf/lua_shared_dict/.gitignore new file mode 100644 index 0000000..4ba6098 --- /dev/null +++ b/conf/lua_shared_dict/.gitignore @@ -0,0 +1 @@ +/*.conf diff --git a/conf/nginx.conf.template b/conf/nginx.conf.template index 6d75090..7bd3b6f 100644 --- a/conf/nginx.conf.template +++ b/conf/nginx.conf.template @@ -1,7 +1,3 @@ -env SESSION_SECRET; -env SESSION_COOKIE_PERSISTENT; -env SESSION_COOKIE_LIFETIME; -env SESSION_COOKIE_SAMESITE; env OIDC_DISCOVERY; env OIDC_SSL_VERIFY; env OIDC_CLIENT_ID; @@ -9,10 +5,20 @@ env OIDC_USE_PKCE; env OIDC_CLIENT_SECRET; env OIDC_SCOPE; env OIDC_REDIRECT_URI; -env OIDC_LOGOUT_URI; +env OIDC_LOGOUT_PATH; env OIDC_POST_LOGOUT_REDIRECT_URI; env OIDC_PROMPT; env OIDC_ACR_VALUES; +env SESSION_COOKIE_SAME_SITE; +env SESSION_COOKIE_SECURE; +env SESSION_IDLING_TIMEOUT; +env SESSION_REMEMBER; +env SESSION_SECRET; +env IPAX_APP_NAME; +env IPAX_DISPLAY_NAME; +env IPAX_BASE_URL; +env IPAX_MODE; +env API_BASE_URL; env KC_DELETE_ACCOUNT_ACTION; env KC_DELETE_ACCOUNT_LABEL; env KC_UPDATE_EMAIL_ACTION; @@ -21,8 +27,9 @@ env KC_UPDATE_PASSWORD_ACTION; env KC_UPDATE_PASSWORD_LABEL; env KC_ENROL_BIOMETRICS_ACTION; env KC_ENROL_BIOMETRICS_LABEL; -env IPAX_APP_NAME; -env API_BASEURL; +env KC_ADD_PASSKEY_ACTION; +env KC_ADD_PASSKEY_LABEL; + error_log stderr $NGINX_LOG_LEVEL; events { @@ -43,9 +50,14 @@ http { proxy_buffers 8 32k; proxy_buffer_size 32k; - include lua.conf; - include filter_cookie.conf; - include server.conf; + lua_package_path '/var/ipax/lua/?.lua;;'; + + include /var/ipax/conf/lua_shared_dict/*.conf; + + # bigger (chunked) cookie size to accomodate 'member_of' Claim + large_client_header_buffers 8 32k; + + variables_hash_max_size 2048; - include /etc/ipax/conf.d/*.conf; + include /usr/local/openresty/nginx/conf/server.conf; } diff --git a/conf/norobots.conf b/conf/norobots.conf deleted file mode 100644 index d798a3f..0000000 --- a/conf/norobots.conf +++ /dev/null @@ -1,6 +0,0 @@ -default_type text/plain; - -content_by_lua_block { - ngx.say("User-agent: *"); - ngx.say("Disallow: /"); -} diff --git a/conf/server.conf b/conf/server.conf deleted file mode 100644 index ec9f1cb..0000000 --- a/conf/server.conf +++ /dev/null @@ -1,46 +0,0 @@ -server { - set $template_root /var/ipax/templates; - listen 80; - location / { - root /var/ipax/html/; - add_header Cache-Control no-store; - add_header Pragma no-cache; - # if ($request_uri = /){ - # return 302 $scheme://$server_name:$server_port/landing; - # } - } - location /landing { - add_header Cache-Control no-store; - add_header Pragma no-cache; - default_type text/html; - content_by_lua_block { - local template = require("resty.template") - local data = { - app_name = os.getenv("IPAX_APP_NAME") - } - template.render_file("landing.html", data) - } - } - location /private/ { - add_header Cache-Control no-store; - add_header Pragma no-cache; - access_by_lua_block { - local user = require("ipax").get_user(); - } - } - location /private/info { - add_header Cache-Control no-store; - add_header Pragma no-cache; - include info.conf; - } - location /robots.txt { - add_header Cache-Control no-store; - add_header Pragma no-cache; - include norobots.conf; - } - location /ipax/health { - add_header Cache-Control no-store; - add_header Pragma no-cache; - include health.conf; - } -} \ No newline at end of file diff --git a/conf/server/demoapp.conf b/conf/server/demoapp.conf new file mode 100644 index 0000000..c61e1e6 --- /dev/null +++ b/conf/server/demoapp.conf @@ -0,0 +1,7 @@ +server { + listen 80; + + include /var/ipax/conf/demoapp_template.conf; + include /var/ipax/conf/location_norobots.conf; + include /var/ipax/conf/location_health.conf; +} \ No newline at end of file diff --git a/conf/server/demoapps.conf b/conf/server/demoapps.conf new file mode 100644 index 0000000..2fe47a2 --- /dev/null +++ b/conf/server/demoapps.conf @@ -0,0 +1,5 @@ +server { + include /var/ipax/conf/default_vhost.conf; + + include /var/ipax/conf/location_conf.d/*.conf; +} \ No newline at end of file diff --git a/conf/server/proxy.conf b/conf/server/proxy.conf new file mode 100644 index 0000000..5108e03 --- /dev/null +++ b/conf/server/proxy.conf @@ -0,0 +1,4 @@ +server { + include /var/ipax/conf/default_vhost.conf; +} +include /var/ipax/conf/conf.d/*.conf; \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..b8addbe --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +get_demoapps_list() { + local demoapps_list="" + for demoapp_conf_file in ${DEMOAPPS_VARIABLES_CONFIG_PATH}/*.conf; do + demoapp_name=$(basename "$demoapp_conf_file" .conf) + demoapps_list+="$demoapp_name " + done + # Trim trailing space and print + echo "${demoapps_list%% }" +} + +delete_lua_shared_dict() { + echo "Deleting existing ${LUA_SHARED_DICT_PATH}/*.conf" + rm -f ${LUA_SHARED_DICT_PATH}/*.conf +} + +create_lua_shared_dict_file() { + local demoapp_name="$1" + local lua_shared_dict_path="${LUA_SHARED_DICT_PATH}/${demoapp_name}.conf" + echo "Creating ${lua_shared_dict_path}" + echo "lua_shared_dict ${demoapp_name}_jwks 1m;" > ${lua_shared_dict_path} + echo "lua_shared_dict ${demoapp_name}_discovery 1m;" >> ${lua_shared_dict_path} + echo "lua_shared_dict ${demoapp_name}_oidc_state 1m;" >> ${lua_shared_dict_path} + echo "lua_shared_dict ${demoapp_name}_oidc_access_tokens 1m;" >> ${lua_shared_dict_path} + echo "lua_shared_dict ${demoapp_name}_oidc_refresh_tokens 1m;" >> ${lua_shared_dict_path} + echo "lua_shared_dict ${demoapp_name}_oidc_id_tokens 1m;" >> ${lua_shared_dict_path} +} + +delete_multi_configs() { + echo "Deleting existing ${DEMOAPPS_CONFIG_PATH}/*.conf" + rm -f ${DEMOAPPS_CONFIG_PATH}/*.conf +} + +create_multi_config_file() { + local demoapp_name="$1" + local demoapps_config_path="${DEMOAPPS_CONFIG_PATH}/${demoapp_name}.conf" + echo "Creating ${demoapps_config_path}" + cat /var/ipax/conf/demoapp_template.conf > ${demoapps_config_path} + sed -i "s#include /var/ipax/conf/default_variables.conf;#include /var/ipax/conf/default_variables.conf;\n include ${DEMOAPPS_VARIABLES_CONFIG_PATH}/${demoapp_name}.conf;#g" ${demoapps_config_path} + sed -i "s#location /#location /${demoapp_name}/#g" ${demoapps_config_path} + sed -i "s#root /var/ipax/html/#alias /var/ipax/html/#g" ${demoapps_config_path} +} + +NGINX_CONF_TEMPLATE="/var/ipax/conf/nginx.conf.template" +NGINX_CONF="/usr/local/openresty/nginx/conf/nginx.conf" +echo "Replacing variables in ${NGINX_CONF_TEMPLATE} to generate ${NGINX_CONF}" +envsubst < ${NGINX_CONF_TEMPLATE} > ${NGINX_CONF} + +echo "IPAX_MODE: '${IPAX_MODE}'" +if [ "$IPAX_MODE" = "proxy" ]; then + delete_lua_shared_dict + create_lua_shared_dict_file "${IPAX_APP_NAME}" +fi + +if [ "$IPAX_MODE" = "single" ]; then + cp /var/ipax/conf/server/demoapp.conf /usr/local/openresty/nginx/conf/server.conf + delete_lua_shared_dict + create_lua_shared_dict_file "${IPAX_APP_NAME}" +fi + +if [ "$IPAX_MODE" = "demoapps" ]; then + cp /var/ipax/conf/server/demoapps.conf /usr/local/openresty/nginx/conf/server.conf + delete_lua_shared_dict + delete_multi_configs + demoapps_list=$(get_demoapps_list) + for demoapp_name in $demoapps_list; do + echo "Processing demoapp: '${demoapp_name}'" + create_lua_shared_dict_file "${demoapp_name}" + create_multi_config_file "${demoapp_name}" + done + echo "Finished processing demoapps" +fi + +# troubleshooting +# tail -f /etc/alpine-release + +echo "Starting nginx..." +/usr/local/openresty/bin/openresty -g 'daemon off;' diff --git a/html/index.html b/html/index.html index 266a964..c5f7e0b 100644 --- a/html/index.html +++ b/html/index.html @@ -1,12 +1,12 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/html/loginError.html b/html/loginError.html new file mode 100644 index 0000000..b6ed061 --- /dev/null +++ b/html/loginError.html @@ -0,0 +1,9 @@ + + + + Identicum + + +

Login error

+ + \ No newline at end of file diff --git a/multiapps/compose.yaml b/localhost/demoapps/compose.yaml similarity index 56% rename from multiapps/compose.yaml rename to localhost/demoapps/compose.yaml index 4fc76d7..9bf0b57 100644 --- a/multiapps/compose.yaml +++ b/localhost/demoapps/compose.yaml @@ -1,3 +1,5 @@ +name: ipax-demoapps + services: idp: container_name: idp @@ -7,36 +9,36 @@ services: ports: - 8080:8080 environment: - KEYCLOAK_ADMIN: admin - KEYCLOAK_ADMIN_PASSWORD: admin + KC_BOOTSTRAP_ADMIN_USERNAME: admin + KC_BOOTSTRAP_ADMIN_PASSWORD: admin KC_FEATURES: update-email volumes: - ./keycloak/:/opt/keycloak/data/import/ command: "start-dev --import-realm" demoapps: container_name: demoapps - image: ghcr.io/identicum/ipax:latest - pull_policy: always + image: ghcr.io/identicum/ipax:refactor restart: always ports: - 80:80 environment: NGINX_RESOLVER: 127.0.0.11 valid=5s NGINX_LOG_LEVEL: debug + IPAX_MODE: demoapps volumes: - - ../lua/:/etc/ipax/lua/ - - ../conf/nginx.conf.template:/usr/local/openresty/nginx/conf/nginx.conf.template - - ../conf/demoapp.conf:/usr/local/openresty/nginx/conf/demoapp.conf - - ../conf/demoapp_default_variables.conf:/usr/local/openresty/nginx/conf/demoapp_default_variables.conf - - ../templates:/var/ipax/templates/ - - ./server.conf:/usr/local/openresty/nginx/conf/server.conf - - ./conf.d/:/etc/ipax/conf.d/ + - ../../entrypoint.sh:/entrypoint.sh + - ../../conf/:/var/ipax/conf/ + - ./conf/:/var/ipax/conf/demoapps/ + - ../../html/:/var/ipax/html/ + - ../../lua/:/var/ipax/lua/ + - ../../templates:/var/ipax/templates/ depends_on: idp: condition: service_healthy mailcatcher: container_name: mailcatcher image: ghcr.io/identicum/mailcatcher:latest + pull_policy: always restart: always ports: - 1080:1080 \ No newline at end of file diff --git a/localhost/demoapps/conf/demoapp1.conf b/localhost/demoapps/conf/demoapp1.conf new file mode 100644 index 0000000..a0e1e11 --- /dev/null +++ b/localhost/demoapps/conf/demoapp1.conf @@ -0,0 +1,12 @@ +set $ipax_app_name 'demoapp1'; +set $ipax_display_name 'DemoApp1'; +set $ipax_base_url 'http://demoapps/demoapp1'; + +set $oidc_discovery 'http://idp:8080/realms/demorealm/.well-known/openid-configuration'; +set $oidc_client_id 'demoapp1_client_id'; +set $oidc_client_secret 'demoapp1_client_secret'; + +set $kc_update_password_action 'UPDATE_PASSWORD'; +set $kc_update_email_action 'UPDATE_EMAIL'; + +set $kc_add_passkey_action 'webauthn-register-passwordless'; \ No newline at end of file diff --git a/localhost/demoapps/conf/demoapp2.conf b/localhost/demoapps/conf/demoapp2.conf new file mode 100644 index 0000000..300b03d --- /dev/null +++ b/localhost/demoapps/conf/demoapp2.conf @@ -0,0 +1,10 @@ +set $ipax_app_name 'demoapp2'; +set $ipax_display_name 'DemoApp2'; +set $ipax_base_url 'http://demoapps/demoapp2'; + +set $oidc_discovery 'http://idp:8080/realms/demorealm/.well-known/openid-configuration'; +set $oidc_client_id 'demoapp2_client_id'; +set $oidc_use_pkce 'true'; +set $oidc_scope 'openid profile email'; + +set $kc_delete_account_action 'delete_account'; \ No newline at end of file diff --git a/localhost/demoapps/conf/demoapp3.conf b/localhost/demoapps/conf/demoapp3.conf new file mode 100644 index 0000000..525548c --- /dev/null +++ b/localhost/demoapps/conf/demoapp3.conf @@ -0,0 +1,11 @@ +set $ipax_app_name 'demoapp3'; +set $ipax_display_name 'DemoApp3'; +set $ipax_base_url 'http://demoapps/demoapp3'; + +set $oidc_discovery 'http://idp:8080/realms/demorealm/.well-known/openid-configuration'; +set $oidc_client_id 'demoapp3_client_id'; +set $oidc_use_pkce 'true'; +set $oidc_scope 'openid profile email'; +set $oidc_prompt 'none'; + +set $kc_delete_account_action 'delete_account'; \ No newline at end of file diff --git a/multiapps/keycloak/demorealm.json b/localhost/demoapps/keycloak/demorealm.json similarity index 72% rename from multiapps/keycloak/demorealm.json rename to localhost/demoapps/keycloak/demorealm.json index 7cace92..8dca2ac 100644 --- a/multiapps/keycloak/demorealm.json +++ b/localhost/demoapps/keycloak/demorealm.json @@ -18,8 +18,8 @@ "directAccessGrantsEnabled": false, "serviceAccountsEnabled": false, "frontchannelLogout": true, - "rootUrl": "http://demoapp1", - "baseUrl": "http://demoapp1", + "rootUrl": "http://demoapps/demoapp1", + "baseUrl": "/", "redirectUris": [ "/private/redirect_uri", "/private/info" @@ -33,14 +33,35 @@ { "name": "demoapp2", "clientId": "demoapp2_client_id", - "secret": "demoapp2_client_secret", + "publicClient": true, "standardFlowEnabled": true, "implicitFlowEnabled": false, "directAccessGrantsEnabled": false, "serviceAccountsEnabled": false, "frontchannelLogout": true, - "rootUrl": "http://demoapp2", - "baseUrl": "http://demoapp2", + "rootUrl": "http://demoapps/demoapp2", + "baseUrl": "/", + "redirectUris": [ + "/private/redirect_uri", + "/private/info" + ], + "defaultClientScopes": [ ], + "optionalClientScopes": [ "profile", "email", "roles", "phone" ], + "attributes": { + "post.logout.redirect.uris": "/logoutSuccess.html" + } + }, + { + "name": "demoapp3", + "clientId": "demoapp3_client_id", + "publicClient": true, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "frontchannelLogout": true, + "rootUrl": "http://demoapps/demoapp3", + "baseUrl": "/", "redirectUris": [ "/private/redirect_uri", "/private/info" @@ -103,6 +124,15 @@ "defaultAction": false, "priority": 61, "config": {} + }, + { + "alias": "webauthn-register-passwordless", + "name": "Webauthn Register Passwordless", + "providerId": "webauthn-register-passwordless", + "enabled": true, + "defaultAction": false, + "priority": 62, + "config": {} } ], "smtpServer": { diff --git a/compose.yaml b/localhost/single/compose.yaml similarity index 52% rename from compose.yaml rename to localhost/single/compose.yaml index 1c79550..239a574 100644 --- a/compose.yaml +++ b/localhost/single/compose.yaml @@ -1,3 +1,5 @@ +name: ipax-single + services: idp: container_name: idp @@ -7,8 +9,8 @@ services: ports: - 8080:8080 environment: - KEYCLOAK_ADMIN: admin - KEYCLOAK_ADMIN_PASSWORD: admin + KC_BOOTSTRAP_ADMIN_USERNAME: admin + KC_BOOTSTRAP_ADMIN_PASSWORD: admin KC_FEATURES: update-email volumes: - ./keycloak/:/opt/keycloak/data/import/ @@ -16,7 +18,7 @@ services: ipax: container_name: ipax - image: ghcr.io/identicum/ipax:latest + image: ghcr.io/identicum/ipax:refactor restart: always ports: - 80:80 @@ -25,31 +27,21 @@ services: NGINX_LOG_LEVEL: debug OIDC_DISCOVERY: http://idp:8080/realms/demorealm/.well-known/openid-configuration OIDC_CLIENT_ID: ipax_client_id - OIDC_USE_PKCE: "false" OIDC_CLIENT_SECRET: ipax_client_secret OIDC_SCOPE: openid profile email roles phone - OIDC_POST_LOGOUT_REDIRECT_URI: http://localhost/logoutSuccess.html + IPAX_DISPLAY_NAME: IPAx single demoapp + IPAX_BASE_URL: http://demoapp1 + IPAX_MODE: single KC_DELETE_ACCOUNT_ACTION: delete_account KC_DELETE_ACCOUNT_LABEL: Delete user KC_UPDATE_EMAIL_ACTION: UPDATE_EMAIL KC_UPDATE_PASSWORD_ACTION: UPDATE_PASSWORD volumes: - - ./conf/health.conf:/usr/local/openresty/nginx/conf/health.conf - - ./conf/info.conf:/usr/local/openresty/nginx/conf/info.conf - - ./conf/lua.conf:/usr/local/openresty/nginx/conf/lua.conf - - ./conf/filter_cookie.conf:/usr/local/openresty/nginx/conf/filter_cookie.conf - - ./conf/nginx.conf.template:/usr/local/openresty/nginx/conf/nginx.conf.template - - ./conf/norobots.conf:/usr/local/openresty/nginx/conf/norobots.conf - - ./conf/server.conf:/usr/local/openresty/nginx/conf/server.conf - - ./html/:/var/ipax/html/ - - ./lua/:/etc/ipax/lua/ - - ./templates:/var/ipax/templates/ - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost/ipax/health"] - interval: 10s - timeout: 1s - retries: 5 - start_period: 5s + - ../../entrypoint.sh:/entrypoint.sh + - ../../conf/:/var/ipax/conf/ + - ../../html/:/var/ipax/html/ + - ../../lua/:/var/ipax/lua/ + - ../../templates:/var/ipax/templates/ depends_on: idp: condition: service_healthy diff --git a/keycloak/demorealm.json b/localhost/single/keycloak/demorealm.json similarity index 96% rename from keycloak/demorealm.json rename to localhost/single/keycloak/demorealm.json index 0594159..a63e579 100644 --- a/keycloak/demorealm.json +++ b/localhost/single/keycloak/demorealm.json @@ -18,8 +18,8 @@ "directAccessGrantsEnabled": false, "serviceAccountsEnabled": false, "frontchannelLogout": true, - "rootUrl": "http://localhost", - "baseUrl": "http://localhost", + "rootUrl": "http://demoapp1", + "baseUrl": "/", "redirectUris": [ "/private/redirect_uri", "/private/info" diff --git a/lua/ipax.lua b/lua/ipax.lua index 20844dc..e91276a 100644 --- a/lua/ipax.lua +++ b/lua/ipax.lua @@ -1,355 +1,262 @@ --- -------------------------------------------------------------------------------------------------------------- --- -------------------------------------------------------------------------------------------------------------- --- https://github.com/zmartzone/lua-resty-openidc/blob/master/lib/resty/openidc.lua -local http = require("resty.http") -local function openidc_cache_get(type, key) - local dict = ngx.shared[type] - local value - if dict then - value = dict:get(key) - if value then ngx.log(ngx.DEBUG, "cache hit: type=", type, " key=", key) end - end - return value - end - local function openidc_configure_timeouts(httpc, timeout) - if timeout then - if type(timeout) == "table" then - local r, e = httpc:set_timeouts(timeout.connect or 0, timeout.send or 0, timeout.read or 0) - else - local r, e = httpc:set_timeout(timeout) - end - end - end - -- Set outgoing proxy options -local function openidc_configure_proxy(httpc, proxy_opts) - if httpc and proxy_opts and type(proxy_opts) == "table" then - ngx.log(ngx.DEBUG, "openidc_configure_proxy : use http proxy") - httpc:set_proxy_options(proxy_opts) - else - ngx.log(ngx.DEBUG, "openidc_configure_proxy : don't use http proxy") - end - end --- get the Discovery metadata from the specified URL -local function openidc_discover(url, ssl_verify, keepalive, timeout, exptime, proxy_opts, http_request_decorator) - ngx.log(ngx.DEBUG, "openidc_discover: URL is: " .. url) - local json, err - local v = openidc_cache_get("discovery", url) - if not v then - ngx.log(ngx.DEBUG, "discovery data not in cache, making call to discovery endpoint") - -- make the call to the discovery endpoint - local httpc = http.new() - openidc_configure_timeouts(httpc, timeout) - openidc_configure_proxy(httpc, proxy_opts) - local res, error = httpc:request_uri(url, decorate_request(http_request_decorator, { - ssl_verify = (ssl_verify ~= "no"), - keepalive = (keepalive ~= "no") - })) - if not res then - err = "accessing discovery url (" .. url .. ") failed: " .. error - ngx.log(ngx.DEBUG, err) - else - ngx.log(ngx.DEBUG, "response data: " .. res.body) - json, err = openidc_parse_json_response(res) - if json then - openidc_cache_set("discovery", url, cjson.encode(json), exptime or 24 * 60 * 60) - else - err = "could not decode JSON from Discovery data" .. (err and (": " .. err) or '') - ngx.log(ngx.DEBUG, err) - end - end - else - json = cjson.decode(v) - end - return json, err -end +local _M = {} --- turn a discovery url set in the opts dictionary into the discovered information -local function openidc_ensure_discovered_data(opts) - local err - if type(opts.discovery) == "string" then - local discovery - discovery, err = openidc_discover(opts.discovery, opts.ssl_verify, opts.keepalive, opts.timeout, opts.discovery_expires_in, opts.proxy_opts, opts.http_request_decorator) - if not err then - opts.discovery = discovery - end - end - return err +local function is_true(input) + if string.lower(input) == "true" then + return true + else + return false + end end -local function get_first(table_or_string) - local res = table_or_string - if table_or_string and type(table_or_string) == 'table' then - res = table_or_string[1] +local function get_authorization_params(oidc_acr_values) + ngx.log(ngx.DEBUG, "Starting") + local authorization_params_table = {} + if oidc_acr_values ~= '' then + authorization_params_table["acr_values"]=oidc_acr_values end - return res + return authorization_params_table end -local function get_first_header(headers, header_name) - local header = headers[header_name] - return get_first(header) +function _M.get_var_or_env(ngx_var_key) + local env_var_name = string.upper(ngx_var_key) + if ngx.var[ngx_var_key] and ngx.var[ngx_var_key] ~= "" then + return ngx.var[ngx_var_key] + else + return os.getenv(env_var_name) + end end -local function get_first_header_and_strip_whitespace(headers, header_name) - local header = get_first_header(headers, header_name) - return header and header:gsub('%s', '') -end +function _M.get_oidc_opts() + ngx.log(ngx.DEBUG, "Starting") + local ipax_base_url = _M.get_var_or_env("ipax_base_url") -local function get_forwarded_parameter(headers, param_name) - local forwarded = get_first_header(headers, 'Forwarded') - local params = {} - if forwarded then - local function parse_parameter(pv) - local name, value = pv:match("^%s*([^=]+)%s*=%s*(.-)%s*$") - if name and value then - if value:sub(1, 1) == '"' then - value = value:sub(2, -2) - end - params[name:lower()] = value - end - end - -- this assumes there is no quoted comma inside the header's value which should be fine as comma is not legal inside a node name, a URI scheme or a host name. The only thing that might bite us are extensions. - local first_part = forwarded - local first_comma = forwarded:find("%s*,%s*") - if first_comma then - first_part = forwarded:sub(1, first_comma - 1) - end - first_part:gsub("[^;]+", parse_parameter) + local oidc_redirect_uri + if ngx.var.oidc_redirect_uri and ngx.var.oidc_redirect_uri ~= "" then + oidc_redirect_uri = ngx.var.oidc_redirect_uri + else + oidc_redirect_uri = ipax_base_url .. os.getenv("OIDC_REDIRECT_URI") end - return params[param_name:gsub("^%s*(.-)%s*$", "%1"):lower()] -end - -local function get_scheme(headers) - return get_forwarded_parameter(headers, 'proto') - or get_first_header_and_strip_whitespace(headers, 'X-Forwarded-Proto') - or ngx.var.scheme -end - -local function get_host_name_from_x_header(headers) - local header = get_first_header_and_strip_whitespace(headers, 'X-Forwarded-Host') - return header and header:gsub('^([^,]+),?.*$', '%1') -end - -local function get_host_name(headers) - return get_forwarded_parameter(headers, 'host') - or get_host_name_from_x_header(headers) - or ngx.var.http_host -end --- -------------------------------------------------------------------------------------------------------------- --- -------------------------------------------------------------------------------------------------------------- --- IPAx module -local _M = {} - - -local function isTrue(input) - if string.lower(input) == "true" then - return true + local oidc_post_logout_redirect_uri + if ngx.var.oidc_post_logout_redirect_uri and ngx.var.oidc_post_logout_redirect_uri ~= "" then + oidc_post_logout_redirect_uri = ngx.var.oidc_post_logout_redirect_uri else - return false - end -end - -local function getAuthorizationParams() - local authorizationParamsTable = {} + oidc_post_logout_redirect_uri = ipax_base_url .. os.getenv("OIDC_POST_LOGOUT_REDIRECT_URI") + end - local acr_values = os.getenv("OIDC_ACR_VALUES") - if acr_values ~= '' then - authorizationParamsTable["acr_values"]=acr_values + local logout_path = _M.get_var_or_env("oidc_logout_path") + if os.getenv("IPAX_MODE")=='demoapps' then + logout_path = "/" .. _M.get_var_or_env("ipax_app_name") .. logout_path end - return authorizationParamsTable + local oidc_opts = { + discovery = _M.get_var_or_env("oidc_discovery"), + ssl_verify = _M.get_var_or_env("oidc_ssl_verify"), + client_id = _M.get_var_or_env("oidc_client_id"), + use_pkce = is_true(_M.get_var_or_env("oidc_use_pkce")), + scope = _M.get_var_or_env("oidc_scope"), + redirect_uri = oidc_redirect_uri, + logout_path = logout_path, + post_logout_redirect_uri = oidc_post_logout_redirect_uri, + authorization_params = get_authorization_params(_M.get_var_or_env("oidc_acr_values")), + renew_access_token_on_expiry = true, + session_contents = {id_token=true, enc_id_token=true, access_token=true, user=true}, + -- Custom shared dictionaries per instance + jwks_uri_lua_shared_dict = _M.get_var_or_env("ipax_app_name") .. "_jwks", + discovery_lua_shared_dict = _M.get_var_or_env("ipax_app_name") .. "_discovery", + state_lua_shared_dict = _M.get_var_or_env("ipax_app_name") .. "_oidc_state", + access_tokens_lua_shared_dict = _M.get_var_or_env("ipax_app_name") .. "_oidc_access_tokens", + refresh_tokens_lua_shared_dict = _M.get_var_or_env("ipax_app_name") .. "_oidc_refresh_tokens", + id_tokens_lua_shared_dict = _M.get_var_or_env("ipax_app_name") .. "_oidc_id_tokens" + } + local oidc_client_secret = _M.get_var_or_env("oidc_client_secret") + if oidc_client_secret ~= '' then + oidc_opts["client_secret"]=oidc_client_secret + end + local prompt_override = _M.get_var_or_env("oidc_prompt") + if prompt_override ~= '' then + oidc_opts["prompt"]=prompt_override + end + return oidc_opts end -local oidc_opts = { - discovery = os.getenv("OIDC_DISCOVERY"), - ssl_verify = os.getenv("OIDC_SSL_VERIFY"), - client_id = os.getenv("OIDC_CLIENT_ID"), - use_pkce = isTrue(os.getenv("OIDC_USE_PKCE")), - client_secret = os.getenv("OIDC_CLIENT_SECRET"), - scope = os.getenv("OIDC_SCOPE"), - redirect_uri = os.getenv("OIDC_REDIRECT_URI"), - logout_path = os.getenv("OIDC_LOGOUT_URI"), - post_logout_redirect_uri = os.getenv("OIDC_POST_LOGOUT_REDIRECT_URI"), - authorization_params = getAuthorizationParams(), - renew_access_token_on_expiry = true, - session_contents = {id_token=true, enc_id_token=true, access_token=true, user=true} -} -local prompt_override = os.getenv("OIDC_PROMPT") -if prompt_override ~= '' then - oidc_opts["prompt"]=prompt_override +function _M.get_session_opts() + ngx.log(ngx.DEBUG, "Starting") + local session_opts = { + cookie_name = _M.get_var_or_env("ipax_app_name") .. "_session", + cookie_same_site = _M.get_var_or_env("session_cookie_same_site"), + cookie_secure = is_true(_M.get_var_or_env("session_cookie_secure")), + idling_timeout = tonumber(_M.get_var_or_env("session_idling_timeout")), + remember = is_true(_M.get_var_or_env("session_remember")), + remember_cookie_name = _M.get_var_or_env("ipax_app_name") .. "_remember", + secret = _M.get_var_or_env("ipax_base_url") .. _M.get_var_or_env("session_secret"), + cookie_http_only = true + } + return session_opts end -local session_opts = { - secret = os.getenv("SESSION_SECRET"), - cookie = { - persistent = os.getenv("SESSION_COOKIE_PERSISTENT"), - lifetime = os.getenv("SESSION_COOKIE_LIFETIME"), - samesite = os.getenv("SESSION_COOKIE_SAMESITE") - } -} +function _M.get_id_token(res) + ngx.log(ngx.DEBUG, "Starting") + return res.id_token +end -local function split(input, separator) - if separator == nil then - separator = "%s" - end - local t={} - for str in string.gmatch(input, "([^" .. separator .. "]+)") do - table.insert(t, str) - end - return t +function _M.get_access_token(res) + ngx.log(ngx.DEBUG, "Starting") + return res.access_token end -function _M.check_authentication(err) - if err then - -- ngx.log(ngx.DEBUG, "check_authentication() err: " .. err) - error = string.match(err, "error=(.*)&+") - if (error == 'login_required') then - return ngx.redirect("/loginRequired.html", ngx.HTTP_MOVED_TEMPORARILY) - else - return ngx.redirect("/authenticationError.html", ngx.HTTP_MOVED_TEMPORARILY) - end - end - return true +local function get_refresh_token(res) + ngx.log(ngx.DEBUG, "Starting") + local refresh_token = res.refresh_token or nil + return refresh_token end -function _M.get_user() - local res = _M.get_res() - return res.user +function _M.get_user(res) + ngx.log(ngx.DEBUG, "Starting") + local user = res.user or nil + return user end -function _M.get_userinfo_json() - local res = _M.get_res() +function _M.get_userinfo_json(res) + ngx.log(ngx.DEBUG, "Starting") local json = require("json").encode(res.user) ngx.log(ngx.DEBUG, "userinfo_json: " .. json) return json end -function _M.get_preferred_name_from_userinfo() - ngx.log(ngx.DEBUG, "preferred_username: " .. (preferred_username or "nil")) - local userinfo_json = _M.get_userinfo_json() +local function get_preferred_name_from_userinfo(res) + ngx.log(ngx.DEBUG, "Starting") + local userinfo_json = _M.get_userinfo_json(res) local userinfo_table = require("json").decode(userinfo_json) - local preferred_username = userinfo_table.preferred_username - ngx.log(ngx.DEBUG, "preferred_username: " .. (preferred_username or "nil")) + local preferred_username = userinfo_table.preferred_username or "nil" + ngx.log(ngx.DEBUG, "returning preferred_username: " .. preferred_username) return preferred_username end -function _M.get_preferred_username_from_userinfo_or_idtoken() - local userinfo_preferred_username = _M.get_preferred_name_from_userinfo() - +local function get_preferred_username_from_userinfo_or_idtoken(res) + ngx.log(ngx.DEBUG, "Starting") + local userinfo_preferred_username = get_preferred_name_from_userinfo(res) if userinfo_preferred_username == nil then local id_token = _M.get_id_token() return id_token.preferred_username else return userinfo_preferred_username end - -end - -function _M.get_id_token() - local res = _M.get_res() - return res.id_token -end - -function _M.get_access_token() - local res = _M.get_res() - return res.access_token end -function _M.get_refresh_token() - local res = _M.get_res() - local refresh_token = res.refresh_token or nil - return refresh_token -end +function _M.get_res(oidc_opts, session_opts) + ngx.log(ngx.DEBUG, "Starting for client_id: " .. oidc_opts.client_id) + -- for k, v in pairs(oidc_opts) do + -- ngx.log(ngx.DEBUG, "Using oidc_opts[" .. k .. "] = " .. tostring(v)) + -- end + -- for k, v in pairs(session_opts) do + -- ngx.log(ngx.DEBUG, "Using session_opts[" .. k .. "] = " .. tostring(v)) + -- end + local res, err, target, session = require("resty.openidc").authenticate(oidc_opts, nil, nil, session_opts) + if err then + if string.find(err, "error=login_required") then + ngx.redirect("../loginRequired.html", ngx.HTTP_MOVED_TEMPORARILY) + else + ngx.log(ngx.ERR, "Authentication failed: ", err) + ngx.redirect("../loginError.html", ngx.HTTP_MOVED_TEMPORARILY) + end + end -function _M.get_res() - local res, err, target, session = require("resty.openidc").authenticate(oidc_opts, null, action, session_opts) - --ngx.log(ngx.DEBUG, "refresh_token: " .. session:get("refresh_token")) res["refresh_token"] = session:get("refresh_token") session:close() - local authentication_feedback = _M.check_authentication(err) return res end -function _M.check_multivalued_user_claim(claim_values, check_item) - for index, value in pairs(claim_values) do - -- ToDo: compare case-insensitive - if value == check_item then - return true - end - end - ngx.exit(ngx.HTTP_FORBIDDEN) - return false -end - -function _M.is_value_in_list(value_list, check_item) - for index, value in pairs(value_list) do - if value == check_item then - return true - end - end - return false -end - -function _M.get_names_from_dns(object_dns) - local object_names={} - if object_dns == nil then - ngx.log(ngx.DEBUG, 'get_names_from_dns() object_dns is nil') - return object_names - end - for index, value in pairs(object_dns) do - local object_rdn = split(value, ",")[1] - local object_name = split(object_rdn, "=")[2] - object_names[index] = object_name - end - return object_names -end - -function _M.get_group_names(claim_values, separator) - if separator == nil then - separator = "|" - end - local group_names = _M.get_names_from_dns(claim_values) - return table.concat(group_names, separator) -end - -local function get_kc_user_action_url(kc_action) - local headers = ngx.req.get_headers() - local redirect_uri = get_scheme(headers) .. "://" .. get_host_name(headers) .. "/private/info" +local function get_kc_user_action_url(ipax_base_url, client_id, kc_action, authorization_endpoint) + ngx.log(ngx.DEBUG, "Starting for client_id: " .. client_id) + local redirect_uri = ipax_base_url .. "/private/info" local params = { - client_id = oidc_opts.client_id, + client_id = client_id, response_type = "code", scope = "openid", redirect_uri = redirect_uri, kc_action = kc_action } - openidc_ensure_discovered_data(oidc_opts) - return oidc_opts.discovery.authorization_endpoint .. "?" .. ngx.encode_args(params) + return authorization_endpoint .. "?" .. ngx.encode_args(params) +end + +local function get_discovery_document(oidc_opts) + ngx.log(ngx.DEBUG, "Starting for discovery: " .. oidc_opts.discovery) + local http = require("resty.http") + local http = require("resty.http") + local httpc = http.new() + local res, err = httpc:request_uri(oidc_opts.discovery, { method = "GET", oidc_opts.ssl_verify }) + if not res then + ngx.log(ngx.ERR, "failed to request discovery document: ", err) + return nil + end + if res.status ~= 200 then + ngx.log(ngx.ERR, "discovery document request failed with status: ", res.status) + return nil + end + return require("cjson").decode(res.body) end -function _M.get_user_actions() +local function get_user_actions(oidc_opts, ipax_base_url) + ngx.log(ngx.DEBUG, "Starting for client_id: " .. oidc_opts.client_id) local userActionsTable = {} + local discovery_document = get_discovery_document(oidc_opts) + local authorization_endpoint = discovery_document.authorization_endpoint + local client_id = oidc_opts.client_id - local kc_delete_account_action = os.getenv("KC_DELETE_ACCOUNT_ACTION") + local kc_delete_account_action = _M.get_var_or_env("kc_delete_account_action") if kc_delete_account_action ~= '' then - userActionsTable["kc_delete_account_action"]='' .. os.getenv("KC_DELETE_ACCOUNT_LABEL") .. '' + userActionsTable["kc_delete_account_action"]='' .. _M.get_var_or_env("kc_delete_account_label") .. '' end - local kc_update_password_action = os.getenv("KC_UPDATE_PASSWORD_ACTION") + local kc_update_password_action = _M.get_var_or_env("kc_update_password_action") if kc_update_password_action ~= '' then - userActionsTable["kc_update_password_action"]='' .. os.getenv("KC_UPDATE_PASSWORD_LABEL") .. '' + userActionsTable["kc_update_password_action"]='' .. _M.get_var_or_env("kc_update_password_label") .. '' end - local kc_update_email_action = os.getenv("KC_UPDATE_EMAIL_ACTION") + local kc_update_email_action = _M.get_var_or_env("kc_update_email_action") if kc_update_email_action ~= '' then - userActionsTable["kc_update_email_action"]='' .. os.getenv("KC_UPDATE_EMAIL_LABEL") .. '' + userActionsTable["kc_update_email_action"]='' .. _M.get_var_or_env("kc_update_email_label") .. '' end - local kc_enrol_biometrics_action = os.getenv("KC_ENROL_BIOMETRICS_ACTION") + local kc_enrol_biometrics_action = _M.get_var_or_env("kc_enrol_biometrics_action") if kc_enrol_biometrics_action ~= '' then - userActionsTable["kc_enrol_biometrics_action"]='' .. os.getenv("KC_ENROL_BIOMETRICS_LABEL") .. '' + userActionsTable["kc_enrol_biometrics_action"]='' .. _M.get_var_or_env("kc_enrol_biometrics_label") .. '' + end + + local kc_add_passkey_action = _M.get_var_or_env("kc_add_passkey_action") + if kc_add_passkey_action ~= '' then + userActionsTable["kc_add_passkey_action"]='' .. _M.get_var_or_env("kc_add_passkey_label") .. '' end return userActionsTable end +function _M.get_info_data(oidc_opts, session_opts, ipax_display_name, ipax_app_name, ipax_base_url, headers) + ngx.log(ngx.DEBUG, "Starting for client_id: " .. oidc_opts.client_id) + local res = _M.get_res(oidc_opts, session_opts) + -- id_token is returned as a lua table + local id_token = _M.get_id_token(res); + -- access_token is returned as string + local access_token = _M.get_access_token(res); + -- ngx.log(ngx.DEBUG, "access_token: " .. access_token) + local refresh_token = get_refresh_token(res); + -- ngx.log(ngx.DEBUG, "refresh_token: " .. tostring(refresh_token)) + + local data = { + user = _M.get_user(res), + headers = headers, + access_token = access_token, + refresh_token = refresh_token or "Not Provided in token endpoint response", + id_token = id_token, + userinfo_json = _M.get_userinfo_json(res), + username = get_preferred_username_from_userinfo_or_idtoken(res) or "Not Informed", + user_actions = get_user_actions(oidc_opts, ipax_base_url), + logout_path = oidc_opts.logout_path, + ipax_app_name = ipax_app_name, + ipax_display_name = ipax_display_name + } + return data +end + return _M diff --git a/lua/ipax_openidc.lua b/lua/ipax_openidc.lua deleted file mode 100644 index 22ead0d..0000000 --- a/lua/ipax_openidc.lua +++ /dev/null @@ -1,170 +0,0 @@ --- -------------------------------------------------------------------------------------------------------------- --- -------------------------------------------------------------------------------------------------------------- --- https://github.com/zmartzone/lua-resty-openidc/blob/master/lib/resty/openidc.lua -local _M = {} -local http = require("resty.http") -local cjson = require("cjson") -local cjson_s = require("cjson.safe") - -local function openidc_cache_get(type, key) - local dict = ngx.shared[type] - local value - if dict then - value = dict:get(key) - if value then ngx.log(ngx.DEBUG, "cache hit: type=", type, " key=", key) end - end - return value -end - -local function openidc_configure_timeouts(httpc, timeout) - if timeout then - if type(timeout) == "table" then - local r, e = httpc:set_timeouts(timeout.connect or 0, timeout.send or 0, timeout.read or 0) - else - local r, e = httpc:set_timeout(timeout) - end - end -end - --- Set outgoing proxy options -local function openidc_configure_proxy(httpc, proxy_opts) - if httpc and proxy_opts and type(proxy_opts) == "table" then - ngx.log(ngx.DEBUG, "openidc_configure_proxy : use http proxy") - httpc:set_proxy_options(proxy_opts) - else - ngx.log(ngx.DEBUG, "openidc_configure_proxy : don't use http proxy") - end -end - -local function decorate_request(http_request_decorator, req) - return http_request_decorator and http_request_decorator(req) or req -end - --- parse the JSON result from a call to the OP -local function openidc_parse_json_response(response, ignore_body_on_success) - local ignore_body_on_success = ignore_body_on_success or false - local err - local res - -- check the response from the OP - if response.status ~= 200 then - err = "response indicates failure, status=" .. response.status .. ", body=" .. response.body - else - if ignore_body_on_success then - return nil, nil - end - -- decode the response and extract the JSON object - res = cjson_s.decode(response.body) - if not res then - err = "JSON decoding failed" - end - end - return res, err -end - --- get the Discovery metadata from the specified URL -local function openidc_discover(url, ssl_verify, keepalive, timeout, exptime, proxy_opts, http_request_decorator) - ngx.log(ngx.DEBUG, "openidc_discover: URL is: " .. url) - local json, err - local v = openidc_cache_get("discovery", url) - if not v then - ngx.log(ngx.DEBUG, "discovery data not in cache, making call to discovery endpoint") - -- make the call to the discovery endpoint - local httpc = http.new() - openidc_configure_timeouts(httpc, timeout) - openidc_configure_proxy(httpc, proxy_opts) - local res, error = httpc:request_uri(url, decorate_request(http_request_decorator, { - ssl_verify = (ssl_verify ~= "no"), - keepalive = (keepalive ~= "no") - })) - if not res then - err = "accessing discovery url (" .. url .. ") failed: " .. error - ngx.log(ngx.DEBUG, err) - else - ngx.log(ngx.DEBUG, "response data: " .. res.body) - json, err = openidc_parse_json_response(res) - if json then - openidc_cache_set("discovery", url, cjson.encode(json), exptime or 24 * 60 * 60) - else - err = "could not decode JSON from Discovery data" .. (err and (": " .. err) or '') - ngx.log(ngx.DEBUG, err) - end - end - else - json = cjson.decode(v) - end - return json, err -end - --- turn a discovery url set in the opts dictionary into the discovered information -function _M.openidc_ensure_discovered_data(opts) - local err - if type(opts.discovery) == "string" then - local discovery - discovery, err = openidc_discover(opts.discovery, opts.ssl_verify, opts.keepalive, opts.timeout, opts.discovery_expires_in, opts.proxy_opts, opts.http_request_decorator) - if not err then - opts.discovery = discovery - end - end - return err -end - -local function get_first(table_or_string) - local res = table_or_string - if table_or_string and type(table_or_string) == 'table' then - res = table_or_string[1] - end - return res -end - -local function get_first_header(headers, header_name) - local header = headers[header_name] - return get_first(header) -end - -local function get_first_header_and_strip_whitespace(headers, header_name) - local header = get_first_header(headers, header_name) - return header and header:gsub('%s', '') -end - -local function get_forwarded_parameter(headers, param_name) - local forwarded = get_first_header(headers, 'Forwarded') - local params = {} - if forwarded then - local function parse_parameter(pv) - local name, value = pv:match("^%s*([^=]+)%s*=%s*(.-)%s*$") - if name and value then - if value:sub(1, 1) == '"' then - value = value:sub(2, -2) - end - params[name:lower()] = value - end - end - -- this assumes there is no quoted comma inside the header's value which should be fine as comma is not legal inside a node name, a URI scheme or a host name. The only thing that might bite us are extensions. - local first_part = forwarded - local first_comma = forwarded:find("%s*,%s*") - if first_comma then - first_part = forwarded:sub(1, first_comma - 1) - end - first_part:gsub("[^;]+", parse_parameter) - end - return params[param_name:gsub("^%s*(.-)%s*$", "%1"):lower()] -end - -function _M.get_scheme(headers) - return get_forwarded_parameter(headers, 'proto') - or get_first_header_and_strip_whitespace(headers, 'X-Forwarded-Proto') - or ngx.var.scheme -end - -local function get_host_name_from_x_header(headers) - local header = get_first_header_and_strip_whitespace(headers, 'X-Forwarded-Host') - return header and header:gsub('^([^,]+),?.*$', '%1') -end - -function _M.get_host_name(headers) - return get_forwarded_parameter(headers, 'host') - or get_host_name_from_x_header(headers) - or ngx.var.http_host -end - -return _M diff --git a/lua/multiapps.lua b/lua/multiapps.lua deleted file mode 100644 index 048b60e..0000000 --- a/lua/multiapps.lua +++ /dev/null @@ -1,116 +0,0 @@ --- -------------------------------------------------------------------------------------------------------------- --- -------------------------------------------------------------------------------------------------------------- --- IPAx multiapps -local _M = {} -local ipax = require("ipax") -local ipax_openidc = require("ipax_openidc") - -local session_opts = { - secret = os.getenv("SESSION_SECRET"), - cookie = { - persistent = os.getenv("SESSION_COOKIE_PERSISTENT"), - lifetime = os.getenv("SESSION_COOKIE_LIFETIME"), - samesite = os.getenv("SESSION_COOKIE_SAMESITE") - } -} - -function _M.get_res(oidc_opts, base_url, prompt_override) - oidc_opts["renew_access_token_on_expiry"] = true - oidc_opts["session_contents"] = {id_token=true, enc_id_token=true, access_token=true, user=true} - oidc_opts["redirect_uri"] = base_url .. os.getenv("OIDC_REDIRECT_URI") - oidc_opts["logout_path"] = os.getenv("OIDC_LOGOUT_URI") - oidc_opts["post_logout_redirect_uri"] = base_url .. "/logoutSuccess.html" - ngx.log(ngx.DEBUG, "prompt_override: " .. prompt_override) - if prompt_override ~= '' then - oidc_opts["prompt"]=prompt_override - end - local res, err, target, session = require("resty.openidc").authenticate(oidc_opts, null, action, session_opts) - --ngx.log(ngx.DEBUG, "refresh_token: " .. session:get("refresh_token")) - res["refresh_token"] = session:get("refresh_token") - session:close() - local authentication_feedback = ipax.check_authentication(err) - return res -end - -function _M.get_userinfo_json(res) - local json = require("json").encode(res.user) - return json -end - -function _M.get_id_token(res) - return res.id_token -end - -function _M.get_access_token(res) - return res.access_token -end - -function _M.get_refresh_token(res) - return res.refresh_token -end - -function _M.get_userinfo_json(res) - local json = require("json").encode(res.user) - -- ngx.log(ngx.DEBUG, "userinfo_json: " .. json) - return json -end - -function _M.get_preferred_name_from_userinfo(res) - local userinfo_json = _M.get_userinfo_json(res) - local userinfo_table = require("json").decode(userinfo_json) - local preferred_username = userinfo_table.preferred_username - if preferred_username == nil then - return "(unknown)" - else - return preferred_username - end -end - -function _M.get_preferred_username_from_userinfo_or_idtoken(res) - local id_token = _M.get_id_token(res) - local preferred_username = id_token.preferred_username - - if preferred_username == nil then - return _M.get_preferred_name_from_userinfo(res) - else - return preferred_username - end -end - -local function get_kc_user_action_url(oidc_opts, kc_action) - local headers = ngx.req.get_headers() - local redirect_uri = ipax_openidc.get_scheme(headers) .. "://" .. ipax_openidc.get_host_name(headers) .. "/private/info" - local params = { - client_id = oidc_opts.client_id, - response_type = "code", - scope = "openid", - redirect_uri = redirect_uri, - kc_action = kc_action - } - ipax_openidc.openidc_ensure_discovered_data(oidc_opts) - return oidc_opts.discovery.authorization_endpoint .. "?" .. ngx.encode_args(params) -end - -function _M.get_user_actions(oidc_opts, kc_actions) - local userActionsTable = {} - - if kc_actions.delete_account ~= '' then - userActionsTable["kc_delete_account_action"]='' .. os.getenv("KC_DELETE_ACCOUNT_LABEL") .. '' - end - - if kc_actions.update_password ~= '' then - userActionsTable["kc_update_password_action"]='' .. os.getenv("KC_UPDATE_PASSWORD_LABEL") .. '' - end - - if kc_actions.update_email ~= '' then - userActionsTable["kc_update_email_action"]='' .. os.getenv("KC_UPDATE_EMAIL_LABEL") .. '' - end - - if kc_actions.enrol_biometrics ~= '' then - userActionsTable["kc_enrol_biometrics_action"]='' .. os.getenv("KC_ENROL_BIOMETRICS_LABEL") .. '' - end - - return userActionsTable -end - -return _M diff --git a/multiapps/conf.d/demoapp1.conf b/multiapps/conf.d/demoapp1.conf deleted file mode 100644 index 4eac5e2..0000000 --- a/multiapps/conf.d/demoapp1.conf +++ /dev/null @@ -1,15 +0,0 @@ -server { - server_name demoapp1; - include demoapp_default_variables.conf; - - set $demoapp_alias 'DemoApp1'; - set $demoapp_base_url 'http://demoapp1'; - set $oidc_discovery 'http://idp:8080/realms/demorealm/.well-known/openid-configuration'; - set $client_id 'demoapp1_client_id'; - set $client_secret 'demoapp1_client_secret'; - set $scope 'openid profile email roles phone'; - set $kc_update_password_action 'UPDATE_PASSWORD'; - set $kc_update_email_action 'UPDATE_EMAIL'; - - include demoapp.conf; -} diff --git a/multiapps/conf.d/demoapp2.conf b/multiapps/conf.d/demoapp2.conf deleted file mode 100644 index 4c6094b..0000000 --- a/multiapps/conf.d/demoapp2.conf +++ /dev/null @@ -1,14 +0,0 @@ -server { - server_name demoapp2; - include demoapp_default_variables.conf; - - set $demoapp_alias 'DemoApp2'; - set $demoapp_base_url 'http://demoapp2'; - set $oidc_discovery 'http://idp:8080/realms/demorealm/.well-known/openid-configuration'; - set $client_id 'demoapp2_client_id'; - set $client_secret 'demoapp2_client_secret'; - set $scope 'openid profile email roles phone'; - set $kc_delete_account_action 'delete_account'; - - include demoapp.conf; -} diff --git a/multiapps/server.conf b/multiapps/server.conf deleted file mode 100644 index 736b979..0000000 --- a/multiapps/server.conf +++ /dev/null @@ -1,9 +0,0 @@ -server { - listen 80; - server_name localhost; - location / { - root /var/ipax/html/; - add_header Cache-Control no-store; - add_header Pragma no-cache; - } -} diff --git a/templates/demoapps.html b/templates/demoapps.html new file mode 100644 index 0000000..ff2742e --- /dev/null +++ b/templates/demoapps.html @@ -0,0 +1,10 @@ + + + + + demoapps + + +

demoapps

+ + \ No newline at end of file diff --git a/templates/info.html b/templates/info.html index e5bb5f9..53f4518 100644 --- a/templates/info.html +++ b/templates/info.html @@ -3,7 +3,7 @@ - Identicum | {{app_name}} + Identicum | {{ipax_app_name}}