diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c90b128 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 + +[*.{yml,yaml,json}] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a3eb060 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,38 @@ +# Changelog + +All notable changes to `laranail/authkit` are documented here. + +The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project +adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Changed + +- **Breaking.** Renamed from `laranail/auth-kit` to + `laranail/authkit`, and the namespace moved to `Simtabi\Laranail\AuthKit\`. The family now shares one root + namespace with each sibling as a segment under it. +- **Breaking.** Every public name is vendor-scoped. Laravel keeps these in flat global maps, where + a second package claiming the same key silently replaces the first: + +| Surface | Before | After | +|---|---|---| +| Config key | `auth-kit` | `laranail.authkit` | +| Config file | `config/auth-kit.php` | `config/laranail/authkit.php` | +| Publish tags | `auth-kit-config`, … | `laranail::authkit-*` | +| Env prefix | `AUTH_KIT_*` | `AUTHKIT_*` | + + +### Added + +- A `NamingConventionTest` that asserts the public names against the **live registries** on a booted + application, rather than the provider source, so the guard survives a refactor. + + +### Fixed + +- The user-model exception named the old package. + +### Removed + +- `composer.lock` is no longer tracked. A library's lock records a resolution consumers never use. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..988002a --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,10 @@ +# Code of conduct + +This project follows the [Contributor Covenant](https://www.contributor-covenant.org/version/2/1/code_of_conduct/), +version 2.1. + +In short: be respectful, assume good faith, and focus on what is best for the project and the +people using it. Harassment of any kind is unacceptable. + +Report unacceptable behaviour to **opensource@simtabi.com**. Reports are handled confidentially, +and the maintainers will respond in a way proportionate to the circumstances. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..dbf7225 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# Contributing + +Thanks for helping improve `laranail/authkit`. + +## Getting set up + +`laranail/*` packages resolve through git rather than Packagist, so the repositories block in +`composer.json` is what makes `composer install` work. Then: + +```bash +composer install +composer test +``` + +## What a change needs + +- **Tests.** Every behavioural change carries one; a bug fix carries a test that fails before it. +- **Live-registry assertions for public names.** Anything registered into a Laravel registry — + config key, publish tag, view or translation namespace, command name, middleware alias — is + asserted against the booted application, never by grepping the provider. See + `tests/Feature/NamingConventionTest.php`. Grepping proves how the registration was written; it + does not prove what the framework ended up holding. +- **Style.** `composer format` runs Pint. CI checks it. +- **A CHANGELOG entry** under `## [Unreleased]`. + +## Naming rules that are not negotiable + +Laravel keeps these in flat, global maps. A second package claiming the same key does not collide +loudly — it silently replaces the first, and the damage surfaces far away as a missing view, a +missing translation, or a security control attached to nothing. + +| Surface | Shape | +|---|---| +| Config key | `laranail.authkit` | +| Config file | `config/laranail/authkit.php` | +| Publish tag | `laranail::authkit-` | +| View namespace | `laranail-authkit::` | +| Translation namespace | `laranail-authkit::` | +| Artisan command | `laranail::authkit.` | +| Middleware alias | `laranail-authkit` | + +The separators differ because each registry parses its key differently, and that is forced rather +than stylistic. Commands use `::` because Symfony resolves an exact name before splitting on `:`. +Middleware aliases must not, because Laravel does `explode(':', $name, 2)` to take parameters. +Blade prefixes must not, because the tag already spends `::` between prefix and component. + +**No bare short aliases.** A `authkit:install` +alias hands back exactly the collision the namespaced name exists to prevent. + +## Verify against a real application + +The package test suite runs in an isolated Testbench app, which is not the same as a real one. A +change to routing, publishing, the installer, or anything that interacts with Fortify should also +be checked in the demo application at `laranail/demos/authkit`, which installs both packages the way +a consumer does. + +## Security + +Do not open a public issue for a security problem. See [SECURITY.md](SECURITY.md). diff --git a/README.md b/README.md index f6ec93e..8d9649c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # laranail/authkit +[![Packagist Version](https://img.shields.io/packagist/v/laranail/authkit.svg?style=flat-square)](https://packagist.org/packages/laranail/authkit) +[![Tests](https://img.shields.io/github/actions/workflow/status/laranail/authkit/tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/laranail/authkit/actions/workflows/tests.yml) +[![Static analysis](https://img.shields.io/github/actions/workflow/status/laranail/authkit/static.yml?branch=main&label=static%20analysis&style=flat-square)](https://github.com/laranail/authkit/actions/workflows/static.yml) +[![License MIT](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE) + Headless authentication for Laravel 13+. No views, routes, or controllers. > [!WARNING] @@ -14,29 +19,56 @@ Headless authentication for Laravel 13+. No views, routes, or controllers. PHP 8.4+ / Laravel 13.x -## Installation +## Install + +`laranail/*` packages resolve through git, not Packagist. Add the repositories block to your +application's `composer.json` — Composer ignores a dependency's own `repositories`, so it must list +the whole transitive closure: + +```json +"repositories": [ + { "type": "vcs", "url": "https://github.com/laranail/authkit.git" }, + { "type": "vcs", "url": "https://github.com/laranail/console.git" }, + { "type": "vcs", "url": "https://github.com/laranail/enumerator.git" }, + { "type": "vcs", "url": "https://github.com/laranail/package-tools.git" }, + { "type": "vcs", "url": "https://github.com/laranail/captcha.git" }, + { "type": "vcs", "url": "https://github.com/laranail/db-tools.git" } +] +``` + +Then: ```bash composer require laranail/authkit php artisan vendor:publish --tag=laranail::authkit-config ``` -For a ready-made Blade UI, install `laranail/authkit-preset` instead. +See [installation](docs/installation.md) for the full walkthrough. + +## Documentation + +Full documentation: + +### Guides -## Documentation +- [Installation](docs/installation.md) — requirements, the repositories block, publishing config +- [Getting started](docs/getting-started.md) — authenticate a user with the core alone +- [Configuration](docs/configuration.md) — guard, rate limits, Fortify features, social credentials +- [Architecture](docs/architecture.md) — layering, what is delegated to Fortify, the extension seams +- [Security](docs/security.md) — the guarantees this package makes and the ones it does not +- [Release](docs/release.md) — versioning, tagging, and ordering across the family -- [Registration](docs/registration.md) -- [Login](docs/login.md) -- [Password reset](docs/password-reset.md) -- [Profile management](docs/profile-management.md) -- [Password updates](docs/password-updates.md) -- [Email verification](docs/email-verification.md) -- [Social login](docs/social-login.md) -- [Passkeys](docs/passkeys.md) -- [API tokens](docs/api-tokens.md) -- [Configuration](docs/configuration.md) -- [Security](docs/security.md) -- [Testing](docs/testing.md) +### Reference + +- [Login](docs/login.md) · [Registration](docs/registration.md) · [Logout](docs/logout.md) +- [Password reset](docs/password-reset.md) · [Password updates](docs/password-updates.md) +- [Profile management](docs/profile-management.md) · [Email verification](docs/email-verification.md) +- [Social login](docs/social-login.md) · [Passkeys](docs/passkeys.md) · [API tokens](docs/api-tokens.md) + +### Project + +- [Testing](docs/testing.md) — how the suite is arranged and what it does not cover +- [Changelog](CHANGELOG.md) · [Contributing](CONTRIBUTING.md) · [Security policy](SECURITY.md) ## Configuration @@ -269,9 +301,23 @@ $result = app(SocialCallbackAction::class)->execute( ); ``` -## Related packages +## Sister packages + +| Package | Role | +|---|---| +| [`laranail/authkit`](https://github.com/laranail/authkit) | Headless core — actions, contracts, result objects, REST API | +| [`laranail/authkit-preset`](https://github.com/laranail/authkit-preset) | Blade scaffolding on top of the core | +| `laranail/authkit-sso` | SAML 2.0 and OIDC single sign-on | +| `laranail/authkit-oauth` | OAuth and social identity | +| `laranail/authkit-tenancy` | Multi-tenancy | +| `laranail/authkit-ldap` | LDAP and Active Directory | + +The family shares one root namespace, `Simtabi\Laranail\AuthKit\`, with each sibling a segment +under it. + +## Contributing and security -- `laranail/authkit-preset` — Blade views + routes for this package +See [CONTRIBUTING.md](CONTRIBUTING.md). Report security issues privately — [SECURITY.md](SECURITY.md). ## License diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..02fadf1 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,26 @@ +# Security policy + +## Reporting a vulnerability + +Report security issues privately to **opensource@simtabi.com**. Do not open a public issue. + +Include the affected version, the steps to reproduce, and the impact you believe it has. You will +get an acknowledgement within three business days and an assessment within ten. + +This package sits directly on the authentication path, so please err toward reporting. A finding +here may also affect its sibling, `laranail/authkit-preset`; say so if you think it does, and we will +coordinate the fix across the family rather than patching one package in isolation. + +## Supported versions + +The `main` line receives security fixes. Pre-1.0 releases are not separately maintained — upgrade +to the current line rather than expecting a backport. + +## Scope + +In scope: authentication bypass, privilege escalation, account takeover, credential or token +disclosure, session fixation, and any weakening of the rate limiting or bot protection this package +configures. + +Out of scope: findings that require an already-compromised application key, and misconfiguration of +an application that this package documents correctly. diff --git a/docs/api-tokens.md b/docs/api-tokens.md index d2ea12a..82ea025 100644 --- a/docs/api-tokens.md +++ b/docs/api-tokens.md @@ -11,4 +11,8 @@ $result = app(IssueTokenForUser::class)->execute( The consuming application's model must use Sanctum's `HasApiTokens` trait and its Sanctum migration must be installed. Auth Kit does not register API routes or decide when a token should be issued; authenticate and authorize the request first, then choose a token name and abilities appropriate to the client. Return the plain-text token only at issuance, never log it, and use Sanctum ability middleware plus token revocation for client lifecycle management. -For a ready-made API route set and Sanctum response handling, use `laranail/authkit-preset` and see its [API routes guide](../../authkit-preset/docs/api-routes.md). \ No newline at end of file +For a ready-made API route set and Sanctum response handling, use `laranail/authkit-preset` and see its [API routes guide](../../authkit-preset/docs/api-routes.md). + +--- + +[← Docs index](../README.md#documentation) diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..ece78cd --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,72 @@ +# Architecture + +How the core is layered, what it delegates to Fortify, and the seams the sibling packages extend. + +## The family + +``` +laranail/authkit Simtabi\Laranail\AuthKit\ this package — headless core +laranail/authkit-preset Simtabi\Laranail\AuthKit\Preset\ Blade scaffolding +laranail/authkit-sso Simtabi\Laranail\AuthKit\Sso\ SAML and OIDC +laranail/authkit-oauth Simtabi\Laranail\AuthKit\OAuth\ OAuth and social identity +laranail/authkit-tenancy Simtabi\Laranail\AuthKit\Tenancy\ multi-tenancy +laranail/authkit-ldap Simtabi\Laranail\AuthKit\Ldap\ LDAP and Active Directory +``` + +The family shares one root namespace and each sibling is a segment under it. Two packages mapping +nested PSR-4 prefixes is fine: Composer's loader matches the longest prefix first, so +`Simtabi\Laranail\AuthKit\Preset\` resolves into the preset and everything shallower resolves +here. + +## Layering + +The intended shape is **form request → controller → action → service**, with DTOs in and result +objects out. + +| Layer | Responsibility | +|---|---| +| Form request | Validation and authorization. Overridable by swapping the request, without touching the action. | +| Controller | HTTP shape only — resolve, delegate, respond. | +| Action | One use case, orchestration only. Behind a contract, container-bound. | +| Service | Reusable domain logic and infrastructure adapters. Unit-testable with no HTTP. | + +Being honest about the current state: the middle two are partly collapsed. Several actions build a +validator inline instead of taking a form request, and there are more actions than services. That +is a known gap, not the target. + +## What is delegated + +Password reset, email verification, profile and password updates, and passkey ceremonies are +Fortify's. This package supplies the actions Fortify calls — `CreateNewUser`, `ResetUserPassword`, +`UpdateUserPassword`, `UpdateUserProfileInformation` — and configures which features are on. + +That is a deliberate trade: first-party, audited code for the flows where hand-rolling is most +dangerous, and this package's own actions where the shape matters more than the cryptography. + +## Extension seams + +The sibling packages exist only if these are real. Each is a container-bound contract: + +| Seam | Purpose | Consumers | +|---|---|---| +| `ResolveIdentityInterface` | One linking-and-provisioning path for social, SAML, OIDC and directory sign-in, including the verified-identity guard | all | +| `IdentityProviderRegistryInterface` | A registry a sibling pushes a provider into, replacing a closed enum as the universe of providers | oauth, sso | +| `IssueTokenForUserInterface` | Token issuance, with ability scoping and expiry | all | +| `TenantResolverInterface` | Resolves the active tenant; identity tables carry a nullable tenant key | tenancy | +| `DirectoryResolverInterface` | Resolves and syncs a directory entry to a local user | ldap | +| `GuardProfileInterface` | A named guard profile — domain or prefix, redirects, features, user model | all | + +**Sequencing rule.** Every one of these changes a published contract, so they land here before the +first sibling is written. A sub-package that has to edit the core to do its job is not extending +the core, it is forking it. + +## Why social login refuses an unverified email + +`ResolveSocialIdentity` will link an external identity to an existing local account only when the +provider asserts the email is verified. Without that check, any provider returning an +attacker-controlled address is an account-takeover path. The guard belongs in the shared resolution +seam so SAML, OIDC and LDAP inherit it rather than each re-deriving it. + +--- + +[← Docs index](../README.md#documentation) diff --git a/docs/configuration.md b/docs/configuration.md index 4020632..899ac44 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -8,4 +8,8 @@ php artisan vendor:publish --tag=laranail::authkit-config Set `AUTHKIT_GUARD` to the application's intended guard. The `laranail.authkit.fortify.features` list enables Fortify capabilities individually: `reset-passwords`, `update-profile-information`, `update-passwords`, `email-verification`, and `passkeys`. Removing an item prevents Auth Kit from enabling that capability; remove corresponding application routes and UI too. -Credential throttling is controlled by `AUTHKIT_RATE_LIMIT_MAX_ATTEMPTS` and `AUTHKIT_RATE_LIMIT_DECAY_MINUTES`, defaulting to five attempts per minute. Social credentials, callbacks, and enabled providers are configured in `laranail.authkit.social`; see [social login](social-login.md) for provider settings. \ No newline at end of file +Credential throttling is controlled by `AUTHKIT_RATE_LIMIT_MAX_ATTEMPTS` and `AUTHKIT_RATE_LIMIT_DECAY_MINUTES`, defaulting to five attempts per minute. Social credentials, callbacks, and enabled providers are configured in `laranail.authkit.social`; see [social login](social-login.md) for provider settings. + +--- + +[← Docs index](../README.md#documentation) diff --git a/docs/email-verification.md b/docs/email-verification.md index 7cbcb89..a05a455 100644 --- a/docs/email-verification.md +++ b/docs/email-verification.md @@ -18,4 +18,8 @@ Auth Kit provides headless controller bases for the verification lifecycle: The application owns route registration, presentation, redirects, and mail configuration. The user model must implement Laravel's `MustVerifyEmail` contract for verification notifications and state changes to apply. Use Laravel's signed URL and `verified` middleware on the verification endpoint and routes that require a verified address. A profile email change for such a user clears its verification timestamp and sends a new notification. -Social login has its own verified-email rules. See [Social login](social-login.md) before enabling automatic social-account linking. \ No newline at end of file +Social login has its own verified-email rules. See [Social login](social-login.md) before enabling automatic social-account linking. + +--- + +[← Docs index](../README.md#documentation) diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..469d142 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,63 @@ +# Getting started + +Authenticate a user with the core alone, without any UI. + +## The shape of the API + +Every capability is an action behind a contract, bound in the container. Inputs are typed, outputs +are result objects rather than booleans or nullable users — so a caller branches on a status, not +on a truthiness accident. + +```php +use Simtabi\Laranail\AuthKit\Contracts\AttemptEmailPasswordLoginInterface; +use Simtabi\Laranail\AuthKit\Contracts\LoginUserInterface; +use Simtabi\Laranail\AuthKit\Enums\AuthStatus; + +public function store( + Request $request, + AttemptEmailPasswordLoginInterface $attempt, + LoginUserInterface $login, +) { + $result = $attempt->execute(request: $request, guard: 'web'); + + return match ($result->status) { + AuthStatus::Passed => tap(redirect()->intended(), fn () => $login->execute($result->user, 'web')), + AuthStatus::Failed => back()->withErrors(['email' => 'Invalid credentials.']), + AuthStatus::Throttled => abort(429), + }; +} +``` + +Credential verification and session issuance are separate actions on purpose. A flow that needs to +check a password without logging anyone in — a re-authentication prompt, a token exchange — calls +the first and not the second. + +## Issuing an API token instead + +```php +use Simtabi\Laranail\AuthKit\Contracts\IssueTokenForUserInterface; + +$token = app(IssueTokenForUserInterface::class)->execute($user, name: 'mobile'); +// $token->token — the plain-text token, shown once +``` + +## Swapping any of it + +Every contract is bound in the container, so replacing one is a binding, not a fork: + +```php +$this->app->bind( + AttemptEmailPasswordLoginInterface::class, + MyCredentialCheck::class, +); +``` + +## Where to go next + +- [Configuration](configuration.md) — the guard, rate limits, providers +- [Architecture](architecture.md) — why it is shaped this way, and the extension seams +- [Login](login.md), [registration](registration.md), [password reset](password-reset.md) — flow by flow + +--- + +[← Docs index](../README.md#documentation) diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..75dc97b --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,55 @@ +# Installation + +Requirements, the repositories block, and publishing the config. + +## Requirements + +PHP 8.4 or 8.5, Laravel 13. + +## The repositories block + +`laranail/*` packages resolve through git rather than Packagist, so Composer needs to be told where +to find them. Composer ignores a dependency's own `repositories`, which is why this goes in the +**application's** `composer.json` and lists the whole transitive closure: + +```json +"repositories": [ + { "type": "vcs", "url": "https://github.com/laranail/authkit.git" }, + { "type": "vcs", "url": "https://github.com/laranail/console.git" }, + { "type": "vcs", "url": "https://github.com/laranail/enumerator.git" }, + { "type": "vcs", "url": "https://github.com/laranail/package-tools.git" }, + { "type": "vcs", "url": "https://github.com/laranail/captcha.git" }, + { "type": "vcs", "url": "https://github.com/laranail/db-tools.git" } +] +``` + +## Require it + +```bash +composer require laranail/authkit +php artisan vendor:publish --tag=laranail::authkit-config +``` + +That writes `config/laranail/authkit.php`. The nested directory is deliberate: Laravel turns a +nested config directory into a nested key, so the file is read as `laranail.authkit`, which is the +key the package merges its defaults into. + +## Optional migrations + +Publish only what the features you enable actually need: + +```bash +php artisan vendor:publish --tag=laranail::authkit-social-migrations +php artisan vendor:publish --tag=laranail::authkit-passkey-migrations +php artisan migrate +``` + +## For a Blade UI + +This package is headless by design — no views, no routes, no shipped controllers. For a ready-made +interface use [`laranail/authkit-preset`](https://github.com/laranail/authkit-preset), which +consumes these actions and adds the Blade layer. + +--- + +[← Docs index](../README.md#documentation) diff --git a/docs/login.md b/docs/login.md index c33b6b3..5d02c3b 100644 --- a/docs/login.md +++ b/docs/login.md @@ -19,4 +19,8 @@ app(LoginUser::class)->execute($result->user, 'web'); `LoginUser` creates the session and regenerates it. `LogoutUser` logs the user out, invalidates the session, and regenerates the CSRF token. For controller-based integration, extend `AbstractAttemptEmailPasswordLoginController` and customize `passed()`, `failed()`, or `throttled()`; extend `AbstractLogoutController` and customize `loggedOut()`. -The default guard is `web`; change it with `AUTHKIT_GUARD` or pass the intended guard explicitly. Use the same guard for credential validation and session login. \ No newline at end of file +The default guard is `web`; change it with `AUTHKIT_GUARD` or pass the intended guard explicitly. Use the same guard for credential validation and session login. + +--- + +[← Docs index](../README.md#documentation) diff --git a/docs/passkeys.md b/docs/passkeys.md index 0836870..30cddea 100644 --- a/docs/passkeys.md +++ b/docs/passkeys.md @@ -40,4 +40,8 @@ Configure the relying-party ID and exact allowed browser origins in `config/fort Auth Kit does not ship JavaScript. Install `@laravel/passkeys` and connect the browser client to Fortify's canonical passkey endpoints. Passkey login and confirmation use the stateful guard and session, so they are not available as Sanctum API equivalents. -Keep `passkeys` in `laranail.authkit.fortify.features` to enable Fortify's passkey routes. Fortify's `confirmPassword` option and `passkeys` limiter continue to govern management operations. \ No newline at end of file +Keep `passkeys` in `laranail.authkit.fortify.features` to enable Fortify's passkey routes. Fortify's `confirmPassword` option and `passkeys` limiter continue to govern management operations. + +--- + +[← Docs index](../README.md#documentation) diff --git a/docs/password-reset.md b/docs/password-reset.md index 2264aca..f16017e 100644 --- a/docs/password-reset.md +++ b/docs/password-reset.md @@ -6,4 +6,8 @@ Enable password resets by retaining `reset-passwords` in `laranail.authkit.forti `ResetUserPassword` implements Fortify's `ResetsUserPasswords` contract. It validates a confirmed password with Laravel's default password rules, hashes it, clears the remember token, and revokes personal access tokens when the model exposes a `tokens()` relation. Use it through Fortify's reset-token flow or when an application-owned endpoint has already validated the reset token; do not accept a raw reset request without Laravel's broker validation. -Retaining or removing `reset-passwords` in `laranail.authkit.fortify.features` enables or disables this Fortify capability. Ensure application routes and UI do not expose a flow that has been disabled. \ No newline at end of file +Retaining or removing `reset-passwords` in `laranail.authkit.fortify.features` enables or disables this Fortify capability. Ensure application routes and UI do not expose a flow that has been disabled. + +--- + +[← Docs index](../README.md#documentation) diff --git a/docs/password-updates.md b/docs/password-updates.md index 7d387c6..571f871 100644 --- a/docs/password-updates.md +++ b/docs/password-updates.md @@ -2,4 +2,8 @@ Retain `update-passwords` in `laranail.authkit.fortify.features` to enable authenticated password updates. `UpdateUserPassword` implements Fortify's `UpdatesUserPasswords` contract, requires the current password for the configured guard, validates and hashes the replacement, clears the remember token, and revokes personal tokens when available. -For the currently authenticated user, the action also asks a compatible guard to log out other devices. Invoke it only from an authenticated, CSRF-protected flow. Removing the feature prevents Auth Kit from enabling this Fortify capability; do not expose a corresponding application route or UI. \ No newline at end of file +For the currently authenticated user, the action also asks a compatible guard to log out other devices. Invoke it only from an authenticated, CSRF-protected flow. Removing the feature prevents Auth Kit from enabling this Fortify capability; do not expose a corresponding application route or UI. + +--- + +[← Docs index](../README.md#documentation) diff --git a/docs/profile-management.md b/docs/profile-management.md index 55a0ce6..80eda86 100644 --- a/docs/profile-management.md +++ b/docs/profile-management.md @@ -4,4 +4,8 @@ Auth Kit exposes profile behavior through Laravel Fortify actions and abstract c Retain `update-profile-information` in `laranail.authkit.fortify.features` to enable profile changes. `UpdateUserProfileInformation` implements Fortify's `UpdatesUserProfileInformation` contract and accepts a required name plus a unique email address. If a user implements Laravel's `MustVerifyEmail` contract and changes email, it clears `email_verified_at` and sends a new verification notification. Extend or replace the action if the application has additional profile fields or different validation requirements. -Removing the feature prevents Auth Kit from enabling the related Fortify capability; ensure application routes and UI do not expose a disabled flow. For authenticated password changes, see [password updates](password-updates.md). \ No newline at end of file +Removing the feature prevents Auth Kit from enabling the related Fortify capability; ensure application routes and UI do not expose a disabled flow. For authenticated password changes, see [password updates](password-updates.md). + +--- + +[← Docs index](../README.md#documentation) diff --git a/docs/registration.md b/docs/registration.md index 18a032b..2bacf6f 100644 --- a/docs/registration.md +++ b/docs/registration.md @@ -6,4 +6,8 @@ Use `CreateNewUser` for registration. It implements Fortify's `CreatesNewUsers` Extend `AbstractRegisterController` to connect registration to an application route. Override `registered()` when the default JSON response does not fit the application. -For session authentication and logout, see [login](login.md). Configure bot protection separately in [security](security.md). \ No newline at end of file +For session authentication and logout, see [login](login.md). Configure bot protection separately in [security](security.md). + +--- + +[← Docs index](../README.md#documentation) diff --git a/docs/release.md b/docs/release.md new file mode 100644 index 0000000..f68399c --- /dev/null +++ b/docs/release.md @@ -0,0 +1,44 @@ +# Release + +How a version is cut, and what a release has to carry. + +## Versioning + +Semantic versioning. While pre-1.0 the minor position carries breaking changes, so consumers pin +with `^0.1`. + +`composer.json` declares `branch-alias` `dev-main → 0.1.x-dev`, so a path or dev checkout satisfies +a `^0.1` constraint during development. + +## Before tagging + +- CI green on `main` — tests and style. +- The naming guard passing. That is what proves no public name regressed to a bare form, which is + the failure mode that does not announce itself. +- A `CHANGELOG.md` section written for someone deciding whether to upgrade. +- Verified in a real application, not only the package suite — the demo at `laranail/demos/authkit` + installs this package the way a consumer does. + +## Ordering across the family + +The siblings depend on this package. When a change here is breaking, tag it first and let the +siblings follow; a sibling tagged against a contract that has not shipped cannot resolve. + +## Cutting the release + +```bash +git tag vX.Y.Z +git push origin vX.Y.Z +``` + +The release body is the CHANGELOG section for that version. A release with auto-generated notes or +a "see CHANGELOG" stub is incomplete — the description is part of the release, not a pointer to it. + +## Resolution + +`laranail/*` packages resolve through git, not Packagist. Consumers add the repositories block from +[installation](installation.md); nothing is pushed to Packagist as part of a release. + +--- + +[← Docs index](../README.md#documentation) diff --git a/docs/security.md b/docs/security.md index ac25342..385c251 100644 --- a/docs/security.md +++ b/docs/security.md @@ -14,4 +14,8 @@ TURNSTILE_SECRET_KEY= The configured challenge input is `cf-turnstile-response`. The rule is applied during Auth Kit user creation; attach `ValidateTurnstile` or `TurnstileRule` deliberately to other application-owned endpoints. Protect only routes where a challenge is appropriate, verify the callback server-side, and never treat the browser response as sufficient on its own. -Two-factor authentication is not enabled by default; MFA remains work in progress. Review the [social linking rules](social-login.md#identity-resolution-and-account-linking-safety) and [passkey origin requirements](passkeys.md#relying-party-and-browser-client) before deploying either feature. See [configuration](configuration.md) for feature and guard settings, and [testing](testing.md) for validation guidance. \ No newline at end of file +Two-factor authentication is not enabled by default; MFA remains work in progress. Review the [social linking rules](social-login.md#identity-resolution-and-account-linking-safety) and [passkey origin requirements](passkeys.md#relying-party-and-browser-client) before deploying either feature. See [configuration](configuration.md) for feature and guard settings, and [testing](testing.md) for validation guidance. + +--- + +[← Docs index](../README.md#documentation) diff --git a/docs/social-login.md b/docs/social-login.md index 215ae19..257a0ca 100644 --- a/docs/social-login.md +++ b/docs/social-login.md @@ -61,4 +61,8 @@ Only Google, LinkedIn, and PayPal are currently trusted for `email_verified`. Fa ## Adding a provider -The accepted route values are the `SocialProvider` enum cases. Adding a provider requires a package change: add its enum case, add its credentials, redirect, and scopes under `laranail.authkit.social`, and ensure Socialite has a driver for that key. First-party Socialite drivers work through the normal `services.` configuration; a third-party driver must be registered with Socialite's extension mechanism, as PayPal is. Add callback tests for an existing identity, a trusted verified email, an unverified or missing email, and authenticated linking before exposing the new provider. \ No newline at end of file +The accepted route values are the `SocialProvider` enum cases. Adding a provider requires a package change: add its enum case, add its credentials, redirect, and scopes under `laranail.authkit.social`, and ensure Socialite has a driver for that key. First-party Socialite drivers work through the normal `services.` configuration; a third-party driver must be registered with Socialite's extension mechanism, as PayPal is. Add callback tests for an existing identity, a trusted verified email, an unverified or missing email, and authenticated linking before exposing the new provider. + +--- + +[← Docs index](../README.md#documentation) diff --git a/docs/testing.md b/docs/testing.md index dd4b96f..d2a8d29 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -7,4 +7,8 @@ composer test composer lint ``` -The feature suite covers credentials, registration, logout, resets, profile and password updates, social identity handling, passkeys, tokens, and Turnstile behavior. In the consuming application, test application-owned routes, response overrides, middleware, mail delivery, the configured guard, and external-provider callbacks. \ No newline at end of file +The feature suite covers credentials, registration, logout, resets, profile and password updates, social identity handling, passkeys, tokens, and Turnstile behavior. In the consuming application, test application-owned routes, response overrides, middleware, mail delivery, the configured guard, and external-provider callbacks. + +--- + +[← Docs index](../README.md#documentation)