Deployment

Installation

Install OmniPasskey by initializing the shared passkey database, deploying the management interface, installing the Shibboleth IdP plugin, and validating that the relying-party and database settings line up across the stack.

Example hostnames

This guide uses app.example.edu for the OmniPasskey management interface and idp.example.edu for the Shibboleth IdP. Replace those values with your production names before deployment.

Before you begin

A Shibboleth IdP 5.2.3 or later deployment for passkey authentication.
A Shibboleth SP deployment or host that will serve the OmniPasskey management interface.
TLS certificates and DNS names for the management interface and the IdP.
A shared PostgreSQL or MySQL/MariaDB database for passkey records, policy, and audit data.
The OmniPasskey distribution bundle containing database/, app/, packages/, and idp_plugin/.

Installation order

  1. Initialize the shared passkey database.
  2. Install the OmniPasskey management interface using either the container or package path.
  3. Install and configure the OmniPasskey Shibboleth IdP plugin.
  4. Align the relying-party ID, frontend URL, inline-registration URL, and database connectivity across all components.
  5. Validate registration, credential management, and sign-in.

Step 1: Initialize the shared database

Choose the bootstrap SQL that matches your database backend:

  • PostgreSQL: database/postgresql/setup.sql
  • MySQL or MariaDB: database/mysql/setup.sql

Create the database

  1. Replace every <changeme> placeholder in the selected setup.sql file with strong passwords.
  2. Connect as a database administrator.
  3. Load the bootstrap SQL so it can create the shared schema, runtime users, tables, and indexes.
psql -h db.example.edu -U postgres -d postgres \
  -f database/postgresql/setup.sql
mysql -h db.example.edu -u root -p \
  < database/mysql/setup.sql

Example shared backend settings

Both the management interface and the IdP plugin need matching backend details in their respective passkey-database.properties files.

passkey.db.backend=postgresql
passkey.db.name=shibboleth_passkey
passkey.db.validationQuery=select 1
passkey.db.postgresql.host=db.example.edu
passkey.db.postgresql.schema=shibboleth_passkey
passkey.db.postgresql.appUser=django
passkey.db.postgresql.appPassword=<app-password>
passkey.db.postgresql.idpUser=shib
passkey.db.postgresql.idpPassword=<idp-password>
passkey.db.backend=mysql
passkey.db.name=shibboleth_passkey
passkey.db.validationQuery=select 1
passkey.db.mysql.host=db.example.edu
passkey.db.mysql.appUser=django
passkey.db.mysql.appPassword=<app-password>
passkey.db.mysql.idpUser=shib
passkey.db.mysql.idpPassword=<idp-password>

Step 2: Install the management interface

Choose one deployment path for the customer-facing OmniPasskey management interface.

Container deployment path

Use the app/ directory when you want a full SP-facing OmniPasskey container image.

  1. Review app/Dockerfile.
  2. Add or replace your Shibboleth SP files under app/container_files/shibboleth/, including metadata, keys, and certificates.
  3. Update app/container_files/httpd/ssl.conf with your external hostname and the runtime paths for the web-server TLS certificate and private key.
  4. Keep the TLS private key outside the image and mount the certificate and key read-only at runtime. The shipped paths are /etc/pki/tls/certs/localhost.crt and /etc/pki/tls/private/localhost.key.
  5. Update app/container_files/httpd/omnipasskey.conf for your real hostname and authorization rules. Set the HTTP redirect and HTTPS ServerName to the public application hostname, replace the deny-by-default admin and helpdesk rules with your Shibboleth user or entitlement rules, and allow /registration-server-backend only from the IdP if inline registration is enabled.
  6. Set the backend in app/container_files/registration-server/passkey-database.properties, using the same backend type, database name/host, and matching application credentials as the IdP plugin’s passkey-database.properties.
  7. Create app/container_files/registration-server/.env with the deployment-specific values shown below.
    SERVER_NAME="Example University"
    ID=example.edu
    DJANGO_SECRET_KEY=<strong-random-secret>
    DJANGO_ALLOWED_HOSTS=localhost,app.example.edu
    DJANGO_DEBUG=false

    ID is the WebAuthn relying-party ID. It is normally the registrable domain used by the application and must match the IdP plugin’s relying-party ID. DJANGO_SECRET_KEY is private signing material for Django sessions and other cryptographic operations; generate a strong, unique value and keep it out of source control. DJANGO_ALLOWED_HOSTS is the comma-separated list of hostnames Django may serve, and must include the management interface hostname. Keep DJANGO_DEBUG=false in customer-facing deployments so Django does not expose debug details.

  8. Optionally replace app/container_files/www/logo.png and add app/container_files/www/theme.json for site branding; see Branding for the supported files and display guidance.
docker build \
  --build-arg REACT_APP_API_BASE=https://app.example.edu \
  --build-arg REACT_APP_LOGOUT_URL=https://app.example.edu/Shibboleth.sso/Logout \
  --build-arg SERVER_NAME="Example University" \
  --build-arg SERVER_ID=example.edu \
  -t omnipasskey-management \
  app
docker run -d \
  --name omnipasskey-management \
  -p 80:80 \
  -p 443:443 \
  -e PASSKEY_SERVER_NAME="Example University" \
  -e PASSKEY_SERVER_ID=example.edu \
  -e DJANGO_SECRET_KEY=<strong-random-secret> \
  --mount type=bind,src=/secure/app.example.edu.crt,dst=/etc/pki/tls/certs/localhost.crt,readonly \
  --mount type=bind,src=/secure/app.example.edu.key,dst=/etc/pki/tls/private/localhost.key,readonly \
  omnipasskey-management

The default logout target, /Shibboleth.sso/Logout, signs the user out of the local Shibboleth SP. If your institution uses a combined SP/IdP logout page, deploy that page separately and set REACT_APP_LOGOUT_URL to it. Adjust the TLS mount paths and ssl.conf together, and ensure Apache can read the mounted files.

Authorization defaults are intentionally restrictive.

The shipped Apache template keeps /passkey/admin, /passkey/helpdesk, the matching API paths, and /registration-server-backend denied until you replace those rules with your site-specific allow-lists.

Package deployment path

Use the native package path when you already operate an Apache and Shibboleth SP host.

Supported operating systems

Package Supported operating systems Requirements
DEB Debian 12 and 13 Bookworm and Trixie
DEB Ubuntu 22.04, 24.04, and 26.04 LTS Use a fully updated LTS installation.
RPM Rocky Linux 8, 9, and 10 Keep Rocky Linux 8 and 9 fully updated so Python 3.12 packages are available.
RPM RHEL 8, 9, and 10 RHEL 8.10 or later and RHEL 9.4 or later are required.
Ubuntu 20.04 is not supported.

This includes ESM deployments. Its native Python and mod_wsgi stack uses Python 3.8, while Django 5.2 requires Python 3.10 or later.

What the host must already provide

The native package path assumes that the host already runs Apache and a configured Shibboleth SP. OmniPasskey adds its application routes to that environment; it does not create your site’s HTTPS virtual host or choose its Shibboleth entityID, metadata, keys, certificates, and handlers.

Install and configure the Shibboleth SP using the repository and operating procedures approved for your site. Before adding OmniPasskey, confirm that the SP can establish a session on the intended HTTPS host and that these host-owned Apache modules are enabled:

  • mod_ssl for the required HTTPS virtual host.
  • mod_rewrite for management-app client-side routes.
  • mod_shib for Shibboleth session enforcement and attributes.

What the OmniPasskey package installs

The DEB and RPM declare their application runtime dependencies. Let apt or dnf resolve them when installing OmniPasskey; do not select a separate Python, WSGI, or cron package by hand.

  • The DEB installs the distribution Python 3 runtime and libapache2-mod-wsgi-py3. The RPM selects Python 3.12 and the native WSGI provider: python3.12-mod_wsgi on EL 8 and 9 or python3-mod_wsgi on EL 10.
  • The DEB installs cron; the RPM installs cronie.
  • The post-install step creates the Django virtual environment, installs the bundled Python dependencies, registers the Django maintenance jobs in crontab, and attempts the initial metadata and icon download.

Debian or Ubuntu

apt install ./packages/shibboleth-passkey-app_*.deb

Rocky Linux or RHEL

dnf install ./packages/shibboleth-passkey-app-*.rpm

The Apache snippet is installed at /etc/apache2/conf-available/omnipasskey.conf on Debian/Ubuntu and /etc/httpd/conf.d/omnipasskey.conf on Rocky Linux/RHEL. DEB installation also runs a2enconf omnipasskey when the Apache helper is present.

After package installation:

  1. Edit the package-installed omnipasskey.conf file in the native Apache configuration directory. Replace the deny-by-default admin and helpdesk rules with your Shibboleth user or entitlement rules. If inline registration is enabled, allow /registration-server-backend only from the IdP host or network.
  2. Review /var/www/html/passkey/env.js.
    window.__ENV__ = {
      API_BASE: "https://app.example.edu",
      LOGOUT_URL: "https://app.example.edu/Shibboleth.sso/Logout"
    }

    API_BASE is the browser’s base URL for the OmniPasskey API and application assets; set it to the user-facing HTTPS origin served by this installation. LOGOUT_URL is the Shibboleth SP logout endpoint, unless your institution uses a separately deployed combined SP/IdP logout page.

  3. Review /var/www/registration-server/.env.
    SERVER_NAME="Example University"
    ID=example.edu
    DJANGO_SECRET_KEY=<strong-random-secret>
    DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1,app.example.edu
    DJANGO_DEBUG=false

    ID is the WebAuthn relying-party ID and must match the IdP plugin configuration. DJANGO_SECRET_KEY is private signing material for Django sessions and other cryptographic operations; use a strong, unique value. DJANGO_ALLOWED_HOSTS is the comma-separated list of hostnames Django may serve and must include the management interface hostname. Keep DJANGO_DEBUG=false in customer-facing deployments.

  4. Set the backend in /var/www/registration-server/passkey-database.properties. Select postgresql or mysql, then set the database host, name, schema where applicable, and application credentials. Use matching backend settings and IdP credentials in the IdP plugin’s passkey-database.properties. After changing these files, restart or reload the application and IdP services as appropriate.
  5. Optionally replace /var/www/html/logo.png and add /var/www/html/theme.json; see Branding for the supported files and display guidance.
  6. Validate Apache and the Shibboleth SP, make sure the cron service is running, and reload Apache. Restart shibd only if you changed its configuration.

For a standard systemd host, verify the required modules and configurations, start the package-installed cron service, and reload Apache:

# Debian or Ubuntu
apache2ctl -M | grep -E 'ssl|rewrite|shib|wsgi'
apache2ctl configtest
shibd -t
systemctl enable --now cron
systemctl reload apache2

# Rocky Linux or RHEL
httpd -M | grep -E 'ssl|rewrite|shib|wsgi'
httpd -t
shibd -t
systemctl enable --now crond
systemctl reload httpd

On Debian/Ubuntu, the WSGI dependency enables its module during package installation. If a host-owned module is missing, enable it with a2enmod rewrite shib ssl before repeating validation. Use the equivalent service commands on a host that does not use systemd.

Step 3: Install the Shibboleth IdP plugin

Use the signed plugin tarball in idp_plugin/ as the input to the Shibboleth plugin installer, and keep its matching .asc signature in the same directory. The installer rebuilds the IdP WAR, so stop the IdP or its container first and run the command from the IdP home directory. This example assumes /opt/shibboleth-idp.

cd /opt/shibboleth-idp
bin/plugin.sh \
  -i /path/to/shibboleth-idp-plugin-passkey-<version>.tar.gz

On first installation, verify the displayed signing key before accepting the trust prompt. Use --noPrompt only when that signing key is already trusted by this IdP. Confirm the installed plugin before configuration:

bin/plugin.sh -l

After installation, update the following files on the IdP:

  • conf/authn/passkey.properties
  • conf/authn/passkey-database.properties
  • conf/authn/authn.properties
  • Your live MFA configuration, using conf/authn/mfa-authn-config.xml.passkey as the merge source
idp.authn.passkey.relyingPartyId=example.edu
idp.authn.passkey.relyingPartyOrigin=https://idp.example.edu
idp.authn.passkey.relyingPartyName=Example University IdP
idp.authn.passkey.usernameless=true
idp.authn.passkey.2fa=true
idp.authn.passkey.2fa.factorsAllowed=authn/Password

Set idp.authn.passkey.2fa=true to permit passkeys to run as a second factor after Password. This does not prevent passkeys from also being used as a primary factor; primary-factor use is controlled by the OmniPasskey default and per-device MFA rules. If a device type is marked MFA only, this setting must be true and the first factor must be listed in idp.authn.passkey.2fa.factorsAllowed, or that device type cannot authenticate.

Enable the MFA flow in conf/authn/authn.properties if your deployment will use the passkey flow through MFA:

idp.authn.flows = MFA

If passkeys are exposed through authn/MFA, replace the stock idp.authn.MFA.supportedPrincipals sample with principals that match the branches your MFA map can really satisfy. The supported principals for each flow must accurately describe the assurance it provides, and the top-level MFA list must cover the principals produced by its configured branches. Configure the SAML 2.0 and SAML 1.1 passkey classrefs consistently wherever the passkey branch is advertised or requested.

Start the IdP after configuration and confirm that it initializes without plugin, datasource, or MFA-flow errors.

Step 4: Optional inline registration

If you want users to be prompted for passkey registration immediately after a successful password-based login, enable inline registration in the IdP plugin.

idp.authn.passkey.inlineRegistration.enabled=true
idp.authn.passkey.inlineRegistration.backendUrl=https://app.example.edu/registration-server-backend/webauthn/idp/inline-registration
idp.authn.passkey.inlineRegistration.promptTargetPasskeys=1
idp.authn.passkey.inlineRegistration.promptIntervalDays=30

When this feature is enabled, allow only the IdP host or network to reach /registration-server-backend. Configure this in the <Location /registration-server-backend> block of omnipasskey.conf: replace Require all denied with a narrow Require ip or Require host rule for the IdP. The file is /etc/apache2/conf-available/omnipasskey.conf on Debian/Ubuntu, /etc/httpd/conf.d/omnipasskey.conf on Rocky/RHEL, or app/container_files/httpd/omnipasskey.conf in the container source.

Open the admin Manage Settings page at https://app.example.edu/passkey/admin/settings. In Registration Allowed Origins, include the IdP origin because inline registration runs on the IdP page, and keep the management application origin for self-service registration. Enter one origin per line and save the setting:

https://app.example.edu
https://idp.example.edu

Use your actual HTTPS origins, including any nonstandard port, and match the IdP entry to idp.authn.passkey.relyingPartyOrigin. Each hostname must equal the relying-party ID or be its subdomain. See Inline registration controls for the complete setup steps.

Step 5: Validate the deployment

Open https://app.example.edu/passkey/account/credentials and confirm the OmniPasskey interface loads successfully.
From My Account, select New Passkey, complete Register Passkey, and verify that the result appears in My Passkeys.
Test a Shibboleth sign-in that uses the new passkey.
Open the admin page at https://app.example.edu/passkey/admin/credentials and verify access is limited to the admin roles you assigned.
Open the helpdesk page at https://app.example.edu/passkey/helpdesk/credentials and verify access is limited to the helpdesk roles you assigned.
If inline registration is enabled, complete a password login and confirm the post-password prompt can create a passkey.
If Conditional Create is enabled, verify that a capable browser and its default password manager can create a passkey after a saved-password sign-in. Test the normal setup-button fallback as well, because unsupported, ineligible, declined, or timed-out automatic attempts return to it.
In My Passkeys, confirm a newly registered passkey reports Discoverable as Yes or No when the browser provides that information; some clients may show Not reported.
Review Registration Client Hints and Authentication Client Hints on the admin Manage Settings page if you want to guide browser passkey UI behavior.
OmniPasskey customer documentation. Copyright .