@famgia/omnify-laravel 0.0.114 → 0.0.115
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@famgia/omnify-laravel",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.115",
|
|
4
4
|
"description": "Laravel migration and TypeScript type generator for omnify-schema",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"README.md"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@famgia/omnify-types": "0.0.
|
|
29
|
-
"@famgia/omnify-
|
|
30
|
-
"@famgia/omnify-
|
|
28
|
+
"@famgia/omnify-types": "0.0.104",
|
|
29
|
+
"@famgia/omnify-atlas": "0.0.100",
|
|
30
|
+
"@famgia/omnify-core": "0.0.106"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "tsup",
|
|
@@ -32,7 +32,7 @@ Route::get('/users', [UserController::class, 'index']);
|
|
|
32
32
|
| `api.php` | `/api` | `/users` | `/users` |
|
|
33
33
|
| `web.php` | (none) | `/dashboard` | `/dashboard` |
|
|
34
34
|
|
|
35
|
-
**Full guide:** `.claude/guides/laravel/openapi.md`
|
|
35
|
+
**Full guide:** `.claude/omnify/guides/laravel/openapi.md`
|
|
36
36
|
|
|
37
37
|
---
|
|
38
38
|
|
|
@@ -6,8 +6,8 @@ alwaysApply: false
|
|
|
6
6
|
|
|
7
7
|
# Laravel Rules
|
|
8
8
|
|
|
9
|
-
> **Documentation:** `.claude/guides/laravel/`
|
|
10
|
-
> **Team Migration Guide:** `.claude/guides/laravel/migrations-team.md`
|
|
9
|
+
> **Documentation:** `.claude/omnify/guides/laravel/`
|
|
10
|
+
> **Team Migration Guide:** `.claude/omnify/guides/laravel/migrations-team.md`
|
|
11
11
|
> **Specific Rules:** See also `laravel-controller.mdc`, `laravel-resource.mdc`, `laravel-request.mdc`, `laravel-testing.mdc`, `migrations-workflow.mdc`, `omnify-migrations.mdc`
|
|
12
12
|
|
|
13
13
|
## ⛔ CRITICAL: DO NOT EDIT
|
|
@@ -77,6 +77,32 @@ See `migrations-workflow.mdc` for complete guide.
|
|
|
77
77
|
| Reusable action | Action class |
|
|
78
78
|
| Async task | Job |
|
|
79
79
|
|
|
80
|
+
## Authentication Models
|
|
81
|
+
|
|
82
|
+
When `authenticatable: true` is set in schema, model extends `Authenticatable`:
|
|
83
|
+
|
|
84
|
+
```yaml
|
|
85
|
+
# schemas/auth/User.yaml
|
|
86
|
+
options:
|
|
87
|
+
authenticatable: true
|
|
88
|
+
authenticatableLoginIdField: email
|
|
89
|
+
authenticatablePasswordField: password
|
|
90
|
+
authenticatableGuardName: web # Optional: for multi-guard
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Required `config/auth.php` for custom guards:**
|
|
94
|
+
|
|
95
|
+
```php
|
|
96
|
+
'guards' => [
|
|
97
|
+
'admin' => ['driver' => 'session', 'provider' => 'admins'],
|
|
98
|
+
],
|
|
99
|
+
'providers' => [
|
|
100
|
+
'admins' => ['driver' => 'eloquent', 'model' => App\Models\Admin::class],
|
|
101
|
+
],
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**See:** `.claude/omnify/guides/omnify/schema-guide.md` → "Authenticatable Option - Detailed Guide"
|
|
105
|
+
|
|
80
106
|
## Pre-Edit Checklist
|
|
81
107
|
|
|
82
108
|
**BEFORE editing any file, MUST:**
|
|
@@ -109,4 +135,4 @@ public function store(): JsonResponse
|
|
|
109
135
|
| "Improve" unrelated code | Change only what's needed |
|
|
110
136
|
| Build for future "just in case" | YAGNI - build when needed |
|
|
111
137
|
|
|
112
|
-
**Full examples:** `.claude/guides/laravel/architecture.md`
|
|
138
|
+
**Full examples:** `.claude/omnify/guides/laravel/architecture.md`
|
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
# Laravel Authentication with Omnify
|
|
2
|
+
|
|
3
|
+
> Complete guide for implementing authentication using Omnify-generated models.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
When you set `authenticatable: true` in your schema, Omnify generates a Laravel model that:
|
|
8
|
+
|
|
9
|
+
1. Extends `Illuminate\Foundation\Auth\User` (alias `Authenticatable`)
|
|
10
|
+
2. Includes the `Notifiable` trait
|
|
11
|
+
3. Auto-hides password and remember_token fields
|
|
12
|
+
4. Works with Laravel's built-in auth system
|
|
13
|
+
|
|
14
|
+
## Schema Configuration
|
|
15
|
+
|
|
16
|
+
### Basic User Schema
|
|
17
|
+
|
|
18
|
+
```yaml
|
|
19
|
+
name: User
|
|
20
|
+
displayName:
|
|
21
|
+
ja: ユーザー
|
|
22
|
+
en: User
|
|
23
|
+
options:
|
|
24
|
+
timestamps: true
|
|
25
|
+
softDelete: true
|
|
26
|
+
authenticatable: true
|
|
27
|
+
authenticatableLoginIdField: email # Field for login
|
|
28
|
+
authenticatablePasswordField: password # Field for password
|
|
29
|
+
# authenticatableGuardName: web # Optional: guard name
|
|
30
|
+
|
|
31
|
+
properties:
|
|
32
|
+
email:
|
|
33
|
+
type: Email
|
|
34
|
+
unique: true
|
|
35
|
+
password:
|
|
36
|
+
type: Password
|
|
37
|
+
name:
|
|
38
|
+
type: String
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Multi-Guard Setup (Admin + User)
|
|
42
|
+
|
|
43
|
+
For separate admin and user authentication:
|
|
44
|
+
|
|
45
|
+
**User Schema:**
|
|
46
|
+
```yaml
|
|
47
|
+
name: User
|
|
48
|
+
options:
|
|
49
|
+
authenticatable: true
|
|
50
|
+
authenticatableGuardName: web
|
|
51
|
+
|
|
52
|
+
properties:
|
|
53
|
+
email:
|
|
54
|
+
type: Email
|
|
55
|
+
unique: true
|
|
56
|
+
password:
|
|
57
|
+
type: Password
|
|
58
|
+
name:
|
|
59
|
+
type: String
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Admin Schema:**
|
|
63
|
+
```yaml
|
|
64
|
+
name: Admin
|
|
65
|
+
options:
|
|
66
|
+
authenticatable: true
|
|
67
|
+
authenticatableGuardName: admin # Different guard
|
|
68
|
+
|
|
69
|
+
properties:
|
|
70
|
+
email:
|
|
71
|
+
type: Email
|
|
72
|
+
unique: true
|
|
73
|
+
password:
|
|
74
|
+
type: Password
|
|
75
|
+
name:
|
|
76
|
+
type: String
|
|
77
|
+
role:
|
|
78
|
+
type: EnumRef
|
|
79
|
+
enum: AdminRole
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Laravel Configuration
|
|
83
|
+
|
|
84
|
+
### config/auth.php
|
|
85
|
+
|
|
86
|
+
```php
|
|
87
|
+
<?php
|
|
88
|
+
|
|
89
|
+
return [
|
|
90
|
+
'defaults' => [
|
|
91
|
+
'guard' => 'web',
|
|
92
|
+
'passwords' => 'users',
|
|
93
|
+
],
|
|
94
|
+
|
|
95
|
+
'guards' => [
|
|
96
|
+
'web' => [
|
|
97
|
+
'driver' => 'session',
|
|
98
|
+
'provider' => 'users',
|
|
99
|
+
],
|
|
100
|
+
'admin' => [ // For Admin model
|
|
101
|
+
'driver' => 'session',
|
|
102
|
+
'provider' => 'admins',
|
|
103
|
+
],
|
|
104
|
+
'api' => [ // For API authentication
|
|
105
|
+
'driver' => 'sanctum',
|
|
106
|
+
'provider' => 'users',
|
|
107
|
+
],
|
|
108
|
+
],
|
|
109
|
+
|
|
110
|
+
'providers' => [
|
|
111
|
+
'users' => [
|
|
112
|
+
'driver' => 'eloquent',
|
|
113
|
+
'model' => App\Models\User::class,
|
|
114
|
+
],
|
|
115
|
+
'admins' => [ // For Admin model
|
|
116
|
+
'driver' => 'eloquent',
|
|
117
|
+
'model' => App\Models\Admin::class,
|
|
118
|
+
],
|
|
119
|
+
],
|
|
120
|
+
|
|
121
|
+
'passwords' => [
|
|
122
|
+
'users' => [
|
|
123
|
+
'provider' => 'users',
|
|
124
|
+
'table' => 'password_reset_tokens',
|
|
125
|
+
'expire' => 60,
|
|
126
|
+
'throttle' => 60,
|
|
127
|
+
],
|
|
128
|
+
'admins' => [ // For Admin password reset
|
|
129
|
+
'provider' => 'admins',
|
|
130
|
+
'table' => 'password_reset_tokens',
|
|
131
|
+
'expire' => 60,
|
|
132
|
+
'throttle' => 60,
|
|
133
|
+
],
|
|
134
|
+
],
|
|
135
|
+
|
|
136
|
+
'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
|
|
137
|
+
];
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Generated Model Structure
|
|
141
|
+
|
|
142
|
+
### Base Model (Auto-generated, DO NOT EDIT)
|
|
143
|
+
|
|
144
|
+
```php
|
|
145
|
+
// app/Models/Base/UserBaseModel.php
|
|
146
|
+
<?php
|
|
147
|
+
|
|
148
|
+
namespace App\Models\Base;
|
|
149
|
+
|
|
150
|
+
use Illuminate\Foundation\Auth\User as Authenticatable;
|
|
151
|
+
use Illuminate\Notifications\Notifiable;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* DO NOT EDIT - This file is auto-generated by Omnify.
|
|
155
|
+
* Any changes will be overwritten on next generation.
|
|
156
|
+
*/
|
|
157
|
+
class UserBaseModel extends Authenticatable
|
|
158
|
+
{
|
|
159
|
+
use Notifiable;
|
|
160
|
+
use HasLocalizedDisplayName;
|
|
161
|
+
|
|
162
|
+
protected $table = 'users';
|
|
163
|
+
protected $primaryKey = 'id';
|
|
164
|
+
public $timestamps = true;
|
|
165
|
+
|
|
166
|
+
protected $fillable = [
|
|
167
|
+
'email',
|
|
168
|
+
'password',
|
|
169
|
+
'name',
|
|
170
|
+
];
|
|
171
|
+
|
|
172
|
+
protected $hidden = [
|
|
173
|
+
'password',
|
|
174
|
+
'remember_token',
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
protected function casts(): array
|
|
178
|
+
{
|
|
179
|
+
return [
|
|
180
|
+
'email_verified_at' => 'datetime',
|
|
181
|
+
'password' => 'hashed',
|
|
182
|
+
];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Your Model (Customizable)
|
|
188
|
+
|
|
189
|
+
```php
|
|
190
|
+
// app/Models/User.php
|
|
191
|
+
<?php
|
|
192
|
+
|
|
193
|
+
namespace App\Models;
|
|
194
|
+
|
|
195
|
+
use App\Models\Base\UserBaseModel;
|
|
196
|
+
|
|
197
|
+
class User extends UserBaseModel
|
|
198
|
+
{
|
|
199
|
+
// Add your custom methods here
|
|
200
|
+
|
|
201
|
+
public function posts(): HasMany
|
|
202
|
+
{
|
|
203
|
+
return $this->hasMany(Post::class);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
public function profile(): HasOne
|
|
207
|
+
{
|
|
208
|
+
return $this->hasOne(UserProfile::class);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Custom scopes
|
|
212
|
+
public function scopeActive($query)
|
|
213
|
+
{
|
|
214
|
+
return $query->where('status', 'active');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Custom accessors
|
|
218
|
+
public function getFullNameAttribute(): string
|
|
219
|
+
{
|
|
220
|
+
return $this->name;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Authentication Controllers
|
|
226
|
+
|
|
227
|
+
### Login Controller
|
|
228
|
+
|
|
229
|
+
```php
|
|
230
|
+
<?php
|
|
231
|
+
|
|
232
|
+
namespace App\Http\Controllers\Auth;
|
|
233
|
+
|
|
234
|
+
use App\Http\Controllers\Controller;
|
|
235
|
+
use App\Http\Requests\Auth\LoginRequest;
|
|
236
|
+
use Illuminate\Http\JsonResponse;
|
|
237
|
+
use Illuminate\Support\Facades\Auth;
|
|
238
|
+
|
|
239
|
+
class LoginController extends Controller
|
|
240
|
+
{
|
|
241
|
+
public function store(LoginRequest $request): JsonResponse
|
|
242
|
+
{
|
|
243
|
+
$request->authenticate();
|
|
244
|
+
$request->session()->regenerate();
|
|
245
|
+
|
|
246
|
+
return response()->json([
|
|
247
|
+
'user' => Auth::user(),
|
|
248
|
+
'message' => 'ログインしました',
|
|
249
|
+
]);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
public function destroy(): JsonResponse
|
|
253
|
+
{
|
|
254
|
+
Auth::guard('web')->logout();
|
|
255
|
+
request()->session()->invalidate();
|
|
256
|
+
request()->session()->regenerateToken();
|
|
257
|
+
|
|
258
|
+
return response()->json([
|
|
259
|
+
'message' => 'ログアウトしました',
|
|
260
|
+
]);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Login Request
|
|
266
|
+
|
|
267
|
+
```php
|
|
268
|
+
<?php
|
|
269
|
+
|
|
270
|
+
namespace App\Http\Requests\Auth;
|
|
271
|
+
|
|
272
|
+
use Illuminate\Auth\Events\Lockout;
|
|
273
|
+
use Illuminate\Foundation\Http\FormRequest;
|
|
274
|
+
use Illuminate\Support\Facades\Auth;
|
|
275
|
+
use Illuminate\Support\Facades\RateLimiter;
|
|
276
|
+
use Illuminate\Support\Str;
|
|
277
|
+
use Illuminate\Validation\ValidationException;
|
|
278
|
+
|
|
279
|
+
class LoginRequest extends FormRequest
|
|
280
|
+
{
|
|
281
|
+
public function authorize(): bool
|
|
282
|
+
{
|
|
283
|
+
return true;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
public function rules(): array
|
|
287
|
+
{
|
|
288
|
+
return [
|
|
289
|
+
'email' => ['required', 'string', 'email'],
|
|
290
|
+
'password' => ['required', 'string'],
|
|
291
|
+
];
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
public function authenticate(): void
|
|
295
|
+
{
|
|
296
|
+
$this->ensureIsNotRateLimited();
|
|
297
|
+
|
|
298
|
+
if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
|
|
299
|
+
RateLimiter::hit($this->throttleKey());
|
|
300
|
+
|
|
301
|
+
throw ValidationException::withMessages([
|
|
302
|
+
'email' => __('auth.failed'),
|
|
303
|
+
]);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
RateLimiter::clear($this->throttleKey());
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
protected function ensureIsNotRateLimited(): void
|
|
310
|
+
{
|
|
311
|
+
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
event(new Lockout($this));
|
|
316
|
+
|
|
317
|
+
$seconds = RateLimiter::availableIn($this->throttleKey());
|
|
318
|
+
|
|
319
|
+
throw ValidationException::withMessages([
|
|
320
|
+
'email' => trans('auth.throttle', [
|
|
321
|
+
'seconds' => $seconds,
|
|
322
|
+
'minutes' => ceil($seconds / 60),
|
|
323
|
+
]),
|
|
324
|
+
]);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
protected function throttleKey(): string
|
|
328
|
+
{
|
|
329
|
+
return Str::transliterate(Str::lower($this->string('email')).'|'.$this->ip());
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## API Authentication (Laravel Sanctum)
|
|
335
|
+
|
|
336
|
+
### Install Sanctum
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
composer require laravel/sanctum
|
|
340
|
+
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
|
|
341
|
+
php artisan migrate
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Add to User Model
|
|
345
|
+
|
|
346
|
+
```php
|
|
347
|
+
// app/Models/User.php
|
|
348
|
+
use Laravel\Sanctum\HasApiTokens;
|
|
349
|
+
|
|
350
|
+
class User extends UserBaseModel
|
|
351
|
+
{
|
|
352
|
+
use HasApiTokens; // Add this trait
|
|
353
|
+
|
|
354
|
+
// ...
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### API Login Controller
|
|
359
|
+
|
|
360
|
+
```php
|
|
361
|
+
<?php
|
|
362
|
+
|
|
363
|
+
namespace App\Http\Controllers\Api\Auth;
|
|
364
|
+
|
|
365
|
+
use App\Http\Controllers\Controller;
|
|
366
|
+
use App\Models\User;
|
|
367
|
+
use Illuminate\Http\JsonResponse;
|
|
368
|
+
use Illuminate\Http\Request;
|
|
369
|
+
use Illuminate\Support\Facades\Hash;
|
|
370
|
+
use Illuminate\Validation\ValidationException;
|
|
371
|
+
|
|
372
|
+
class TokenController extends Controller
|
|
373
|
+
{
|
|
374
|
+
public function store(Request $request): JsonResponse
|
|
375
|
+
{
|
|
376
|
+
$request->validate([
|
|
377
|
+
'email' => 'required|email',
|
|
378
|
+
'password' => 'required',
|
|
379
|
+
'device_name' => 'required',
|
|
380
|
+
]);
|
|
381
|
+
|
|
382
|
+
$user = User::where('email', $request->email)->first();
|
|
383
|
+
|
|
384
|
+
if (! $user || ! Hash::check($request->password, $user->password)) {
|
|
385
|
+
throw ValidationException::withMessages([
|
|
386
|
+
'email' => [__('auth.failed')],
|
|
387
|
+
]);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return response()->json([
|
|
391
|
+
'token' => $user->createToken($request->device_name)->plainTextToken,
|
|
392
|
+
'user' => $user,
|
|
393
|
+
]);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
public function destroy(Request $request): JsonResponse
|
|
397
|
+
{
|
|
398
|
+
$request->user()->currentAccessToken()->delete();
|
|
399
|
+
|
|
400
|
+
return response()->json(['message' => 'Token revoked']);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
## Routes Configuration
|
|
406
|
+
|
|
407
|
+
### Web Routes (routes/web.php)
|
|
408
|
+
|
|
409
|
+
```php
|
|
410
|
+
use App\Http\Controllers\Auth\LoginController;
|
|
411
|
+
|
|
412
|
+
Route::middleware('guest')->group(function () {
|
|
413
|
+
Route::post('/login', [LoginController::class, 'store']);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
Route::middleware('auth')->group(function () {
|
|
417
|
+
Route::post('/logout', [LoginController::class, 'destroy']);
|
|
418
|
+
});
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### API Routes (routes/api.php)
|
|
422
|
+
|
|
423
|
+
```php
|
|
424
|
+
use App\Http\Controllers\Api\Auth\TokenController;
|
|
425
|
+
|
|
426
|
+
Route::post('/tokens', [TokenController::class, 'store']);
|
|
427
|
+
|
|
428
|
+
Route::middleware('auth:sanctum')->group(function () {
|
|
429
|
+
Route::delete('/tokens', [TokenController::class, 'destroy']);
|
|
430
|
+
|
|
431
|
+
Route::get('/user', function (Request $request) {
|
|
432
|
+
return $request->user();
|
|
433
|
+
});
|
|
434
|
+
});
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
## Multi-Guard Middleware
|
|
438
|
+
|
|
439
|
+
### Admin-Only Routes
|
|
440
|
+
|
|
441
|
+
```php
|
|
442
|
+
Route::middleware('auth:admin')->prefix('admin')->group(function () {
|
|
443
|
+
Route::get('/dashboard', [AdminDashboardController::class, 'index']);
|
|
444
|
+
});
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### Admin Login Controller
|
|
448
|
+
|
|
449
|
+
```php
|
|
450
|
+
public function store(AdminLoginRequest $request): JsonResponse
|
|
451
|
+
{
|
|
452
|
+
if (! Auth::guard('admin')->attempt($request->only('email', 'password'))) {
|
|
453
|
+
throw ValidationException::withMessages([
|
|
454
|
+
'email' => __('auth.failed'),
|
|
455
|
+
]);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
$request->session()->regenerate();
|
|
459
|
+
|
|
460
|
+
return response()->json([
|
|
461
|
+
'admin' => Auth::guard('admin')->user(),
|
|
462
|
+
]);
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
## Common Patterns
|
|
467
|
+
|
|
468
|
+
### Check Authentication
|
|
469
|
+
|
|
470
|
+
```php
|
|
471
|
+
// In controller
|
|
472
|
+
if (Auth::check()) {
|
|
473
|
+
$user = Auth::user();
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// In middleware
|
|
477
|
+
Route::middleware('auth')->group(function () {
|
|
478
|
+
// Protected routes
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
// In Blade
|
|
482
|
+
@auth
|
|
483
|
+
Welcome, {{ Auth::user()->name }}
|
|
484
|
+
@endauth
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Get Current User
|
|
488
|
+
|
|
489
|
+
```php
|
|
490
|
+
// Via Auth facade
|
|
491
|
+
$user = Auth::user();
|
|
492
|
+
$userId = Auth::id();
|
|
493
|
+
|
|
494
|
+
// Via request
|
|
495
|
+
$user = $request->user();
|
|
496
|
+
|
|
497
|
+
// Via helper
|
|
498
|
+
$user = auth()->user();
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### Password Hashing
|
|
502
|
+
|
|
503
|
+
```php
|
|
504
|
+
// Hash password (automatic in Omnify models via cast)
|
|
505
|
+
$user->password = 'plain-text'; // Auto-hashed by 'hashed' cast
|
|
506
|
+
$user->save();
|
|
507
|
+
|
|
508
|
+
// Manual hashing if needed
|
|
509
|
+
use Illuminate\Support\Facades\Hash;
|
|
510
|
+
$hashed = Hash::make('plain-text');
|
|
511
|
+
|
|
512
|
+
// Verify password
|
|
513
|
+
if (Hash::check('plain-text', $user->password)) {
|
|
514
|
+
// Correct
|
|
515
|
+
}
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
## Testing Authentication
|
|
519
|
+
|
|
520
|
+
```php
|
|
521
|
+
use App\Models\User;
|
|
522
|
+
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
523
|
+
|
|
524
|
+
class AuthTest extends TestCase
|
|
525
|
+
{
|
|
526
|
+
use RefreshDatabase;
|
|
527
|
+
|
|
528
|
+
public function test_user_can_login(): void
|
|
529
|
+
{
|
|
530
|
+
$user = User::factory()->create([
|
|
531
|
+
'email' => 'test@example.com',
|
|
532
|
+
'password' => 'password',
|
|
533
|
+
]);
|
|
534
|
+
|
|
535
|
+
$response = $this->postJson('/login', [
|
|
536
|
+
'email' => 'test@example.com',
|
|
537
|
+
'password' => 'password',
|
|
538
|
+
]);
|
|
539
|
+
|
|
540
|
+
$response->assertOk();
|
|
541
|
+
$this->assertAuthenticated();
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
public function test_authenticated_user_can_access_protected_route(): void
|
|
545
|
+
{
|
|
546
|
+
$user = User::factory()->create();
|
|
547
|
+
|
|
548
|
+
$response = $this->actingAs($user)
|
|
549
|
+
->getJson('/api/user');
|
|
550
|
+
|
|
551
|
+
$response->assertOk();
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
public function test_guest_cannot_access_protected_route(): void
|
|
555
|
+
{
|
|
556
|
+
$response = $this->getJson('/api/user');
|
|
557
|
+
|
|
558
|
+
$response->assertUnauthorized();
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
## Future: Trait-based Authentication
|
|
564
|
+
|
|
565
|
+
> **Coming Soon:** In future versions, Omnify may support a trait-based approach:
|
|
566
|
+
>
|
|
567
|
+
> ```php
|
|
568
|
+
> use Illuminate\Database\Eloquent\Model;
|
|
569
|
+
> use Illuminate\Auth\Authenticatable;
|
|
570
|
+
> use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
|
571
|
+
>
|
|
572
|
+
> class UserBaseModel extends Model implements AuthenticatableContract
|
|
573
|
+
> {
|
|
574
|
+
> use Authenticatable; // Trait instead of extending class
|
|
575
|
+
> }
|
|
576
|
+
> ```
|
|
577
|
+
>
|
|
578
|
+
> This will provide more flexibility in model inheritance while maintaining
|
|
579
|
+
> full authentication support.
|
|
580
|
+
|
|
581
|
+
## Summary
|
|
582
|
+
|
|
583
|
+
| Option | Type | Default | When to Use |
|
|
584
|
+
|--------|------|---------|-------------|
|
|
585
|
+
| `authenticatable` | boolean | `false` | User can login |
|
|
586
|
+
| `authenticatableLoginIdField` | string | `email` | Custom login field |
|
|
587
|
+
| `authenticatablePasswordField` | string | `password` | Custom password field |
|
|
588
|
+
| `authenticatableGuardName` | string | (none) | Multi-guard setup |
|