| name | atomic-framework-app-auth |
| description | Use when working on Atomic App layer (Controller, Model, Page, Storage, PluginManager), Auth flows (Auth, GoogleAuth, TelegramAuth, adapters, services), Session (SessionManager, drivers), and app-level models (Meta, Options, MutexLock). |
| argument-hint | Controllers, models, page, storage, login, OAuth, session, user provider |
| user-invocable | true |
Atomic Framework App and Auth
When to Use
- Writing controllers, page controllers, or app models.
- Implementing login, logout, Google OAuth, or Telegram login.
- Checking auth state, roles, or session lifetime.
- Working with app-level storage, system state, or plugin wiring.
Engine Files
engine/Atomic/App/*, engine/Atomic/Auth/* (Auth, GoogleAuth, TelegramAuth, Adapters/, Interfaces/, Services/*)
Reference Docs
docs/applications.md, docs/model.md, docs/session.md, docs/telegram.md, docs/security.md, docs/transient.md
Controller
namespace App\Http\Controllers;
use Engine\Atomic\App\Controller;
final class Posts extends Controller
{
public function index(\Base $f3): void
{
$posts = (new \App\Models\Post())->find();
$f3->set('posts', $posts);
echo \Template::instance()->render('posts/index.htm');
}
public function store(\Base $f3): void
{
$post = new \App\Models\Post();
$post->title = $f3->get('POST.title');
if (!$post->save()) {
[$code, $vars] = $post->get_last_err_data();
send_json_error('Validation failed', 422, ['code' => $code]);
return;
}
send_json(['id' => $post->id]);
}
}
Page Controller
Engine\Atomic\App\Page is used for rendering theme-based pages (head, body, title, meta).
Model (App\Model)
See atomic-framework-data skill for full Model API.
use Engine\Atomic\App\Model;
final class Post extends Model
{
protected $table = 'posts';
protected $fieldConf = [
'title' => ['type' => 'VARCHAR256', 'nullable' => false],
];
}
App Models
Engine\Atomic\App\Models\Options -- app-wide key-value store (global config at APP_UUID scope)
Engine\Atomic\App\Models\Meta -- per-entity metadata keyed by UUID + key
Engine\Atomic\App\Models\MutexLock -- DB-backed mutex lock records
Options (global app config)
use Engine\Atomic\App\Models\Options;
Options::set_option('site_name', 'My Store');
Options::set_option('session_ttl', '3600', ttl: 86400);
$name = Options::get_option('site_name');
$name = Options::get_option('site_name', 'Shop');
Options::delete_option('site_name');
$result = Options::has_option('site_name', $val);
if ($result !== false) {
[$ts, $ttl] = $result;
}
$map = Options::get_option_like('cache_%');
Options::delete_option_like('temp_%');
add_option('site_name', 'My Store');
update_option('site_name', 'New Name');
$name = get_option('site_name', 'Default');
delete_option('site_name');
Meta (per-entity metadata)
use Engine\Atomic\App\Models\Meta;
Meta::set_meta($userUuid, 'profile_pic', '/avatars/1.jpg');
$pic = Meta::get_meta($userUuid, 'profile_pic');
$ok = Meta::has_meta($userUuid, 'profile_pic');
Meta::delete_meta($userUuid, 'profile_pic');
$all = Meta::get_meta_like($userUuid, 'upload_%');
Meta::delete_meta_like($userUuid, 'upload_%');
add_meta($uuid, 'key', 'value');
update_meta($uuid, 'key', 'value');
$val = get_meta($uuid, 'key');
delete_meta($uuid, 'key');
Storage (abstract base)
Engine\Atomic\App\Storage is the base class for Options and Meta. It provides _set, _get, _has, _delete, _get_like, _delete_like as protected static methods keyed by uuid + key. Extend it to build custom per-entity stores.
Page (CMS Model)
Engine\Atomic\App\Page extends Model and provides a rich pre-configured field set for CMS-style content:
namespace App\Models;
use Engine\Atomic\App\Page;
final class Article extends Page
{
protected $table = 'articles';
protected static function category_model(): ?string
{
return Category::class;
}
protected static function author_model(): ?string
{
return \App\Models\User::class;
}
}
Built-in fields (pre-configured, no need to declare):
| Field | Type | Notes |
|---|
route | VARCHAR256 | custom URL |
template | VARCHAR256 | template file name |
title | VARCHAR256 | required, min 3 chars |
slug | VARCHAR256 | unique, required |
subtitle | VARCHAR256 | optional |
excerpt | TEXT | optional |
content | LONGTEXT | optional |
content_type | VARCHAR128 | default 'page' |
category | INT | FK -- auto-wired from category_model() |
main_image | VARCHAR256 | optional |
thumbnail | VARCHAR256 | optional |
gallery | LONGTEXT | JSON array |
attributes | LONGTEXT | JSON |
custom_fields | LONGTEXT | JSON |
seo_title | VARCHAR256 | optional |
seo_description | VARCHAR512 | optional |
og_title | VARCHAR256 | optional |
og_description | VARCHAR512 | optional |
og_image | VARCHAR256 | optional |
is_published | BOOL | default 0 |
publish_at | DATETIME | optional |
expires_at | DATETIME | optional |
author | INT | FK -- auto-wired from author_model() |
views | INT | view counter |
User Role Helpers
atomic_get_current_user();
is_authenticated();
is_guest();
has_role(Role::ADMIN);
has_role('admin');
has_any_role([Role::ADMIN, Role::SELLER]);
is_admin();
is_seller();
is_buyer();
is_support();
is_impersonating();
get_real_admin();
Auth Facade
Engine\Atomic\Auth\Auth is the main auth entry point.
use Engine\Atomic\Auth\Auth;
$auth = Auth::instance();
$user = $auth->get_current_user();
$auth->logout();
$auth->login_by_id('user-uuid');
$user = $auth->login_with_secret(['email' => 'a@b.com'], $secret);
$ok = $auth->check_rate_limit(['email' => 'a@b.com'], 'register');
$auth->impersonate_user('target-user-uuid');
$auth->stop_impersonation();
$auth->is_impersonating();
$auth->get_real_admin();
Session Facade
use Engine\Atomic\Auth\Session;
Session::init();
Session::start();
Session::start('550e8400-e29b-41d4-a716-446655440000');
if (Session::is_started()) { }
if (Session::is_expired()) { }
Session::destroy();
Session data lives in the F3 hive:
$f3 = \Base::instance();
$f3->set('SESSION.user_uuid', '550e...');
$uuid = $f3->get('SESSION.user_uuid');
$f3->clear('SESSION.user_uuid');
Session Manager (Storage Inspection)
use Engine\Atomic\Session\SessionManager;
$manager = new SessionManager();
$redis = new SessionManager('redis');
$sql = new SessionManager('database');
$exists = $manager->session_exists($session_id);
$data = $manager->get_session_data($session_id);
$manager->delete_session($session_id);
$manager->delete_sessions([$id1, $id2]);
Drivers: database (SQL via SqlSessionTrait) or redis (via RedisSessionTrait).
Google Auth
Engine\Atomic\Auth\GoogleAuth + GoogleAuthService + GoogleClientAdapter.
use Engine\Atomic\Auth\GoogleAuth;
use Engine\Atomic\Auth\Interfaces\OAuthUserResolverInterface;
$google = GoogleAuth::instance();
if (!$google->is_configured()) {
throw new \RuntimeException('Google OAuth not configured');
}
$loginUrl = $google->get_login_url();
\Base::instance()->reroute($loginUrl);
$redirectUrl = $google->handle_callback($code, $state);
Required config keys (in .env / config/auth.php): GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_CALLBACK_URL.
Telegram Auth
Engine\Atomic\Auth\TelegramAuth + TelegramAuthService + TelegramClientAdapter.
use Engine\Atomic\Auth\TelegramAuth;
$tg = TelegramAuth::instance();
if (!$tg->is_configured()) {
throw new \RuntimeException('Telegram auth not configured');
}
$attrs = $tg->get_widget_attributes(
size: 'large', // 'small', 'medium', 'large'
request_access: false, // request write access
use_avatar: true,
corner_radius: 12,
);
$authData = $_POST;
$verified = $tg->verify_auth_data($authData);
if ($verified !== false) {
$redirectUrl = $tg->handle_callback($verified);
}
$tg->get_bot_username();
$tg->get_callback_url();
Required config keys: TELEGRAM_BOT_TOKEN, TELEGRAM_CALLBACK_URL.
Auth Interfaces
| Interface | Purpose |
|---|
AuthenticatableInterface | User model contract |
AuthSessionInterface | Session-based auth contract |
HasRolesInterface | Role-check contract (Guard::has_role()) |
LoginInterface | Login action contract |
OAuthUserResolverInterface | OAuth user upsert contract |
UserProviderInterface | Resolve user from session/token |
Guardrails
- Never store passwords in session or plaintext. Use
BcryptHasherAdapter.
- Always check
HasRolesInterface before calling Guard::has_role().
- Use
Session::destroy() on logout, not raw session_destroy().
- Keep adapters thin; business logic belongs in services.