{"id":19794109,"url":"https://github.com/simplesamlphp/simplesamlphp-module-webauthn","last_synced_at":"2025-05-01T02:30:55.225Z","repository":{"id":43088127,"uuid":"209513962","full_name":"simplesamlphp/simplesamlphp-module-webauthn","owner":"simplesamlphp","description":"A module implementing FIDO2 / WebAuthn as a second authentication factor","archived":false,"fork":false,"pushed_at":"2025-04-17T08:22:00.000Z","size":2140,"stargazers_count":16,"open_issues_count":2,"forks_count":9,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-17T21:19:45.647Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-2.1","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/simplesamlphp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-09-19T09:27:03.000Z","updated_at":"2025-04-17T08:22:03.000Z","dependencies_parsed_at":"2024-07-16T02:03:25.505Z","dependency_job_id":"0cc43fbe-97fb-4d5b-ba9a-9f84e64c70a6","html_url":"https://github.com/simplesamlphp/simplesamlphp-module-webauthn","commit_stats":{"total_commits":287,"total_committers":11,"mean_commits":26.09090909090909,"dds":0.519163763066202,"last_synced_commit":"3f3337d353a83cfa31f4542b10827d4b48bcfae1"},"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simplesamlphp%2Fsimplesamlphp-module-webauthn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simplesamlphp%2Fsimplesamlphp-module-webauthn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simplesamlphp%2Fsimplesamlphp-module-webauthn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simplesamlphp%2Fsimplesamlphp-module-webauthn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simplesamlphp","download_url":"https://codeload.github.com/simplesamlphp/simplesamlphp-module-webauthn/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251812302,"owners_count":21647884,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T07:12:07.937Z","updated_at":"2025-05-01T02:30:55.217Z","avatar_url":"https://github.com/simplesamlphp.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WebAuthn as Second Factor module\n\n![Build Status](https://github.com/simplesamlphp/simplesamlphp-module-webauthn/actions/workflows/php.yml/badge.svg)\n[![Coverage Status](https://codecov.io/gh/simplesamlphp/simplesamlphp-module-webauthn/branch/master/graph/badge.svg)](https://codecov.io/gh/simplesamlphp/simplesamlphp-module-webauthn)\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/simplesamlphp/simplesamlphp-module-webauthn/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/simplesamlphp/simplesamlphp-module-webauthn/?branch=master)\n[![Type Coverage](https://shepherd.dev/github/simplesamlphp/simplesamlphp-module-webauthn/coverage.svg)](https://shepherd.dev/github/simplesamlphp/simplesamlphp-module-webauthn)\n[![Psalm Level](https://shepherd.dev/github/simplesamlphp/simplesamlphp-module-webauthn/level.svg)](https://shepherd.dev/github/simplesamlphp/simplesamlphp-module-webauthn)\n\n\u003c!-- {{TOC}} --\u003e\n\nThe module is implemented as an Authentication Processing Filter. That\nmeans it can be configured in the global config.php file or the SP remote or\nIdP hosted metadata.\n\n## Installation\n\nYou can install this module with composer:\n\n```bash\n% composer require simplesamlphp/simplesamlphp-module-webauthn\n```\n\nIf you are using PHP 7, you also need to install either the GMP extension (recommended) or the BCMath extension.\n\n## How to setup the webauthn module as a second-factor (an authprocfilter)\n\nYou need to enable the module's authprocfilter at a priority level\nso that it takes place AFTER the first-factor authentication. E.g. at 100 and\nif standalone registration and name2oid are used together, then the WebAuthn\nauth proc filter has to run after name2oid.\n\nThe authproc filter takes a number of optional parameter that steer which users\nwill be forced into 2FA.\n\n```php\n100 =\u003e [\n    'class' =\u003e 'webauthn:WebAuthn',\n\n    /* should FIDO2 be enabled by default for all users? If not, users need to\n     * be white-listed in the database - other users simply pass through the\n     * filter without being subjected to 2FA.\n     *\n     * defaults to \"disabled by default\" === false\n     */\n    'default_enable' =\u003e false,\n\n    /* only if default_enable is false:\n     * the toggle to turn on 2FA can either be a database lookup in the module's\n     * internal database or be dependent on the existence or absence of a\n     * user attribute as retrieved in the first-factor auth. The following\n     * options control which variant to use.\n     */\n\n    /*\n     * this parameter determines if the database will be used to check\n     * whether to trigger second factor authentication or use the \"attrib_toggle\" instead.\n     * Default value of this attribute is true\n     */\n    'use_database' =\u003e true,\n\n    /* this parameter is used only if \"use_database\" is false. If the value of\n     * \"force\" is true then we trigger WebAuthn only if \"attrib_toggle\" from the\n     * user is not empty. If the value of \"force\" is false then we switch the value of\n     * \"default_enable\" only if \"attrib_toggle\" from the user is not empty.\n     * Default falue is true.\n     */\n    'force' =\u003e true,\n\n    /* this parameter stores the name of the attribute that is sent with user and which\n     * determines whether to trigger WebAuthn.\n     * Default value is 'toggle'\n     */\n    'attrib_toggle' =\u003e 'toggle',\n\n    /**\n     * The module can be configured to assert that MFA was executed towards the\n     * SP by setting an appropriate \u003cAuthnContextClassRef\u003e tag in the response.\n     * The original SAML 2.0 spec in that regard contains only contexts which\n     * are rather useless in a FIDO2 context.\n     *\n     * FIDO alliance has its own to indicate that a FIDO key was used, and it\n     * is the default if unset. The semantics does not indicate then that an\n     * additional authentication besides the FIDO key was used (i.e. your\n     * first-factor authsource authentication). Thus, you may want to consider\n     * setting the more accurate REFEDS identifier below instead.\n     *\n     * Defaults to 'urn:rsa:names:tc:SAML:2.0:ac:classes:FIDO' if not set\n     *\n     * If you authenticate towards Microsoft 365 SPs which may trigger their\n     * own variant of 2FA, then you can tell them to skip this by\n     * - setting the SP tenant parameter \"supportsMFA\" to \"true\"\n     * - returning the AuthnContextClassRef\n     *   \"http://schemas.microsoft.com/claims/multipleauthn\"\n     */\n\n    // 'authncontextclassref' =\u003e 'https://refeds.org/profile/mfa',\n\n    /**\n     * Earlier versions of the Authproc filter required the second factor\n     * every time a new SP requested user auth, even if the user was already\n     * (first-factor) authenticated at the IdP.\n     * This can be seen as an inconvenience or a security feature, as it\n     * mitigates even unlikely scenarios such as a session cookie theft.\n     * \n     * This new option makes the behaviour configurable. \n     *\n     * If set to any negative number or unset, the old behaviour is maintained.\n     * \n     * If set to an amount of seconds (i.e. integer value), second-factor auth\n     * is requested only on initial authentication and if the last second-factor\n     * was more than this amount of seconds ago.\n     */\n     // 'secondfactormaxage' =\u003e -1,\n],\n```\n\nThen you need to copy config-templates/module_webauthn.php to your config directory\nand adjust settings accordingly. See the file for parameters description.\n\n## How to set up (pure) Passwordless authentication\n\nIn passwordless mode, the module provides an AuthSource, to be configured as\nusual in simpleSAMLphp's config/authsources.php\n\nUsers' FIDO2 Keys need to be registered with the \"Passwordless\" checkbox set -\nthis triggers the mandatory registration with a second factor intrinsic to the\nkey (fingerprint, face recognition, transaction PIN, etc. ).\n\nThis authsource takes little configuration because authentications happen before\nthe username is known - so no user-specific configuration is possible.\n\nThe authsource takes the following parameters in authsources.php:\n\n```php\n'name-your-source' =\u003e [\n    'webauthn:Passwordless',\n    /*\n     * Defaults to 'urn:rsa:names:tc:SAML:2.0:ac:classes:FIDO' if not set\n     *\n     * If you authenticate towards Microsoft 365 SPs which may trigger their\n     * own variant of 2FA, then you can tell them to skip this by\n     * - setting the SP tenant parameter \"supportsMFA\" to \"true\"\n     * - returning the AuthnContextClassRef\n     *   \"http://schemas.microsoft.com/claims/multipleauthn\"\n     */\n\n    // 'authncontextclassref' =\u003e 'https://refeds.org/profile/mfa',\n],\n```\n\n## How to set up simultaneous Passwordless and traditional two-factor\n\nIn this mode, the authentication prompt simultaneously allows for either\ntriggering a Passwordless auth, or to enter a username/password as traditional\nfirst-factor.\n\nThe configuration is almost identical to Passwordless above, but requires one\nextra required configuration parameter: the authsource that should be used to\nvalidate the username/password, if supplied by the user.\n\nThe authsource takes the following parameters in authsources.php:\n\n```php\n'name-your-source' =\u003e [\n    'webauthn:Supercharged',\n    'password_authsource' =\u003e 'whatever-authsource',\n    // 'authncontextclassref' =\u003e 'https://refeds.org/profile/mfa',\n\n],\n```\n\n## Using storage\n\nThe database schema sets itself up on first use automatically. The schema can be\nfound in the sources at src/WebAuthN/Store/Database.php (\\_\\_construct).\n\nIf you want to trim down permissions for the database user, here is the minimal\nset of required permissions:\n\n```sql\n\nGRANT SELECT,INSERT,UPDATE,DELETE ON ...credentials TO '...dbuser'@'1.2.3.4' IDENTIFIED BY '...dbpass';\n\n\nGRANT SELECT ON ...userstatus TO '...dbuser'@'1.2.3.4' IDENTIFIED BY '...dbpass';\n```\n\nThe `webauthn:Database` backend storage has the following options:\n\n`class`\n: Must be set to `webauthn:Database`.\n\n`database.dsn`\n: Data Source Name must comply to the syntax for the PHP PDO layer.\n\n`database.username`\n: Username for the database user to be used for the connection.\n\n`database.password`\n: Password for the database user used for the connection.\n\n`timeout`\n: The number of seconds to wait for a connection to the database server. This option is optional. If unset, it uses the default from the database-driver.\n\nExample config using PostgreSQL database:\n\n```php\n100 =\u003e [\n    'class' =\u003e 'webauthn:WebAuthn',\n    'store' =\u003e [\n        'webauthn:Database',\n        'database.dsn' =\u003e 'pgsql:host=sql.example.org;dbname=fido2',\n        'database.username' =\u003e 'simplesaml',\n        'database.password' =\u003e 'sdfsdf',\n    ],\n],\n```\n\nExample config using MySQL database:\n\n```php\n100 =\u003e [\n    'class' =\u003e 'webauthn:WebAuthn',\n    'store' =\u003e [\n        'webauthn:Database',\n        'database.dsn' =\u003e 'mysql:host=db.example.org;dbname=fido2',\n        'database.username' =\u003e 'simplesaml',\n        'database.password' =\u003e 'sdfsdf',\n    ],\n],\n```\n\n## Options\n\n### Options of auth proc filter\n\n`default_enable`\n: Should WebAuthn be enabled by default for all users? If not, users need to be white-listed in the database - other users simply pass through the filter without being subjected to 2FA. Defaults to \"disabled by default\" === false\n\n`force`\n: This parameter is used only if \"use_database\" is false. If the value of \"force\" is true then we trigger WebAuthn only if \"attrib_toggle\" from the user is not empty. If the value of \"force\" is false then we switch the value of \"default_enable\" only if \"attrib_toggle\" from the user is not empty. Default value is true.\n\n`attrib_toggle`\n: This parameter stores the name of the attribute that is sent with user and which determines whether to trigger WebAuthn. Default value is 'toggle'.\n\n`use_database`\n: This parameter determines if the database will be used to check whether to trigger second factor authentication or use the \"attrib_toggle\" instead. Default value of this attribute is true.\n\n### Options in `module_webauthn.php`\n\n`scope`\n: FIDO2 is phishing-resistent by binding generated credentials to a scope. Browsers will only invoke the registration/authentication if the scope matches the principal domain name the user is currently visiting. If not specified, the scope will be the hostname of the IdP as per its metadata. It is permissible to widen the scope up to the prinicpal domain though (e.g. authentication service is \"saml.example.com\" =\u003e scope can be extended to \"example.com\"; but not \"examp1e.com\". A registered FIDO2 token can then also be used on other servers in the same domain. If configuring this item, be sure that the authentication server name and the desired scope are a suffix match.\n\n`registration / use_inflow_registration`\n: Optional parameter which determines whether you will be able to register and manage tokens while authenticating or you want to use the standalone registration page for these purposes. If set to false =\u003e standalone registration page, if true =\u003e inflow registration. If this parameter is not explicitly set, the value is considered to be true.\n\n`registration / auth_source`\n: Optional parameter to define how the user authenticates to the dedicated registration page. Defaults to \"default-sp\"; ignored if inflow registration was configured.\n\n`registration / policy_2fa / minimum_certification_level`\n: Required parameter which specifies the required FIDO certification level for registration, allowed values:\n\n- `CERTIFICATION_NOT_REQUIRED` - attestation is not checked\n- `FIDO_CERTIFIED_L1` - FIDO L1 certified\n- `FIDO_CERTIFIED_L1plus` - FIDO L1+ certified\n- `FIDO_CERTIFIED_L2` - FIDO L2 certified\n- `FIDO_CERTIFIED_L3` - FIDO L3 certified\n- `FIDO_CERTIFIED_L3plus` - FIDO L3+ certified\n\n`registration / policy_2fa / aaguid_whitelist`\n: Optional list of AAGUIDs which are excluded from the minimum certification level check. Defaults to an empty list. Ignored when `minimum_certification_level` is set to `CERTIFICATION_NOT_REQUIRED`.\n\n`registration / policy_2fa / attestation_format_whitelist`\n: Optional list of attestation formats which are excluded from the minimum certification level check. Defaults to an empty list. Ignored when `minimum_certification_level` is set to `CERTIFICATION_NOT_REQUIRED`.\n\n`registration / policy_passwordless / minimum_certification_level`\n: Required parameter, same meaning as `registration / policy_2fa / minimum_certification_level` but for passwordless authentication\n\n`registration / policy_passwordless / aaguid_whitelist`\n: Optional parameter, same meaning as `registration / policy_2fa / aaguid_whitelist` but for passwordless authentication\n\n`registration / policy_passwordless / attestation_format_whitelist`\n: Optional parameter, same meaning as `registration / policy_2fa / attestation_format_whitelist` but for passwordless authentication\n\n## User Experience / Workflow\n\nUsers for which WebAuthn is enabled cannot continue without a FIDO2 token. The\nUI is different depending on the number of tokens the user has registered:\n\n- User has 0 tokens: UI requires the user to register a token. The user can\n  choose a convenient name for the token to recognise it later. If\n  request_tokenmodel is set, the name will be appended with the token model and\n  vendor.\n  After successful registration, the authprocfilter is done (user continues to\n  SP)\n- User has 1 token: UI requires the user to authenticate. After the\n  authentication, user can optionally enroll another token.\n- User has 2+ tokens: UI requires the user to authenticate. After the\n  authentication, user can optionally enroll another token or delete an obsolete\n  one.\n\nIf a user is enabled but has forgotten all of his tokens, the person would need\nto contact his administrator and have his account temporarily disabled for two-\nfactor authentication.\n\nAs long as a user account has 0 tokens there is no benefit yet; it's effectively\nstill single factor authentication because anyone with the user's password can\nregister any token. That is in the nature of things. It could be avoided with\nan out-of-band registration process (in the same scope).\n\nIf the standalone registration page is used, the user can't optionally enroll\nand manage tokens while logging in.\nThe standalone registration page can be found under `\u003cbasedir\u003emodule.php/webauthn/registration`,\nit requires authentication (with the auth source registration_auth_source) and\nafter that you are redirected to a page where you can manage tokens.\n\n## Device model detection\n\nThe option `registration / minimum_certification_level` can be used to get a\ntoken's so-called AAGUID which uniquely identifies the model and manufacturer\n(it is not a serial number). You can then make decisions about the acceptability\nof a certain authenticator model.\n\nMapping the AAGUID to a cleartext model and manufacturer name is done by having\n(or not) meta-information about the AAGUID. The FIDO Alliance operates a\nMetadata Service (MDS) which has a good number of AAGUIDs registered. However,\nmanufacturers are not required to submit their AAGUIDs and metadata to that MDS,\nand indeed, some manufacturers are missing.\n\nThe module contains a full list of AAGUIDs and relevant metadata as pulled from\nthe FIDO MDS. This list is in the `config/webauthn-aaguid.json` file, and this\nfile needs to be moved to your SimpleSAMLphp configuration directory.\n\nIf you want, you can also manually update this file, if you believe there might\nbe new models listed. In order to do that, run the `bin/updateMetadata.php`\nscript like this:\n\n```bash\n% php bin/updateMetadata.php \u003cblob file\u003e\n```\n\nwhere `\u003cblob file\u003e` is the metadata JWT blob you get from\n[here](https://mds3.fidoalliance.org).\n\nAs a consequence, depending on the token model the user uses, even if the AAGUID\nis being sent as part of the registration process, it may be that the device is\nstill stored as unknown model/unknown vendor.\n\nI contacted FIDO Alliance to ask about the lack of complete information in their\nMDS. Purportedly, listing in the MDS has chances of becoming mandatory in a\nfuture FIDO Certification. Until then, there is no good solution to the problem.\n\n## Disabling WebAuthn\n\nYou can disable the module entirely by not listing it as an authprocfilter.\n\nYou can disable the module by default by setting default_enable = false. You can\nthen enable WebAuthn second-factor authentication for individual users by adding\nthem with status \"FIDO2Enabled\" to the `userstatus` table or if you don't want to\nuse the `userstatus` table, you can send an attribute whose name is stored in\n`attrib_toggle` for this.\n\nIf the module is enabled by default, you can selectively disable WebAuthn\nsecond-factor authentication by adding the username with status FIDO2Disabled to\nthe `userstatus` table or if you don't want to use the `userstatus` table, you\ncan send an attribute whose name is stored in `attrib_toggle` for this.\n\n## Limitations / Design Decisions\n\nThis implementation does not validate token bindings, if sent by the\nauthenticator (§7.1 Step 7 / §7.2 Step 11 skip token binding information\nvalidation if present). That is because Yubikeys do not support token binding\nand the corresponding functionality thus has no test case.\n\nBoth User Present and User Verified variants are considered sufficient to\nauthenticate successfully in second-factor scenarios (§7.1 steps 11 and 12 are\njoined into one condition).\nThe module logs into the credential database which of the two was used during\nregistration time and does not allow downgrades during authentication time.\nPasswordless authentication always requires User Verified during registration\nand authentication transactions.\n\nThe implementation requests and supports ECDSA and RSA keys (algorithms -7,\n-257).\n\nThe implementation does not request any client extensions. The specification\ngives implementations a policy choice on what to do if a client sends extensions\nanyway: this implementation chose to then fail the registration/authentication.\n\nThe implementation supports the attestation formats\n\n- \"none\" (No Attestation)\n- \"packed / x5c\" (Packed Attestation, X.509 certificate)\n- \"packed / self\" (Packed Attestation, Self-Attestation)\n- \"fido-u2f\" (FIDO U2F Attestation)\n- \"apple\" (Apple Anonymous Attestation)\n- \"android-key\" (Android key attestation with Keymaster 4, for Android 9 and up)\n  Other attestation formats lead to a registration failure.\n\nFor the attation type \"packed / x5c\",\n\n- the optional OCSP checks are not performed (this is explicitly permitted in\n  the spec due to other means of revocation checking in the FIDO MDS).\n\nFor both \"packed / x5c\" and \"fido-u2f\":\n\n- all attestations are classified as \"Basic\" (i.e. no \"AttCA\" level); i.e.\n  validation as per §7.1 Step 18 is not executed.\n- Regarding §7.1 Step 21: When minimum certification levels are configured,\n  \"Self\" and \"Basic\" attestation levels are considered acceptable; \"None\" is\n  not acceptable.\n\nIf the implementation detects signs of physical object cloning (not incremented\nsignature counter), it follows the policy of failing authentication.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimplesamlphp%2Fsimplesamlphp-module-webauthn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimplesamlphp%2Fsimplesamlphp-module-webauthn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimplesamlphp%2Fsimplesamlphp-module-webauthn/lists"}