diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/.devcontainer/activate.sh b/wp-content/plugins/daggerhart-openid-connect-generic/.devcontainer/activate.sh
deleted file mode 100644
index 8b675afc..00000000
--- a/wp-content/plugins/daggerhart-openid-connect-generic/.devcontainer/activate.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env bash
-
-set -eu
-
-# Activate the plugin.
-cd "/app"
-echo "Activating plugin..."
-if ! wp plugin is-active daggerhart-openid-connect-generic 2>/dev/null; then
- wp plugin activate daggerhart-openid-connect-generic --quiet
-fi
-
-echo "Done!"
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/.devcontainer/devcontainer.json b/wp-content/plugins/daggerhart-openid-connect-generic/.devcontainer/devcontainer.json
deleted file mode 100644
index 314f4b0f..00000000
--- a/wp-content/plugins/daggerhart-openid-connect-generic/.devcontainer/devcontainer.json
+++ /dev/null
@@ -1,57 +0,0 @@
-// For format details, https://containers.dev/implementors/json_reference/.
-{
- "name": "WordPress Development Environment",
- "dockerComposeFile": "../docker-compose.yml",
- "service": "app",
- "mounts": ["source=dind-var-lib-docker,target=/var/lib/docker,type=volume"],
- "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
-
- "customizations": {
- "vscode": {
- // Set *default* container specific settings.json values on container create.
- "settings": {},
-
- // Add the IDs of extensions you want installed when the container is created.
- "extensions": ["ms-azuretools.vscode-docker"]
- }
- },
-
- // Features to add to the dev container. More info: https://containers.dev/features.
- "features": {
- "./local-features/welcome-message": "latest"
- },
-
- // Use 'forwardPorts' to make a list of ports inside the container available locally.
- "forwardPorts": [8080, 8081, 8026, 3306],
-
- // Maps a port number, "host:port" value, range, or regular expression to a set of default options. See port attributes for available options
- "portsAttributes": {
- "8080": {
- "label": "WordPress Development/Testing Site"
- },
- "8081": {
- "label": "phpMyAdmin"
- },
- "8026": {
- "label": "MailHog"
- },
- "3306": {
- "label": "MariaDB"
- }
- },
-
- // Use `onCreateCommand` to run commands as part of the container creation.
- //"onCreateCommand": "chmod +x .devcontainer/install.sh && .devcontainer/install.sh",
-
- // Use 'postCreateCommand' to run commands after the container is created.
- "postCreateCommand": "chmod +x .devcontainer/setup.sh && .devcontainer/setup.sh",
-
- // Use 'postStartCommand' to run commands after the container has started.
- "postStartCommand": "chmod +x .devcontainer/activate.sh && .devcontainer/activate.sh",
-
- // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
- "remoteUser": "wp_php",
-
- // A set of name-value pairs that sets or overrides environment variables for the devcontainer.json supporting service / tool (or sub-processes like terminals) but not the container as a whole.
- "remoteEnv": { "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" }
-}
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/.devcontainer/local-features/welcome-message/devcontainer-feature.json b/wp-content/plugins/daggerhart-openid-connect-generic/.devcontainer/local-features/welcome-message/devcontainer-feature.json
deleted file mode 100644
index c4f59354..00000000
--- a/wp-content/plugins/daggerhart-openid-connect-generic/.devcontainer/local-features/welcome-message/devcontainer-feature.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "id": "welcome-message",
- "name": "Install the First Start Welcome Message",
- "install": {
- "app": "",
- "file": "install.sh"
- }
-}
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/.devcontainer/local-features/welcome-message/install.sh b/wp-content/plugins/daggerhart-openid-connect-generic/.devcontainer/local-features/welcome-message/install.sh
deleted file mode 100644
index 8df27c1c..00000000
--- a/wp-content/plugins/daggerhart-openid-connect-generic/.devcontainer/local-features/welcome-message/install.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env bash
-
-set -eux
-
-export DEBIAN_FRONTEND=noninteractive
-
-# Copy the welcome message
-if [ ! -f /usr/local/etc/vscode-dev-containers/first-run-notice.txt ]; then
- echo "Installing First Run Notice..."
- echo -e "š Welcome to \"OpenID Connect for WP Development\" in Dev Containers!\n\nš ļø Your environment is fully setup with all the required software.\n\nš To get started, wait for the \"postCreateCommand\" to finish setting things up, then open the portforwarded URL and append '/wp/wp-admin'. Login to the WordPress Dashboard using \`admin/password\` for the credentials.\n" | sudo tee /usr/local/etc/vscode-dev-containers/first-run-notice.txt
-fi
-
-echo "Done!"
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/.devcontainer/setup.sh b/wp-content/plugins/daggerhart-openid-connect-generic/.devcontainer/setup.sh
deleted file mode 100644
index a35da8f4..00000000
--- a/wp-content/plugins/daggerhart-openid-connect-generic/.devcontainer/setup.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env bash
-
-set -eu
-
-# true is shell command and always return 0
-# false always return 1
-if [ -z "${CODESPACES}" ] ; then
- SITE_HOST="http://localhost:8080"
-else
- SITE_HOST="https://${CODESPACE_NAME}-8080.${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}"
-fi
-
-PLUGIN_DIR=/workspaces/openid-connect-generic
-
-# Attempt to make ipv4 traffic have a higher priority than ipv6.
-sudo sh -c "echo 'precedence ::ffff:0:0/96 100' >> /etc/gai.conf"
-
-# Install Composer dependencies.
-cd "${PLUGIN_DIR}"
-echo "Installing Composer dependencies..."
-COMPOSER_NO_INTERACTION=1 COMPOSER_ALLOW_XDEBUG=0 COMPOSER_MEMORY_LIMIT=-1 composer install --no-progress --quiet
-
-# Install NPM dependencies.
-cd "${PLUGIN_DIR}"
-if [ ! -d "node_modules" ]; then
- echo "Installing NPM dependencies..."
- npm ci
-fi
-
-# Setup the WordPress environment.
-cd "/app"
-if ! wp core is-installed 2>/dev/null; then
- echo "Setting up WordPress at $SITE_HOST"
- wp core install --url="$SITE_HOST" --title="OpenID Connect Development" --admin_user="admin" --admin_email="admin@example.com" --admin_password="password" --skip-email --quiet
-fi
-
-echo "Done!"
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/.gitpod.yml b/wp-content/plugins/daggerhart-openid-connect-generic/.gitpod.yml
deleted file mode 100644
index b5e460b7..00000000
--- a/wp-content/plugins/daggerhart-openid-connect-generic/.gitpod.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-# List the start up tasks. Learn more https://www.gitpod.io/docs/config-start-tasks/
-tasks:
- - name: WordPress Development Environment
- init: npm run setup # runs during prebuild
- command: |
- npm start -- --update
- npm stop
- npm start -- --update
-
-# List the ports to expose. Learn more https://www.gitpod.io/docs/config-ports/
-ports:
- - port: 8888
- onOpen: notify
- visibility: public
- - port: 8889
- onOpen: notify
- visibility: public
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/.vscode/tasks.json b/wp-content/plugins/daggerhart-openid-connect-generic/.vscode/tasks.json
deleted file mode 100644
index 62c02b1d..00000000
--- a/wp-content/plugins/daggerhart-openid-connect-generic/.vscode/tasks.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "version": "2.0.0",
- "tasks": [
- {
- "type": "npm",
- "script": "build",
- "group": {
- "kind": "build",
- "isDefault": true
- },
- "problemMatcher": [],
- "label": "npm: build",
- "detail": "npm run grunt build"
- }
- ]
-}
\ No newline at end of file
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/CHANGELOG.md b/wp-content/plugins/daggerhart-openid-connect-generic/CHANGELOG.md
deleted file mode 100644
index a7eadf7e..00000000
--- a/wp-content/plugins/daggerhart-openid-connect-generic/CHANGELOG.md
+++ /dev/null
@@ -1,203 +0,0 @@
-# OpenId Connect Generic Changelog
-
-**3.10.0**
-
-- Chore: @timnolte - Dependency updates.
-- Fix: @drzraf - Prevents running the auth url filter twice.
-- Fix: @timnolte - Updates the log cleanup handling to properly retain the configured number of log entries.
-- Fix: @timnolte - Updates the log display output to reflect the log retention policy.
-- Chore: @timnolte - Adds Unit Testing & New Local Development Environment.
-- Feature: @timnolte - Updates logging to allow for tracking processing time.
-- Feature: @menno-ll - Adds a remember me feature via a new filter.
-- Improvement: @menno-ll - Updates WP Cookie Expiration to Same as Session Length.
-
-**3.9.1**
-
-- Improvement: @timnolte - Refactors Composer setup and GitHub Actions.
-- Improvement: @timnolte - Bumps WordPress tested version compatibility.
-
-**3.9.0**
-
-- Feature: @matchaxnb - Added support for additional configuration constants.
-- Feature: @schanzen - Added support for agregated claims.
-- Fix: @rkcreation - Fixed access token not updating user metadata after login.
-- Fix: @danc1248 - Fixed user creation issue on Multisite Networks.
-- Feature: @RobjS - Added plugin singleton to support for more developer customization.
-- Feature: @jkouris - Added action hook to allow custom handling of session expiration.
-- Fix: @tommcc - Fixed admin CSS loading only on the plugin settings screen.
-- Feature: @rkcreation - Added method to refresh the user claim.
-- Feature: @Glowsome - Added acr_values support & verification checks that it when defined in options is honored.
-- Fix: @timnolte - Fixed regression which caused improper fallback on missing claims.
-- Fix: @slykar - Fixed missing query string handling in redirect URL.
-- Fix: @timnolte - Fixed issue with some user linking and user creation handling.
-- Improvement: @timnolte - Fixed plugin settings typos and screen formatting.
-- Security: @timnolte - Updated build tooling security vulnerabilities.
-- Improvement: @timnolte - Changed build tooling scripts.
-
-**3.8.5**
-
-- Fix: @timnolte - Fixed missing URL request validation before use & ensure proper current page URL is setup for Redirect Back.
-- Fix: @timnolte - Fixed Redirect URL Logic to Handle Sub-directory Installs.
-- Fix: @timnolte - Fixed issue with redirecting user back when the openid_connect_generic_auth_url shortcode is used.
-
-**3.8.4**
-
-- Fix: @timnolte - Fixed invalid State object access for redirection handling.
-- Improvement: @timnolte - Fixed local wp-env Docker development environment.
-- Improvement: @timnolte - Fixed Composer scripts for linting and static analysis.
-
-**3.8.3**
-
-- Fix: @timnolte - Fixed problems with proper redirect handling.
-- Improvement: @timnolte - Changes redirect handling to use State instead of cookies.
-- Improvement: @timnolte - Refactored additional code to meet coding standards.
-
-**3.8.2**
-
-- Fix: @timnolte - Fixed reported XSS vulnerability on WordPress login screen.
-
-**3.8.1**
-
-- Fix: @timnolte - Prevent SSO redirect on password protected posts.
-- Fix: @timnolte - CI/CD build issues.
-- Fix: @timnolte - Invalid redirect handling on logout for Auto Login setting.
-
-**3.8.0**
-
-- Feature: @timnolte - Ability to use 6 new constants for setting client configuration instead of storing in the DB.
-- Improvement: @timnolte - NPM version requirements for development.
-- Improvement: @timnolte - Travis CI build fixes.
-- Improvement: @timnolte - GrumPHP configuration updates for code contributions.
-- Improvement: @timnolte - Refactored to meet WordPress coding standards.
-- Improvement: @timnolte - Refactored to provide localization.
-- Improvement: @timnolte - Refactored to provide a Docker-based local development environment.
-
-**3.7.1**
-
-- Fix: Release Version Number.
-
-**3.7.0**
-
-- Feature: @timnolte - Ability to enable/disable token refresh. Useful for IDPs that don't support token refresh.
-- Feature: @timnolte - Support custom redirect URL(`redirect_to`) with the authentication URL & login button shortcodes.
-- Supports additional attribute overrides including login `button_text`, `endpoint_login`, `scope`, `redirect_uri`.
-
-**3.6.0**
-
-- Improvement: @RobjS - Improved error messages during login state failure.
-- Improvement: @RobjS - New developer filter for login form button URL.
-- Fix: @cs1m0n - Only increment username during new user creation if the "Link existing user" setting is enabled.
-- Fix: @xRy-42 - Allow periods and spaces in usernames to match what WordPress core allows.
-- Feature: @benochen - New setting named "Create user if does not exist" determines whether new users are created during login attempts.
-- Improvement: @flat235 - Username transliteration and normalization.
-
-**3.5.1**
-
-- Fix: @daggerhart - New approach to state management using transients.
-
-**3.5.0**
-
-- Readme fix: @thijskh - Fix syntax error in example openid-connect-generic-login-button-text
-- Feature: @slavicd - Allow override of the plugin by posting credentials to wp-login.php
-- Feature: @gassan - New action on use login
-- Fix: @daggerhart - Avoid double question marks in auth url query string
-- Fix: @drzraf - wp-cli bootstrap must not inhibit custom rewrite rules
-- Syntax change: @mullikine - Change PHP keywords to comply with PSR2
-
-**3.4.1**
-
-- Minor documentation update and additional error checking.
-
-**3.4.0**
-
-- Feature: @drzraf - New filter hook: ability to filter claim and derived user data before user creation.
-- Feature: @anttileppa - State time limit can now be changed on the settings page.
-- Fix: @drzraf - Fix PHP notice when using traditional login, $token_response may be empty.
-- Fix: @drzraf - Fixed a notice when cookie does not contain expected redirect_url
-
-**3.3.1**
-
-- Prefixing classes for more efficient autoloading.
-- Avoid altering global wp_remote_post() parameters.
-- Minor metadata updates for wp.org
-
-**3.3.0**
-
-- Fix: @pjeby - Handle multiple user sessions better by using the `WP_Session_Tokens` object. Predecessor to fixes for multiple other issues: #49, #50, #51
-
-**3.2.1**
-
-- Bug fix: @svenvanhal - Exit after issuing redirect. Fixes #46
-
-**3.2.0**
-
-- Feature: @robbiepaul - trigger core action `wp_login` when user is logged in through this plugin
-- Feature: @moriyoshi - Determine the WP_User display name with replacement tokens on the settings page. Tokens can be any property of the user_claim.
-- Feature: New setting to set redirect URL when session expires.
-- Feature: @robbiepaul - New filter for modifying authentication URL
-- Fix: @cedrox - Adding id_token_hint to logout URL according to spec
-- Bug fix: Provide port to the request header when requesting the user_claim
-
-**3.1.0**
-
-- Feature: @rwasef1830 - Refresh tokens
-- Feature: @rwasef1830 - Integrated logout support with end_session endpoint
-- Feature: May use an alternate redirect_uri that doesn't rely on admin-ajax
-- Feature: @ahatherly - Support for IDP behind reverse proxy
-- Bug fix: @robertstaddon - case insensitive check for Bearer token
-- Bug fix: @rwasef1830 - "redirect to origin when auto-sso" cookie issue
-- Bug fix: @rwasef1830 - PHP Warnings headers already sent due to attempts to redirect and set cookies during login form message
-- Bug fix: @rwasef1830 - expire session when access_token expires if no refresh token found
-- UX fix: @rwasef1830 - Show login button on error redirect when using auto-sso
-
-**3.0.8**
-
-- Feature: @wgengarelly - Added `openid-connect-generic-update-user-using-current-claim` action hook allowing other plugins/themes
- to take action using the fresh claims received when an existing user logs in.
-
-**3.0.7**
-
-- Bug fix: @wgengarelly - When requesting userinfo, send the access token using the Authorization header field as recommended in
- section 5.3.1 of the specs.
-
-**3.0.6**
-
-- Bug fix: @robertstaddon - If "Link Existing Users" is enabled, allow users who login with OpenID Connect to also log in with WordPress credentials
-
-**3.0.5**
-
-- Feature: @robertstaddon - Added `[openid_connect_generic_login_button]` shortcode to allow the login button to be placed anywhere
-- Feature: @robertstaddon - Added setting to "Redirect Back to Origin Page" after a successful login instead of redirecting to the home page.
-
-**3.0.4**
-
-- Feature: @robertstaddon - Added setting to allow linking existing WordPress user accounts with newly-authenticated OpenID Connect login
-
-**3.0.3**
-
-- Using WordPresss's is_ssl() for setcookie()'s "secure" parameter
-- Bug fix: Incrementing username in case of collision.
-- Bug fix: Wrong error sent when missing token body
-
-**3.0.2**
-
-- Added http_request_timeout setting
-
-**3.0.1**
-
-- Finalizing 3.0.x api
-
-**3.0**
-
-- Complete rewrite to separate concerns
-- Changed settings keys for clarity (requires updating settings if upgrading from another version)
-- Error logging
-
-**2.1**
-
-- Working my way closer to spec. Possible breaking change. Now checking for preferred_username as priority.
-- New username determination to avoid collisions
-
-**2.0**
-
-Complete rewrite
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/HOWTO.md b/wp-content/plugins/daggerhart-openid-connect-generic/HOWTO.md
deleted file mode 100644
index be34dfe1..00000000
--- a/wp-content/plugins/daggerhart-openid-connect-generic/HOWTO.md
+++ /dev/null
@@ -1,383 +0,0 @@
-# OpenID Connect Generic Client
-
-License: GPLv2 or later
-License URI: http://www.gnu.org/licenses/gpl-2.0.html
-
-A simple client that provides SSO or opt-in authentication against a generic OAuth2 Server implementation.
-
-## Description
-
-This plugin allows to authenticate users against OpenID Connect OAuth2 API with Authorization Code Flow.
-Once installed, it can be configured to automatically authenticate users (SSO), or provide a "Login with OpenID Connect"
-button on the login form. After consent has been obtained, an existing user is automatically logged into WordPress, while
-new users are created in WordPress database.
-
-Much of the documentation can be found on the Settings > OpenID Connect Generic dashboard page.
-
-## Table of Contents
-
-- [Installation](#installation)
- - [Composer](#composer)
-- [Frequently Asked Questions](#frequently-asked-questions)
- - [What is the client's Redirect URI?](#what-is-the-clients-redirect-uri)
- - [Can I change the client's Redirect URI?](#can-i-change-the-clients-redirect-uri)
-- [Configuration Environment Variables/Constants](#configuration-environment-variables-constants)
-- [Hooks](#hooks)
- - [Filters](#filters)
- - [openid-connect-generic-alter-request](#openid-connect-generic-alter-request)
- - [openid-connect-generic-login-button-text](#openid-connect-generic-login-button-text)
- - [openid-connect-generic-auth-url](#openid-connect-generic-auth-url)
- - [openid-connect-generic-user-login-test](#openid-connect-generic-user-login-test)
- - [openid-connect-generic-user-creation-test](#openid-connect-generic-user-creation-test)
- - [openid-connect-generic-alter-user-claim](#openid-connect-generic-alter-user-claim)
- - [openid-connect-generic-alter-user-data](#openid-connect-generic-alter-user-data)
- - [openid-connect-generic-settings-fields](#openid-connect-generic-settings-fields)
- - [Actions](#actions)
- - [openid-connect-generic-user-create](#openid-connect-generic-user-create)
- - [openid-connect-generic-user-update](#openid-connect-generic-user-update)
- - [openid-connect-generic-update-user-using-current-claim](#openid-connect-generic-update-user-using-current-claim)
- - [openid-connect-generic-redirect-user-back](#openid-connect-generic-redirect-user-back)
-
-
-## Installation
-
-1. Upload to the `/wp-content/plugins/` directory
-1. Activate the plugin
-1. Visit Settings > OpenID Connect and configure to meet your needs
-
-### Composer
-
-[OpenID Connect Generic on packagist](https://packagist.org/packages/daggerhart/openid-connect-generic)
-
-Installation:
-
-`composer require daggerhart/openid-connect-generic`
-
-
-## Frequently Asked Questions
-
-### What is the client's Redirect URI?
-
-Most OAuth2 servers should require a whitelist of redirect URIs for security purposes. The Redirect URI provided
-by this client is like so: `https://example.com/wp-admin/admin-ajax.php?action=openid-connect-authorize`
-
-Replace `example.com` with your domain name and path to WordPress.
-
-### Can I change the client's Redirect URI?
-
-Some OAuth2 servers do not allow for a client redirect URI to contain a query string. The default URI provided by
-this module leverages WordPress's `admin-ajax.php` endpoint as an easy way to provide a route that does not include
-HTML, but this will naturally involve a query string. Fortunately, this plugin provides a setting that will make use of
-an alternate redirect URI that does not include a query string.
-
-On the settings page for this plugin (Dashboard > Settings > OpenID Connect Generic) there is a checkbox for
-**Alternate Redirect URI**. When checked, the plugin will use the Redirect URI
-`https://example.com/openid-connect-authorize`.
-
-## Configuration Environment Variables/Constants
-
-- Client ID: `OIDC_CLIENT_ID`
-- Client Secret Key: `OIDC_CLIENT_SECRET`
-- Login Endpoint URL: `OIDC_ENDPOINT_LOGIN_URL`
-- Userinfo Endpoint URL: `OIDC_ENDPOINT_USERINFO_URL`
-- Token Validation Endpoint URL: `OIDC_ENDPOINT_TOKEN_URL`
-- End Session Endpoint URL: `OIDC_ENDPOINT_LOGOUT_URL`
-- OpenID scope: `OIDC_CLIENT_SCOPE` (space separated)
-- OpenID login type: `OIDC_LOGIN_TYPE` ('button' or 'auto')
-- Enforce privacy: `OIDC_ENFORCE_PRIVACY` (boolean)
-- Create user if they do not exist: `OIDC_CREATE_IF_DOES_NOT_EXIST` (boolean)
-- Link existing user: `OIDC_LINK_EXISTING_USERS` (boolean)
-- Redirect user back to origin page: `OIDC_REDIRECT_USER_BACK` (boolean)
-- Redirect on logout: `OIDC_REDIRECT_ON_LOGOUT` (boolean)
-
-## Hooks
-
-This plugin provides a number of hooks to allow for a significant amount of customization of the plugin operations from
-elsewhere in the WordPress system.
-
-### Filters
-
-Filters are WordPress hooks that are used to modify data. The first argument in a filter hook is always expected to be
-returned at the end of the hook.
-
-WordPress filters API - [`add_filter()`](https://developer.wordpress.org/reference/functions/add_filter/) and
-[`apply_filters()`](https://developer.wordpress.org/reference/functions/apply_filters/).
-
-Most often you'll only need to use `add_filter()` to hook into this plugin's code.
-
-#### `openid-connect-generic-alter-request`
-
-Hooks directly into client before requests are sent to the OpenID Server.
-
-Provides 2 arguments: the request array being sent to the server, and the operation currently being executed by this
-plugin.
-
-Possible operations:
-
-- get-authentication-token
-- refresh-token
-- get-userinfo
-
-```
-add_filter('openid-connect-generic-alter-request', function( $request, $operation ) {
- if ( $operation == 'get-authentication-token' ) {
- $request['some_key'] = 'modified value';
- }
-
- return $request;
-}, 10, 2);
-```
-
-#### `openid-connect-generic-login-button-text`
-
-Modify the login button text. Default value is `__( 'Login with OpenID Connect' )`.
-
-Provides 1 argument: the current login button text.
-
-```
-add_filter('openid-connect-generic-login-button-text', function( $text ) {
- $text = __('Login to my super cool IDP server');
-
- return $text;
-});
-```
-
-#### `openid-connect-generic-auth-url`
-
-Modify the authentication URL before presented to the user. This is the URL that will send the user to the IDP server
-for login.
-
-Provides 1 argument: the plugin generated URL.
-
-```
-add_filter('openid-connect-generic-auth-url', function( $url ) {
- // Add some custom data to the url.
- $url.= '&my_custom_data=123abc';
- return $url;
-});
-```
-
-#### `openid-connect-generic-user-login-test`
-
-Determine whether or not the user should be logged into WordPress.
-
-Provides 2 arguments: the boolean result of the test (default `TRUE`), and the `$user_claim` array from the server.
-
-```
-add_filter('openid-connect-generic-user-login-test', function( $result, $user_claim ) {
- // Don't let Terry login.
- if ( $user_claim['email'] == 'terry@example.com' ) {
- $result = FALSE;
- }
-
- return $result;
-}, 10, 2);
-```
-
-#### `openid-connect-generic-user-creation-test`
-
-Determine whether or not the user should be created. This filter is called when a new user is trying to login and they
-do not currently exist within WordPress.
-
-Provides 2 arguments: the boolean result of the test (default `TRUE`), and the `$user_claim` array from the server.
-
-```
-add_filter('', function( $result, $user_claim ) {
- // Don't let anyone from example.com create an account.
- $email_array = explode( '@', $user_claim['email'] );
- if ( $email_array[1] == 'example.com' ) {
- $result = FALSE;
- }
-
- return $result;
-}, 10, 2)
-```
-
-#### `openid-connect-generic-alter-user-claim`
-
-Modify the `$user_claim` before the plugin builds the `$user_data` array for new user created.
-
-**Deprecated** - This filter is not very useful due to some changes that were added later. Recommend not using this
-filter, and using the `openid-connect-generic-alter-user-data` filter instead. Practically, you can only change the
-user's `first_name` and `last_name` values with this filter, but you could easily do that in
-`openid-connect-generic-alter-user-data` as well.
-
-Provides 1 argument: the `$user_claim` from the server.
-
-```
-// Not a great example because the hook isn't very useful.
-add_filter('openid-connect-generic-alter-user-claim', function( $user_claim ) {
- // Use the beginning of the user's email address as the user's first name.
- if ( empty( $user_claim['given_name'] ) ) {
- $email_array = explode( '@', $user_claim['email'] );
- $user_claim['given_name'] = $email_array[0];
- }
-
- return $user_claim;
-});
-```
-
-#### `openid-connect-generic-alter-user-data`
-
-Modify a new user's data immediately before the user is created.
-
-Provides 2 arguments: the `$user_data` array that will be sent to `wp_insert_user()`, and the `$user_claim` from the
-server.
-
-```
-add_filter('openid-connect-generic-alter-user-data', function( $user_data, $user_claim ) {
- // Don't register any user with their real email address. Create a fake internal address.
- if ( !empty( $user_data['user_email'] ) ) {
- $email_array = explode( '@', $user_data['user_email'] );
- $email_array[1] = 'my-fake-domain.co';
- $user_data['user_email'] = implode( '@', $email_array );
- }
-
- return $user_data;
-}, 10, 2);
-```
-
-#### `openid-connect-generic-settings-fields`
-
-For extending the plugin with a new setting field (found on Dashboard > Settings > OpenID Connect Generic) that the site
-administrator can modify. Also useful to alter the existing settings fields.
-
-See `/includes/openid-connect-generic-settings-page.php` for how fields are constructed.
-
-New settings fields will be automatically saved into the wp_option for this plugin's settings, and will be available in
-the `\OpenID_Connect_Generic_Option_Settings` object this plugin uses.
-
-**Note:** It can be difficult to get a copy of the settings from within other hooks. The easiest way to make use of
-settings in your custom hooks is to call
-`$settings = get_option('openid_connect_generic_settings', array());`.
-
-Provides 1 argument: the existing fields array.
-
-```
-add_filter('openid-connect-generic-settings-fields', function( $fields ) {
-
- // Modify an existing field's title.
- $fields['endpoint_userinfo']['title'] = __('User information endpoint url');
-
- // Add a new field that is a simple checkbox.
- $fields['block_terry'] = array(
- 'title' => __('Block Terry'),
- 'description' => __('Prevent Terry from logging in'),
- 'type' => 'checkbox',
- 'section' => 'authorization_settings',
- );
-
- // A select field that provides options.
-
- $fields['deal_with_terry'] = array(
- 'title' => __('Manage Terry'),
- 'description' => __('How to deal with Terry when he tries to log in.'),
- 'type' => 'select',
- 'options' => array(
- 'allow' => __('Allow login'),
- 'block' => __('Block'),
- 'redirect' => __('Redirect'),
- ),
- 'section' => 'authorization_settings',
- );
-
- return $fields;
-});
-```
-"Sections" are where your setting appears on the admin settings page. Keys for settings sections:
-
-- client_settings
-- user_settings
-- authorization_settings
-- log_settings
-
-Field types:
-
-- text
-- checkbox
-- select (requires an array of "options")
-
-### Actions
-
-WordPress actions are generic events that other plugins can react to.
-
-Actions API: [`add_action`](https://developer.wordpress.org/reference/functions/add_action/) and [`do_actions`](https://developer.wordpress.org/reference/functions/do_action/)
-
-You'll probably only ever want to use `add_action` when hooking into this plugin.
-
-#### `openid-connect-generic-user-create`
-
-React to a new user being created by this plugin.
-
-Provides 2 arguments: the `\WP_User` object that was created, and the `$user_claim` from the IDP server.
-
-```
-add_action('openid-connect-generic-user-create', function( $user, $user_claim ) {
- // Send the user an email when their account is first created.
- wp_mail(
- $user->user_email,
- __('Welcome to my web zone'),
- "Hi {$user->first_name},\n\nYour account has been created at my cool website.\n\n Enjoy!"
- );
-}, 10, 2);
-```
-
-#### `openid-connect-generic-user-update`
-
-React to the user being updated after login. This is the event that happens when a user logins and they already exist as
-a user in WordPress, as opposed to a new WordPress user being created.
-
-Provides 1 argument: the user's WordPress user ID.
-
-```
-add_action('openid-connect-generic-user-update', function( $uid ) {
- // Keep track of the number of times the user has logged into the site.
- $login_count = get_user_meta( $uid, 'my-user-login-count', TRUE);
- $login_count += 1;
- add_user_meta( $uid, 'my-user-login-count', $login_count, TRUE);
-});
-```
-
-#### `openid-connect-generic-update-user-using-current-claim`
-
-React to an existing user logging in (after authentication and authorization).
-
-Provides 2 arguments: the `WP_User` object, and the `$user_claim` provided by the IDP server.
-
-```
-add_action('openid-connect-generic-update-user-using-current-claim', function( $user, $user_claim) {
- // Based on some data in the user_claim, modify the user.
- if ( !empty( $user_claim['wp_user_role'] ) ) {
- if ( $user_claim['wp_user_role'] == 'should-be-editor' ) {
- $user->set_role( 'editor' );
- }
- }
-}, 10, 2);
-```
-
-#### `openid-connect-generic-redirect-user-back`
-
-React to a user being redirected after a successful login. This hook is the last hook that will fire when a user logs
-in. It will only fire if the plugin setting "Redirect Back to Origin Page" is enabled at Dashboard > Settings >
-OpenID Connect Generic. It will fire for both new and existing users.
-
-Provides 2 arguments: the url where the user will be redirected, and the `WP_User` object.
-
-```
-add_action('openid-connect-generic-redirect-user-back', function( $redirect_url, $user ) {
- // Take over the redirection complete. Send users somewhere special based on their capabilities.
- if ( $user->has_cap( 'edit_users' ) ) {
- wp_redirect( admin_url( 'users.php' ) );
- exit();
- }
-}, 10, 2);
-```
-
-### User Meta Data
-
-This plugin stores meta data about the user for both practical and debugging purposes.
-
-* `openid-connect-generic-subject-identity` - The identity of the user provided by the IDP server.
-* `openid-connect-generic-last-id-token-claim` - The user's most recent `id_token` claim, decoded and stored as an array.
-* `openid-connect-generic-last-user-claim` - The user's most recent `user_claim`, stored as an array.
-* `openid-connect-generic-last-token-response` - The user's most recent `token_response`, stored as an array.
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/README.md b/wp-content/plugins/daggerhart-openid-connect-generic/README.md
deleted file mode 100644
index 6b8776ef..00000000
--- a/wp-content/plugins/daggerhart-openid-connect-generic/README.md
+++ /dev/null
@@ -1,133 +0,0 @@
-# OpenID Connect Generic Client #
-**Contributors:** [daggerhart](https://profiles.wordpress.org/daggerhart/), [tnolte](https://profiles.wordpress.org/tnolte/)
-**Tags:** security, login, oauth2, openidconnect, apps, authentication, autologin, sso
-**Requires at least:** 5.0
-**Tested up to:** 6.4.3
-**Stable tag:** 3.10.1
-**Requires PHP:** 7.4
-**License:** GPLv2 or later
-**License URI:** http://www.gnu.org/licenses/gpl-2.0.html
-
-A simple client that provides SSO or opt-in authentication against a generic OAuth2 Server implementation.
-
-## Description ##
-
-This plugin allows to authenticate users against OpenID Connect OAuth2 API with Authorization Code Flow.
-Once installed, it can be configured to automatically authenticate users (SSO), or provide a "Login with OpenID Connect"
-button on the login form. After consent has been obtained, an existing user is automatically logged into WordPress, while
-new users are created in WordPress database.
-
-Much of the documentation can be found on the Settings > OpenID Connect Generic dashboard page.
-
-Please submit issues to the Github repo: https://github.com/oidc-wp/openid-connect-generic
-
-## Installation ##
-
-1. Upload to the `/wp-content/plugins/` directory
-1. Activate the plugin
-1. Visit Settings > OpenID Connect and configure to meet your needs
-
-## Frequently Asked Questions ##
-
-### What is the client's Redirect URI? ###
-
-Most OAuth2 servers will require whitelisting a set of redirect URIs for security purposes. The Redirect URI provided
-by this client is like so: https://example.com/wp-admin/admin-ajax.php?action=openid-connect-authorize
-
-Replace `example.com` with your domain name and path to WordPress.
-
-### Can I change the client's Redirect URI? ###
-
-Some OAuth2 servers do not allow for a client redirect URI to contain a query string. The default URI provided by
-this module leverages WordPress's `admin-ajax.php` endpoint as an easy way to provide a route that does not include
-HTML, but this will naturally involve a query string. Fortunately, this plugin provides a setting that will make use of
-an alternate redirect URI that does not include a query string.
-
-On the settings page for this plugin (Dashboard > Settings > OpenID Connect Generic) there is a checkbox for
-**Alternate Redirect URI**. When checked, the plugin will use the Redirect URI
-`https://example.com/openid-connect-authorize`.
-
-
-## Changelog ##
-
-### 3.10.1 ###
-
-* Chore: @daggerhart - Readme updates and clarifications.
-* Chore: @daggerhart - Release workflow updates.
-* Improved error handling for malformed urls.
-* Fix: @JUVOJustin - Change request for userinfo to GET.
-* Feature: @JUVOJustin - New filter for settings values `openid-connect-generic-settings`.
-* Feature: @JUVOJustin - New filter for state values `openid-connect-generic-new-state-value`.
-
-### 3.10.0 ###
-
-* Chore: @timnolte - Dependency updates.
-* Fix: @drzraf - Prevents running the auth url filter twice.
-* Fix: @timnolte - Updates the log cleanup handling to properly retain the configured number of log entries.
-* Fix: @timnolte - Updates the log display output to reflect the log retention policy.
-* Chore: @timnolte - Adds Unit Testing & New Local Development Environment.
-* Feature: @timnolte - Updates logging to allow for tracking processing time.
-* Feature: @menno-ll - Adds a remember me feature via a new filter.
-* Improvement: @menno-ll - Updates WP Cookie Expiration to Same as Session Length.
-
-### 3.9.1 ###
-
-* Improvement: @timnolte - Refactors Composer setup and GitHub Actions.
-* Improvement: @timnolte - Bumps WordPress tested version compatibility.
-
-### 3.9.0 ###
-
-* Feature: @matchaxnb - Added support for additional configuration constants.
-* Feature: @schanzen - Added support for agregated claims.
-* Fix: @rkcreation - Fixed access token not updating user metadata after login.
-* Fix: @danc1248 - Fixed user creation issue on Multisite Networks.
-* Feature: @RobjS - Added plugin singleton to support for more developer customization.
-* Feature: @jkouris - Added action hook to allow custom handling of session expiration.
-* Fix: @tommcc - Fixed admin CSS loading only on the plugin settings screen.
-* Feature: @rkcreation - Added method to refresh the user claim.
-* Feature: @Glowsome - Added acr_values support & verification checks that it when defined in options is honored.
-* Fix: @timnolte - Fixed regression which caused improper fallback on missing claims.
-* Fix: @slykar - Fixed missing query string handling in redirect URL.
-* Fix: @timnolte - Fixed issue with some user linking and user creation handling.
-* Improvement: @timnolte - Fixed plugin settings typos and screen formatting.
-* Security: @timnolte - Updated build tooling security vulnerabilities.
-* Improvement: @timnolte - Changed build tooling scripts.
-
-### 3.8.5 ###
-
-* Fix: @timnolte - Fixed missing URL request validation before use & ensure proper current page URL is setup for Redirect Back.
-* Fix: @timnolte - Fixed Redirect URL Logic to Handle Sub-directory Installs.
-* Fix: @timnolte - Fixed issue with redirecting user back when the openid_connect_generic_auth_url shortcode is used.
-
-### 3.8.4 ###
-
-* Fix: @timnolte - Fixed invalid State object access for redirection handling.
-* Improvement: @timnolte - Fixed local wp-env Docker development environment.
-* Improvement: @timnolte - Fixed Composer scripts for linting and static analysis.
-
-### 3.8.3 ###
-
-* Fix: @timnolte - Fixed problems with proper redirect handling.
-* Improvement: @timnolte - Changes redirect handling to use State instead of cookies.
-* Improvement: @timnolte - Refactored additional code to meet coding standards.
-
-### 3.8.2 ###
-
-* Fix: @timnolte - Fixed reported XSS vulnerability on WordPress login screen.
-
-### 3.8.1 ###
-
-* Fix: @timnolte - Prevent SSO redirect on password protected posts.
-* Fix: @timnolte - CI/CD build issues.
-* Fix: @timnolte - Invalid redirect handling on logout for Auto Login setting.
-
-### 3.8.0 ###
-
-* Feature: @timnolte - Ability to use 6 new constants for setting client configuration instead of storing in the DB.
-* Improvement: @timnolte - Plugin development & contribution updates.
-* Improvement: @timnolte - Refactored to meet WordPress coding standards.
-* Improvement: @timnolte - Refactored to provide localization.
-
---------
-
-[See the previous changelogs here](https://github.com/oidc-wp/openid-connect-generic/blob/main/CHANGELOG.md#changelog)
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/SECURITY.md b/wp-content/plugins/daggerhart-openid-connect-generic/SECURITY.md
deleted file mode 100644
index 99ee0ea0..00000000
--- a/wp-content/plugins/daggerhart-openid-connect-generic/SECURITY.md
+++ /dev/null
@@ -1,17 +0,0 @@
-# Security Policy
-
-## Supported Versions
-
-We follow the [WordPress Core style of versioning](https://make.wordpress.org/core/handbook/about/release-cycle/version-numbering/) rather than traditional [SemVer](https://semver.org/). This means that a move from version 3.9 to 4.0 is no different from a move from version 3.8 to 3.9. When a **PATCH** version is released it represents a bug fix, or non-code, only change.
-
-The latest version released is the only version that will receive security updates, generally as a **PATCH** release unless a security issue requires a functionality change in which requires a minor/major version bump.
-
-## Reporting a Vulnerability
-
-For security reasons, the following are acceptable options for reporting all security issues.
-
-1. Via Keybase secure message to [timnolte](https://keybase.io/timnolte/chat) or [daggerhart](https://keybase.io/daggerhart/chat).
-2. Send a DM via the [WordPress Slack](https://make.wordpress.org/chat/) to `tnolte`.
-3. Via a private [security advisory](https://github.com/oidc-wp/openid-connect-generic/security/advisories) notice.
-
-Please disclose responsibly and not via public GitHub Issues (which allows for exploiting issues in the wild before the patch is released).
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/codecov.yml b/wp-content/plugins/daggerhart-openid-connect-generic/codecov.yml
deleted file mode 100644
index 2e3090aa..00000000
--- a/wp-content/plugins/daggerhart-openid-connect-generic/codecov.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-coverage:
- status:
- project:
- default:
- target: auto
- threshold: 0.5%
- patch: off
-
-comment:
- require_changes: true
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/css/styles-admin.css b/wp-content/plugins/daggerhart-openid-connect-generic/css/styles-admin.css
index cc4c6f99..f1a86fa5 100644
--- a/wp-content/plugins/daggerhart-openid-connect-generic/css/styles-admin.css
+++ b/wp-content/plugins/daggerhart-openid-connect-generic/css/styles-admin.css
@@ -30,3 +30,43 @@
#logger-table .col-details label {
font-weight: bold;
}
+
+/* Discovery Document Import Form */
+.oidc-discovery-section {
+ margin: 20px 0;
+}
+
+.oidc-discovery-summary {
+ cursor: pointer;
+ font-size: 1.1em;
+ font-weight: 600;
+ padding: 10px;
+ background: #f0f0f1;
+ border: 1px solid #c3c4c7;
+ border-radius: 4px;
+}
+
+.oidc-discovery-summary:hover {
+ background: #e8e8e9;
+}
+
+.oidc-discovery-content {
+ margin: 15px 0 0 0;
+ padding: 15px;
+ border-left: 4px solid #72aee6;
+}
+
+.oidc-discovery-url-input {
+ width: 500px;
+ max-width: 100%;
+}
+
+.oidc-discovery-separator {
+ margin: 30px 0;
+}
+
+/* Warning text styling */
+.oidc-warning {
+ color: #d63638;
+ font-weight: bold;
+}
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/docker-compose.yml b/wp-content/plugins/daggerhart-openid-connect-generic/docker-compose.yml
deleted file mode 100644
index a2778087..00000000
--- a/wp-content/plugins/daggerhart-openid-connect-generic/docker-compose.yml
+++ /dev/null
@@ -1,95 +0,0 @@
-# This is the Compose file for command-line services.
-# Anything that doesn't need to be run as part of the main `docker-compose up'
-# command should reside in here and be invoked by a helper script.
-version: "3.7"
-
-services:
- app:
- image: ghcr.io/ndigitals/wp-dev-container:php-8.0-node-16
- restart: always
- depends_on:
- - db
- - phpmyadmin
- - web
- - mailhog
- working_dir: /workspaces/openid-connect-generic
- environment: &env
- WORDPRESS_DB_HOST: db
- WORDPRESS_DB_NAME: wordpress
- WORDPRESS_DB_USER: wordpress
- WORDPRESS_DB_PASSWORD: wordpress
- WORDPRESS_TEST_DB_NAME: wordpress_test
- CODESPACES: "${CODESPACES}"
- CODESPACE_NAME: "${CODESPACE_NAME}"
- GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN: "${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}"
- volumes:
- - .:/workspaces/openid-connect-generic:cached
- - ./tools/local-env:/app:cached
- - ./tools/php/php-cli.ini:/usr/local/etc/php/php-cli.ini:ro,cached
- - .:/app/wp-content/plugins/daggerhart-openid-connect-generic:ro,cached
- - ~/.composer:/root/.composer:cached
- - ~/.npm:/root/.npm:cached
- networks:
- - oidcwp-net
-
- web:
- image: httpd
- restart: unless-stopped
- depends_on:
- - db
- ports:
- - 8080:80
- environment:
- <<: *env
- volumes:
- - ./tools/local-env:/app:cached
- - .:/app/wp-content/plugins/daggerhart-openid-connect-generic:ro,cached
- - ./tools/apache/httpd.conf:/usr/local/apache2/conf/httpd.conf:ro,cached
- networks:
- - oidcwp-net
-
- db:
- image: mariadb
- restart: unless-stopped
- ports:
- - 3306:3306
- environment:
- MYSQL_ROOT_PASSWORD: password
- MYSQL_DATABASE: wordpress
- MYSQL_USER: wordpress
- MYSQL_PASSWORD: wordpress
- volumes:
- - db:/var/lib/mysql
- - ./tests/db-wordpress_test.sql:/docker-entrypoint-initdb.d/db-wordpress_test.sql
- networks:
- - oidcwp-net
-
- phpmyadmin:
- image: phpmyadmin
- restart: unless-stopped
- depends_on:
- - db
- ports:
- - 8081:8081
- environment:
- PMA_HOST: db
- APACHE_PORT: 8081
- networks:
- - oidcwp-net
-
- ## SMTP Server + Web Interface for viewing and testing emails during development.
- mailhog:
- image: mailhog/mailhog
- restart: unless-stopped
- ports:
- - 1025:1025 # smtp server
- - 8026:8025 # web ui
- networks:
- - oidcwp-net
-
-volumes:
- db:
-
-networks:
- oidcwp-net:
-
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-client-wrapper.php b/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-client-wrapper.php
index 0b0a97f0..6cb70051 100644
--- a/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-client-wrapper.php
+++ b/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-client-wrapper.php
@@ -99,6 +99,11 @@ class OpenID_Connect_Generic_Client_Wrapper {
// Alter the requests according to settings.
add_filter( 'openid-connect-generic-alter-request', array( $client_wrapper, 'alter_request' ), 10, 2 );
+ // Ensure tokens are refreshed before they expire.
+ if ( $settings->token_refresh_enable ) {
+ add_action( 'init', array( $client_wrapper, 'ensure_tokens_still_fresh' ) );
+ }
+
if ( is_admin() ) {
/*
* Use the ajax url to handle processing authorization without any html output
@@ -252,7 +257,15 @@ class OpenID_Connect_Generic_Client_Wrapper {
}
$user_id = wp_get_current_user()->ID;
- $last_token_response = get_user_meta( $user_id, 'openid-connect-generic-last-token-response', true );
+ $last_token_response = get_user_option( 'openid-connect-generic-last-token-response', $user_id );
+
+ if ( false === $last_token_response ) {
+ $last_token_response = get_user_meta(
+ $user_id,
+ 'openid-connect-generic-last-token-response',
+ true
+ );
+ }
if ( ! empty( $last_token_response['expires_in'] ) && ! empty( $last_token_response['time'] ) ) {
/*
@@ -296,9 +309,9 @@ class OpenID_Connect_Generic_Client_Wrapper {
}
// Capture the time so that access token expiration can be calculated later.
- $token_response[] = time();
+ $token_response['time'] = time();
- update_user_meta( $user_id, 'openid-connect-generic-last-token-response', $token_response );
+ update_user_option( $user_id, 'openid-connect-generic-last-token-response', $token_response );
$this->save_refresh_token( $manager, $token, $token_response );
}
@@ -368,7 +381,7 @@ class OpenID_Connect_Generic_Client_Wrapper {
$redirect_url = home_url();
}
- $token_response = $user->get( 'openid-connect-generic-last-token-response' );
+ $token_response = get_user_option( 'openid-connect-generic-last-token-response', $user->ID );
if ( ! $token_response ) {
// Happens if non-openid login was used.
return $redirect_url;
@@ -377,7 +390,7 @@ class OpenID_Connect_Generic_Client_Wrapper {
$redirect_url = site_url( $redirect_url );
}
- $claim = $user->get( 'openid-connect-generic-last-id-token-claim' );
+ $claim = get_user_option( 'openid-connect-generic-last-id-token-claim', $user->ID );
if ( isset( $claim['iss'] ) && 'https://accounts.google.com' == $claim['iss'] ) {
/*
@@ -407,8 +420,20 @@ class OpenID_Connect_Generic_Client_Wrapper {
$request['timeout'] = intval( $this->settings->http_request_timeout );
}
- if ( $this->settings->no_sslverify ) {
+ // Only allow SSL bypass in local development environments.
+ if (
+ $this->settings->no_sslverify &&
+ defined( 'WP_DEBUG' ) && WP_DEBUG === true &&
+ ( ! defined( 'WP_ENVIRONMENT_TYPE' ) || WP_ENVIRONMENT_TYPE === 'local' )
+ ) {
+
$request['sslverify'] = false;
+
+ // Log warning every time this is used.
+ $this->logger->log(
+ 'SSL verification disabled - ONLY for development. NEVER use in production!',
+ 'ssl-bypass-warning'
+ );
}
return $request;
@@ -427,6 +452,32 @@ class OpenID_Connect_Generic_Client_Wrapper {
$authentication_request = $client->validate_authentication_request( $_GET );
if ( is_wp_error( $authentication_request ) ) {
+ // Check if this is a retryable IDP error (e.g. Safari ITP causing
+ // Keycloak session cookies to be blocked on cross-site navigation).
+ $retryable_idp_errors = array(
+ 'temporarily_unavailable',
+ 'authentication_expired',
+ 'login_required',
+ );
+
+ $error_code = $authentication_request->get_error_code();
+ $is_retryable = in_array( $error_code, $retryable_idp_errors, true );
+ $already_retried = isset( $_GET['openid-connect-generic-retry'] );
+
+ if ( $is_retryable && ! $already_retried ) {
+ // Log the original error before retrying.
+ $this->logger->log( $authentication_request, 'retry' );
+ $this->logger->log( "Retrying authentication due to IDP error: {$error_code}", 'retry' );
+
+ // Build a fresh authentication URL and append a retry flag
+ // to prevent infinite redirect loops (max 1 retry).
+ $auth_url = $this->get_authentication_url();
+ $auth_url = add_query_arg( 'openid-connect-generic-retry', '1', $auth_url );
+
+ wp_redirect( $auth_url );
+ exit;
+ }
+
$this->error_redirect( $authentication_request );
}
@@ -559,7 +610,10 @@ class OpenID_Connect_Generic_Client_Wrapper {
// Provide backwards compatibility for customization using the deprecated cookie method.
if ( ! empty( $_COOKIE[ self::COOKIE_REDIRECT_KEY ] ) ) {
- $redirect_url = esc_url_raw( wp_unslash( $_COOKIE[ self::COOKIE_REDIRECT_KEY ] ) );
+ $redirect_url = wp_validate_redirect(
+ esc_url_raw( wp_unslash( $_COOKIE[ self::COOKIE_REDIRECT_KEY ] ) ),
+ home_url()
+ );
}
// Only do redirect-user-back action hook when the plugin is configured for it.
@@ -640,9 +694,9 @@ class OpenID_Connect_Generic_Client_Wrapper {
}
// Store the tokens for future reference.
- update_user_meta( $user->ID, 'openid-connect-generic-last-token-response', $token_response );
- update_user_meta( $user->ID, 'openid-connect-generic-last-id-token-claim', $id_token_claim );
- update_user_meta( $user->ID, 'openid-connect-generic-last-user-claim', $user_claim );
+ update_user_option( $user->ID, 'openid-connect-generic-last-token-response', $token_response );
+ update_user_option( $user->ID, 'openid-connect-generic-last-id-token-claim', $id_token_claim );
+ update_user_option( $user->ID, 'openid-connect-generic-last-user-claim', $user_claim );
return $user_claim;
}
@@ -660,9 +714,9 @@ class OpenID_Connect_Generic_Client_Wrapper {
*/
public function login_user( $user, $token_response, $id_token_claim, $user_claim, $subject_identity ): void {
// Store the tokens for future reference.
- update_user_meta( $user->ID, 'openid-connect-generic-last-token-response', $token_response );
- update_user_meta( $user->ID, 'openid-connect-generic-last-id-token-claim', $id_token_claim );
- update_user_meta( $user->ID, 'openid-connect-generic-last-user-claim', $user_claim );
+ update_user_option( $user->ID, 'openid-connect-generic-last-token-response', $token_response );
+ update_user_option( $user->ID, 'openid-connect-generic-last-id-token-claim', $id_token_claim );
+ update_user_option( $user->ID, 'openid-connect-generic-last-user-claim', $user_claim );
// Allow plugins / themes to take action using current claims on existing user (e.g. update role).
do_action( 'openid-connect-generic-update-user-using-current-claim', $user, $user_claim );
@@ -713,14 +767,21 @@ class OpenID_Connect_Generic_Client_Wrapper {
* @return false|WP_User
*/
public function get_user_by_identity( $subject_identity ) {
+ global $wpdb;
+
// Look for user by their openid-connect-generic-subject-identity value.
$user_query = new WP_User_Query(
array(
'meta_query' => array(
+ 'relation' => 'OR',
array(
'key' => 'openid-connect-generic-subject-identity',
'value' => $subject_identity,
),
+ array(
+ 'key' => $wpdb->get_blog_prefix() . 'openid-connect-generic-subject-identity',
+ 'value' => $subject_identity,
+ ),
),
// Override the default blog_id (get_current_blog_id) to find users on different sites of a multisite install.
'blog_id' => 0,
@@ -847,13 +908,48 @@ class OpenID_Connect_Generic_Client_Wrapper {
return false;
}
/**
- * Extract claim from JWT.
- * FIXME: We probably want to verify the JWT signature/issuer here.
- * For example, using JWKS if applicable. For symmetrically signed
- * JWTs (HMAC), we need a way to specify the acceptable secrets
- * and each possible issuer in the config.
+ * Extract claim from JWT with signature verification.
*/
$jwt = $src['JWT'];
+
+ // Check if JWKS endpoint is configured for JWT signature verification.
+ if ( ! empty( $this->settings->endpoint_jwks ) ) {
+ // Use configured issuer if provided, otherwise derive from endpoint_login.
+ $issuer = ! empty( $this->settings->issuer ) ?
+ $this->settings->issuer :
+ ( ! empty( $this->settings->endpoint_login ) ? $this->client->get_issuer_from_endpoint( $this->settings->endpoint_login ) : '' );
+
+ // Use JWT validator for secure signature verification.
+ $jwt_validator = new OpenID_Connect_Generic_JWT_Validator(
+ $this->settings->endpoint_jwks,
+ $this->settings->client_id,
+ $issuer,
+ $this->settings->jwks_cache_ttl,
+ $this->settings->allow_internal_idp,
+ $this->logger
+ );
+
+ // Validate JWT signature and claims.
+ $body_json = $jwt_validator->validate_id_token( $jwt );
+
+ if ( is_wp_error( $body_json ) ) {
+ $this->logger->log( $body_json, 'aggregated-claim-jwt-validation-failed' );
+ return false;
+ }
+
+ if ( ! array_key_exists( $claimname, $body_json ) ) {
+ return false;
+ }
+ $claimvalue = $body_json[ $claimname ];
+ return true;
+ }
+
+ $this->logger->log(
+ 'SECURITY WARNING: JWKS endpoint not configured. Aggregated claim JWT signatures are NOT being verified. This is a security vulnerability. Configure the JWKS endpoint to secure aggregated claims.',
+ 'aggregated-jwt-not-verified'
+ );
+
+ // Legacy JWT decoding without signature verification (INSECURE).
list ( $header, $body, $rest ) = explode( '.', $jwt, 3 );
$body_str = base64_decode( $body, false );
if ( ! $body_str ) {
@@ -1098,7 +1194,7 @@ class OpenID_Connect_Generic_Client_Wrapper {
$user = get_user_by( 'id', $uid );
// Save some meta data about this new user for the future.
- add_user_meta( $user->ID, 'openid-connect-generic-subject-identity', (string) $subject_identity, true );
+ update_user_option( $user->ID, 'openid-connect-generic-subject-identity', (string) $subject_identity, true );
// Log the results.
$end_time = microtime( true );
@@ -1120,7 +1216,7 @@ class OpenID_Connect_Generic_Client_Wrapper {
*/
public function update_existing_user( $uid, $subject_identity ) {
// Add the OpenID Connect meta data.
- update_user_meta( $uid, 'openid-connect-generic-subject-identity', strval( $subject_identity ) );
+ update_user_option( $uid, 'openid-connect-generic-subject-identity', strval( $subject_identity ), true );
// Allow plugins / themes to take action on user update.
do_action( 'openid-connect-generic-user-update', $uid );
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-client.php b/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-client.php
index 2cbfa5dd..ed4781b4 100644
--- a/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-client.php
+++ b/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-client.php
@@ -91,6 +91,33 @@ class OpenID_Connect_Generic_Client {
*/
private $acr_values;
+ /**
+ * The JWKS endpoint URL for JWT signature verification.
+ *
+ * @see OpenID_Connect_Generic_Option_Settings::endpoint_jwks
+ *
+ * @var string
+ */
+ private $endpoint_jwks;
+
+ /**
+ * The issuer URL for JWT validation.
+ *
+ * @see OpenID_Connect_Generic_Option_Settings::issuer
+ *
+ * @var string
+ */
+ private $issuer;
+
+ /**
+ * The JWKS cache TTL in seconds.
+ *
+ * @see OpenID_Connect_Generic_Option_Settings::jwks_cache_ttl
+ *
+ * @var int
+ */
+ private $jwks_cache_ttl;
+
/**
* The state time limit. States are only valid for 3 minutes.
*
@@ -100,6 +127,15 @@ class OpenID_Connect_Generic_Client {
*/
private $state_time_limit = 180;
+ /**
+ * Allow HTTP requests to internal/private network endpoints.
+ *
+ * @see OpenID_Connect_Generic_Option_Settings::allow_internal_idp
+ *
+ * @var bool
+ */
+ private $allow_internal_idp;
+
/**
* The logger object instance.
*
@@ -110,18 +146,22 @@ class OpenID_Connect_Generic_Client {
/**
* Client constructor.
*
- * @param string $client_id @see OpenID_Connect_Generic_Option_Settings::client_id for description.
- * @param string $client_secret @see OpenID_Connect_Generic_Option_Settings::client_secret for description.
- * @param string $scope @see OpenID_Connect_Generic_Option_Settings::scope for description.
- * @param string $endpoint_login @see OpenID_Connect_Generic_Option_Settings::endpoint_login for description.
- * @param string $endpoint_userinfo @see OpenID_Connect_Generic_Option_Settings::endpoint_userinfo for description.
- * @param string $endpoint_token @see OpenID_Connect_Generic_Option_Settings::endpoint_token for description.
- * @param string $redirect_uri @see OpenID_Connect_Generic_Option_Settings::redirect_uri for description.
- * @param string $acr_values @see OpenID_Connect_Generic_Option_Settings::acr_values for description.
- * @param int $state_time_limit @see OpenID_Connect_Generic_Option_Settings::state_time_limit for description.
- * @param OpenID_Connect_Generic_Option_Logger $logger The plugin logging object instance.
+ * @param string $client_id @see OpenID_Connect_Generic_Option_Settings::client_id for description.
+ * @param string $client_secret @see OpenID_Connect_Generic_Option_Settings::client_secret for description.
+ * @param string $scope @see OpenID_Connect_Generic_Option_Settings::scope for description.
+ * @param string $endpoint_login @see OpenID_Connect_Generic_Option_Settings::endpoint_login for description.
+ * @param string $endpoint_userinfo @see OpenID_Connect_Generic_Option_Settings::endpoint_userinfo for description.
+ * @param string $endpoint_token @see OpenID_Connect_Generic_Option_Settings::endpoint_token for description.
+ * @param string $redirect_uri @see OpenID_Connect_Generic_Option_Settings::redirect_uri for description.
+ * @param string $acr_values @see OpenID_Connect_Generic_Option_Settings::acr_values for description.
+ * @param string $endpoint_jwks @see OpenID_Connect_Generic_Option_Settings::endpoint_jwks for description.
+ * @param string $issuer @see OpenID_Connect_Generic_Option_Settings::issuer for description.
+ * @param int $jwks_cache_ttl @see OpenID_Connect_Generic_Option_Settings::jwks_cache_ttl for description.
+ * @param int $state_time_limit @see OpenID_Connect_Generic_Option_Settings::state_time_limit for description.
+ * @param bool $allow_internal_idp @see OpenID_Connect_Generic_Option_Settings::allow_internal_idp for description.
+ * @param OpenID_Connect_Generic_Option_Logger $logger The plugin logging object instance.
*/
- public function __construct( $client_id, $client_secret, $scope, $endpoint_login, $endpoint_userinfo, $endpoint_token, $redirect_uri, $acr_values, $state_time_limit, $logger ) {
+ public function __construct( $client_id, $client_secret, $scope, $endpoint_login, $endpoint_userinfo, $endpoint_token, $redirect_uri, $acr_values, $endpoint_jwks, $issuer, $jwks_cache_ttl, $state_time_limit, $allow_internal_idp, $logger ) {
$this->client_id = $client_id;
$this->client_secret = $client_secret;
$this->scope = $scope;
@@ -130,10 +170,52 @@ class OpenID_Connect_Generic_Client {
$this->endpoint_token = $endpoint_token;
$this->redirect_uri = $redirect_uri;
$this->acr_values = $acr_values;
+ $this->endpoint_jwks = $endpoint_jwks;
+ $this->issuer = $issuer;
+ $this->jwks_cache_ttl = $jwks_cache_ttl;
$this->state_time_limit = $state_time_limit;
+ $this->allow_internal_idp = $allow_internal_idp;
$this->logger = $logger;
}
+ /**
+ * Make a safe HTTP GET request with optional internal endpoint support.
+ *
+ * By default, uses wp_safe_remote_get() which blocks requests to internal/private
+ * networks (SSRF protection). If allow_internal_idp is enabled, uses wp_remote_get()
+ * to allow connections to localhost and private network identity providers.
+ *
+ * @param string $url The URL to request.
+ * @param array $args Optional. Request arguments.
+ *
+ * @return array|WP_Error Response array or WP_Error on failure.
+ */
+ private function http_get( $url, $args = array() ) {
+ if ( $this->allow_internal_idp ) {
+ return wp_remote_get( $url, $args );
+ }
+ return wp_safe_remote_get( $url, $args );
+ }
+
+ /**
+ * Make a safe HTTP POST request with optional internal endpoint support.
+ *
+ * By default, uses wp_safe_remote_post() which blocks requests to internal/private
+ * networks (SSRF protection). If allow_internal_idp is enabled, uses wp_remote_post()
+ * to allow connections to localhost and private network identity providers.
+ *
+ * @param string $url The URL to request.
+ * @param array $args Optional. Request arguments.
+ *
+ * @return array|WP_Error Response array or WP_Error on failure.
+ */
+ private function http_post( $url, $args = array() ) {
+ if ( $this->allow_internal_idp ) {
+ return wp_remote_post( $url, $args );
+ }
+ return wp_safe_remote_post( $url, $args );
+ }
+
/**
* Provides the configured Redirect URI supplied to the IDP.
*
@@ -162,7 +244,19 @@ class OpenID_Connect_Generic_Client {
public function validate_authentication_request( $request ) {
// Look for an existing error of some kind.
if ( isset( $request['error'] ) ) {
- return new WP_Error( 'unknown-error', 'An unknown error occurred.', $request );
+ $error_code = sanitize_text_field( $request['error'] );
+ $error_message = 'An unknown error occurred.';
+
+ // Use the IDP's error description if available for better diagnostics.
+ if ( ! empty( $request['error_description'] ) ) {
+ $error_message = sprintf(
+ 'IDP error %s: %s',
+ $error_code,
+ sanitize_text_field( $request['error_description'] )
+ );
+ }
+
+ return new WP_Error( $error_code, $error_message, $request );
}
// Make sure we have a legitimate authentication code and valid state.
@@ -232,7 +326,7 @@ class OpenID_Connect_Generic_Client {
// Call the server and ask for a token.
$start_time = microtime( true );
- $response = wp_remote_post( $this->endpoint_token, $request );
+ $response = $this->http_post( $this->endpoint_token, $request );
$end_time = microtime( true );
$this->logger->log( $this->endpoint_token, 'request_authentication_token', $end_time - $start_time );
@@ -265,7 +359,7 @@ class OpenID_Connect_Generic_Client {
// Call the server and ask for new tokens.
$start_time = microtime( true );
- $response = wp_remote_post( $this->endpoint_token, $request );
+ $response = $this->http_post( $this->endpoint_token, $request );
$end_time = microtime( true );
$this->logger->log( $this->endpoint_token, 'request_new_tokens', $end_time - $start_time );
@@ -341,8 +435,16 @@ class OpenID_Connect_Generic_Client {
// Attempt the request including the access token in the query string for backwards compatibility.
$start_time = microtime( true );
- $response = wp_remote_get( $this->endpoint_userinfo, $request );
- $end_time = microtime( true );
+ $response = $this->http_get( $this->endpoint_userinfo, $request );
+
+ // This endpoint can support GET or POST requests according to spec, but some IDPs only allow one.
+ // If the GET request failed to produce valid json, attempt a POST request.
+ // Spec: https://openid.net/specs/openid-connect-core-1_0.html#UserInfoRequest.
+ if ( ! is_wp_error( $response ) && json_decode( $response['body'] ) === null ) {
+ $response = $this->http_post( $this->endpoint_userinfo, $request );
+ }
+
+ $end_time = microtime( true );
$this->logger->log( $this->endpoint_userinfo, 'request_userinfo', $end_time - $start_time );
if ( is_wp_error( $response ) ) {
@@ -360,8 +462,8 @@ class OpenID_Connect_Generic_Client {
* @return string
*/
public function new_state( $redirect_to ) {
- // New state w/ timestamp.
- $state = md5( mt_rand() . microtime( true ) );
+ // New state with cryptographically secure random bytes.
+ $state = bin2hex( random_bytes( 16 ) );
$state_value = array(
$state => array(
'redirect_to' => $redirect_to,
@@ -438,7 +540,7 @@ class OpenID_Connect_Generic_Client {
}
/**
- * Extract the id_token_claim from the token_response.
+ * Extract and validate the id_token_claim from the token_response.
*
* @param array $token_response The token response.
*
@@ -450,14 +552,47 @@ class OpenID_Connect_Generic_Client {
return new WP_Error( 'no-identity-token', __( 'No identity token.', 'daggerhart-openid-connect-generic' ), $token_response );
}
- // Break apart the id_token in the response for decoding.
+ // Check if JWKS endpoint is configured for JWT signature verification.
+ if ( ! empty( $this->endpoint_jwks ) ) {
+ // Use configured issuer if provided, otherwise derive from endpoint_login.
+ $issuer = ! empty( $this->issuer )
+ ? $this->issuer
+ : $this->get_issuer_from_endpoint( $this->endpoint_login );
+
+ // Use JWT validator for secure signature verification.
+ $jwt_validator = new OpenID_Connect_Generic_JWT_Validator(
+ $this->endpoint_jwks,
+ $this->client_id,
+ $issuer,
+ $this->jwks_cache_ttl,
+ $this->allow_internal_idp,
+ $this->logger
+ );
+
+ // Validate JWT signature and claims.
+ $id_token_claim = $jwt_validator->validate_id_token( $token_response['id_token'] );
+
+ if ( is_wp_error( $id_token_claim ) ) {
+ $this->logger->log( $id_token_claim, 'jwt-validation-failed' );
+ return $id_token_claim;
+ }
+
+ return $id_token_claim;
+ }
+
+ $this->logger->log(
+ 'SECURITY WARNING: JWKS endpoint not configured. JWT signatures are NOT being verified. This is a critical security vulnerability. Configure the JWKS endpoint immediately in Settings > OpenID Connect Client to secure authentication.',
+ 'jwks-not-configured-insecure'
+ );
+
+ // Legacy JWT decoding without signature verification (INSECURE).
$tmp = explode( '.', $token_response['id_token'] );
if ( ! isset( $tmp[1] ) ) {
return new WP_Error( 'missing-identity-token', __( 'Missing identity token.', 'daggerhart-openid-connect-generic' ), $token_response );
}
- // Extract the id_token's claims from the token.
+ // Extract the id_token's claims from the token (no signature verification).
$id_token_claim = json_decode(
base64_decode(
str_replace( // Because token is encoded in base64 URL (and not just base64).
@@ -472,6 +607,38 @@ class OpenID_Connect_Generic_Client {
return $id_token_claim;
}
+ /**
+ * Extract issuer URL from endpoint URL.
+ *
+ * The issuer is typically the base URL (scheme + host + trailing slash).
+ *
+ * @param string $endpoint_url The full endpoint URL.
+ *
+ * @return string The issuer URL.
+ */
+ public function get_issuer_from_endpoint( $endpoint_url ) {
+ $parsed = wp_parse_url( $endpoint_url );
+
+ if ( ! $parsed || ! isset( $parsed['scheme'] ) || ! isset( $parsed['host'] ) ) {
+ return $endpoint_url;
+ }
+
+ $issuer = $parsed['scheme'] . '://' . $parsed['host'];
+
+ // Add port if non-standard.
+ if ( isset( $parsed['port'] ) ) {
+ $default_ports = array(
+ 'http' => 80,
+ 'https' => 443,
+ );
+ if ( ! isset( $default_ports[ $parsed['scheme'] ] ) || $parsed['port'] != $default_ports[ $parsed['scheme'] ] ) {
+ $issuer .= ':' . $parsed['port'];
+ }
+ }
+
+ return $issuer;
+ }
+
/**
* Ensure the id_token_claim contains the required values.
*
@@ -484,11 +651,71 @@ class OpenID_Connect_Generic_Client {
return new WP_Error( 'bad-id-token-claim', __( 'Bad ID token claim.', 'daggerhart-openid-connect-generic' ), $id_token_claim );
}
- // Validate the identification data and it's value.
+ // Validate the identification data and its value.
if ( ! isset( $id_token_claim['sub'] ) || empty( $id_token_claim['sub'] ) ) {
return new WP_Error( 'no-subject-identity', __( 'No subject identity.', 'daggerhart-openid-connect-generic' ), $id_token_claim );
}
+ // Validate expiration claim.
+ if ( ! isset( $id_token_claim['exp'] ) ) {
+ return new WP_Error( 'missing-exp', __( 'Token missing expiration claim.', 'daggerhart-openid-connect-generic' ), $id_token_claim );
+ }
+ if ( time() >= $id_token_claim['exp'] ) {
+ return new WP_Error( 'token-expired', __( 'Token has expired.', 'daggerhart-openid-connect-generic' ), $id_token_claim );
+ }
+
+ // Validate issued at claim.
+ if ( ! isset( $id_token_claim['iat'] ) ) {
+ return new WP_Error( 'missing-iat', __( 'Token missing issued at claim.', 'daggerhart-openid-connect-generic' ), $id_token_claim );
+ }
+
+ // Validate audience claim matches client_id (can be string or array).
+ if ( ! isset( $id_token_claim['aud'] ) ) {
+ return new WP_Error( 'missing-aud', __( 'Token missing audience claim.', 'daggerhart-openid-connect-generic' ), $id_token_claim );
+ }
+
+ $aud = $id_token_claim['aud'];
+ $audience_valid = false;
+
+ if ( is_array( $aud ) ) {
+ $audience_valid = in_array( $this->client_id, $aud, true );
+ } elseif ( is_string( $aud ) ) {
+ $audience_valid = ( $aud === $this->client_id );
+ }
+
+ if ( ! $audience_valid ) {
+ return new WP_Error( 'invalid-aud', __( 'Token audience does not match client.', 'daggerhart-openid-connect-generic' ), $id_token_claim );
+ }
+
+ // Validate issuer claim if configured or endpoint_login is available.
+ $expected_issuer = ! empty( $this->issuer ) ?
+ $this->issuer :
+ ( ! empty( $this->endpoint_login ) ? $this->get_issuer_from_endpoint( $this->endpoint_login ) : '' );
+
+ if ( ! empty( $expected_issuer ) ) {
+ if ( ! isset( $id_token_claim['iss'] ) ) {
+ return new WP_Error( 'missing-iss', __( 'Token missing issuer claim.', 'daggerhart-openid-connect-generic' ), $id_token_claim );
+ }
+
+ if ( rtrim( $id_token_claim['iss'], '/' ) !== rtrim( $expected_issuer, '/' ) ) {
+ $this->logger->log(
+ sprintf(
+ 'Issuer mismatch - Expected: "%s", Received: "%s". Configure the correct issuer in Settings > OpenID Connect Client > Issuer field, or via the OIDC_ISSUER constant.',
+ $expected_issuer,
+ $id_token_claim['iss']
+ ),
+ 'issuer-mismatch'
+ );
+ return new WP_Error(
+ 'invalid-iss',
+ sprintf(
+ __( 'Token issuer does not match expected issuer.', 'daggerhart-openid-connect-generic' ),
+ ),
+ $id_token_claim
+ );
+ }
+ }
+
// Validate acr values when the option is set in the configuration.
if ( ! empty( $this->acr_values ) && isset( $id_token_claim['acr'] ) ) {
if ( $this->acr_values != $id_token_claim['acr'] ) {
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-jwt-validator.php b/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-jwt-validator.php
new file mode 100644
index 00000000..f7c1b584
--- /dev/null
+++ b/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-jwt-validator.php
@@ -0,0 +1,364 @@
+
+ * @copyright 2015-2020 daggerhart
+ * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
+ */
+
+use Firebase\JWT\JWT;
+use Firebase\JWT\JWK;
+use Firebase\JWT\Key;
+
+/**
+ * OpenID_Connect_Generic_JWT_Validator class.
+ *
+ * Handles JWT signature verification and claim validation using JWKS.
+ *
+ * @package OpenID_Connect_Generic
+ * @category Authentication
+ */
+class OpenID_Connect_Generic_JWT_Validator {
+
+ /**
+ * The JWKS endpoint URL.
+ *
+ * @var string
+ */
+ private $jwks_uri;
+
+ /**
+ * The expected client ID (audience).
+ *
+ * @var string
+ */
+ private $client_id;
+
+ /**
+ * The expected issuer.
+ *
+ * @var string
+ */
+ private $issuer;
+
+ /**
+ * JWKS cache TTL in seconds.
+ *
+ * @var int
+ */
+ private $cache_ttl;
+
+ /**
+ * Allow HTTP requests to internal/private network endpoints.
+ *
+ * @var bool
+ */
+ private $allow_internal_idp;
+
+ /**
+ * Logger instance.
+ *
+ * @var OpenID_Connect_Generic_Option_Logger
+ */
+ private $logger;
+
+ /**
+ * Constructor.
+ *
+ * @param string $jwks_uri The JWKS endpoint URL.
+ * @param string $client_id The client ID for audience validation.
+ * @param string $issuer The expected issuer.
+ * @param int $cache_ttl JWKS cache TTL in seconds.
+ * @param bool $allow_internal_idp Allow internal/private network endpoints.
+ * @param OpenID_Connect_Generic_Option_Logger $logger Logger instance.
+ */
+ public function __construct( $jwks_uri, $client_id, $issuer, $cache_ttl, $allow_internal_idp, $logger ) {
+ $this->jwks_uri = $jwks_uri;
+ $this->client_id = $client_id;
+ $this->issuer = $issuer;
+ $this->cache_ttl = $cache_ttl;
+ $this->allow_internal_idp = $allow_internal_idp;
+ $this->logger = $logger;
+ }
+
+ /**
+ * Make a safe HTTP GET request with optional internal endpoint support.
+ *
+ * By default, uses wp_safe_remote_get() which blocks requests to internal/private
+ * networks (SSRF protection). If allow_internal_idp is enabled, uses wp_remote_get()
+ * to allow connections to localhost and private network identity providers.
+ *
+ * @param string $url The URL to request.
+ * @param array $args Optional. Request arguments.
+ *
+ * @return array|WP_Error Response array or WP_Error on failure.
+ */
+ private function http_get( $url, $args = array() ) {
+ if ( $this->allow_internal_idp ) {
+ return wp_remote_get( $url, $args );
+ }
+ return wp_safe_remote_get( $url, $args );
+ }
+
+ /**
+ * Fetch JWKS from the IDP endpoint with caching.
+ *
+ * @return array|WP_Error Array of keys or WP_Error on failure.
+ */
+ private function fetch_jwks() {
+ // Check cache first.
+ $cache_key = 'openid_connect_jwks_' . md5( $this->jwks_uri );
+ $cached_jwks = get_transient( $cache_key );
+
+ if ( false !== $cached_jwks ) {
+ return $cached_jwks;
+ }
+
+ // Fetch JWKS from IDP.
+ $response = $this->http_get( $this->jwks_uri, array( 'timeout' => 10 ) );
+
+ if ( is_wp_error( $response ) ) {
+ $this->logger->log( $response, 'jwks-fetch-failed' );
+ return new WP_Error(
+ 'jwks-fetch-failed',
+ __( 'Failed to fetch JWKS from identity provider.', 'daggerhart-openid-connect-generic' ),
+ $response
+ );
+ }
+
+ $response_code = wp_remote_retrieve_response_code( $response );
+ if ( 200 !== $response_code ) {
+ $error = new WP_Error(
+ 'jwks-fetch-failed',
+ sprintf(
+ /* translators: %d is the HTTP response code */
+ __( 'JWKS endpoint returned HTTP %d', 'daggerhart-openid-connect-generic' ),
+ $response_code
+ )
+ );
+ $this->logger->log( $error, 'jwks-fetch-failed' );
+ return $error;
+ }
+
+ $body = wp_remote_retrieve_body( $response );
+ $jwks = json_decode( $body, true );
+
+ if ( ! $jwks || ! isset( $jwks['keys'] ) ) {
+ $error = new WP_Error(
+ 'jwks-invalid-format',
+ __( 'Invalid JWKS format received from identity provider.', 'daggerhart-openid-connect-generic' )
+ );
+ $this->logger->log( $error, 'jwks-invalid-format' );
+ return $error;
+ }
+
+ // Cache the JWKS.
+ set_transient( $cache_key, $jwks, $this->cache_ttl );
+
+ return $jwks;
+ }
+
+ /**
+ * Validate JWT claims.
+ *
+ * @param object $decoded_jwt The decoded JWT payload.
+ *
+ * @return true|WP_Error True if valid, WP_Error on failure.
+ */
+ private function validate_jwt_claims( $decoded_jwt ) {
+ // Validate subject (sub) claim.
+ if ( ! isset( $decoded_jwt->sub ) || empty( $decoded_jwt->sub ) ) {
+ return new WP_Error(
+ 'missing-sub',
+ __( 'Token missing subject claim.', 'daggerhart-openid-connect-generic' )
+ );
+ }
+
+ // Validate expiration (exp) claim - JWT library already validates this, but double-check.
+ if ( ! isset( $decoded_jwt->exp ) ) {
+ return new WP_Error(
+ 'missing-exp',
+ __( 'Token missing expiration claim.', 'daggerhart-openid-connect-generic' )
+ );
+ }
+
+ // Validate issued at (iat) claim.
+ if ( ! isset( $decoded_jwt->iat ) ) {
+ return new WP_Error(
+ 'missing-iat',
+ __( 'Token missing issued at claim.', 'daggerhart-openid-connect-generic' )
+ );
+ }
+
+ // Validate audience (aud) claim.
+ if ( ! isset( $decoded_jwt->aud ) ) {
+ return new WP_Error(
+ 'missing-aud',
+ __( 'Token missing audience claim.', 'daggerhart-openid-connect-generic' )
+ );
+ }
+
+ // Audience can be string or array.
+ $aud = $decoded_jwt->aud;
+ $audience_valid = false;
+
+ if ( is_array( $aud ) ) {
+ $audience_valid = in_array( $this->client_id, $aud, true );
+ } elseif ( is_string( $aud ) ) {
+ $audience_valid = ( $aud === $this->client_id );
+ }
+
+ if ( ! $audience_valid ) {
+ return new WP_Error(
+ 'invalid-aud',
+ __( 'Token audience does not match client.', 'daggerhart-openid-connect-generic' )
+ );
+ }
+
+ // Validate issuer (iss) claim if configured.
+ if ( ! empty( $this->issuer ) ) {
+ if ( ! isset( $decoded_jwt->iss ) ) {
+ return new WP_Error(
+ 'missing-iss',
+ __( 'Token missing issuer claim.', 'daggerhart-openid-connect-generic' )
+ );
+ }
+
+ if ( rtrim( $decoded_jwt->iss, '/' ) !== rtrim( $this->issuer, '/' ) ) {
+ $this->logger->log(
+ sprintf(
+ 'Issuer mismatch - Expected: "%s", Received: "%s". Configure the correct issuer in Settings > OpenID Connect Client > Issuer field, or via the OIDC_ISSUER constant.',
+ $this->issuer,
+ $decoded_jwt->iss
+ ),
+ 'issuer-mismatch'
+ );
+ return new WP_Error(
+ 'invalid-iss',
+ __( 'Token issuer does not match expected issuer.', 'daggerhart-openid-connect-generic' )
+ );
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Extract the algorithm from JWT header.
+ *
+ * @param string $id_token The JWT ID token.
+ *
+ * @return string|null The algorithm from JWT header or null if not found.
+ */
+ private function get_jwt_header_alg( $id_token ) {
+ $token_parts = explode( '.', $id_token );
+ if ( count( $token_parts ) < 2 ) {
+ return null;
+ }
+
+ $header_base64 = $token_parts[0];
+ $header_json = JWT::urlsafeB64Decode( $header_base64 );
+ $header = json_decode( $header_json, true );
+
+ return isset( $header['alg'] ) ? $header['alg'] : null;
+ }
+
+ /**
+ * Enrich JWKS with algorithm from JWT header if missing.
+ *
+ * Some identity providers (like Microsoft Entra ID) return JWKs without
+ * the "alg" parameter. This method adds the algorithm from the JWT header
+ * to each key that's missing it, ensuring compatibility with the Firebase
+ * JWT library which requires "alg" to be present.
+ *
+ * @param array $jwks The JWKS array with keys.
+ * @param string $id_token The JWT ID token.
+ *
+ * @return array The enriched JWKS array.
+ */
+ private function enrich_jwks_with_alg( $jwks, $id_token ) {
+ // Extract algorithm from JWT header.
+ $jwt_alg = $this->get_jwt_header_alg( $id_token );
+
+ // If we couldn't extract the algorithm, default to RS256 (most common for OIDC).
+ if ( empty( $jwt_alg ) ) {
+ $jwt_alg = 'RS256';
+ }
+
+ // Add algorithm to keys that are missing it.
+ if ( isset( $jwks['keys'] ) && is_array( $jwks['keys'] ) ) {
+ foreach ( $jwks['keys'] as &$key ) {
+ if ( ! isset( $key['alg'] ) ) {
+ $key['alg'] = $jwt_alg;
+ }
+ }
+ }
+
+ return $jwks;
+ }
+
+ /**
+ * Validate and verify an ID token.
+ *
+ * @param string $id_token The JWT ID token to validate.
+ *
+ * @return array|WP_Error Array of claims if valid, WP_Error on failure.
+ */
+ public function validate_id_token( $id_token ) {
+ // Check if JWKS URI is configured.
+ if ( empty( $this->jwks_uri ) ) {
+ $error = new WP_Error(
+ 'jwks-not-configured',
+ __( 'JWKS URI not configured. JWT signature verification requires JWKS endpoint.', 'daggerhart-openid-connect-generic' )
+ );
+ $this->logger->log( $error, 'jwks-not-configured' );
+ return $error;
+ }
+
+ // Fetch JWKS.
+ $jwks = $this->fetch_jwks();
+ if ( is_wp_error( $jwks ) ) {
+ return $jwks;
+ }
+
+ // Enrich JWKS with algorithm from JWT header if keys are missing "alg".
+ // This ensures compatibility with providers like Microsoft Entra ID that
+ // don't include "alg" in their JWKS.
+ $jwks = $this->enrich_jwks_with_alg( $jwks, $id_token );
+
+ // Verify JWT signature and decode.
+ try {
+ // Parse JWKS into Key objects.
+ $keys = JWK::parseKeySet( $jwks );
+
+ // Decode and verify JWT signature.
+ // The JWT library will automatically validate exp, nbf, and signature.
+ $decoded_jwt = JWT::decode( $id_token, $keys );
+
+ } catch ( Exception $e ) {
+ $error = new WP_Error(
+ 'jwt-verification-failed',
+ sprintf(
+ /* translators: %s is the error message */
+ __( 'JWT verification failed: %s', 'daggerhart-openid-connect-generic' ),
+ $e->getMessage()
+ )
+ );
+ $this->logger->log( $error, 'jwt-verification-failed' );
+ return $error;
+ }
+
+ // Validate additional claims.
+ $claims_valid = $this->validate_jwt_claims( $decoded_jwt );
+ if ( is_wp_error( $claims_valid ) ) {
+ $this->logger->log( $claims_valid, 'jwt-claims-invalid' );
+ return $claims_valid;
+ }
+
+ // Convert stdClass to array for consistency with existing code.
+ return json_decode( json_encode( $decoded_jwt ), true );
+ }
+}
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-login-form.php b/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-login-form.php
index 9ef81d8a..eac7bc49 100644
--- a/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-login-form.php
+++ b/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-login-form.php
@@ -33,15 +33,24 @@ class OpenID_Connect_Generic_Login_Form {
*/
private $client_wrapper;
+ /**
+ * The client object instance.
+ *
+ * @var OpenID_Connect_Generic_Client
+ */
+ private $client;
+
/**
* The class constructor.
*
* @param OpenID_Connect_Generic_Option_Settings $settings A plugin settings object instance.
* @param OpenID_Connect_Generic_Client_Wrapper $client_wrapper A plugin client wrapper object instance.
+ * @param OpenID_Connect_Generic_Client $client A plugin client object instance.
*/
- public function __construct( $settings, $client_wrapper ) {
+ public function __construct( $settings, $client_wrapper, $client ) {
$this->settings = $settings;
$this->client_wrapper = $client_wrapper;
+ $this->client = $client;
}
/**
@@ -49,11 +58,12 @@ class OpenID_Connect_Generic_Login_Form {
*
* @param OpenID_Connect_Generic_Option_Settings $settings A plugin settings object instance.
* @param OpenID_Connect_Generic_Client_Wrapper $client_wrapper A plugin client wrapper object instance.
+ * @param OpenID_Connect_Generic_Client $client A plugin client object instance.
*
* @return void
*/
- public static function register( $settings, $client_wrapper ) {
- $login_form = new self( $settings, $client_wrapper );
+ public static function register( $settings, $client_wrapper, $client ) {
+ $login_form = new self( $settings, $client_wrapper, $client );
// Alter the login form as dictated by settings.
add_filter( 'login_message', array( $login_form, 'handle_login_page' ), 99 );
@@ -136,9 +146,20 @@ class OpenID_Connect_Generic_Login_Form {
*/
public function make_login_button( $atts = array() ) {
+ // Use admin-configured button text, or fall back to default.
+ $default_button_text = ! empty( trim( $this->settings->login_button_text ?? '' ) )
+ ? $this->settings->login_button_text
+ : __( 'Login with OpenID Connect', 'daggerhart-openid-connect-generic' );
+
$atts = shortcode_atts(
array(
- 'button_text' => __( 'Login with OpenID Connect', 'daggerhart-openid-connect-generic' ),
+ 'button_text' => $default_button_text,
+ 'endpoint_login' => $this->settings->endpoint_login,
+ 'scope' => $this->settings->scope,
+ 'client_id' => $this->settings->client_id,
+ 'redirect_uri' => $this->client->get_redirect_uri(),
+ 'redirect_to' => $this->client_wrapper->get_redirect_to(),
+ 'acr_values' => $this->settings->acr_values,
),
$atts,
'openid_connect_generic_login_button'
@@ -147,7 +168,16 @@ class OpenID_Connect_Generic_Login_Form {
$text = apply_filters( 'openid-connect-generic-login-button-text', $atts['button_text'] );
$text = esc_html( $text );
- $href = $this->client_wrapper->get_authentication_url( $atts );
+ $href = $this->client_wrapper->get_authentication_url(
+ array(
+ 'endpoint_login' => $atts['endpoint_login'],
+ 'scope' => $atts['scope'],
+ 'client_id' => $atts['client_id'],
+ 'redirect_uri' => $atts['redirect_uri'],
+ 'redirect_to' => $atts['redirect_to'],
+ 'acr_values' => $atts['acr_values'],
+ )
+ );
$href = esc_url_raw( $href );
$login_button = <<log_limit;
+ $items_to_remove = is_array( $logs ) ?
+ count( $logs ) - $this->log_limit :
+ 0;
if ( $items_to_remove > 0 ) {
// Only keep the last $log_limit messages from the end.
@@ -250,10 +252,13 @@ class OpenID_Connect_Generic_Option_Logger {
-
+
-
+
+
+
+
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-option-settings.php b/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-option-settings.php
index 91e03223..fdbece06 100644
--- a/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-option-settings.php
+++ b/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-option-settings.php
@@ -26,6 +26,7 @@
* OAuth Client Settings:
*
* @property string $login_type How the client (login form) should provide login options.
+ * @property string $login_button_text Customizable text for the OpenID Connect login button.
* @property string $client_id The ID the client will be recognized as when connecting the to Identity provider server.
* @property string $client_secret The secret key the IDP server expects from the client.
* @property string $scope The list of scopes this client should access.
@@ -33,12 +34,16 @@
* @property string $endpoint_userinfo The IDP User information endpoint URL.
* @property string $endpoint_token The IDP token validation endpoint URL.
* @property string $endpoint_end_session The IDP logout endpoint URL.
+ * @property string $endpoint_jwks The IDP JWKS endpoint URL for JWT signature verification.
+ * @property string $issuer The IDP issuer URL for JWT validation (optional - derived from endpoint_login if not set).
+ * @property int $jwks_cache_ttl The JWKS cache TTL in seconds.
* @property string $acr_values The Authentication contract as defined on the IDP.
*
* Non-standard Settings:
*
* @property bool $no_sslverify The flag to enable/disable SSL verification during authorization.
* @property int $http_request_timeout The timeout for requests made to the IDP. Default value is 5.
+ * @property bool $allow_internal_idp The flag to allow HTTP requests to internal/private network endpoints. Default is false.
* @property string $identity_key The key in the user claim array to find the user's identification data.
* @property string $nickname_key The key in the user claim array to find the user's nickname.
* @property string $email_format The key(s) in the user claim array to formulate the user's email address.
@@ -93,6 +98,8 @@ class OpenID_Connect_Generic_Option_Settings {
'endpoint_login' => 'OIDC_ENDPOINT_LOGIN_URL',
'endpoint_token' => 'OIDC_ENDPOINT_TOKEN_URL',
'endpoint_userinfo' => 'OIDC_ENDPOINT_USERINFO_URL',
+ 'endpoint_jwks' => 'OIDC_ENDPOINT_JWKS_URL',
+ 'issuer' => 'OIDC_ISSUER',
'login_type' => 'OIDC_LOGIN_TYPE',
'scope' => 'OIDC_CLIENT_SCOPE',
'create_if_does_not_exist' => 'OIDC_CREATE_IF_DOES_NOT_EXIST',
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-settings-page.php b/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-settings-page.php
index 9543dd26..d19af471 100644
--- a/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-settings-page.php
+++ b/wp-content/plugins/daggerhart-openid-connect-generic/includes/openid-connect-generic-settings-page.php
@@ -79,6 +79,25 @@ class OpenID_Connect_Generic_Settings_Page {
$this->settings_fields = $fields;
}
+ /**
+ * Make a safe HTTP GET request with optional internal endpoint support.
+ *
+ * By default, uses wp_safe_remote_get() which blocks requests to internal/private
+ * networks (SSRF protection). If allow_internal_idp is enabled, uses wp_remote_get()
+ * to allow connections to localhost and private network identity providers.
+ *
+ * @param string $url The URL to request.
+ * @param array $args Optional. Request arguments.
+ *
+ * @return array|WP_Error Response array or WP_Error on failure.
+ */
+ private function http_get( $url, $args = array() ) {
+ if ( $this->settings->allow_internal_idp ) {
+ return wp_remote_get( $url, $args );
+ }
+ return wp_safe_remote_get( $url, $args );
+ }
+
/**
* Hook the settings page into WordPress.
*
@@ -219,6 +238,13 @@ class OpenID_Connect_Generic_Settings_Page {
'disabled' => defined( 'OIDC_LOGIN_TYPE' ),
'section' => 'client_settings',
),
+ 'login_button_text' => array(
+ 'title' => __( 'Login Button Text', 'daggerhart-openid-connect-generic' ),
+ 'description' => __( 'Customize the text shown on the OpenID Connect login button. Leave empty to use the default text.', 'daggerhart-openid-connect-generic' ),
+ 'example' => 'Login with Single Sign-On',
+ 'type' => 'text',
+ 'section' => 'client_settings',
+ ),
'client_id' => array(
'title' => __( 'Client ID', 'daggerhart-openid-connect-generic' ),
'description' => __( 'The ID this client will be recognized as when connecting the to Identity provider server.', 'daggerhart-openid-connect-generic' ),
@@ -274,6 +300,29 @@ class OpenID_Connect_Generic_Settings_Page {
'disabled' => defined( 'OIDC_ENDPOINT_LOGOUT_URL' ),
'section' => 'client_settings',
),
+ 'endpoint_jwks' => array(
+ 'title' => __( 'JWKS URI', 'daggerhart-openid-connect-generic' ),
+ 'description' => __( 'Identity provider JWKS (JSON Web Key Set) endpoint for JWT signature verification. Usually found at /.well-known/jwks.json', 'daggerhart-openid-connect-generic' ),
+ 'example' => 'https://example.com/.well-known/jwks.json',
+ 'type' => 'text',
+ 'disabled' => defined( 'OIDC_ENDPOINT_JWKS_URL' ),
+ 'section' => 'client_settings',
+ ),
+ 'issuer' => array(
+ 'title' => __( 'Issuer', 'daggerhart-openid-connect-generic' ),
+ 'description' => __( 'Identity provider issuer URL for JWT validation. If not set, the issuer will be automatically derived from the Login Endpoint URL. Only configure this if your IDP uses a different issuer than the base URL of the login endpoint.', 'daggerhart-openid-connect-generic' ),
+ 'example' => 'https://example.com',
+ 'type' => 'text',
+ 'disabled' => defined( 'OIDC_ISSUER' ),
+ 'section' => 'client_settings',
+ ),
+ 'jwks_cache_ttl' => array(
+ 'title' => __( 'JWKS Cache TTL (seconds)', 'daggerhart-openid-connect-generic' ),
+ 'description' => __( 'Time in seconds to cache JWKS keys. Default: 3600 (1 hour)', 'daggerhart-openid-connect-generic' ),
+ 'example' => 3600,
+ 'type' => 'number',
+ 'section' => 'client_settings',
+ ),
'acr_values' => array(
'title' => __( 'ACR values', 'daggerhart-openid-connect-generic' ),
'description' => __( 'Use a specific defined authentication contract from the IDP - optional.', 'daggerhart-openid-connect-generic' ),
@@ -288,13 +337,6 @@ class OpenID_Connect_Generic_Settings_Page {
'type' => 'text',
'section' => 'client_settings',
),
- 'no_sslverify' => array(
- 'title' => __( 'Disable SSL Verify', 'daggerhart-openid-connect-generic' ),
- // translators: %1$s HTML tags for layout/styles, %2$s closing HTML tag for styles.
- 'description' => sprintf( __( 'Do not require SSL verification during authorization. The OAuth extension uses curl to make the request. By default CURL will generally verify the SSL certificate to see if its valid an issued by an accepted CA. This setting disabled that verification.%1$sNot recommended for production sites.%2$s', 'daggerhart-openid-connect-generic' ), ' ', '' ),
- 'type' => 'checkbox',
- 'section' => 'client_settings',
- ),
'http_request_timeout' => array(
'title' => __( 'HTTP Request Timeout', 'daggerhart-openid-connect-generic' ),
'description' => __( 'Set the timeout for requests made to the IDP. Default value is 5.', 'daggerhart-openid-connect-generic' ),
@@ -354,6 +396,20 @@ class OpenID_Connect_Generic_Settings_Page {
'type' => 'checkbox',
'section' => 'client_settings',
),
+ 'no_sslverify' => array(
+ 'title' => __( 'Disable SSL Verify', 'daggerhart-openid-connect-generic' ),
+ // translators: %1$s HTML tags for layout/styles (strong tag start with warning class), %2$s closing HTML tag for styles.
+ 'description' => sprintf( __( 'Do not require SSL verification during authorization. %1$sOnly works in local development (WP_DEBUG=true, WP_ENVIRONMENT_TYPE=local).%2$s This setting is automatically disabled in production. If you need this in production, fix your SSL certificates instead.', 'daggerhart-openid-connect-generic' ), ' ', '' ),
+ 'type' => 'checkbox',
+ 'section' => 'client_settings',
+ ),
+ 'allow_internal_idp' => array(
+ 'title' => __( 'Allow Internal IDP', 'daggerhart-openid-connect-generic' ),
+ // translators: %1$s HTML tags for layout/styles (strong tag start with warning class), %2$s closing HTML tag for styles.
+ 'description' => sprintf( __( 'Allow HTTP requests to internal/private network endpoints (localhost, 127.0.0.1, 10.x.x.x, 192.168.x.x, 172.16-31.x.x). %1$sOnly enable this for local development or corporate internal identity providers. Disabling SSRF protection can expose your server to security risks.%2$s', 'daggerhart-openid-connect-generic' ), ' ', '' ),
+ 'type' => 'checkbox',
+ 'section' => 'client_settings',
+ ),
'link_existing_users' => array(
'title' => __( 'Link Existing Users', 'daggerhart-openid-connect-generic' ),
'description' => __( 'If a WordPress account already exists with the same identity as a newly-authenticated user over OpenID Connect, login as that user instead of generating an error.', 'daggerhart-openid-connect-generic' ),
@@ -429,6 +485,9 @@ class OpenID_Connect_Generic_Settings_Page {
* @return void
*/
public function settings_page() {
+ // Handle discovery form submission before any output.
+ $this->handle_discovery_import();
+
wp_enqueue_style( 'daggerhart-openid-connect-generic-admin', plugin_dir_url( __DIR__ ) . 'css/styles-admin.css', array(), OpenID_Connect_Generic::VERSION, 'all' );
$redirect_uri = admin_url( 'admin-ajax.php?action=openid-connect-authorize' );
@@ -440,16 +499,16 @@ class OpenID_Connect_Generic_Settings_Page {
+ render_discovery_form();
+ ?>
+
@@ -600,4 +659,281 @@ class OpenID_Connect_Generic_Settings_Page {
public function log_settings_description() {
esc_html_e( 'Log information about login attempts through OpenID Connect Generic.', 'daggerhart-openid-connect-generic' );
}
+
+ /**
+ * Fetch OpenID Connect discovery document from provider.
+ *
+ * @param string $discovery_url The discovery document URL (.well-known/openid-configuration).
+ *
+ * @return array|WP_Error Array of discovery data on success, WP_Error on failure.
+ */
+ private function fetch_discovery_document( $discovery_url ) {
+ // Validate URL is provided.
+ if ( empty( $discovery_url ) ) {
+ return new WP_Error(
+ 'empty-discovery-url',
+ __( 'Please enter a discovery URL.', 'daggerhart-openid-connect-generic' )
+ );
+ }
+
+ // Validate HTTPS in production.
+ $parsed_url = wp_parse_url( $discovery_url );
+ if ( ! $parsed_url || ! isset( $parsed_url['scheme'] ) ) {
+ return new WP_Error(
+ 'invalid-discovery-url',
+ __( 'Invalid discovery URL format.', 'daggerhart-openid-connect-generic' )
+ );
+ }
+
+ // Require HTTPS except in local development.
+ $is_local_dev = defined( 'WP_DEBUG' ) && WP_DEBUG === true &&
+ ( ! defined( 'WP_ENVIRONMENT_TYPE' ) || WP_ENVIRONMENT_TYPE === 'local' );
+
+ if ( 'https' !== $parsed_url['scheme'] && ! $is_local_dev ) {
+ return new WP_Error(
+ 'discovery-url-not-https',
+ __( 'Discovery URL must use HTTPS in production environments.', 'daggerhart-openid-connect-generic' )
+ );
+ }
+
+ // Fetch discovery document.
+ $response = $this->http_get(
+ $discovery_url,
+ array(
+ 'timeout' => 10,
+ 'headers' => array(
+ 'Accept' => 'application/json',
+ ),
+ )
+ );
+
+ if ( is_wp_error( $response ) ) {
+ return new WP_Error(
+ 'discovery-fetch-failed',
+ sprintf(
+ /* translators: %s: error message */
+ __( 'Failed to fetch discovery document: %s', 'daggerhart-openid-connect-generic' ),
+ $response->get_error_message()
+ )
+ );
+ }
+
+ $response_code = wp_remote_retrieve_response_code( $response );
+ if ( 200 !== $response_code ) {
+ return new WP_Error(
+ 'discovery-fetch-failed',
+ sprintf(
+ /* translators: %d: HTTP status code */
+ __( 'Discovery document request returned HTTP %d.', 'daggerhart-openid-connect-generic' ),
+ $response_code
+ )
+ );
+ }
+
+ // Parse JSON response.
+ $body = wp_remote_retrieve_body( $response );
+ $discovery = json_decode( $body, true );
+
+ if ( null === $discovery || ! is_array( $discovery ) ) {
+ return new WP_Error(
+ 'discovery-invalid-json',
+ __( 'Discovery document is not valid JSON.', 'daggerhart-openid-connect-generic' )
+ );
+ }
+
+ // Validate required fields are present.
+ $required_fields = array( 'authorization_endpoint', 'token_endpoint', 'jwks_uri' );
+ $missing_fields = array();
+
+ foreach ( $required_fields as $field ) {
+ if ( ! isset( $discovery[ $field ] ) || empty( $discovery[ $field ] ) ) {
+ $missing_fields[] = $field;
+ }
+ }
+
+ if ( ! empty( $missing_fields ) ) {
+ return new WP_Error(
+ 'discovery-missing-fields',
+ sprintf(
+ /* translators: %s: comma-separated list of missing fields */
+ __( 'Discovery document is missing required fields: %s', 'daggerhart-openid-connect-generic' ),
+ implode( ', ', $missing_fields )
+ )
+ );
+ }
+
+ return $discovery;
+ }
+
+ /**
+ * Populate plugin settings from discovery document.
+ *
+ * Maps discovery document fields to plugin setting keys.
+ * Does not save to database - only updates in-memory values.
+ *
+ * @param array $discovery The discovery document data.
+ *
+ * @return array Array of setting keys that were populated.
+ */
+ private function populate_settings_from_discovery( $discovery ) {
+ $populated_fields = array();
+
+ // Map discovery fields to plugin settings.
+ $field_mapping = array(
+ 'authorization_endpoint' => 'endpoint_login',
+ 'token_endpoint' => 'endpoint_token',
+ 'userinfo_endpoint' => 'endpoint_userinfo',
+ 'jwks_uri' => 'endpoint_jwks',
+ 'issuer' => 'issuer',
+ 'end_session_endpoint' => 'endpoint_end_session',
+ );
+
+ foreach ( $field_mapping as $discovery_key => $setting_key ) {
+ if ( isset( $discovery[ $discovery_key ] ) && ! empty( $discovery[ $discovery_key ] ) ) {
+ // Update the setting value (not saved yet).
+ $this->settings->{ $setting_key } = $discovery[ $discovery_key ];
+ $populated_fields[] = $setting_key;
+ }
+ }
+
+ return $populated_fields;
+ }
+
+ /**
+ * Handle discovery document import form submission.
+ *
+ * Checks if the discovery form was submitted, validates it,
+ * fetches the discovery document, and populates settings.
+ *
+ * @return void
+ */
+ private function handle_discovery_import() {
+ // Check if discovery form was submitted.
+ if ( ! isset( $_POST['oidc_discovery_submit'] ) ) {
+ return;
+ }
+
+ // Verify nonce.
+ if (
+ ! isset( $_POST['oidc_discovery_nonce'] ) ||
+ ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['oidc_discovery_nonce'] ) ), 'oidc_discovery_import' )
+ ) {
+ add_settings_error(
+ 'openid-connect-generic',
+ 'invalid-nonce',
+ __( 'Security check failed. Please try again.', 'daggerhart-openid-connect-generic' ),
+ 'error'
+ );
+ return;
+ }
+
+ // Get discovery URL from form.
+ $discovery_url = isset( $_POST['oidc_discovery_url'] )
+ ? esc_url_raw( wp_unslash( $_POST['oidc_discovery_url'] ) )
+ : '';
+
+ // Fetch discovery document.
+ $discovery = $this->fetch_discovery_document( $discovery_url );
+
+ if ( is_wp_error( $discovery ) ) {
+ add_settings_error(
+ 'openid-connect-generic',
+ $discovery->get_error_code(),
+ $discovery->get_error_message(),
+ 'error'
+ );
+ return;
+ }
+
+ // Populate settings from discovery document.
+ $populated_fields = $this->populate_settings_from_discovery( $discovery );
+
+ // Log the import.
+ $this->logger->log(
+ sprintf(
+ 'Configuration loaded from discovery URL: %s. Populated fields: %s',
+ $discovery_url,
+ implode( ', ', $populated_fields )
+ ),
+ 'discovery-import'
+ );
+
+ // Show success message.
+ $field_count = count( $populated_fields );
+ add_settings_error(
+ 'openid-connect-generic',
+ 'discovery-success',
+ sprintf(
+ /* translators: %d: number of fields populated */
+ _n(
+ 'Configuration loaded successfully! %d field was populated. Review the settings below and click "Save Changes" to apply.',
+ 'Configuration loaded successfully! %d fields were populated. Review the settings below and click "Save Changes" to apply.',
+ $field_count,
+ 'daggerhart-openid-connect-generic'
+ ),
+ $field_count
+ ),
+ 'success'
+ );
+ }
+
+ /**
+ * Render the discovery document import form.
+ *
+ * Outputs HTML form for importing configuration from discovery document.
+ * Collapsed by default if endpoint_login is already configured.
+ *
+ * @return void
+ */
+ private function render_discovery_form() {
+ // Auto-expand if plugin is not yet configured.
+ $is_configured = ! empty( $this->settings->endpoint_login );
+ $open_attribute = $is_configured ? '' : ' open';
+ ?>
+ class="oidc-discovery-section">
+
+ ā”
+
+
+
+
+
+
+
+
+
+ \n"
"Language-Team: LANGUAGE \n"
"Language: en\n"
@@ -25,316 +25,397 @@ msgstr ""
"X-Textdomain-Support: yes\n"
"X-Generator: grunt-wp-i18n 1.0.3\n"
-#: includes/openid-connect-generic-client-wrapper.php:293
-msgid "Session expired. Please login again."
-msgstr ""
-
-#: includes/openid-connect-generic-client-wrapper.php:540
+#: includes/openid-connect-generic-client-wrapper.php:578
msgid "User identity is not linked to an existing WordPress user."
msgstr ""
-#: includes/openid-connect-generic-client-wrapper.php:598
+#: includes/openid-connect-generic-client-wrapper.php:639
msgid "Invalid user."
msgstr ""
-#: includes/openid-connect-generic-client-wrapper.php:816
+#: includes/openid-connect-generic-client-wrapper.php:828
msgid "No appropriate username found."
msgstr ""
-#: includes/openid-connect-generic-client-wrapper.php:826
+#: includes/openid-connect-generic-client-wrapper.php:838
#. translators: %1$s is the santitized version of the username from the IDP.
msgid "Username %1$s could not be sanitized."
msgstr ""
-#: includes/openid-connect-generic-client-wrapper.php:848
+#: includes/openid-connect-generic-client-wrapper.php:860
#. translators: %1$s is the configured User Claim nickname key.
msgid "No nickname found in user claim using key: %1$s."
msgstr ""
-#: includes/openid-connect-generic-client-wrapper.php:945
+#: includes/openid-connect-generic-client-wrapper.php:992
msgid "User claim incomplete."
msgstr ""
-#: includes/openid-connect-generic-client-wrapper.php:1048
+#: includes/openid-connect-generic-client-wrapper.php:1095
msgid "Bad user claim result."
msgstr ""
-#: includes/openid-connect-generic-client-wrapper.php:1114
+#: includes/openid-connect-generic-client-wrapper.php:1161
msgid "Can not authorize."
msgstr ""
-#: includes/openid-connect-generic-client-wrapper.php:1143
+#: includes/openid-connect-generic-client-wrapper.php:1190
msgid "Failed user creation."
msgstr ""
-#: includes/openid-connect-generic-client.php:176
+#: includes/openid-connect-generic-client.php:270
msgid "Missing state."
msgstr ""
-#: includes/openid-connect-generic-client.php:180
+#: includes/openid-connect-generic-client.php:274
msgid "Invalid state."
msgstr ""
-#: includes/openid-connect-generic-client.php:195
+#: includes/openid-connect-generic-client.php:289
msgid "Missing authentication code."
msgstr ""
-#: includes/openid-connect-generic-client.php:240
+#: includes/openid-connect-generic-client.php:334
msgid "Request for authentication token failed."
msgstr ""
-#: includes/openid-connect-generic-client.php:273
+#: includes/openid-connect-generic-client.php:367
msgid "Refresh token failed."
msgstr ""
-#: includes/openid-connect-generic-client.php:288
+#: includes/openid-connect-generic-client.php:382
msgid "Missing token body."
msgstr ""
-#: includes/openid-connect-generic-client.php:296
+#: includes/openid-connect-generic-client.php:390
msgid "Invalid token."
msgstr ""
-#: includes/openid-connect-generic-client.php:349
+#: includes/openid-connect-generic-client.php:451
msgid "Request for userinfo failed."
msgstr ""
-#: includes/openid-connect-generic-client.php:409
+#: includes/openid-connect-generic-client.php:515
msgid "Missing authentication state."
msgstr ""
-#: includes/openid-connect-generic-client.php:446
+#: includes/openid-connect-generic-client.php:552
msgid "No identity token."
msgstr ""
-#: includes/openid-connect-generic-client.php:453
+#: includes/openid-connect-generic-client.php:592
msgid "Missing identity token."
msgstr ""
-#: includes/openid-connect-generic-client.php:480
+#: includes/openid-connect-generic-client.php:651
msgid "Bad ID token claim."
msgstr ""
-#: includes/openid-connect-generic-client.php:485
+#: includes/openid-connect-generic-client.php:656
msgid "No subject identity."
msgstr ""
-#: includes/openid-connect-generic-client.php:491
+#: includes/openid-connect-generic-client.php:661
+#: includes/openid-connect-generic-jwt-validator.php:184
+msgid "Token missing expiration claim."
+msgstr ""
+
+#: includes/openid-connect-generic-client.php:664
+msgid "Token has expired."
+msgstr ""
+
+#: includes/openid-connect-generic-client.php:669
+#: includes/openid-connect-generic-jwt-validator.php:192
+msgid "Token missing issued at claim."
+msgstr ""
+
+#: includes/openid-connect-generic-client.php:674
+#: includes/openid-connect-generic-jwt-validator.php:200
+msgid "Token missing audience claim."
+msgstr ""
+
+#: includes/openid-connect-generic-client.php:687
+#: includes/openid-connect-generic-jwt-validator.php:217
+msgid "Token audience does not match client."
+msgstr ""
+
+#: includes/openid-connect-generic-client.php:697
+#: includes/openid-connect-generic-jwt-validator.php:226
+msgid "Token missing issuer claim."
+msgstr ""
+
+#: includes/openid-connect-generic-client.php:712
+#: includes/openid-connect-generic-jwt-validator.php:241
+msgid "Token issuer does not match expected issuer."
+msgstr ""
+
+#: includes/openid-connect-generic-client.php:722
msgid "No matching acr values."
msgstr ""
-#: includes/openid-connect-generic-client.php:511
+#: includes/openid-connect-generic-client.php:742
msgid "Bad user claim."
msgstr ""
-#: includes/openid-connect-generic-client.php:531
+#: includes/openid-connect-generic-client.php:762
msgid "Invalid user claim."
msgstr ""
-#: includes/openid-connect-generic-client.php:536
+#: includes/openid-connect-generic-client.php:767
msgid "Error from the IDP."
msgstr ""
-#: includes/openid-connect-generic-client.php:545
+#: includes/openid-connect-generic-client.php:776
msgid "Incorrect user claim."
msgstr ""
-#: includes/openid-connect-generic-client.php:552
+#: includes/openid-connect-generic-client.php:783
msgid "Unauthorized access."
msgstr ""
-#: includes/openid-connect-generic-login-form.php:122
+#: includes/openid-connect-generic-jwt-validator.php:127
+msgid "Failed to fetch JWKS from identity provider."
+msgstr ""
+
+#: includes/openid-connect-generic-jwt-validator.php:138
+#. translators: %d is the HTTP response code
+msgid "JWKS endpoint returned HTTP %d"
+msgstr ""
+
+#: includes/openid-connect-generic-jwt-validator.php:152
+msgid "Invalid JWKS format received from identity provider."
+msgstr ""
+
+#: includes/openid-connect-generic-jwt-validator.php:176
+msgid "Token missing subject claim."
+msgstr ""
+
+#: includes/openid-connect-generic-jwt-validator.php:315
+msgid "JWKS URI not configured. JWT signature verification requires JWKS endpoint."
+msgstr ""
+
+#: includes/openid-connect-generic-jwt-validator.php:346
+#. translators: %s is the error message
+msgid "JWT verification failed: %s"
+msgstr ""
+
+#: includes/openid-connect-generic-login-form.php:132
#. translators: %1$s is the error code from the IDP.
msgid "ERROR (%1$s)"
msgstr ""
-#: includes/openid-connect-generic-login-form.php:141
+#: includes/openid-connect-generic-login-form.php:152
msgid "Login with OpenID Connect"
msgstr ""
-#: includes/openid-connect-generic-option-logger.php:228
+#: includes/openid-connect-generic-option-logger.php:230
msgid "Details"
msgstr ""
-#: includes/openid-connect-generic-option-logger.php:229
+#: includes/openid-connect-generic-option-logger.php:231
msgid "Data"
msgstr ""
-#: includes/openid-connect-generic-option-logger.php:236
+#: includes/openid-connect-generic-option-logger.php:238
msgid "Date"
msgstr ""
-#: includes/openid-connect-generic-option-logger.php:240
+#: includes/openid-connect-generic-option-logger.php:242
msgid "Type"
msgstr ""
-#: includes/openid-connect-generic-option-logger.php:244
+#: includes/openid-connect-generic-option-logger.php:246
msgid "User"
msgstr ""
-#: includes/openid-connect-generic-option-logger.php:248
+#: includes/openid-connect-generic-option-logger.php:250
msgid "URI "
msgstr ""
-#: includes/openid-connect-generic-option-logger.php:252
+#: includes/openid-connect-generic-option-logger.php:254
msgid "Response Time (sec)"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:108
+#: includes/openid-connect-generic-settings-page.php:127
msgid "OpenID Connect - Generic Client"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:109
+#: includes/openid-connect-generic-settings-page.php:128
msgid "OpenID Connect Client"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:133
+#: includes/openid-connect-generic-settings-page.php:152
msgid "Client Settings"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:140
+#: includes/openid-connect-generic-settings-page.php:159
msgid "WordPress User Settings"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:147
+#: includes/openid-connect-generic-settings-page.php:166
msgid "Authorization Settings"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:154
+#: includes/openid-connect-generic-settings-page.php:173
msgid "Log Settings"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:212
+#: includes/openid-connect-generic-settings-page.php:231
msgid "Login Type"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:213
+#: includes/openid-connect-generic-settings-page.php:232
msgid "Select how the client (login form) should provide login options."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:216
+#: includes/openid-connect-generic-settings-page.php:235
msgid "OpenID Connect button on login form"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:217
+#: includes/openid-connect-generic-settings-page.php:236
msgid "Auto Login - SSO"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:223
+#: includes/openid-connect-generic-settings-page.php:242
+msgid "Login Button Text"
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:243
+msgid ""
+"Customize the text shown on the OpenID Connect login button. Leave empty to "
+"use the default text."
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:249
msgid "Client ID"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:224
+#: includes/openid-connect-generic-settings-page.php:250
msgid ""
"The ID this client will be recognized as when connecting the to Identity "
"provider server."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:231
+#: includes/openid-connect-generic-settings-page.php:257
msgid "Client Secret Key"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:232
+#: includes/openid-connect-generic-settings-page.php:258
msgid ""
"Arbitrary secret key the server expects from this client. Can be anything, "
"but should be very unique."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:238
+#: includes/openid-connect-generic-settings-page.php:264
msgid "OpenID Scope"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:239
+#: includes/openid-connect-generic-settings-page.php:265
msgid "Space separated list of scopes this client should access."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:246
+#: includes/openid-connect-generic-settings-page.php:272
msgid "Login Endpoint URL"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:247
+#: includes/openid-connect-generic-settings-page.php:273
msgid "Identify provider authorization endpoint."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:254
+#: includes/openid-connect-generic-settings-page.php:280
msgid "Userinfo Endpoint URL"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:255
+#: includes/openid-connect-generic-settings-page.php:281
msgid "Identify provider User information endpoint."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:262
+#: includes/openid-connect-generic-settings-page.php:288
msgid "Token Validation Endpoint URL"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:263
+#: includes/openid-connect-generic-settings-page.php:289
msgid "Identify provider token endpoint."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:270
+#: includes/openid-connect-generic-settings-page.php:296
msgid "End Session Endpoint URL"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:271
+#: includes/openid-connect-generic-settings-page.php:297
msgid "Identify provider logout endpoint."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:278
+#: includes/openid-connect-generic-settings-page.php:304
+msgid "JWKS URI"
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:305
+msgid ""
+"Identity provider JWKS (JSON Web Key Set) endpoint for JWT signature "
+"verification. Usually found at /.well-known/jwks.json"
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:312
+msgid "Issuer"
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:313
+msgid ""
+"Identity provider issuer URL for JWT validation. If not set, the issuer "
+"will be automatically derived from the Login Endpoint URL. Only configure "
+"this if your IDP uses a different issuer than the base URL of the login "
+"endpoint."
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:320
+msgid "JWKS Cache TTL (seconds)"
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:321
+msgid "Time in seconds to cache JWKS keys. Default: 3600 (1 hour)"
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:327
msgid "ACR values"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:279
+#: includes/openid-connect-generic-settings-page.php:328
msgid "Use a specific defined authentication contract from the IDP - optional."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:285
+#: includes/openid-connect-generic-settings-page.php:334
msgid "Identity Key"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:286
+#: includes/openid-connect-generic-settings-page.php:335
msgid ""
"Where in the user claim array to find the user's identification data. "
"Possible standard values: preferred_username, name, or sub. If you're "
"having trouble, use \"sub\"."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:292
-msgid "Disable SSL Verify"
-msgstr ""
-
-#: includes/openid-connect-generic-settings-page.php:294
-#. translators: %1$s HTML tags for layout/styles, %2$s closing HTML tag for
-#. styles.
-msgid ""
-"Do not require SSL verification during authorization. The OAuth extension "
-"uses curl to make the request. By default CURL will generally verify the "
-"SSL certificate to see if its valid an issued by an accepted CA. This "
-"setting disabled that verification.%1$sNot recommended for production "
-"sites.%2$s"
-msgstr ""
-
-#: includes/openid-connect-generic-settings-page.php:299
+#: includes/openid-connect-generic-settings-page.php:341
msgid "HTTP Request Timeout"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:300
+#: includes/openid-connect-generic-settings-page.php:342
msgid "Set the timeout for requests made to the IDP. Default value is 5."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:306
+#: includes/openid-connect-generic-settings-page.php:348
msgid "Enforce Privacy"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:307
+#: includes/openid-connect-generic-settings-page.php:349
msgid "Require users be logged in to see the site."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:313
+#: includes/openid-connect-generic-settings-page.php:355
msgid "Alternate Redirect URI"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:314
+#: includes/openid-connect-generic-settings-page.php:356
msgid ""
"Provide an alternative redirect route. Useful if your server is causing "
"issues with the default admin-ajax method. You must flush rewrite rules "
@@ -342,78 +423,106 @@ msgid ""
"settings page."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:319
+#: includes/openid-connect-generic-settings-page.php:361
msgid "Nickname Key"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:320
+#: includes/openid-connect-generic-settings-page.php:362
msgid ""
"Where in the user claim array to find the user's nickname. Possible "
"standard values: preferred_username, name, or sub."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:326
+#: includes/openid-connect-generic-settings-page.php:368
msgid "Email Formatting"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:327
+#: includes/openid-connect-generic-settings-page.php:369
msgid ""
"String from which the user's email address is built. Specify \"{email}\" as "
"long as the user claim contains an email claim."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:333
+#: includes/openid-connect-generic-settings-page.php:375
msgid "Display Name Formatting"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:334
+#: includes/openid-connect-generic-settings-page.php:376
msgid "String from which the user's display name is built."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:340
+#: includes/openid-connect-generic-settings-page.php:382
msgid "Identify with User Name"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:341
+#: includes/openid-connect-generic-settings-page.php:383
msgid ""
"If checked, the user's identity will be determined by the user name instead "
"of the email address."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:346
+#: includes/openid-connect-generic-settings-page.php:388
msgid "State time limit"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:347
+#: includes/openid-connect-generic-settings-page.php:389
msgid "State valid time in seconds. Defaults to 180"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:352
+#: includes/openid-connect-generic-settings-page.php:394
msgid "Enable Refresh Token"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:353
+#: includes/openid-connect-generic-settings-page.php:395
msgid ""
"If checked, support refresh tokens used to obtain access tokens from "
"supported IDPs."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:358
+#: includes/openid-connect-generic-settings-page.php:400
+msgid "Disable SSL Verify"
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:402
+#. translators: %1$s HTML tags for layout/styles (strong tag start with warning
+#. class), %2$s closing HTML tag for styles.
+msgid ""
+"Do not require SSL verification during authorization. %1$sOnly works in "
+"local development (WP_DEBUG=true, WP_ENVIRONMENT_TYPE=local).%2$s This "
+"setting is automatically disabled in production. If you need this in "
+"production, fix your SSL certificates instead."
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:407
+msgid "Allow Internal IDP"
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:409
+#. translators: %1$s HTML tags for layout/styles (strong tag start with warning
+#. class), %2$s closing HTML tag for styles.
+msgid ""
+"Allow HTTP requests to internal/private network endpoints (localhost, "
+"127.0.0.1, 10.x.x.x, 192.168.x.x, 172.16-31.x.x). %1$sOnly enable this for "
+"local development or corporate internal identity providers. Disabling SSRF "
+"protection can expose your server to security risks.%2$s"
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:414
msgid "Link Existing Users"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:359
+#: includes/openid-connect-generic-settings-page.php:415
msgid ""
"If a WordPress account already exists with the same identity as a "
"newly-authenticated user over OpenID Connect, login as that user instead of "
"generating an error."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:365
+#: includes/openid-connect-generic-settings-page.php:421
msgid "Create user if does not exist"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:366
+#: includes/openid-connect-generic-settings-page.php:422
msgid ""
"If the user identity is not linked to an existing WordPress user, it is "
"created. If this setting is not enabled, and if the user authenticates with "
@@ -421,11 +530,11 @@ msgid ""
"authentication will fail."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:372
+#: includes/openid-connect-generic-settings-page.php:428
msgid "Redirect Back to Origin Page"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:373
+#: includes/openid-connect-generic-settings-page.php:429
msgid ""
"After a successful OpenID Connect authentication, this will redirect the "
"user back to the page on which they clicked the OpenID Connect login "
@@ -436,84 +545,179 @@ msgid ""
"account page."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:379
+#: includes/openid-connect-generic-settings-page.php:435
msgid "Redirect to the login screen when session is expired"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:380
+#: includes/openid-connect-generic-settings-page.php:436
msgid ""
"When enabled, this will automatically redirect the user back to the "
"WordPress login page if their access token has expired."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:386
+#: includes/openid-connect-generic-settings-page.php:442
msgid "Enable Logging"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:387
+#: includes/openid-connect-generic-settings-page.php:443
msgid "Very simple log messages for debugging purposes."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:393
+#: includes/openid-connect-generic-settings-page.php:449
msgid "Log Limit"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:394
+#: includes/openid-connect-generic-settings-page.php:450
msgid ""
"Number of items to keep in the log. These logs are stored as an option in "
"the database, so space is limited."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:456
+#: includes/openid-connect-generic-settings-page.php:515
msgid "Notes"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:459
+#: includes/openid-connect-generic-settings-page.php:518
msgid "Redirect URI"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:463
+#: includes/openid-connect-generic-settings-page.php:522
msgid "Login Button Shortcode"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:467
+#: includes/openid-connect-generic-settings-page.php:526
msgid "Authentication URL Shortcode"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:472
+#: includes/openid-connect-generic-settings-page.php:531
msgid "Logs"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:561
+#: includes/openid-connect-generic-settings-page.php:620
msgid "Example"
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:574
+#: includes/openid-connect-generic-settings-page.php:633
msgid "Enter your OpenID Connect identity provider settings."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:583
+#: includes/openid-connect-generic-settings-page.php:642
msgid "Modify the interaction between OpenID Connect and WordPress users."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:592
+#: includes/openid-connect-generic-settings-page.php:651
msgid "Control the authorization mechanics of the site."
msgstr ""
-#: includes/openid-connect-generic-settings-page.php:601
+#: includes/openid-connect-generic-settings-page.php:660
msgid "Log information about login attempts through OpenID Connect Generic."
msgstr ""
-#: openid-connect-generic.php:242
+#: includes/openid-connect-generic-settings-page.php:675
+msgid "Please enter a discovery URL."
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:684
+msgid "Invalid discovery URL format."
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:695
+msgid "Discovery URL must use HTTPS in production environments."
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:715
+#. translators: %s: error message
+msgid "Failed to fetch discovery document: %s"
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:727
+#. translators: %d: HTTP status code
+msgid "Discovery document request returned HTTP %d."
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:740
+msgid "Discovery document is not valid JSON."
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:759
+#. translators: %s: comma-separated list of missing fields
+msgid "Discovery document is missing required fields: %s"
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:824
+msgid "Security check failed. Please try again."
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:868
+#. translators: %d: number of fields populated
+msgid ""
+"Configuration loaded successfully! %d field was populated. Review the "
+"settings below and click \"Save Changes\" to apply."
+msgid_plural ""
+"Configuration loaded successfully! %d fields were populated. Review the "
+"settings below and click \"Save Changes\" to apply."
+msgstr[0] ""
+msgstr[1] ""
+
+#: includes/openid-connect-generic-settings-page.php:895
+msgid "Quick Setup: Import from Discovery Document"
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:899
+msgid ""
+"Auto-populate endpoint settings from your identity provider's OpenID "
+"Connect discovery document. After loading, review the populated fields "
+"below and click \"Save Changes\" to apply."
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:907
+msgid "Discovery URL"
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:919
+msgid "Enter your identity provider's OpenID Connect discovery endpoint URL."
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:921
+msgid "Examples:"
+msgstr ""
+
+#: includes/openid-connect-generic-settings-page.php:932
+msgid "Load Configuration"
+msgstr ""
+
+#: openid-connect-generic.php:252
msgid "Private site"
msgstr ""
+#: openid-connect-generic.php:282
+msgid "OpenID Connect Generic - Security Configuration Required"
+msgstr ""
+
+#: openid-connect-generic.php:289
+#. translators: %s is a link to the settings page
+msgid ""
+"Your OpenID Connect authentication is using an insecure fallback method. "
+"You must configure the JWKS endpoint in plugin settings as soon as possible."
+msgstr ""
+
+#: openid-connect-generic.php:296
+msgid ""
+"The current insecure fallback will be removed in version 3.12.0. After that "
+"update, authentication will fail until the JWKS endpoint is configured."
+msgstr ""
+
+#: openid-connect-generic.php:299
+msgid "Common JWKS endpoints:"
+msgstr ""
+
#. Plugin Name of the plugin/theme
msgid "OpenID Connect Generic"
msgstr ""
#. Plugin URI of the plugin/theme
-msgid "https://github.com/daggerhart/openid-connect-generic"
+msgid "https://github.com/oidc-wp/openid-connect-generic"
msgstr ""
#. Description of the plugin/theme
@@ -527,5 +731,5 @@ msgid "daggerhart"
msgstr ""
#. Author URI of the plugin/theme
-msgid "http://www.daggerhart.com"
-msgstr ""
+msgid "https://www.daggerhartlab.com"
+msgstr ""
\ No newline at end of file
diff --git a/wp-content/plugins/daggerhart-openid-connect-generic/openid-connect-generic.php b/wp-content/plugins/daggerhart-openid-connect-generic/openid-connect-generic.php
index 81895e9a..4dfae74e 100644
--- a/wp-content/plugins/daggerhart-openid-connect-generic/openid-connect-generic.php
+++ b/wp-content/plugins/daggerhart-openid-connect-generic/openid-connect-generic.php
@@ -16,7 +16,7 @@
* Plugin Name: OpenID Connect Generic
* Plugin URI: https://github.com/oidc-wp/openid-connect-generic
* Description: Connect to an OpenID Connect identity provider using Authorization Code Flow.
- * Version: 3.10.2
+ * Version: 3.11.3
* Requires at least: 5.0
* Requires PHP: 7.4
* Author: daggerhart
@@ -59,11 +59,11 @@ Notes
Callable actions
- User Meta
- - openid-connect-generic-subject-identity - the identity of the user provided by the idp
- - openid-connect-generic-last-id-token-claim - the user's most recent id_token claim, decoded
- - openid-connect-generic-last-user-claim - the user's most recent user_claim
- - openid-connect-generic-last-token-response - the user's most recent token response
+ User Meta (since v3.10.4 prefixed with the blog database prefix, for example wp_2_openid-connect-generic-subject-identity)
+ - [[BLOG_DB_PREFIX]]openid-connect-generic-subject-identity - the identity of the user provided by the idp
+ - [[BLOG_DB_PREFIX]]openid-connect-generic-last-id-token-claim - the user's most recent id_token claim, decoded
+ - [[BLOG_DB_PREFIX]]openid-connect-generic-last-user-claim - the user's most recent user_claim
+ - [[BLOG_DB_PREFIX]]openid-connect-generic-last-token-response - the user's most recent token response
Options
- openid_connect_generic_settings - plugin settings
@@ -93,7 +93,7 @@ class OpenID_Connect_Generic {
*
* @var string
*/
- const VERSION = '3.10.2';
+ const VERSION = '3.11.3';
/**
* Plugin settings.
@@ -158,7 +158,11 @@ class OpenID_Connect_Generic {
$this->settings->endpoint_token,
$this->get_redirect_uri( $this->settings ),
$this->settings->acr_values,
+ $this->settings->endpoint_jwks,
+ $this->settings->issuer ?? '',
+ $this->settings->jwks_cache_ttl,
$this->get_state_time_limit( $this->settings ),
+ $this->settings->allow_internal_idp,
$this->logger
);
@@ -167,7 +171,7 @@ class OpenID_Connect_Generic {
return;
}
- OpenID_Connect_Generic_Login_Form::register( $this->settings, $this->client_wrapper );
+ OpenID_Connect_Generic_Login_Form::register( $this->settings, $this->client_wrapper, $this->client );
// Add a shortcode to get the auth URL.
add_shortcode( 'openid_connect_generic_auth_url', array( $this->client_wrapper, 'get_authentication_url' ) );
@@ -179,6 +183,7 @@ class OpenID_Connect_Generic {
if ( is_admin() ) {
OpenID_Connect_Generic_Settings_Page::register( $this->settings, $this->logger );
+ add_action( 'admin_notices', array( $this, 'admin_notice_jwks_required' ) );
}
}
@@ -249,6 +254,59 @@ class OpenID_Connect_Generic {
return $content;
}
+ /**
+ * Display admin notice when JWKS endpoint is not configured.
+ *
+ * @return void
+ */
+ public function admin_notice_jwks_required() {
+ // Only show to users who can manage options.
+ if ( ! current_user_can( 'manage_options' ) ) {
+ return;
+ }
+
+ // Check if JWKS endpoint is configured.
+ if ( ! empty( $this->settings->endpoint_jwks ) ) {
+ return;
+ }
+
+ // Check if any OIDC endpoints are configured (plugin is actually being used).
+ if ( empty( $this->settings->endpoint_login ) ) {
+ return;
+ }
+
+ $settings_url = admin_url( 'options-general.php?page=openid-connect-generic-settings' );
+ ?>
+
+
+
+
+
+ JWKS endpoint in plugin settings as soon as possible.', 'daggerhart-openid-connect-generic' ),
+ esc_url( $settings_url )
+ )
+ );
+ ?>
+