diff --git a/.markdownlint.yml b/.markdownlint.yml new file mode 100644 index 00000000..945987bc --- /dev/null +++ b/.markdownlint.yml @@ -0,0 +1,4 @@ +--- +default: true + +MD013: false \ No newline at end of file diff --git a/.markdownlintignore b/.markdownlintignore new file mode 100644 index 00000000..208a5991 --- /dev/null +++ b/.markdownlintignore @@ -0,0 +1 @@ +vendor/* \ No newline at end of file diff --git a/CONFORMANCE_TEST.md b/CONFORMANCE_TEST.md deleted file mode 100644 index 53b676c9..00000000 --- a/CONFORMANCE_TEST.md +++ /dev/null @@ -1,108 +0,0 @@ -# Overview - -The OpenID foundation provides conformance tests. This is a guide to setting up and running -them against this SSP module. - -# Running Conformance Tests Locally - -This approach is best when you want to test changes without having to deploy your project - -## Run Conformance Images - -Clone the conformance test git repo, build the software and run it. - -```bash -git clone https://gitlab.com/openid/conformance-suite.git -cd conformance-suite -git checkout release-v5.1.35 -MAVEN_CACHE=./m2 docker-compose -f builder-compose.yml run builder -docker-compose up -``` - -This will start up the Java conformance app and a MongoDB server. You'll need to configure a test. - -Visit https://localhost:8443/ and "Create a new plan". -The Test Plan should be "OpenID Connect Core: Basic Certification Profile Authorization server test" -which is under "Test an OpenID Provider / Authorization Server". - -Then click on the JSON tab and enter the JSON from file `conformance-tests/conformance-basic-local.json`. -This file contains several clients and a OIDC discovery config for running against a local SSP OIDC - -You'll need to get your OIDC SSP image running next - -## Run SSP - -You'll need to run SSP with OIDC on the same docker network as the compliance tests, so they are able to communicate. - -See "Docker Compose" section of the main README. - -## Run Conformance Tests - -The conformance tests are interactive to make you authenticate. Some of the tests require you to clear cookies to -confirm certain test scenarios, while others require you to have session cookies to test the RP signaling to the -OP that the user should reauthenticate. The tests may also redirect you to https://localhost.emobix.co.uk:8443/ -which will resolve to the conformance Java container. You'll need to accept any SSL connection warnings. - -## Run automated tests - -Eventually these test can have -[the browser portion automated](https://gitlab.com/openid/conformance-suite/-/wikis/Design/BrowserControl) -though the Conformance tests authors recommend getting them all to pass first. - -To run basic profile test, launch this command in console inside `simplesamlphp-module-oidc` directory: - -```shell -# Run run-test-plan.py script inside conformance-suite/scripts -# Change the relative path to your conformance-suite installation -# conformance-basic-ci.json contains clients, and browser interactions for automating various tests -# Lines like "oidcc-implicit-certification-test-plan[server_metadata=discovery][client_registration=static_client]" -# indicate the conformance plan to run, and any variants (parameters) are passed in [] - -OIDC_MODULE_FOLDER=. # path to your checkout of the OIDC module -# Basic profile -conformance-suite/scripts/run-test-plan.py \ - --expected-failures-file ${OIDC_MODULE_FOLDER}/conformance-tests/basic-warnings.json \ - --expected-skips-file ${OIDC_MODULE_FOLDER}/conformance-tests/basic-skips.json \ - "oidcc-basic-certification-test-plan[server_metadata=discovery][client_registration=static_client]" \ - ${OIDC_MODULE_FOLDER}/conformance-tests/conformance-basic-ci.json - -# Implicit profile -conformance-suite/scripts/run-test-plan.py \ - --expected-failures-file ${OIDC_MODULE_FOLDER}/conformance-tests/implicit-warnings.json \ - --expected-skips-file ${OIDC_MODULE_FOLDER}/conformance-tests/implicit-skips.json \ - "oidcc-implicit-certification-test-plan[server_metadata=discovery][client_registration=static_client]" \ - ${OIDC_MODULE_FOLDER}/conformance-tests/conformance-implicit-ci.json - -# RP Initiated back channel -conformance-suite/scripts/run-test-plan.py \ - "oidcc-backchannel-rp-initiated-logout-certification-test-plan[response_type=code][client_registration=static_client]" \ - ${OIDC_MODULE_FOLDER}/conformance-tests/conformance-back-channel-logout-ci.json - -conformance-suite/scripts/run-test-plan.py \ - "oidcc-rp-initiated-logout-certification-test-plan[response_type=code][client_registration=static_client]" \ - ${OIDC_MODULE_FOLDER}/conformance-tests/conformance-rp-initiated-logout-ci.json -``` - - - -As prerequisites, you need to run first the docker deploy image for conformance test described in [README.md](README.md) -and the conformance test image. - -# Running Hosted Tests - -OpenID foundation hosts the conformance testing software and allows you to test it against your server. -In this situation your OIDC OP must be accessible to the public internet. - -## Deploy SSP OIDC Image - -The docker image created in the README.md is designed to be used for running the conformance tests. -It contains a sqlite database pre-populated with data that can be used for these tests. -Build and run the image somewhere. - -## Register and Create Conformance Tests - -Visit https://openid.net/certification/instructions/ -You can use the `json` deployment configurations under `conformance-tests` to configure your cloud instances. Update -your `discoveryUrl` to reflect the location you deployed SSP. You may also need to adjust `alias` since that is used -in all client redirect URIs and may conflict with existing test suites. - diff --git a/FAQ.md b/FAQ.md deleted file mode 100644 index fcf288c5..00000000 --- a/FAQ.md +++ /dev/null @@ -1,12 +0,0 @@ -# Set JSON type for claims - -You can set the type of claim by prefixing the name with `int:`, `bool:` or `string:`. If no prefix is set then `string` -is assumed. In the rare event that your custom claim name starts with a prefix (example: `int:mycustomclaim`) you can -add one of the type prefixes (example: `string:int:mycustomclaim`) to force the module to release a claim with the -original prefix in it (example: claim `int:mycustomclaim` of type `string`) - -# Release photo - -The OIDC `picture` claim is an URL, while the `jpegPhoto` LDAP attribute is often a b64 string. To use `jpegPhoto` you -can try using an authproc filter to turn it into a data url by adding `data:image/jpeg;base64,` prefix. The support -for data URLs amongst OIDC client is unknown. \ No newline at end of file diff --git a/README.md b/README.md index 74bfc281..37b833b6 100644 --- a/README.md +++ b/README.md @@ -1,481 +1,11 @@ -# simplesamlphp-module-oidc -> A SimpleSAMLphp module for OIDC OP support. - -This module adds support for the OpenID Provider role from the OpenID Connect protocol -through a SimpleSAMLphp module installable via Composer. It is based on -[OAuth2 Server from the PHP League](https://oauth2.thephpleague.com/). +# SimpleSAMLphp Module OIDC -Currently supported flows are: -* Authorization Code flow, with PKCE support (response_type 'code') -* Implicit flow (response_type 'id_token token' or 'id_token') -* Refresh Token flow +> A SimpleSAMLphp module for OIDC OP support. -[![Build Status](https://github.com/simplesamlphp/simplesamlphp-module-oidc/actions/workflows/test.yaml/badge.svg)](https://github.com/simplesamlphp/simplesamlphp-module-oidc/actions/workflows/test.yaml) +[![Build Status](https://github.com/simplesamlphp/simplesamlphp-module-oidc/actions/workflows/test.yaml/badge.svg)](https://github.com/simplesamlphp/simplesamlphp-module-oidc/actions/workflows/test.yaml) [![Coverage Status](https://codecov.io/gh/simplesamlphp/simplesamlphp-module-oidc/branch/master/graph/badge.svg)](https://app.codecov.io/gh/simplesamlphp/simplesamlphp-module-oidc) [![SimpleSAMLphp](https://img.shields.io/badge/simplesamlphp-2.3-brightgreen)](https://simplesamlphp.org/) ![Main screen capture](docs/oidc.png) -### Note on OpenID Federation (OIDFed) support - -OpenID Federation support is in "draft" phase, as is the -[specification](https://openid.net/specs/openid-federation-1_0) itself. This means that you can expect breaking changes -in future releases related to OIDFed capabilities. You can enable / disable OIDFed support at any time in the module -configuration. - -Currently, the following OIDFed features are supported: -* automatic client registration using a Request Object (passing it by value) -* federation participation limiting based on Trust Marks -* endpoint for issuing configuration entity statement (statement about itself) -* fetch endpoint for issuing statements about subordinates (registered clients) -* subordinate listing endpoint - -OIDFed support is implemented using the underlying [SimpleSAMLphp OpenID library](https://github.com/simplesamlphp/openid). - -## Version compatibility - -The minor versions of SimpleSAMLphp listed below indicate which versions of SimpleSAMLphp were used for testing -during module development. SimpleSAMLphp has followed semantic versioning for its API since version 2.0. This means, -for example, that v5.* of the OIDC module should work with any v2.* of SimpleSAMLphp. However, please note that -PHP version requirements have changed in minor SimpleSAMLphp releases. - -| OIDC module | Tested SimpleSAMLphp | PHP | Note | -|:------------|:---------------------|:------:|-------------| -| v6.\* | v2.3.\*, v2.4.\* | \>=8.2 | Recommended | -| v5.\* | v2.1.\* | \>=8.1 | | -| v4.\* | v2.0.\* | \>=8.0 | | -| v3.\* | v2.0.\* | \>=7.4 | | -| v2.\* | v1.19.\* | \>=7.4 | | - -### Upgrading? - -If you are upgrading from a previous version, make sure to check the [upgrade guide](UPGRADE.md). - -## Installation - -Installation is as simple as executing: - - composer require simplesamlphp/simplesamlphp-module-oidc - -### Configure the module - -Copy the module configuration template file to the SimpleSAMLphp config directory: - - cp modules/oidc/config/module_oidc.php.dist config/module_oidc.php - -Review all options in the file and edit them as needed for your environment. - -### Configure the database - -This module uses SimpleSAMLphp's database feature to store access/refresh tokens, user data, and other information. -To set this up, edit your `config/config.php` and configure the database-related settings. Ensure you have at least -the following parameters configured: - - 'database.dsn' => 'mysql:host=server;dbname=simplesamlphp;charset=utf8', - 'database.username' => 'user', - 'database.password' => 'password', - -> [!NOTE] -> The module has been tested with and supports SQLite, PostgreSQL, and MySQL databases. - -### Create Protocol / Federation RSA key pairs - -During the authentication flow, the generated ID Token and Access Token will be in the form of signed JSON Web Tokens (JWS). -For signing these tokens, you need to create a public/private RSA key pair, referred to as "OIDC protocol" keys. - -If you plan to use OpenID Federation capabilities, you should create a separate key pair dedicated to OpenID Federation -operations, such as signing Entity Statement JWS. - -Below are sample commands to create key pairs with default file names for both "protocol" and "federation" purposes: - -To generate the private keys without a passphrase: - - openssl genrsa -out cert/oidc_module.key 3072 - openssl genrsa -out cert/oidc_module_federation.key 3072 - -To generate the private keys with a passphrase: - - openssl genrsa -passout pass:myPassPhrase -out cert/oidc_module.key 3072 - openssl genrsa -passout pass:myPassPhrase -out cert/oidc_module_federation.key 3072 - -Next, extract the public key from each private key: - -Without passphrase: - - openssl rsa -in cert/oidc_module.key -pubout -out cert/oidc_module.crt - openssl rsa -in cert/oidc_module_federation.key -pubout -out cert/oidc_module_federation.crt - -With passphrase: - - openssl rsa -in cert/oidc_module.key -passin pass:myPassPhrase -pubout -out cert/oidc_module.crt - openssl rsa -in cert/oidc_module_federation.key -passin pass:myPassPhrase -pubout -out cert/oidc_module_federation.crt - -If you use different file names or a passphrase, be sure to update these settings in the `module_oidc.php` configuration file. - -### Enabling the module - -To enable the module, add `'oidc' => true` to the list of enabled modules in the main SimpleSAMLphp -configuration file, `config/config.php`: - - 'module.enable' => [ - 'exampleauth' => false, - 'core' => true, - 'admin' => true, - 'saml' => true, - // enable oidc module - 'oidc' => true, - ], - -After enabling the module, you must run the database migrations. - -### Run database migrations - -The module includes default SQL migrations that set up the necessary tables in your configured database. -To run these migrations: - -Option 1: Through the web interface - Navigate to the SimpleSAMLphp administration area, go to -`OIDC` > `Database Migrations`, and click the available button. - -Option 2: Through the command line (recommended for automated/scripted deployments): - - php modules/oidc/bin/install.php - -### Protocol Artifacts Caching - -The configured database serves as the primary storage for protocol artifacts, such as access tokens, authorization -codes, refresh tokens, clients, and user data. In production environments, it is recommended to also set up caching -for these artifacts. The cache layer operates in front of the database, improving performance, particularly during -sudden surges of users attempting to authenticate. The implementation leverages the Symfony Cache component, allowing -the use of any compatible Symfony cache adapter. For more details on configuring the protocol cache, refer to the -module configuration file. - -### Relying Party (RP) Administration - -The module lets you manage (create, read, update and delete) approved RPs from the module user interface itself. - -Once the database schema has been created, in the SimpleSAMLphp administration area go to `OIDC` > -`Client Registry`. - -Note that clients can be marked as confidential or public. If the client is not marked as confidential (it is public), -and is using Authorization Code flow, it will have to provide PKCE parameters during the flow. - -Client ID and secret will be generated, and can be seen after the client creation by clicking on the 'show' button. - -### Cron hook - -In order to purge expired tokens, this module requires [cron module](https://simplesamlphp.org/docs/stable/cron:cron) -to be enabled and configured. - -### Endpoint locations - -Once you deploy the module, in the SimpleSAMLphp administration area go to `OIDC` and then select the -Protocol / Federation Settings page to see the available discovery URLs. These URLs can then be used to set up a -`.well-known` URLs (see below). - -### Key rollover - -The module supports defining additional (new) private / public key pair to be published on relevant JWKS endpoint -or contained in relevant JWKS property. In this way, you can "announce" new public key which can then be fetched -by RPs in order to prepare for the switch of the keys (until the switch of keys, all artifacts continue to be -signed with the "old" private key). - -In this way, after RPs fetch new JWKS (JWKS with "old" and "new" key), you can do the switch of keys when you find -appropriate. - -### Note when using Apache web server - -If you are using Apache web server, you might encounter situations in which Apache strips of Authorization header -with Bearer scheme in HTTP requests, which is a known 'issue' (https://github.com/symfony/symfony/issues/19693). -Although we handle this special situation, it has performance implications, so you should add one of the following -Apache configuration snippets to preserve Authorization header in requests: - -```apacheconf -RewriteEngine On -RewriteCond %{HTTP:Authorization} .+ -RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] -``` -or -```apacheconf -SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 -``` -Choose the one which works for you. If you don't set it, you'll get a warnings about this situation in your logs. - -## Additional considerations -### Private scopes - -This module support the basic OIDC scopes: openid, email, address, phone and profile. -However, you can add your own private scopes in the `module_oidc.php` config file, for example: - -```php - [ - 'private' => [ - 'description' => 'private scope', - 'claim_name_prefix' => '', // Optional prefix for claim names - 'are_multiple_claim_values_allowed' => false, // Allow or disallow multiple values for claims - 'attributes' => ['national_document_id'] - ], - ], -]; -``` - -### Attribute translation - -Default translation table from SAML attributes to OIDC claims is based on -[REFEDS wiki article: "Mapping SAML attributes to OIDC Claims"](https://wiki.refeds.org/display/GROUPS/Mapping+SAML+attributes+to+OIDC+Claims). - -You can change or extend this table in the `module_oidc.php` config file, like in example below. Note that translation -examples use friendly attribute names. If other attribute name format is used, adjust configuration accordingly. - -```php - [ - // Overwrite default translation - 'sub' => [ - 'uid', // added - 'eduPersonPrincipalName', - 'eduPersonTargetedID', - 'eduPersonUniqueId', - ], - // Remove default translation - 'family_name' => [ - ], - - // New claim created from SAML attribute - // Used in previus private scope - 'national_document_id' => [ - 'schacPersonalUniqueId', - ], - ], -]; -``` - -### Auth Proc Filters -This module will not execute standard Auth Proc Filters which are used during regular SAML authN, reason being that -not all expected entities are participating in the authN process (most notably the Service Provider - SP). -Because of that, OIDC module provides its own 'authproc.oidc' configuration option which can be used to designate -specific Auth Proc Filters which will run only during OIDC authN. - -However, there are some considerations. OIDC authN state array will not contain all the keys which are -available during SAML authN, like Service Provider metadata. If you are using an existing filter, make sure it does -not rely on some non-existent state data. At the moment, only the following SAML authN data will be available: -* \['Attributes'\] -* \['Authority'\] -* \['AuthnInstant'\] -* \['Expire'\] - -Source and destination will have entity IDs corresponding to the OP issuer ID and Client ID respectively. -* \['Source'\]\['entityid'\] - contains OpenId Provider issuer ID -* \['Destination'\]\['entityid'\] - contains Relying Party (OIDC Client) ID - -In addition to that, the following OIDC related data will be available in the state array: -* \['Oidc'\]\['OpenIdProviderMetadata'\] - contains information otherwise available from the OIDC configuration URL. -* \['Oidc'\]\['RelyingPartyMetadata'\] - contains information about the OIDC client making the authN request. -* \['Oidc'\]\['AuthorizationRequestParameters'\] - contains relevant authorization request query parameters. - -Auth Proc processing has been tested with a variety of modules including ones that adjust attributes, log -and redirect for user interaction. - -You can add Auth Proc filters in the 'authproc.oidc' config option in the same manner as described in the [Auth Proc -documentation](https://simplesamlphp.org/docs/stable/simplesamlphp-authproc). - -```php - [ - 50 => [ - 'class' => 'core:AttributeAdd', - 'groups' => ['users', 'members'], - ], - ], -]; -``` -### Client registration permissions - -You can allow users to register their own clients. -This is controlled through the `permissions` setting in `module_oidc.php` - -Permissions let the module expose functionality to specific users. In the -below configuration, a user's eduPersonEntitlement attribute is examined. -If the user tries to do something that requires the `client` permission -(such as registering their own client) then they will need one of the -eduPersonEntitlements from the `client` permission array. - -A permission can be disabled by commenting it out. - -```php - \SimpleSAML\Module\oidc\ModuleConfig::OPTION_ADMIN_UI_PERMISSIONS => [ - // Attribute to inspect to determine user's permissions - 'attribute' => 'eduPersonEntitlement', - // Which entitlements allow for registering, editing, delete a client. OIDC clients are owned by the creator - 'client' => ['urn:example:oidc:manage:client'], - ], -``` - -Users can visit the `https://example.com/simplesaml/module.php/oidc/clients/` to create and view their clients. - -## OIDC Discovery Endpoint - -The module offers an OpenID Connect Discovery endpoint at URL: - - https://yourserver/simplesaml/module.php/oidc/.well-known/openid-configuration - -## OpenID Federation Configuration Endpoint - -The module offers an OpenID Federation configuration endpoint at URL: - - https://yourserver/simplesaml/module.php/oidc/.well-known/openid-federation - -### .well-known URLs - -You can configure your web server (Apache, Nginx) in a way to serve the mentioned URLs in a '.well-known' -format. Below are some sample configurations for `openid-configuration`, but you can take the same approach for -`openid-federation`. - -#### nginx - location = /.well-known/openid-configuration { - rewrite ^(.*)$ /simplesaml/module.php/oidc/.well-known/openid-configuration break; - proxy_pass https://localhost; - } - -#### Apache - - RewriteEngine On - RewriteRule ^/.well-known/openid-configuration(.*) /simplesaml/module.php/oidc/.well-known/openid-configuration$1 [PT] - -## Using Docker - -### With current git branch. - -To explore the module using docker run the below command. This will run an SSP image, with the current oidc module -mounted in the container, along with some configuration files. Any code changes you make to your git checkout are -"live" in the container, allowing you to test and iterate different things. - -``` -docker run --name ssp-oidc-dev \ - --mount type=bind,source="$(pwd)",target=/var/simplesamlphp/staging-modules/oidc,readonly \ - -e STAGINGCOMPOSERREPOS=oidc \ - -e COMPOSER_REQUIRE="simplesamlphp/simplesamlphp-module-oidc:@dev" \ - -e SSP_ADMIN_PASSWORD=secret1 \ - --mount type=bind,source="$(pwd)/docker/ssp/module_oidc.php",target=/var/simplesamlphp/config/module_oidc.php,readonly \ - --mount type=bind,source="$(pwd)/docker/ssp/authsources.php",target=/var/simplesamlphp/config/authsources.php,readonly \ - --mount type=bind,source="$(pwd)/docker/ssp/config-override.php",target=/var/simplesamlphp/config/config-override.php,readonly \ - --mount type=bind,source="$(pwd)/docker/ssp/oidc_module.crt",target=/var/simplesamlphp/cert/oidc_module.crt,readonly \ - --mount type=bind,source="$(pwd)/docker/ssp/oidc_module.key",target=/var/simplesamlphp/cert/oidc_module.key,readonly \ - --mount type=bind,source="$(pwd)/docker/apache-override.cf",target=/etc/apache2/sites-enabled/ssp-override.cf,readonly \ - -p 443:443 cirrusid/simplesamlphp:v2.3.5 -``` - -Visit https://localhost/simplesaml/ and confirm you get the default page. -Then navigate to [OIDC screen](https://localhost/simplesaml/module.php/oidc/install.php) -and you can add a client. - -You may view the OIDC configuration endpoint at `https://localhost/.well-known/openid-configuration` - -#### Local Testing with other DBs - -To test local changes against another DB, such as Postgres, we need to: - -* Create a docker network layer -* Run a DB container (and create a DB if one doesn't exist) -* Run SSP and use the DB container - -``` -# Create the network -docker network create ssp-oidc-test -``` - -``` -# Run the db container - docker run --name oidc-db \ - --network ssp-oidc-test \ - -e POSTGRES_PASSWORD=oidcpass \ - -p 25432:5432 \ - -d postgres:15 -``` - -And then use the `docker run` command from `With current git branch` with the following additions - -``` - -e DB.DSN="pgsql:host=oidc-db;dbname=postgres" \ - -e DB.USERNAME="postgres" \ - -e DB.PASSWORD="oidcpass" \ - --network ssp-oidc-test \ - -``` - -#### Testing AuthProc filters - -To perform manual testing of authproc filters, enable the authprocs in `module_oidc.php` that set firstname, sn and performs -a redirect for preprod warning. This setup shows that an authproc can do a redirect and then processing resumes. -Once adjusted, run docker while change the `COMPOSER_REQUIRE` line to - - `-e COMPOSER_REQUIRE="simplesamlphp/simplesamlphp-module-oidc:@dev simplesamlphp/simplesamlphp-module-preprodwarning" \` - -You can register a client from https://oidcdebugger.com/ to test. - -### Build Image to Deploy for Conformance Tests - -Build an image that contains a pre-configured sqlite database. - -```bash -GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD) -# Replace invalid tag characters when doing build -IMAGE_TAG=$(tr '/' '_' <<< $GIT_BRANCH) -docker build -t "simplesamlphp/simplesamlphp-oidc:dev-$IMAGE_TAG" \ - --build-arg OIDC_VERSION=dev-${GIT_BRANCH} \ - -f docker/Dockerfile . - -docker run --name ssp-oidc-dev-image \ - -e SSP_ADMIN_PASSWORD=secret1 \ - -p 443:443 simplesamlphp/simplesamlphp-oidc:dev-$IMAGE_TAG - -``` - -Publish the image somewhere you can retrieve it. -Temporarily, this will occasionally get published into the cirrusid Docker namespace. - -``` -docker tag "simplesamlphp/simplesamlphp-oidc:dev-$IMAGE_TAG" "cirrusid/simplesamlphp-oidc:dev-$IMAGE_TAG" -docker push "cirrusid/simplesamlphp-oidc:dev-$IMAGE_TAG" -``` - -The database is not currently on a share volume, so any changes will get lost if the container restarts. -You may want to back it up. -To dump the database -```bash -docker exec ssp-oidc-dev-image sqlite3 /var/simplesamlphp/data/mydb.sq3 '.dump' > docker/conformance.sql -``` - -Conformance tests are easier to run locally, see the `Docker compose` section and [CONFORMANCE_TEST.md](CONFORMANCE_TEST.md) - -### Docker compose - -Docker compose will run several containers to make it easier to test scenarios. It will build an image -that contains OIDC module. You may remove the ``--build`` argument if you want docker-compose to reuse -previously running container. - -``` -# Use the current branch/git checkout. Composer installs local checkout -OIDC_VERSION=@dev docker-compose -f docker/docker-compose.yml --project-directory . up --build - -# Set OIDC_VERSION to a version that composer can install to use a different version of the module. -OIDC_VERSION=dev-master docker-compose -f docker/docker-compose.yml --project-directory . up --build -``` - -Visit the [OP](https://op.local.stack-dev.cirrusidentity.com/simplesaml/) and confirm a few clients already exist. - -Conformance tests are easier to run locally, see [CONFORMANCE_TEST.md](CONFORMANCE_TEST.md) - -## Running Conformance Tests - -See [CONFORMANCE_TEST.md](CONFORMANCE_TEST.md) - -## Have more questions? - -Check the [FAQ](FAQ.md). +To get started, refer to our [documentation](docs/oidc.md). diff --git a/UPGRADE.md b/UPGRADE.md deleted file mode 100644 index 81a738ca..00000000 --- a/UPGRADE.md +++ /dev/null @@ -1,182 +0,0 @@ - -# Version 5 to 6 - -## New features -- Caching support for OIDC protocol artifacts like Access Tokens, Authorization Codes, Refresh Tokens, but also - client and user data. The cache layer stands in front of the database store, so it can improve performance, especially - in cases of sudden surge of users trying to authenticate. Implementation is based on Symfony Cache component, so any - compatible Symfony cache adapter can be used. Check the module config file for more information on how to set the - protocol cache. -- Key rollover support - you can now define additional (new) private / public key pair which will be published on -relevant JWKS endpoint or contained in JWKS property. In this way, you can "announce" new public key which can then -be fetched by RPs, and do the switch between "old" and "new" key pair when you find appropriate. -- OpenID Federation capabilities: - - Automatic client registration using a Request Object (passing it by value) - - Federation participation limiting based on Trust Marks - - Endpoint for issuing configuration entity statement (statement about itself) - - Fetch endpoint for issuing statements about subordinates (registered clients) - - (from v6.1) Subordinate listing endpoint - - Clients can now be configured with new properties: - - Entity Identifier - - Supported OpenID Federation Registration Types - - Federation JWKS - - Protocol JWKS, JWKS URI and Signed JWKS URI, - - Registration type (manual, federated_automatic, or other in the future) - - Is Federated flag (indicates participation in federation context) - - Timestamps: created_at, updated_at, expires_at -- Improved AuthProc filter support - - Support authproc filters that need to redirect and later resume processing - - `consent` and `preprodwarning` are two authprocs that redirect for user interaction and are now supported - - Uses SSP's ProcessingChain class for closer alignment with SAML IdP configuration. - - Allows additional configuration of authprocs in the main `config.php` under key `authproc.oidc` -- Authorization endpoint now also supports sending request parameters using HTTP POST method, in addition to GET. -- Added support for passing authorization request parameters as JWTs, specifically - passing a Request Object by Value: -https://openid.net/specs/openid-connect-core-1_0.html#RequestObject -- Added support for `private_key_jwt` client authentication method at token endpoint: -https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication - -## New configuration options -- (from v6.1) Show `claims_supported` claim in OP Discovery endpoint - you can now choose to show supported claims, -as is recommended by OpenID Connect Discovery specification https://openid.net/specs/openid-connect-discovery-1_0.html. -- (optional) Issuer - you can now override the issuer (OP identifier). If not set, it falls back to current scheme, host -and optionally a port (as in all previous module versions). -- (optional) Protocol caching adapter and its arguments -- (optional) OpenID Federation related options (needed if federation capabilities are to be used): - - enabled or disabled federation capabilities - - valid trust anchors - - authority hints - - federation caching adapter and its arguments - - PKI keys - federation keys used for example to sign federation entity statements - - federation participation limiting based on Trust Marks for RPs - - (from v6.1) own Trust Marks to dynamically fetch - - signer algorithm - - entity statement duration - - organization name - - display name - - description - - keywords - - contacts - - logo URI - - policy URI - - information URI - - homepage URI (renamed to organization_uri in draft-43) - - organization URI - -## Major impact changes - -- PHP version requirement was bumped to v8.2 - -## Medium impact changes - -- Database schema has been updated, so you'll have to run the DB migrations as described in the README file. -- OIDC protocol endpoints ('authorization_endpoint', 'token_endpoint', 'userinfo_endpoint', 'end_session_endpoint', -'jwks_uri') are now available as new routes which use Symfony routing and container mechanism. This was done as an -effort to move to default SimpleSAMLphp way of working with routes and services. New routes are now published by -default in "OP Configuration" endpoint, which is now also available as -`/module.php/oidc/.well-known/openid-configuration`. If you are publishing that URL as a "well-known" URL -('/.well-known/openid-configuration'), make sure to update your web server configuration to reflect that change -(you can refer to README for examples). All old routes (routes served by PHP files in public folder) will stay -available in this version, which should allow all RPs to update OP configuration in time. Old routes will be -removed in version 7. -- If you are using Apache web server: you should check the README file which now contains a note on how to configure -Apache to preserve Authorization HTTP headers with Bearer token scheme (stripping of this header in Apache is a -known 'issue': https://github.com/symfony/symfony/issues/19693). If you don't set this config, you'll now get warnings -about this situation in your logs. -- The new authproc filter processing will look in an additional location for filters, in the main `config.php` under -key `authproc.oidc` -- Removed support for plain OAuth2 Implicit flow (response_type `token`), because of very low usage. Note that the OIDC -Implicit flow is still supported (response_type `id_token token` or `id_token`). - -## Low impact changes - -- In an effort to move to SimpleSAMLphp way of working with user interface (UI), the client management UI was updated -to extend from the SimpleSAMLphp base template. In addition, we have also introduced some configuration overview pages -where you can take a quick view of some of the configuration values for the module. OIDC related pages are now available -from the main SimpleSAMLphp menu in Administration area. - -- The OIDC config template file has been moved from `config-templates/module_oidc.php` to `config/module_oidc.php.dist`. -This is only relevant for new installations, since initially it is needed to copy the template file to default SSP -config dir. README has been updated to reflect that change. - -Below are also some internal changes that should not have impact for the OIDC OP implementors. However, if you are using -this module as a library or extending from it, you will probably encounter breaking changes, since a lot of code -has been refactored: - -- Upgraded to v5 of lcobucci/jwt https://github.com/lcobucci/jwt -- Upgraded to v3 of laminas/laminas-diactoros https://github.com/laminas/laminas-diactoros -- SimpleSAMLphp version used during development was bumped to v2.3 -- In Authorization Code Flow, a new validation was added which checks for 'openid' value in 'scope' parameter. Up to -now, 'openid' value was dynamically added if not present. In Implicit Code Flow this validation was already present. -- Removed importer from legacy OAuth2 module, as it is very unlikely that someone will upgrade from legacy OAuth2 -module to v6 of oidc module. If needed, one can upgrade to earlier versions of oidc module, and then to v6. - -# Version 4 to 5 - -## Major impact changes -- PHP version requirement was bumped to v8.1 - -## Medium impact changes -- Module config options in file 'module_oidc.php' are now using constants for config keys. The values for constants are -taken from the previous version of the module, so theoretically you don't have to rewrite your current config file, -although it is recommended to do so. - -## Low impact changes -- Removed the 'kid' config option which was not utilized in the codebase (from v2 of the module, the 'kid' value is the -fingerprint of the certificate). - -Below are some internal changes that should not have impact for the OIDC OP implementors. However, if you are using -this module as a library or extending from it, you will probably encounter breaking changes, since a lot of code -has been refactored: - -- psalm error level set to 1, which needed a fair amount of code adjustments -- refactored to strict typing whenever possible (psalm can now infer types for >99% of the codebase) -- refactored to PHP v8.* (up to PHP v8.1) code styling whenever possible, like using constructor property promotion, -match expressions... -- removed dependency on steverhoades/oauth2-openid-connect-server (low maintenance) - -# Version 3 to 4 -- PHP version requirement was bumped to v8.0 to enable updating important dependant packages like 'league/oauth2-server' - which has already moved to PHPv8 between their minor releases. -- SimpleSAMLphp version used during development was bumped v2.0 - -# Version 2 to 3 - - Module code was refactored to make it compatible with SimpleSAMLphp v2 - - Default key name was changed from oidc_module.pem to oidc_module.key. If you don't set custom -key name using option 'privatekey' in module config file, make sure to change the file name of the -key from oidc_module.pem to oidc_module.key. - - Removed config option 'alwaysIssueRefreshToken' - - Removed config option 'alwaysAddClaimsToIdToken' - -# Version 1 to 2 - -There are numerous DB changes that need to be applied. Perform the migration by logging in as an SSP admin to -https://server/simplesaml/module.php/oidc/install.php - -An SSP admin should now use https://server/simplesaml/module.php/oidc/admin-clients/ to manage clients. -The previous `/clients/` path is for authorized users. - -Review the changes to `config-templates/module_oidc.php` and apply relevant changes to your configuration. -For example claim types are now supported. - -In version 1, in authorization code flow, user claims were always included in ID token, instead of only -including them if access token was not released, as per specification. Since changing this behavior is a -potential breaking change for Relying Parties, in version 2 a config option 'alwaysAddClaimsToIdToken' is -introduced to enable OpenID Providers to keep the behavior from version 1 by setting it to 'true'. -If 'alwaysAddClaimsToIdToken' is set to 'false', user claims will only be added to ID token if access token was -not released. If access token was released, user claims will have to be fetched from 'userinfo' endpoint. -Note that this option only applies to authorization code flow since implicit flow was not available in version 1. -If you are to use the spec compliant behavior, make sure to warn existing Relying Parties about the change. - -Similarly, in version 1, in authorization code flow, refresh token was always released, instead of only -releasing it if the client specifically requested it using 'offline_access' scope. Since changing this -behavior is a potential breaking change for Relying Parties, in version 2 a config option -'alwaysIssueRefreshToken' is introduced to enable OpenID Providers to keep the behavior from version 1 -by setting it to 'true'. If 'alwaysIssueRefreshToken' is set to 'false', refresh token will be released -only if it was requested using 'offline_access' scope. If you are to use the spec compliant behavior, make -sure to warn existing Relying Parties about the change. Note that in that case the client must have the -'offline_access' scope registered. - -Token endpoint was renamed from '.../access_token.php' to '.../token.php'. This is a potential breaking change -for clients that do not fetch OP configuration from the /.well-known/openid-configuration URI dynamically, but -instead hardcode endpoints in their configuration. You should probably warn existing Relying Parties about this -change. diff --git a/docker/nginx-certs/README.md b/docker/nginx-certs/README.md index 31efd918..b14f9111 100644 --- a/docker/nginx-certs/README.md +++ b/docker/nginx-certs/README.md @@ -1,9 +1,16 @@ -Every 90 days these certificates expire. The upstream project/container will refresh its certs occasionally, and we -can sync them here. +# Certificates + +Every 90 days these certificates expire. The upstream project/container +will refresh its certs occasionally, and we can sync them here. ```bash docker pull cirrusid/simplesamlphp:latest -docker run -v $PWD:/opt/tmp/certs cirrusid/simplesamlphp /bin/bash -c 'cp /etc/ssl/certs/${APACHE_CERT_NAME}.key /opt/tmp/certs/default.crt && cp /etc/ssl/private/${APACHE_CERT_NAME}.key /opt/tmp/certs/default.key && openssl x509 -noout -enddate -in /opt/tmp/certs/default.crt > /opt/tmp/certs/expiration' +docker run -v $PWD:/opt/tmp/certs cirrusid/simplesamlphp /bin/bash -c \ +'cp /etc/ssl/certs/${APACHE_CERT_NAME}.key /opt/tmp/certs/default.crt && +cp /etc/ssl/private/${APACHE_CERT_NAME}.key /opt/tmp/certs/default.key && +openssl x509 -noout -enddate -in /opt/tmp/certs/default.crt > +/opt/tmp/certs/expiration' ``` -The file `expiration` will get updated with the current expiration date of the certificates. +The file `expiration` will get updated with the current expiration date of +the certificates. diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 00000000..3e3084dd --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,220 @@ +# Configuration + +This guide summarizes key configuration topics for the OIDC module. +It complements the inline comments in `config/module_oidc.php`. + +- Caching protocol artifacts +- Relying Party (RP) administration UI +- Cron integration +- Endpoint locations and well-known URLs +- Key rollover +- Apache Authorization header note +- Private scopes +- Attribute translation +- Auth Proc filters (OIDC) +- Client registration permissions + +## Caching protocol artifacts + +The configured database is the primary storage for protocol artifacts: +access tokens, authorization codes, refresh tokens, clients, and user +records. In production, you should also configure a cache in front of the +DB to improve performance during traffic spikes. + +Caching uses Symfony Cache, so any compatible adapter can be used. See the +`module_oidc.php` configuration file for adapter selection and parameters. + +## Relying Party (RP) administration + +The module provides a UI to manage clients (create, read, update, delete). +After you create the database schema, go to the SimpleSAMLphp admin area: + +- OIDC > Client Registry + +Notes: + +- Clients can be public or confidential. +- Public clients using Authorization Code flow must send PKCE parameters. +- Client ID and secret are generated; use the "show" button to reveal. + +## Cron integration + +Enable and configure the SimpleSAMLphp cron module to purge expired tokens: +[cron](https://simplesamlphp.org/docs/stable/cron/cron.html) + +## Endpoint locations and well-known URLs + +After deployment, visit the SimpleSAMLphp admin area: + +- OIDC > Protocol / Federation Settings + +There you can see discovery URLs. Typical discovery endpoints are: + +- OpenID Connect Discovery: +[https://yourserver/simplesaml/module.php/oidc/.well-known/openid-configuration](https://yourserver/simplesaml/module.php/oidc/.well-known/openid-configuration) +- OpenID Federation configuration: +[https://yourserver/simplesaml/module.php/oidc/.well-known/openid-federation](https://yourserver/simplesaml/module.php/oidc/.well-known/openid-federation) + +You may publish these as ".well-known" URLs at the web root using your +web server. For example, for `openid-configuration`: + +nginx: + +```nginx +location = /.well-known/openid-configuration { + rewrite ^(.*)$ /simplesaml/module.php/oidc/.well-known/openid-configuration break; + proxy_pass https://localhost; +} +``` + +Apache: + +```apache +RewriteEngine On +RewriteRule ^/.well-known/openid-configuration(.*) \ + /simplesaml/module.php/oidc/.well-known/openid-configuration$1 [PT] +``` + +## Key rollover + +You can configure an additional key pair to publish via JWKS endpoints or +properties. This lets RPs pre-fetch the new public key before you switch +signing to the new private key. Once RPs have cached the new JWKS, you can +perform the key switch. + +## Apache Authorization header note + +Apache may strip the `Authorization` header (Bearer) from requests (a known +[issue](https://github.com/symfony/symfony/issues/19693)). + +Although the module includes a fallback, it has performance implications. +Configure Apache to preserve the header using one of these snippets: + +```apache +RewriteEngine On +RewriteCond %{HTTP:Authorization} .+ +RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] +``` + +or + +```apache +SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 +``` + +If not set, you will see warnings about this in the logs. + +## Private scopes + +The module supports the standard scopes: `openid`, `email`, `address`, +`phone`, and `profile`. You can add private scopes in `module_oidc.php`: + +```php + [ + 'private' => [ + 'description' => 'private scope', + 'claim_name_prefix' => '', + 'are_multiple_claim_values_allowed' => false, + 'attributes' => ['national_document_id'], + ], + ], +]; +``` + +## Attribute translation + +Default SAML-to-OIDC claim mapping follows the +[REFEDS guidance](https://wiki.refeds.org/display/GROUPS/Mapping+SAML+attributes+to+OIDC+Claims). + +You can change or extend this mapping in `module_oidc.php`. Example: + +```php + [ + // Overwrite default mapping + 'sub' => [ + 'uid', + 'eduPersonPrincipalName', + 'eduPersonTargetedID', + 'eduPersonUniqueId', + ], + // Remove default mapping by setting an empty array + 'family_name' => [], + + // New claim created from SAML attribute + 'national_document_id' => [ + 'schacPersonalUniqueId', + ], + ], +]; +``` + +## Auth Proc filters (OIDC) + +Standard SAML Auth Proc Filters do not run during OIDC authN because not +all SAML entities are present (like a Service Provider). Instead, use the +`authproc.oidc` configuration option to define filters specific to OIDC. + +The OIDC authN state does not include all keys present in SAML authN. +Available SAML-like keys include: + +- \['Attributes'\] +- \['Authority'\] +- \['AuthnInstant'\] +- \['Expire'\] + +Source and Destination entity IDs correspond to OP issuer and Client ID: + +- \['Source'\]\['entityid'\] → OP issuer ID +- \['Destination'\]\['entityid'\] → RP (client) ID + +Additional OIDC data in the state: + +- \['Oidc'\]\['OpenIdProviderMetadata'\] +- \['Oidc'\]\['RelyingPartyMetadata'\] +- \['Oidc'\]\['AuthorizationRequestParameters'\] + +Example filter configuration: + +```php + [ + 50 => [ + 'class' => 'core:AttributeAdd', + 'groups' => ['users', 'members'], + ], + ], +]; +``` + +## Client registration permissions + +You can allow users to register their own clients. Control this via the +`permissions` setting in `module_oidc.php`. + +Permissions expose functionality to specific users. In the following +example, a user's `eduPersonEntitlement` is examined. To perform an action +requiring the `client` permission (register/edit/delete a client) the user +needs one of the listed entitlements. + +```php + [ + 'attribute' => 'eduPersonEntitlement', + 'client' => ['urn:example:oidc:manage:client'], + ], +]; +``` + +Users can visit the following link for administration: + +- [https://example.com/simplesaml/module.php/oidc/clients/](https://example.com/simplesaml/module.php/oidc/clients/) diff --git a/docs/conformance.md b/docs/conformance.md new file mode 100644 index 00000000..81863b15 --- /dev/null +++ b/docs/conformance.md @@ -0,0 +1,104 @@ +# OpenID Conformance + +This guide summarizes how to run the OpenID Foundation conformance tests +against this module, both locally and using the hosted service. + +- Run conformance tests locally +- Run hosted tests + +## Run conformance tests locally + +This approach is best when you want to test changes without deploying. + +### Run conformance images + +Clone, build, and run the conformance test suite: + +```bash +git clone https://gitlab.com/openid/conformance-suite.git +cd conformance-suite +git checkout release-v5.1.35 +MAVEN_CACHE=./m2 docker-compose -f builder-compose.yml run builder +docker-compose up +``` + +This starts the Java conformance app and a MongoDB server. Then: + +- Visit [https://localhost:8443/](https://localhost:8443/) +- Create a new plan: + "OpenID Connect Core: Basic Certification Profile Authorization server test" +- Click the JSON tab and paste + `conformance-tests/conformance-basic-local.json` from this repo. + +Next, run your SSP OIDC image. + +### Run SSP + +Run SSP with OIDC on the same Docker network as the conformance tests so +containers can communicate. See the "Docker Compose" section in the +README for details. + +### Run conformance tests (interactive) + +The tests are interactive and will ask you to authenticate. Some tests +require clearing cookies to confirm a scenario; others require existing +session cookies. You may be redirected to +`https://localhost.emobix.co.uk:8443/` (the Java app). Accept SSL +warnings as needed. + +### Run automated tests + +Once manual tests pass, you can +[automate the browser portion](https://gitlab.com/openid/conformance-suite/-/wikis/Design/BrowserControl). + +From the `simplesamlphp-module-oidc` directory: + +```bash +# Adjust to your conformance-suite installation path +OIDC_MODULE_FOLDER=. + +# Basic profile +conformance-suite/scripts/run-test-plan.py \ + --expected-failures-file ${OIDC_MODULE_FOLDER}/conformance-tests/basic-warnings.json \ + --expected-skips-file ${OIDC_MODULE_FOLDER}/conformance-tests/basic-skips.json \ + "oidcc-basic-certification-test-plan[server_metadata=discovery][client_registration=static_client]" \ + ${OIDC_MODULE_FOLDER}/conformance-tests/conformance-basic-ci.json + +# Implicit profile +conformance-suite/scripts/run-test-plan.py \ + --expected-failures-file ${OIDC_MODULE_FOLDER}/conformance-tests/implicit-warnings.json \ + --expected-skips-file ${OIDC_MODULE_FOLDER}/conformance-tests/implicit-skips.json \ + "oidcc-implicit-certification-test-plan[server_metadata=discovery][client_registration=static_client]" \ + ${OIDC_MODULE_FOLDER}/conformance-tests/conformance-implicit-ci.json + +# RP initiated back-channel logout +conformance-suite/scripts/run-test-plan.py \ + "oidcc-backchannel-rp-initiated-logout-certification-test-plan[response_type=code][client_registration=static_client]" \ + ${OIDC_MODULE_FOLDER}/conformance-tests/conformance-back-channel-logout-ci.json + +# RP initiated logout +conformance-suite/scripts/run-test-plan.py \ + "oidcc-rp-initiated-logout-certification-test-plan[response_type=code][client_registration=static_client]" \ + ${OIDC_MODULE_FOLDER}/conformance-tests/conformance-rp-initiated-logout-ci.json +``` + +Prerequisites: run the docker deploy image for conformance tests (see +README) and the conformance test image first. + +## Run hosted tests + +The OpenID Foundation hosts the conformance testing software. Your OIDC +OP must be publicly accessible on the internet. + +### Deploy SSP OIDC image + +Use the docker image described in the README. It contains a SQLite DB +pre-populated with data for the tests. Build and run the image. + +### Register and create conformance tests + +Visit [https://openid.net/certification/instructions/](https://openid.net/certification/instructions/). + +Use the `json` configs under `conformance-tests` to configure your cloud +instances. Update `discoveryUrl` to the deployed location. Adjust `alias` +if it conflicts with existing test suites (it is used in redirect URIs). diff --git a/docs/docker.md b/docs/docker.md new file mode 100644 index 00000000..f4923681 --- /dev/null +++ b/docs/docker.md @@ -0,0 +1,136 @@ +# Using Docker + +This document shows how to run and test the module with Docker. + +- Run with the current git branch (live mount) +- Local testing with other DBs +- Testing AuthProc filters +- Build image for conformance tests +- Docker Compose + +## Run with the current git branch (live mount) + +Run an SSP image with the current OIDC module mounted read-only. Changes +in your checkout are reflected live in the container. + +```bash +docker run --name ssp-oidc-dev \ + --mount type=bind,source="$(pwd)",target=/var/simplesamlphp/staging-modules/oidc,readonly \ + -e STAGINGCOMPOSERREPOS=oidc \ + -e COMPOSER_REQUIRE="simplesamlphp/simplesamlphp-module-oidc:@dev" \ + -e SSP_ADMIN_PASSWORD=secret1 \ + --mount type=bind,source="$(pwd)/docker/ssp/module_oidc.php",target=/var/simplesamlphp/config/module_oidc.php,readonly \ + --mount type=bind,source="$(pwd)/docker/ssp/authsources.php",target=/var/simplesamlphp/config/authsources.php,readonly \ + --mount type=bind,source="$(pwd)/docker/ssp/config-override.php",target=/var/simplesamlphp/config/config-override.php,readonly \ + --mount type=bind,source="$(pwd)/docker/ssp/oidc_module.crt",target=/var/simplesamlphp/cert/oidc_module.crt,readonly \ + --mount type=bind,source="$(pwd)/docker/ssp/oidc_module.key",target=/var/simplesamlphp/cert/oidc_module.key,readonly \ + --mount type=bind,source="$(pwd)/docker/apache-override.cf",target=/etc/apache2/sites-enabled/ssp-override.cf,readonly \ + -p 443:443 cirrusid/simplesamlphp:v2.3.5 +``` + +Then visit: + +- [https://localhost/simplesaml/](https://localhost/simplesaml/) + +The OIDC configuration endpoint is available at: + +- [https://localhost/.well-known/openid-configuration](https://localhost/.well-known/openid-configuration) + +## Local testing with other DBs + +You can test it against another database such as PostgreSQL. + +1 - Create a Docker network: + +```bash +docker network create ssp-oidc-test +``` + +2 - Run a DB container: + +```bash +docker run --name oidc-db \ + --network ssp-oidc-test \ + -e POSTGRES_PASSWORD=oidcpass \ + -p 25432:5432 \ + -d postgres:15 +``` + +3 - Run SSP (from the prior command) with these additions: + +```bash +-e DB.DSN="pgsql:host=oidc-db;dbname=postgres" \ +-e DB.USERNAME="postgres" \ +-e DB.PASSWORD="oidcpass" \ +--network ssp-oidc-test \ +``` + +## Testing AuthProc filters + +Enable the example AuthProc filters in `module_oidc.php` that set +`firstname` and `sn` and configure the preprod warning filter. This shows +that an authproc can redirect and processing resumes. + +When running Docker, adjust `COMPOSER_REQUIRE` to include the module: + +```text +-e "COMPOSER_REQUIRE=simplesamlphp/simplesamlphp-module-oidc:@dev \ + simplesamlphp/simplesamlphp-module-preprodwarning" +``` + +You can register a client from +[https://oidcdebugger.com/](https://oidcdebugger.com/) to test. + +## Build image for conformance tests + +Build an image that contains a pre-configured sqlite database. + +```bash +GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD) +IMAGE_TAG=$(tr '/' '_' <<< "$GIT_BRANCH") + +docker build -t "simplesamlphp/simplesamlphp-oidc:dev-$IMAGE_TAG" \ + --build-arg OIDC_VERSION=dev-${GIT_BRANCH} \ + -f docker/Dockerfile . + +docker run --name ssp-oidc-dev-image \ + -e SSP_ADMIN_PASSWORD=secret1 \ + -p 443:443 simplesamlphp/simplesamlphp-oidc:dev-$IMAGE_TAG +``` + +Publish the image where you can retrieve it. Example: + +```bash +docker tag "simplesamlphp/simplesamlphp-oidc:dev-$IMAGE_TAG" \ + "cirrusid/simplesamlphp-oidc:dev-$IMAGE_TAG" + +docker push "cirrusid/simplesamlphp-oidc:dev-$IMAGE_TAG" +``` + +The DB is not on a shared volume. Changes are lost if the container +restarts. Backup example: + +```bash +docker exec ssp-oidc-dev-image sqlite3 /var/simplesamlphp/data/mydb.sq3 '.dump' \ + > docker/conformance.sql +``` + +Conformance tests are easier to run locally. See [Conformance](conformance.md). + +## Docker Compose + +Docker Compose runs multiple containers to ease testing. It builds an +image containing the OIDC module. You can remove `--build` to reuse an +existing container. + +```bash +# Use current branch/git checkout. Composer installs local checkout +OIDC_VERSION=@dev docker-compose -f docker/docker-compose.yml --project-directory . up --build + +# Use a specific module version +OIDC_VERSION=dev-master docker-compose -f docker/docker-compose.yml --project-directory . up --build +``` + +Visit the OP and verify a few clients exist: + +- [https://op.local.stack-dev.cirrusidentity.com/simplesaml/](https://op.local.stack-dev.cirrusidentity.com/simplesaml/) diff --git a/docs/faq.md b/docs/faq.md new file mode 100644 index 00000000..5766dadc --- /dev/null +++ b/docs/faq.md @@ -0,0 +1,20 @@ +# FAQ + +A few common questions gathered from prior discussions. + +## Set JSON type for claims + +You can set the type of claim by prefixing the claim name with `int:`, +`bool:` or `string:`. If no prefix is present, `string` is assumed. + +If a custom claim name starts with a prefix (example: `int:mycustomclaim`) +you can add one of the type prefixes (example: `string:int:mycustomclaim`) +to force the module to release a claim with the original prefix in it +(example: claim `int:mycustomclaim` of type `string`). + +## Release photo + +The OIDC `picture` claim is a URL, while the LDAP attribute `jpegPhoto` +is often a base64 string. To use `jpegPhoto`, try an authproc filter to +turn it into a data URL by adding the `data:image/jpeg;base64,` prefix. +Support for data URLs varies by OIDC client, so test your clients. diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 00000000..50131b77 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,111 @@ +# Installation + +This guide walks you through installing, enabling, and preparing the OIDC +module in SimpleSAMLphp. + +## 1. Install the module + +Run: + +```bash +composer require simplesamlphp/simplesamlphp-module-oidc +``` + +## 2. Configure the module + +Copy the configuration template into your SimpleSAMLphp config directory +and review all options: + +```bash +cp modules/oidc/config/module_oidc.php.dist config/module_oidc.php +``` + +## 3. Configure the database + +The module uses SimpleSAMLphp's database feature to store access and +refresh tokens, user data, and other artifacts. Edit `config/config.php` +and ensure at least the following parameters are set: + +```php +'database.dsn' => 'mysql:host=server;dbname=simplesamlphp;charset=utf8', +'database.username' => 'user', +'database.password' => 'password', +``` + +Note: SQLite, PostgreSQL, and MySQL are supported. + +## 4. Create RSA key pairs + +ID and Access tokens are signed JWTs. Create a public/private RSA key +pair for OIDC protocol operations. If you plan to use OpenID Federation, +create a separate key pair for federation operations. + +Generate private keys without a passphrase: + +```bash +openssl genrsa -out cert/oidc_module.key 3072 +openssl genrsa -out cert/oidc_module_federation.key 3072 +``` + +Generate private keys with a passphrase: + +```bash +openssl genrsa -passout pass:myPassPhrase -out cert/oidc_module.key 3072 +openssl genrsa -passout pass:myPassPhrase -out cert/oidc_module_federation.key 3072 +``` + +Extract public keys: + +Without passphrase: + +```bash +openssl rsa -in cert/oidc_module.key -pubout -out cert/oidc_module.crt +openssl rsa -in cert/oidc_module_federation.key -pubout -out cert/oidc_module_federation.crt +``` + +With a passphrase: + +```bash +openssl rsa -in cert/oidc_module.key -passin pass:myPassPhrase -pubout -out cert/oidc_module.crt +openssl rsa -in cert/oidc_module_federation.key -passin pass:myPassPhrase -pubout -out cert/oidc_module_federation.crt +``` + +If you use different file names or a passphrase, update +`config/module_oidc.php` accordingly. + +## 5. Enable the module + +Edit `config/config.php` and enable `oidc`: + +```php +'module.enable' => [ + 'exampleauth' => false, + 'core' => true, + 'admin' => true, + 'saml' => true, + // enable oidc module + 'oidc' => true, +], +``` + +## 6. Run database migrations + +Run the built-in migrations to create required tables. + +Option A: Web UI + +- Go to the admin area, then `OIDC` > `Database Migrations` and click the + available button. + +Option B: Command line + +```bash +php modules/oidc/bin/install.php +``` + +## 7. Next steps + +- Configure caches, endpoints, and other options: + see [Configuration](configuration.md) +- Administer clients from the UI: + see [Relying Party (RP) Administration](configuration.md#relying-party-rp-administration) diff --git a/docs/oidc.md b/docs/oidc.md new file mode 100644 index 00000000..6b38c22a --- /dev/null +++ b/docs/oidc.md @@ -0,0 +1,62 @@ +# OIDC Module + +This module adds support for the OpenID Provider (OP) role from the +OpenID Connect protocol to SimpleSAMLphp. It is installable via Composer +and is based on the +[OAuth2 Server from the PHP League](https://oauth2.thephpleague.com/). + +Supported flows: + +- Authorization Code, with PKCE (response_type: `code`) +- Implicit (response_type: `id_token token` or `id_token`) +- Refresh Token + +![Main screen capture](oidc.png) + +## Note on OpenID Federation (OIDFed) + +OpenID Federation support is in draft, as is the +[specification](https://openid.net/specs/openid-federation-1_0). You can +expect breaking changes in future releases related to OIDFed +capabilities. OIDFed can be enabled or disabled in the module +configuration. + +Currently supported OIDFed features: + +- Automatic client registration using a Request Object (by value) +- Federation participation limiting based on Trust Marks +- Endpoint for issuing a configuration entity statement (about itself) +- Fetch endpoint for issuing statements about subordinates (clients) +- Subordinate listing endpoint + +OIDFed is implemented using the +[SimpleSAMLphp OpenID library](https://github.com/simplesamlphp/openid). + +## Version compatibility + +Minor versions listed show which SimpleSAMLphp versions were used during +module development. SimpleSAMLphp follows semantic versioning for its +API since v2.0. For example, v5.\* of the OIDC module should work with +any v2.\* of SimpleSAMLphp. PHP version requirements may differ. + +| OIDC module | Tested SimpleSAMLphp | PHP | Note | +|:------------|:---------------------|:------:|-------------| +| v6.\* | v2.3.\*, v2.4.\* | \>=8.2 | Recommended | +| v5.\* | v2.1.\* | \>=8.1 | | +| v4.\* | v2.0.\* | \>=8.0 | | +| v3.\* | v2.0.\* | \>=7.4 | | +| v2.\* | v1.19.\* | \>=7.4 | | + +Upgrading? See the [upgrade guide](upgrade.md). + +## Documentation + +- Getting started: [Installation](installation.md) +- Configure and operate: [Configuration](configuration.md) +- Manage clients and UI: see [Configuration](configuration.md#relying-party-rp-administration) +- Endpoints and discovery: see + [Configuration](configuration.md#endpoint-locations-and-well-known-urls) +- Running with containers: [Using Docker](docker.md) +- Conformance tests: [OpenID Conformance](conformance.md) +- Upgrading between versions: [Upgrade guide](upgrade.md) +- Common questions: [FAQ](faq.md) diff --git a/docs/upgrade.md b/docs/upgrade.md new file mode 100644 index 00000000..65d1d219 --- /dev/null +++ b/docs/upgrade.md @@ -0,0 +1,242 @@ +# Upgrade guide + +This is an upgrade guide from versions 1 → 6. Review the changes and +apply those relevant to your deployment. + +## Version 5 to 6 + +New features: + +- Caching support for OIDC protocol artifacts like Access Tokens, +Authorization Codes, Refresh Tokens, but also client and user data. +The cache layer stands in front of the database store, so it can +improve performance, especially in cases of a sudden surge of +users trying to authenticate. Implementation is based on a Symfony +Cache component, so any compatible Symfony cache adapter can be used. +Check the module config file for more information on how to set the +protocol cache. +- Key rollover support - you can now define an additional (new) +private / public key pair which will be published on the relevant +JWKS endpoint or contained in JWKS property. In this way, you can +"announce" a new public key which can then be fetched by RPs, +and do the switch between "old" and "new" key pair when you +find appropriate. +- OpenID Federation capabilities: + - Automatic client registration using a Request Object (passing it by value) + - Federation participation limiting based on Trust Marks + - Endpoint for issuing a configuration entity statement + (statement about itself) + - Fetch endpoint for issuing statements about subordinates + (registered clients) + - (from v6.1) Subordinate listing endpoint + - Clients can now be configured with new properties: + - Entity Identifier + - Supported OpenID Federation Registration Types + - Federation JWKS + - Protocol JWKS, JWKS URI and Signed JWKS URI, + - Registration type (manual, federated_automatic, or other in the future) + - Is Federated flag (indicates participation in federation context) + - Timestamps: created_at, updated_at, expires_at +- Improved AuthProc filter support + - Support authproc filters that need to redirect and later resume processing + - `consent` and `preprodwarning` are two authprocs that redirect for + user interaction and are now supported + - Uses SSP's ProcessingChain class for closer alignment with SAML IdP + configuration. + - Allows additional configuration of authprocs in the main + `config.php` under key `authproc.oidc` +- Authorization endpoint now also supports sending request parameters using +HTTP POST method, in addition to GET. +- Added support for passing authorization request parameters as JWTs, +specifically - passing a Request Object by Value: +[https://openid.net/specs/openid-connect-core-1_0.html#RequestObject](https://openid.net/specs/openid-connect-core-1_0.html#RequestObject) +- Added support for `private_key_jwt` client authentication method at +token endpoint: +[https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication) + +New configuration options: + +- (from v6.1) Show `claims_supported` claim in OP Discovery endpoint - +you can now choose to show supported claims, as is recommended by OpenID +Connect Discovery specification +[https://openid.net/specs/openid-connect-discovery-1_0.html](https://openid.net/specs/openid-connect-discovery-1_0.html). +- (optional) Issuer - you can now override the issuer (OP identifier). +If not set, it falls back to the current scheme, host, and optionally +a port (as in all previous module versions). +- (optional) Protocol caching adapter and its arguments +- (optional) OpenID Federation-related options (needed if federation +capabilities are to be used): + - enabled or disabled federation capabilities + - valid trust anchors + - authority hints + - federation caching adapter and its arguments + - PKI keys - federation keys used, for example, to sign federation entity + statements + - federation participation limiting based on Trust Marks for RPs + - (from v6.1) own Trust Marks to dynamically fetch + - signer algorithm + - entity statement duration + - organization name + - display name + - description + - keywords + - contacts + - logo URI + - policy URI + - information URI + - homepage URI (renamed to organization_uri in draft-43) + - organization URI + +Major impact changes: + +- PHP version requirement was bumped to v8.2 + +Medium impact changes: + +- Database schema has been updated, so you'll have to run the DB migrations +as described in the README file. +- OIDC protocol endpoints ('authorization_endpoint', 'token_endpoint', +'userinfo_endpoint', 'end_session_endpoint', 'jwks_uri') are now available as +new routes which use Symfony routing and container mechanism. This was done as +an effort to move to the default SimpleSAMLphp way of working with routes and +services. New routes are now published by default in "OP Configuration" +endpoint, which is now also available as +`/module.php/oidc/.well-known/openid-configuration`. If you are +publishing that URL as a "well-known" URL ('/.well-known/openid-configuration'), +make sure to update your web server configuration to reflect that change +(you can refer to README for examples). All old routes (routes served by +PHP files in the public folder) will stay available in this version, +which should allow all RPs to update OP configuration in time. +Old routes will be removed in version 7. +- If you are using Apache web server: you should check the README file which +now contains a note on how to configure Apache to preserve Authorization +HTTP headers with a Bearer token scheme (stripping of this header in Apache is a +known [issue](https://github.com/symfony/symfony/issues/19693)). If you don't +set this config, you'll now get warnings about this situation in your logs. +The new authproc filter processing will look in an additional location for +filters, in the main `config.php` under key `authproc.oidc` +- Removed support for plain OAuth2 Implicit flow (response_type `token`), +because of very low usage. Note that the OIDC Implicit flow is still supported +(response_type `id_token token` or `id_token`). + +Low-impact changes: + +- In an effort to move to SimpleSAMLphp way of working with user interface (UI), +the client management UI was updatedto extend from the SimpleSAMLphp base +template. In addition, we have also introduced some configuration overview +pages where you can take a quick view of some of the configuration values for +the module. OIDC related pages are now available from the main SimpleSAMLphp +menu in the Administration area. +- The OIDC config template file has been moved from +`config-templates/module_oidc.php` to `config/module_oidc.php.dist`. +This is only relevant for new installations, since initially it is necessary +to copy the template file to the default SSP config dir. + +Below are also some internal changes that should not have an impact on the +OIDC OP implementers. However, if you are using this module as a library or +extending from it, you will probably encounter breaking changes, since a lot +of code has been refactored: + +- Upgraded to v5 of [lcobucci/jwt](https://github.com/lcobucci/jwt) +- Upgraded to v3 of [laminas/laminas-diactoros](https://github.com/laminas/laminas-diactoros) +- SimpleSAMLphp version used during development was bumped to v2.3 +- In Authorization Code Flow, a new validation was added which checks for +'openid' value in the 'scope' parameter. Up to now, the 'openid' value was +dynamically added if not present. In Implicit Code Flow this validation was +already present. +- Removed importer from the legacy OAuth2 module, as it is very unlikely that +someone will upgrade from the legacy OAuth2 module to v6 of oidc module. +If needed, one can upgrade to earlier versions of the `oidc` module, and then +to v6. + +## Version 4 to 5 + +Major impact changes: + +- PHP version requirement was bumped to v8.1 + +Medium impact changes: + +- Module config options in the file 'module_oidc.php' are now using constants +for config keys. The values for constants are taken from the previous version +of the module, so theoretically you don't have to rewrite your current config +file, although it is recommended to do so. + +Low-impact changes: + +- Removed the 'kid' config option which was not used in the codebase +(from v2 of the module, the 'kid' value is the fingerprint of the certificate). + +Below are some internal changes that should not have an impact on the OIDC OP +implementers. However, if you are using this module as a library or extending +from it, you will probably encounter breaking changes, since a lot of code +has been refactored: + +- psalm error level set to 1, which needed a fair number of code adjustments +- refactored to strict typing whenever possible (psalm can now infer +types for >99% of the codebase) +- refactored to PHP v8.* (up to PHP v8.1) code styling whenever possible, +like using constructor property promotion, match expressions... +- removed dependency on steverhoades/oauth2-openid-connect-server +(low maintenance) + +## Version 3 to 4 + +- PHP version requirement was bumped to v8.0 to enable updating important +dependent packages like 'league/oauth2-server' which has already moved to +PHPv8 between their minor releases. +- SimpleSAMLphp version used during development was bumped v2.0 + +## Version 2 to 3 + +- Module code was refactored to make it compatible with SimpleSAMLphp v2 +- The default key name was changed from oidc_module.pem to oidc_module.key. +If you don't set a custom key name using the option 'privatekey' in a module +config file, make sure to change the file name of the key from +oidc_module.pem to oidc_module.key. +- Removed config option 'alwaysIssueRefreshToken' +- Removed config option 'alwaysAddClaimsToIdToken' + +## Version 1 to 2 + +There are many DB changes that need to be applied. Perform the migration by +logging in as an SSP admin to +[https://server/simplesaml/module.php/oidc/install.php](https://server/simplesaml/module.php/oidc/install.php) + +An SSP admin should now use +[https://server/simplesaml/module.php/oidc/admin-clients/](https://server/simplesaml/module.php/oidc/admin-clients/) +to manage clients. The previous `/clients/` path is for authorized users. + +Review the changes to `config-templates/module_oidc.php` and apply relevant +changes to your configuration. For example, claim types are now supported. + +In version 1, in authorization code flow, user claims were always included in +ID token, instead of only including them if the access token was not released, +as per specification. Since changing this behavior is a potential breaking +change for Relying Parties, in version 2 a config option +'alwaysAddClaimsToIdToken' is introduced to enable OpenID Providers to keep +the behavior from version 1 by setting it to 'true'. +If 'alwaysAddClaimsToIdToken' is set to 'false', user claims will only be added +to ID token if access token was not released. If an access token was released, +user claims will have to be fetched from the 'userinfo' endpoint. +Note that this option has only applied to authorization code flow since +implicit flow was not available in version 1. If you are to use the spec +compliant behavior, make sure to warn existing Relying Parties about the change. + +Similarly, in version 1, in authorization code flow, the refresh token was +always released, instead of only releasing it if the client specifically +requested it using the 'offline_access' scope. Since changing this +behavior is a potential breaking change for Relying Parties, in version 2 +a config option 'alwaysIssueRefreshToken' is introduced to enable OpenID +Providers to keep the behavior from version 1 by setting it to 'true'. +If 'alwaysIssueRefreshToken' is set to 'false', refresh token will be released +only if it was requested using 'offline_access' scope. If you are to use the +spec compliant behavior, make sure to warn existing Relying Parties about +the change. Note that in that case the client must have the +'offline_access' scope registered. + +Token endpoint was renamed from '.../access_token.php' to '.../token.php'. +This is a potential breaking change for clients that do not fetch +OP configuration from the /.well-known/openid-configuration URI dynamically, +but instead hardcode endpoints in their configuration. You should probably +warn existing Relying Parties about this change.