Download the PHP package do-it-s/easy-auth without Composer

On this page you can find all versions of the php package do-it-s/easy-auth. 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 easy-auth

easy-auth

English | 日本語

A Laravel package providing passwordless authentication via passkeys (WebAuthn) bound to device UUIDs, plus an invitation-chain tenant/role model. It wraps laravel/passkeys and adds an email+password fallback for devices without passkey support, along with per-tenant invitations and backup codes.

⚠️ Beta Notice

This package is registered on Packagist but is still in beta (0.x, pre-1.0). The author is a web application developer, not an authentication or security specialist, and some design decisions still have open questions (see "Known Limitations and Future Considerations" below). The goal is to reach 1.0 once these and other known issues are addressed. Feedback from anyone with authentication or security expertise is very welcome.

Requirements

Installation

1. Add the dependency via Composer

To pick up updates once easy-auth itself has been updated, run:

If this fails with a dependency conflict mentioning brick/math, it's not a problem with easy-auth itself: a fresh laravel/laravel project locks brick/math to 0.18.x, but spomky-labs/cbor-php (pulled in transitively via laravel/passkeysweb-auth/webauthn-lib) only allows brick/math ^0.17 as of its latest tagged release, so Composer's default partial update refuses to touch it. Retrying with -W (--with-all-dependencies) resolves this safely by letting Composer downgrade brick/math to 0.17.xlaravel/framework itself already allows that range, so nothing else breaks:

This gap is expected to close once cbor-php's upstream support for brick/math ^0.18 (already in progress as of this writing) is tagged and released.

2. Publish laravel/passkeys migrations

laravel/passkeys does not auto-load the migration for the passkeys table (since the type of users.id depends on the host app, it uses a publish-based approach so the app can edit it).

3. Wire up the User model

EasyAuthUser extends laravel/passkeys's PasskeyUser, and IsEasyAuthUser uses PasskeyAuthenticatable, so the app only needs this one interface plus one trait (no need to write PasskeyUser/PasskeyAuthenticatable separately).

4. Provide resources/views/layouts/app.blade.php

Every view in this package does @extends('layouts.app'). At minimum, the app's layout must satisfy:

Element Reason
@yield('content') Where each view's @section('content') is injected
@stack('scripts') device/reset.blade.php and others @push('scripts') here
<meta name="csrf-token" content="{{ csrf_token() }}"> Used by the JS side's fetch calls for X-CSRF-TOKEN

Header navigation, branding, and the choice of UI framework (Alpine.js, etc.) are otherwise entirely up to the app. If you build a tenant-switcher UI in the header using helpers like $user->currentTenant(), $user->tenants, $tenant->isAdministeredBy(), $tenant->hasUsableBackupCode(), reference this package's model namespace, e.g. @can('create', [\DoITs\EasyAuth\Models\Invitation::class, $tenant]).

5. Provide a home route

After sign-in, registration, invitation acceptance, tenant switching, password change, etc. complete, this package redirects via redirect()->route('home'). This package does not define a route named home itself, so the app must provide one.

This home screen must correctly handle the case where the signed-in user does not yet belong to any tenant ($user->currentTenant() returns null). This "no tenant" state is a normal, expected condition — for example, right after registering with no invitation yet accepted, or right after leaving the only tenant the user belonged to. This package is designed to allow such users to sign in, create a tenant, or accept an invitation without issue, and currently has no mechanism to automatically delete or clean up tenant-less users (a possible future consideration). The home screen should branch on the "no tenant" state as an expected case.

6. Run migrations

7. Frontend (JS)

The passkey registration/sign-in/password-fallback JS in resources/js/ is packaged as @do-it-s/easy-auth-js and ships pre-bundled (resources/js/dist/easy-auth.js — a single ESM file with no unresolved imports, so no separate npm install step is needed on the package side). Add it to the app's package.json, pointing at the copy that composer require already placed in vendor/:

In the app's resources/js/app.js:

(The app's own JS initialization — Alpine.js, etc. — can happen freely before or after this. easy-auth-js has no dependency on Alpine.)

If a file: dependency causes a symlink permission error on Windows, add install-links=true to .npmrc to switch to a copy-based install instead.

Sign-in (canAttemptSignIn / attemptSignIn)

On the principle that a WebAuthn ceremony should only be attempted in response to an explicit user action, this package does not attempt sign-in automatically on page load. Instead, it exposes two functions so the host app's own guest home page (e.g. the top page) can offer a "Sign in" UI. Deciding whether to show/hide that UI, and where to navigate after the attempt resolves, are entirely the app's responsibility.

Registration and password sign-in (primitives / initEasyAuth)

registerPasskey / registerWithPassword / signInWithPassword are primitives that never touch the DOM; they return either { outcome: 'success', redirect } or { outcome: 'failure', code, errors? }. code is one of name_required / ceremony_failed / validation / server_error / network_error (only validation includes errors, with per-field messages already translated on the Laravel side). If you want to build a fully custom form/UI on the app side, call these directly.

initEasyAuth() is a convenience function that wires up only the known form IDs found in this package's own default views (profile/create.blade.php, auth/sign-in.blade.php, etc.). It bundles the primitives above together with default-view-specific UI conveniences, such as automatically switching to the password form when passkeys aren't supported. Apps using their own custom views simply don't call this function, and this wiring/UI convenience is naturally disabled.

By default, on failure it writes to the status elements (#passkey-status, #sign-in-status) present in this package's own views via textContent. To swap in your own UI (e.g. toast notifications), pass onStatus (the form wiring and WebAuthn ceremony calls themselves stay handled by initEasyAuth() — only the display is replaced):

message is this package's pre-translated copy (for server validation, the joined errors). code is the breakdown for outcome: 'failure', usable for finer-grained branching if needed.

Device credentials (getDeviceCredentials / clearDeviceCredentials)

Use these instead of reading/writing the two device_uuid/auth_method localStorage keys directly.

8. Confirm the exception handler can return JSON

This package's /login, /profile-password, and similar routes are not under api/* — they return JSON via normal content negotiation triggered by the Accept: application/json header (the default behavior of Request::expectsJson()).

If bootstrap/app.php contains something like the following, routes outside api/* are always forced to an HTML response, and things like ValidationException come back as a 302 redirect (HTML) instead. The JS side then fails trying to response.json() that <!DOCTYPE ... with Unexpected token '<' ... is not valid JSON:

This sometimes ships in Laravel's default project skeleton, but for an app without a routes/api.php, it's effectively a constraint that prevents any route from ever being JSON-ified. When adopting easy-auth, either remove this callback (revert to an empty withExceptions) or rewrite it as $request->is('api/*') || $request->expectsJson().

Customizing views

Views provided by easy-auth guarantee the entire auth flow works out of the box with zero app-side setup, while still being customizable via four mechanisms:

1. Whole-page or per-component overrides (vendor:publish)

This copies the full set of views into resources/views/vendor/easy-auth/; from then on, the copied files take priority (standard Laravel view-namespace resolution). resources/views/ is structured in two layers: "one screen = one page" (e.g. auth/sign-in.blade.php), and the "feature components" that hold the actual forms etc. (under resources/views/components/, e.g. components/auth/sign-in-form.blade.php). If you only want to change a page's look, you can copy just the page and keep using the original component via <x-easy-auth::auth.sign-in-form />, or you can replace the component entirely.

The success-status message (the green session('status') text) shown by the sign-in, password-reset-link, profile-edit, and organization-edit forms is factored out of each form into a small shared component, components/shared/status-message.blade.php. If the app wants to remove just this message (e.g. to consolidate on toast notifications), there's no need to copy and edit the whole form — simply create resources/views/vendor/easy-auth/components/shared/status-message.blade.php as an empty file.

2. Adding fields to existing forms (@stack)

Major form components have an injection point right before the submit button, @stack('easy-auth::components.{domain}.{name}.after-fields') (e.g. for the tenants/edit form, it's easy-auth::components.tenants.edit-form.after-fields). The app can add fields without copying the page, just by @pushing from any view.

List views (tenants/members/index, tenants/invitations/index) instead expose a loop-friendly injection point per row, via @includeIf('vendor.easy-auth.tenants.member-row-actions', [...]). By default nothing is rendered; once the app creates resources/views/vendor/easy-auth/tenants/member-row-actions.blade.php (the variables passed differ per component — check each component's source), it can add its own buttons etc. to each row.

3. Before/after hooks for mutating operations (Laravel events)

Major mutating operations — tenant updates, invitation creation, member removal, etc. — fire events under DoITs\EasyAuth\Events\ before and after (e.g. TenantUpdating/TenantUpdated, InvitationCreating/InvitationCreated). The *ing events carry the raw Request (or source data) rather than a validated array, so a listener can read its own fields and save them directly, even for columns not included in fillable.

This package fires the following 25 events. The {Model}{Verb}ing/{Model}{Verb}ed naming mirrors Eloquent's Creating/Created pattern and covers every mutating operation (in-house use of listeners is currently limited, but the full set was laid out proactively so the app can hook into any operation in the future).

Trigger Fired from *ing (before validation) *ed (after save)
Registration (password) Auth\RegisterController - UserRegistered (context: 'password')
Registration (passkey) ProfileController - UserRegistered (context: 'passkey')
Profile edit ProfileController ProfileUpdating ProfileUpdated
Account deletion (self, while signed in) ProfileController AccountDeleting AccountDeleted
Account deletion (self-service, from device mismatch) Auth\AccountDeletionController AccountDeleting AccountDeleted
Password reset Auth\PasswordResetController PasswordResetting PasswordResetCompleted
Backup code issuance BackupCodeController BackupCodeIssuing BackupCodeIssued
Invitation creation InvitationController InvitationCreating InvitationCreated
Invitation revocation InvitationController InvitationRevoking InvitationRevoked
Invitation redemption (join) InvitationRedemptionController InvitationRedeeming InvitationRedeemed
Organization creation TenantController TenantCreating TenantCreated
Organization edit TenantController TenantUpdating TenantUpdated
Organization deletion TenantController TenantDeleting TenantDeleted
Member self-removal TenantLeaveController TenantMemberRemoving TenantMemberRemoved
Member removal (by admin) TenantMemberController TenantMemberRemoving TenantMemberRemoved
Member role change TenantMemberController TenantMemberRoleUpdating TenantMemberRoleUpdated

4. Recomposing feature components

Components under resources/views/components/ are self-contained and not tied to how the package's default pages combine them. For example, profile/create provides passkey registration (passkey-registration-form) and the password-registration fallback (password-registration-form) as independent components, so the app is free to place both on the same screen, split them into separate routes, or use only one.

Customizing routes

The four mechanisms above replace the contents of existing routes; the routes themselves (URL, HTTP method, whether to use them at all) are out of scope. If the app wants to change a URL, or drop a specific feature's route entirely (e.g. the password-registration fallback), call EasyAuth::ignoreRoutes() from AppServiceProvider::register().

Once called, this package registers no routes/web.php at all. From then on, the app writes its own routes in its own routes/web.php, pointing at this package's controllers (DoITs\EasyAuth\Http\Controllers\...) with whatever URLs and middleware it wants (copying this package's own routes/web.php as a starting point is the fastest way). Simply omit routes for any feature you don't want.

Customizing translated copy

All of this package's view and email copy goes through translation keys under the easy-auth:: namespace (lang/en, lang/ja). Thanks to Laravel's standard mechanism, placing a same-named file under lang/vendor/easy-auth/{locale}/ automatically overrides it (no code changes or extra configuration needed). To get a starting template, copy it with:

Audit Log

Every authentication attempt (password and passkey sign-in, sign-out, device mismatches) and every mutating operation from the events table above writes one structured entry to a dedicated log channel, so "who did what, when, with what result" can be reconstructed from disk after the fact — this is what the events table's *ed events feed into internally (DoITs\EasyAuth\Listeners\AuditLogSubscriber), plus Illuminate\Auth\Events\Login/Failed/Logout and Laravel\Passkeys\Events\PasskeyRegistered/PasskeyVerified/PasskeyDeleted for authentication itself.

The channel (config('easy-auth.audit_log_channel'), default easy-auth-audit) is registered automatically with a daily-rotating driver — storage/logs/easy-auth-audit-*.log — retained for config('easy-auth.audit_log_retention_days') days (default 30). There is no in-app viewer; read it directly (tail -f storage/logs/easy-auth-audit-*.log, grep, etc.), or define a channel of the same name in the app's own logging.php to route entries elsewhere (Papertrail, a database sink, whatever the app already uses) instead.

Each line is one JSON-ish log entry with action (e.g. tenant_member.removed, auth.login, auth.failed), outcome (success/failure), actor/target ({id, name}, when known), tenant, ip, user_agent, and device_uuid. Entries never include credentials or an attempted-but-wrong email address — auth.failed (bad credentials) and auth.device_mismatch (a correct password from a device the account isn't bound to) record only the outcome and request metadata, the same "don't confirm whether an email exists" posture SignInController's generic sign_in_failed message already takes.

System Administrator

easy-auth can recognize a single system-administrator user, identified by ID rather than email (config('easy-auth.sysadmin_user_id'), .env's EASY_AUTH_SYSADMIN_USER_ID), since most easy-auth accounts are passkey-only and never set an email. It defaults to null (no system administrator), and only a single ID is supported — there is no current need for more than one at a time.

At this stage the package only provides the identification primitive, Contracts\EasyAuthUser::isSysAdmin(): bool (implemented in Concerns\IsEasyAuthUser); it does not yet grant this user any special access to tenants or package features on its own. Host apps can already use isSysAdmin() to gate their own app-specific administrative features (e.g. a global notification broadcast). Cross-tenant access (letting the system administrator view and act as a member/admin of any tenant without an actual membership row) is planned but not yet implemented.

Pagination

The tenant member list and invitation list query all matching rows and render them on a single page by default, unchanged from earlier versions. Each of the three sections it can be broken into — the member list's admin section, its non-admin ("other members") section, and the invitation list — has its own independent page-size setting: config('easy-auth.members_admins_per_page'), config('easy-auth.members_others_per_page'), and config('easy-auth.invitations_per_page') (.env's EASY_AUTH_MEMBERS_ADMINS_PER_PAGE, EASY_AUTH_MEMBERS_OTHERS_PER_PAGE, EASY_AUTH_INVITATIONS_PER_PAGE), each defaulting to null (no limit). Setting one to an integer turns on Laravel's standard pagination for that section, including its default links() markup in the bundled views. Apps that want their own pager UI instead of the bundled one can override the relevant view (see "Customizing views" above) and call ->links() (or their own component) on the LengthAwarePaginator themselves.

PWA (Home Screen) Sign-In

A device_uuid, stored in localStorage, is how easy-auth recognizes a returning device. On iOS/iPadOS, a home-screen "Add to Home Screen" PWA gets its own isolated localStorage separate from Safari's, even though it's the same origin — so a user who registered in Safari and then adds the app to their home screen lands on that PWA with no device_uuid at all, unable to sign back in.

config('easy-auth.pwa_resync_path') (.env's EASY_AUTH_PWA_RESYNC_PATH) registers a GET route at an app-chosen, unguessable path (a long random string — there is no package-provided default, since a value shipped in this public package's source could never be unguessable) that attempts a passkey ceremony and silently redirects to the app regardless of outcome. A successful ceremony re-stores the device's existing device_uuid into this browsing context's localStorage — no new device or passkey is created. Point the PWA's manifest.json at this path as its start_url, so opening the app from the home screen icon runs this recovery step automatically, once, before the visitor ever sees a guest screen. Left unset (the default), no route is registered and the feature is entirely inert.

This deliberately does not touch the existing device-UUID-mismatch handling (SignInController's forced logout + account-deletion email for a wrong device_uuid) — it only covers a missing one, and only for a browsing context that already proved, by reaching this unguessable URL, that it's allowed to ask. A password-fallback device has no passkey to re-verify with and is excluded; it just proceeds to the app as a guest.

Known Limitations and Future Considerations


All versions of easy-auth with dependencies

PHP Build Version
Package Version
Requires php Version ^8.3
endroid/qr-code Version 6.0.*
illuminate/support Version ^13.0
laravel/passkeys Version ^0.2.1
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 do-it-s/easy-auth contains the following files

Loading the files please wait ...