Download the PHP package schaefersoft/laravel-swiss-eid without Composer

On this page you can find all versions of the php package schaefersoft/laravel-swiss-eid. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package laravel-swiss-eid

Laravel Swiss eID

Tests PHPStan Total downloads Latest Version on Packagist

Laravel package for integrating the Swiss eID (swiyu) verification flow: builds the OpenID4VP 1.0 DCQL request, talks to a swiyu Verifier, persists the verification, handles the webhook callback and emits events. UI is intentionally left to you — the package only exposes data primitives (QR code SVG, deeplink, JSON status endpoint) so you can render in Blade, Livewire, Vue, React, or anything else.


5-Minute Quickstart

This package is a client for the swiyu Verifier — the Spring Boot service that speaks OpenID4VP to the wallet app. You need that verifier running before anything else works.

Step 1 — Start the verifier locally

The wallet app on the user's phone must be able to reach the verifier, so expose it through a public tunnel during local development:

Step 2 — Install the package

The installer publishes the config file and migration, prints all required .env variables, and optionally runs php artisan migrate.

Step 3 — Fill in .env

Step 4 — Start a verification and show the QR code

Step 5 — React to the result

Once the wallet has scanned the QR code the verifier fires the webhook. Listen to the event:

Or poll the status endpoint from the frontend:

Verify your setup

Validates all ENV variables, checks DID formats, and probes webhook reachability — in one pass.


Table of Contents

  1. Prerequisites
    • PHP & Laravel versions
    • A running swiyu Verifier
    • Public reachability & webhook routing
    • Verifier signing key (PEM)
    • Accepted issuer DIDs
    • Swiss wallet app (for testing)
  2. Installation
  3. Configuration
  4. Usage
    • Localising status labels
  5. Database
  6. Artisan Commands
    • swiss-eid:doctor
  7. Testing
  8. Troubleshooting
  9. Contributing
  10. License

Prerequisites

Before installing the package you need a few pieces of Swiss eID infrastructure in place. Each sub-section below covers exactly one requirement.

1. PHP & Laravel versions

Dependency Version
PHP 8.1+ (runtime); CI tests 8.2–8.5
Laravel 10, 11, 12, 13
Composer 2.x

The package itself runs on PHP 8.1+ (uses native enums, readonly DTOs and HasUuids). CI only tests against PHP 8.2+ because the Pest/PHPUnit dev toolchain no longer resolves on PHP 8.1. Consumers on PHP 8.1 can still install and use the package without issue.

2. A running swiyu Verifier

The Swiss eID ecosystem separates the relying party (your Laravel app) from the verifier service, a small Spring Boot process that speaks OpenID4VP to the wallet. This package is a client of that verifier — it does not implement the OpenID4VP protocol itself.

You need to run the official verifier locally (or host it somewhere) before this package can do anything:

A minimal docker-compose.yml typically looks like this (see the upstream repo for the full example):

Confirm the verifier is reachable:

3. Public reachability & webhook routing

The verification flow requires two network paths that both have to work:

  1. Wallet → Verifier: the swiyu wallet on the user's phone fetches the presentation request from the verifier's public URL. During local development, expose the verifier via a tunnel (e.g. ngrok, Cloudflare Tunnel):

    Put that tunnel URL into your Laravel .env as SWISS_EID_VERIFIER_URL. The verifier itself also needs to know its public URL in its own configuration so it can embed it in the QR-code deeplink.

  2. Verifier → Laravel webhook: when the wallet has responded, the verifier POSTs to your Laravel app. If both run in Docker, the verifier must be able to resolve your Laravel host. Two common options:

    • Run the verifier on the same Docker network as your Laravel app (e.g. the DDEV project network) and use the internal hostname in LARAVEL_WEBHOOK_URL, such as http://ddev-myproject-web/swiss-eid/webhook.
    • Or expose Laravel publicly too (another tunnel) and use that URL.

    The webhook is authenticated via a shared secret — the verifier sends X-Verifier-Api-Key: <key> and the package's middleware rejects everything else with 401.

4. Verifier signing key (PEM)

The verifier signs the presentation request it hands to the wallet. It expects an EC P-256 private key in PEM format. Generate one with OpenSSL:

When passing the private key through a .env file, keep the real newlines intact. Use a double-quoted multi-line value — not \n escape sequences, which the verifier will refuse to parse:

5. Accepted issuer DIDs

The Swiss eID beta trust infrastructure uses did:webvh: identifiers. did:tdw: is deprecated: there is no in-place migration, existing did:tdw DIDs are frozen for updates from ~October 2026, and re-onboarding with a new did:webvh DID via the swiyu Service Portal is required (see the onboarding cookbook). For a verification to succeed, the credential presented by the wallet must be issued by a DID that your verifier trusts. In practice you will list at least:

Example for .env (comma-separated):

If you do not know the issuer DID of a credential, you can extract it from a decoded SD-JWT (the iss field) or from the verifier logs when it rejects a presentation with issuer_not_accepted.

Alternatively (or additionally) configure trust anchors — every DID with a trust statement from the anchor is accepted, without listing each issuer. The verifier requires at least one of the two to be non-empty:

6. Swiss wallet app (for testing)

To actually scan a QR code and complete a verification end-to-end you need the swiyu wallet app installed on a phone:

For beta/integration testing against trust-infra.swiyu-int.admin.ch, the app must be in the corresponding environment. Check the official documentation at https://www.eid.admin.ch for environment-specific instructions.


Installation

Run the installer:

This publishes the config file, the migration, prints the required .env variables, and (optionally) runs php artisan migrate.

Manual alternative:


Configuration

All settings live in config/swiss-eid.php and can be overridden with environment variables:

Variable Default Description
SWISS_EID_VERIFIER_URL http://localhost:8083 Base URL of the swiyu verifier (must be reachable from Laravel).
SWISS_EID_TIMEOUT 10 HTTP timeout (seconds) for calls to the verifier.
SWISS_EID_WEBHOOK_PATH /swiss-eid/webhook Route the verifier POSTs to when a wallet has responded.
SWISS_EID_WEBHOOK_KEY_HEADER X-Verifier-Api-Key HTTP header carrying the shared webhook secret.
SWISS_EID_WEBHOOK_API_KEY – Shared secret; required — the middleware returns 401 without it.
SWISS_EID_RESPONSE_MODE direct_post.jwt Response mode for wallet responses. swiyu wallets enforce encrypted responses (direct_post.jwt) since 2026-08-17 (CD-004); direct_post only works against older test setups.
SWISS_EID_CREDENTIAL_TYPE – Credential type(s) (vct) to request from the wallet, comma-separated for multiple. Required — the Beta-ID needs both betaid-sdjwt and urn:vct:ch.admin.bcs.betaid.
SWISS_EID_ACCEPTED_ISSUERS – Comma-separated list of accepted issuer DIDs. At least one is required.
SWISS_EID_VERIFICATION_TTL 300 Seconds a pending verification stays valid before being marked expired.
SWISS_EID_POLLING_ENABLED true Enable the built-in /swiss-eid/status/{id} JSON endpoint.
SWISS_EID_POLLING_PATH /swiss-eid/status Route prefix of the polling endpoint.
SWISS_EID_POLLING_RATE_LIMIT 60,1 Throttle for the polling endpoint (max_attempts,decay_minutes).
SWISS_EID_AUTH_ENABLED false Enable OAuth2 client-credentials auth against the verifier's management API.
SWISS_EID_TOKEN_URL – OAuth2 token endpoint (only if auth is enabled).
SWISS_EID_CLIENT_ID – OAuth2 client ID (only if auth is enabled).
SWISS_EID_CLIENT_SECRET – OAuth2 client secret (only if auth is enabled).
SWISS_EID_TABLE_NAME eid_verifications Override the DB table name if it clashes with your schema.
SWISS_EID_USER_ID_TYPE int Column type for user_id: int (unsignedBigInteger), uuid, or string. Set before running the migration.

Usage

Starting a verification

Use the SwissEid facade's fluent builder. Each call returns the same manager instance, so you can chain freely. create() persists the record and returns a PendingVerification DTO.

Every SwissEid::verify() call returns a new VerificationRequest, just like Http:: returns a new pending request. Builder methods called directly on the facade (e.g. SwissEid::ageOver18()) start a new request as well, so no state leaks between requests, jobs or Octane workers.

The returned $pending has:

Property / method Description
$pending->id Internal UUID of the DB record. Use this to look it up later.
$pending->verifierId ID assigned by the swiyu verifier.
$pending->deeplink Universal link — open on the user's phone to launch the wallet.
$pending->verificationUrl Full URL of the presentation request (for debugging).
$pending->qrCode(int $size = 300) SVG string. Embed with {!! !!}.
$pending->qrCodeDataUri(int $size = 300) data:image/svg+xml;base64,... for <img src>.
$pending->statusUrl() URL of the JSON polling endpoint for this verification.
$pending->expiresAt Carbon instance — TTL cutoff.
$pending->isExpired() Quick boolean check.

Requesting specific fields

Use the CredentialField enum (preferred) or plain field-name strings:

Available cases: AgeOver18, AgeOver16, GivenName, FamilyName, DateOfBirth (resolves to the JSON key birth_date), Nationality, PlaceOfBirth, Gender.

You can also pass a single field by name, dotted path or legacy JSONPath. All forms are normalised to a DCQL claim path:

Overriding credential type / accepted issuers per request

Verification purpose (vqPS)

The productive e-ID requires verifiers to register their data queries and purpose at the trust infrastructure. Set it globally via the verification_purpose config key or per request:

Presenting to the user

The package is UI-agnostic. Render the primitives in any frontend:

The polling endpoint returns JSON:

label is resolved via Laravel's translation system and automatically respects App::getLocale(). See Localising status labels below for details.

Poll every 2–3 seconds until is_terminal === true. If the TTL has passed, the endpoint will also flip pending → expired on the first call after expiry and dispatch the VerificationExpired event.

Localising status labels

The polling endpoint's label field and VerificationState::label() are driven by Laravel's translation system under the swiss-eid::states namespace. The package ships translations for German (de), French (fr), Italian (it), and English (en).

The active locale (App::getLocale()) is used automatically — no configuration needed. If the locale falls through to a string that does not exist in the package's files (e.g. es), Laravel's fallback locale applies next, and then the translation key itself is returned as-is.

Customising or adding languages

Publish the translation files once:

This copies the four files to lang/vendor/swiss-eid/{de,en,fr,it}/states.php. Published files take priority over the package originals. Edit them freely:

To support an additional language, add a new locale directory alongside the existing ones — e.g. lang/vendor/swiss-eid/es/states.php with Spanish labels.

Using label() directly

VerificationState::label() resolves the same translation keys, so it works anywhere — not just in the polling JSON:


Handling the webhook

The webhook route (POST /swiss-eid/webhook by default) is registered automatically by the package's service provider. It:

  1. Validates the X-Verifier-Api-Key header via middleware.
  2. Reads the verification_id from the payload.
  3. Calls the verifier's GET endpoint to fetch the full result.
  4. Writes state, credential_data (encrypted at rest) and webhook_received_at to the DB.
  5. Dispatches VerificationCompleted or VerificationFailed.

You do not register this route yourself. Just make sure:

Retrieving results

VerificationResult methods:

Method Returns
isSuccessful() / isFailed() / isPending() bool
get(string $field, mixed $default = null) Field from credential data.
has(string $field) bool
isAdult() Shortcut for age_over_18 === true.
toArray() Plain array (for JSON responses).

If the ID is not found a VerificationNotFoundException is thrown.

Refreshing without the webhook

getVerification() only reads your database. If the webhook cannot reach your app (local development, firewall, lost retry), pull the state from the verifier instead:

refresh() updates the record and dispatches the same events as the webhook. Terminal verifications are returned without contacting the verifier. To refresh all pending verifications at once, run or schedule php artisan swiss-eid:refresh.

Events

Listen in your EventServiceProvider or with #[AsEventListener]:

Each event carries a single $verification property of type EidVerification (the Eloquent model). Use its toResult() method if you want the DTO view.


Database

The package ships a single table (default name eid_verifications) with:

Column Type Notes
id UUID (PK) Your internal ID — the one you hand to the frontend.
verifier_id string The ID returned by the swiyu verifier.
user_id nullable Your user reference. Column type depends on SWISS_EID_USER_ID_TYPE (int default, uuid, or string).
state enum pending, success, failed, expired.
credential_type string Mirrors SWISS_EID_CREDENTIAL_TYPE.
requested_fields json The DCQL claims you requested.
credential_data encrypted json Decrypted automatically by the cast.
metadata json, nullable Anything you passed via ->metadata([...]).
deeplink, verification_url string Cached from the verifier response.
webhook_received_at datetime, nullable Set when the webhook fires.
expires_at datetime TTL cutoff.
created_at, updated_at datetime Standard.

Useful Eloquent scopes on EidVerification:

Override the table name with SWISS_EID_TABLE_NAME in .env. The migration and the model both read from config('swiss-eid.table_name'), so renaming is a single-line change.


Artisan Commands

Command Description
swiss-eid:install Publish config + migration, print required .env variables, optionally run migrations.
swiss-eid:test-connection Probe the verifier to confirm it is reachable and responding.
swiss-eid:cleanup --days=7 Delete expired records older than N days. Accepts --dry-run.
swiss-eid:expire Mark pending verifications past their TTL as expired and dispatch VerificationExpired. Schedule it so the event fires without relying on status polling.
swiss-eid:refresh {id?} Pull the current state of one or all pending verifications from the verifier. Fallback when the webhook does not reach your app.
swiss-eid:doctor Validate the full configuration, check DID formats, and probe the webhook URL.

Schedule the cleanup in App\Console\Kernel (or routes/console.php on Laravel 11+):

swiss-eid:doctor

A self-contained diagnostic that works through every aspect of the configuration in one pass and exits with a non-zero code if any check fails — useful in CI pipelines or as a post-deploy smoke test.

Checks performed

Section What is validated
Verifier SWISS_EID_VERIFIER_URL is a valid URL · timeout is a positive integer · SWISS_EID_RESPONSE_MODE is direct_post or direct_post.jwt
Webhook Path starts with / · header name is set · SWISS_EID_WEBHOOK_API_KEY is set and ≥ 32 characters (warns if shorter)
Credentials SWISS_EID_CREDENTIAL_TYPE (vct) is set · at least one accepted issuer is configured
OAuth2 Auth Skipped when SWISS_EID_AUTH_ENABLED=false; otherwise validates token URL, client ID and secret
General SWISS_EID_VERIFICATION_TTL is a positive integer (warns if < 60 s) · SWISS_EID_USER_ID_TYPE is one of int, uuid, string
DID Formats Each entry in SWISS_EID_ACCEPTED_ISSUERS matches did:[method]:[id]
Webhook Reachability POSTs to APP_URL + webhook.path without credentials: 401/403 → correctly protected; 404 → route not found; connection error → not publicly reachable

Exit codes

Code Meaning
0 All checks passed (warnings are informational only).
1 One or more errors found.

Example output


Testing

Use the built-in SwissEidFake to avoid real HTTP calls in your tests:

Run the package's own test suite:


Troubleshooting

Symptom Likely cause Fix
createVerificationManagementDto: Either acceptedIssuerDids or trustAnchors must be set SWISS_EID_ACCEPTED_ISSUERS is empty. Set at least one DID.
Empty deeplink / no QR code The verifier's response used different key casing (e.g. verification_deeplink). The package already falls back through several aliases; check storage/logs/laravel.log for the raw response if a new one appears.
Verifier returns 500 on /oid4vp/api/request-object/... PEM signing key is malformed (literal \n instead of real newlines). Use double-quoted multi-line .env value — see the signing key section.
Webhook returns 500, logs say getaddrinfo for db failed Verifier container cannot resolve your Laravel/DB host. Join the verifier to the same Docker network as your Laravel app; use the internal hostname in LARAVEL_WEBHOOK_URL.
Webhook never fires (404) Stale verification IDs in retry queue from a previous failed run. Create a fresh verification — the verifier drops stale webhook retries after a while.
State is always failed with issuer_not_accepted The credential's issuer DID is not in SWISS_EID_ACCEPTED_ISSUERS. Add the real issuer DID (extract from wallet logs / decoded SD-JWT).
Wallet shows "Kein passender Nachweis verfügbar" Requested field name does not match the credential schema (e.g. date_of_birth vs birth_date). Use the CredentialField enum — the package maps the correct keys.

Quick sanity checks:


Contributing

  1. Fork the repository.
  2. Create a feature branch: git checkout -b feature/my-feature.
  3. Run composer test and composer analyse — both must pass.
  4. Open a Pull Request. Conventional-commit messages are appreciated; they drive the automated release workflow.

License

MIT. See LICENSE for details.


All versions of laravel-swiss-eid with dependencies

PHP Build Version
Package Version
Requires php Version ^8.1
chillerlan/php-qrcode Version ^5.0
guzzlehttp/guzzle Version ^7.15.2
illuminate/database Version ^10.0|^11.0|^12.0|^13.0
illuminate/events Version ^10.0|^11.0|^12.0|^13.0
illuminate/http Version ^10.0|^11.0|^12.0|^13.0
illuminate/support Version ^10.0|^11.0|^12.0|^13.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package schaefersoft/laravel-swiss-eid contains the following files

Loading the files please wait ...