Skip to content

Commit 768cb0b

Browse files
committed
Make passkey primary signin method
1 parent 3bbd23d commit 768cb0b

4 files changed

Lines changed: 120 additions & 109 deletions

File tree

resources/js/alpine/components/passkey-login.js

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,4 @@ Alpine.data('passkeyLogin', () => ({
8888

8989
return '';
9090
},
91-
92-
focusPasswordLogin() {
93-
const username = document.getElementById('username');
94-
if (username) {
95-
username.focus();
96-
return;
97-
}
98-
99-
const password = document.getElementById('password');
100-
if (password) {
101-
password.focus();
102-
}
103-
},
10491
}));

resources/js/alpine/lazy-loader.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const lazyComponentMap = {
3939
'releaseMultiOps': () => import('./components/cart-page.js'), // same file
4040
'authPage': () => import('./components/auth-page.js'),
4141
'otpInput': () => import('./components/auth-page.js'), // same file
42+
'loginMode': () => import('./components/login-mode.js'),
4243
'passkeyLogin': () => import('./components/passkey-login.js'),
4344
'passkeyManage': () => import('./components/passkey-manage.js'),
4445
'adminUserPasskeys': () => import('./components/admin-user-passkeys.js'),

resources/views/auth/login.blade.php

Lines changed: 118 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@
2020

2121
<!-- Login Card -->
2222
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow-xl overflow-hidden">
23-
<div class="px-5 py-5 sm:px-8 sm:py-6">
23+
<div
24+
x-data="loginMode"
25+
x-on:use-password-login.stop="usePassword()"
26+
data-prefers-password="{{ $errors->any() ? '1' : '0' }}"
27+
class="px-5 py-5 sm:px-8 sm:py-6"
28+
>
2429
@if($errors->any())
2530
<div class="mb-4 p-4 rounded-lg bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-700">
2631
<div class="flex items-start">
@@ -37,106 +42,130 @@
3742
</div>
3843
@endif
3944

40-
<!-- Login Form -->
41-
<form method="POST" action="{{ route('login') }}" class="space-y-6">
42-
@csrf
43-
44-
<!-- Username/Email Field -->
45-
<div>
46-
<label for="username" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
47-
Username or Email
48-
</label>
49-
<div class="relative">
50-
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
51-
<i class="fas fa-user text-gray-400"></i>
45+
<div x-show="!showPassword" x-cloak>
46+
@include('partials.passkey-authenticate')
47+
48+
<button
49+
type="button"
50+
x-show="supported"
51+
@click="usePassword()"
52+
class="mt-4 inline-flex items-center text-sm font-medium text-blue-600 transition hover:text-blue-500 dark:text-blue-400"
53+
>
54+
Sign in with password instead
55+
<i class="fas fa-arrow-right ml-2"></i>
56+
</button>
57+
</div>
58+
59+
<div x-show="showPassword" x-cloak>
60+
<button
61+
type="button"
62+
x-show="supported"
63+
@click="usePasskey()"
64+
class="mb-4 inline-flex items-center text-sm font-medium text-blue-600 transition hover:text-blue-500 dark:text-blue-400"
65+
>
66+
<i class="fas fa-arrow-left mr-2"></i>
67+
Sign in with passkey
68+
</button>
69+
70+
<!-- Login Form -->
71+
<form method="POST" action="{{ route('login') }}" class="space-y-6">
72+
@csrf
73+
74+
<!-- Username/Email Field -->
75+
<div>
76+
<label for="username" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
77+
Username or Email
78+
</label>
79+
<div class="relative">
80+
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
81+
<i class="fas fa-user text-gray-400"></i>
82+
</div>
83+
<input
84+
id="username"
85+
type="text"
86+
name="username"
87+
value="{{ old('username') }}"
88+
required
89+
autofocus
90+
class="block w-full pl-10 pr-3 py-3 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition @error('username') border-red-500 @enderror"
91+
placeholder="Enter your username or email"
92+
>
5293
</div>
53-
<input
54-
id="username"
55-
type="text"
56-
name="username"
57-
value="{{ old('username') }}"
58-
required
59-
autofocus
60-
class="block w-full pl-10 pr-3 py-3 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition @error('username') border-red-500 @enderror"
61-
placeholder="Enter your username or email"
62-
>
94+
@error('username')
95+
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
96+
@enderror
6397
</div>
64-
@error('username')
65-
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
66-
@enderror
67-
</div>
6898

69-
<!-- Password Field -->
70-
<div>
71-
<label for="password" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
72-
Password
73-
</label>
74-
<div class="relative">
75-
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
76-
<i class="fas fa-lock text-gray-400"></i>
99+
<!-- Password Field -->
100+
<div>
101+
<label for="password" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
102+
Password
103+
</label>
104+
<div class="relative">
105+
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
106+
<i class="fas fa-lock text-gray-400"></i>
107+
</div>
108+
<input
109+
id="password"
110+
type="password"
111+
name="password"
112+
required
113+
class="block w-full pl-10 pr-10 py-3 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition @error('password') border-red-500 @enderror"
114+
placeholder="Enter your password"
115+
>
116+
<button type="button" class="password-toggle-btn absolute inset-y-0 right-0 flex items-center px-3 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200" data-field-id="password">
117+
<i class="fas fa-eye" id="password-eye"></i>
118+
</button>
77119
</div>
78-
<input
79-
id="password"
80-
type="password"
81-
name="password"
82-
required
83-
class="block w-full pl-10 pr-10 py-3 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition @error('password') border-red-500 @enderror"
84-
placeholder="Enter your password"
85-
>
86-
<button type="button" class="password-toggle-btn absolute inset-y-0 right-0 flex items-center px-3 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200" data-field-id="password">
87-
<i class="fas fa-eye" id="password-eye"></i>
88-
</button>
120+
@error('password')
121+
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
122+
@enderror
89123
</div>
90-
@error('password')
91-
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
92-
@enderror
93-
</div>
94124

95-
<!-- Remember Me -->
96-
<div class="flex items-center justify-between">
97-
<div class="flex items-center">
98-
<input
99-
id="rememberme"
100-
name="rememberme"
101-
type="checkbox"
102-
{{ old('rememberme') ? 'checked' : '' }}
103-
class="h-4 w-4 text-blue-600 dark:text-blue-400 focus:ring-blue-500 border-gray-300 dark:border-gray-600 rounded"
104-
>
105-
<label for="rememberme" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">
106-
Remember me
107-
</label>
125+
<!-- Remember Me -->
126+
<div class="flex items-center justify-between">
127+
<div class="flex items-center">
128+
<input
129+
id="rememberme"
130+
name="rememberme"
131+
type="checkbox"
132+
{{ old('rememberme') ? 'checked' : '' }}
133+
class="h-4 w-4 text-blue-600 dark:text-blue-400 focus:ring-blue-500 border-gray-300 dark:border-gray-600 rounded"
134+
>
135+
<label for="rememberme" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">
136+
Remember me
137+
</label>
138+
</div>
139+
140+
@if(Route::has('forgottenpassword'))
141+
<a href="{{ route('forgottenpassword') }}" class="text-sm font-medium text-blue-600 dark:text-blue-400 hover:text-blue-500 transition">
142+
Forgot password?
143+
</a>
144+
@endif
108145
</div>
109146

110-
@if(Route::has('forgottenpassword'))
111-
<a href="{{ route('forgottenpassword') }}" class="text-sm font-medium text-blue-600 dark:text-blue-400 hover:text-blue-500 transition">
112-
Forgot password?
113-
</a>
147+
<!-- CAPTCHA (reCAPTCHA or Turnstile) -->
148+
@if(\App\Support\CaptchaHelper::isEnabled())
149+
<div>
150+
{!! \App\Support\CaptchaHelper::display() !!}
151+
</div>
152+
@error(\App\Support\CaptchaHelper::getResponseFieldName())
153+
<p class="mt-2 text-sm text-red-600 dark:text-red-400">{{ $message }}</p>
154+
@enderror
114155
@endif
115-
</div>
116156

117-
<!-- CAPTCHA (reCAPTCHA or Turnstile) -->
118-
@if(\App\Support\CaptchaHelper::isEnabled())
157+
<!-- Submit Button -->
119158
<div>
120-
{!! \App\Support\CaptchaHelper::display() !!}
159+
<button
160+
type="submit"
161+
class="w-full flex justify-center items-center py-3 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-blue-600 dark:bg-blue-700 hover:bg-blue-700 dark:hover:bg-blue-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition duration-150 ease-in-out"
162+
>
163+
<i class="fas fa-sign-in-alt mr-2"></i>
164+
Sign In
165+
</button>
121166
</div>
122-
@error(\App\Support\CaptchaHelper::getResponseFieldName())
123-
<p class="mt-2 text-sm text-red-600 dark:text-red-400">{{ $message }}</p>
124-
@enderror
125-
@endif
126-
127-
<!-- Submit Button -->
128-
<div>
129-
<button
130-
type="submit"
131-
class="w-full flex justify-center items-center py-3 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-blue-600 dark:bg-blue-700 hover:bg-blue-700 dark:hover:bg-blue-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition duration-150 ease-in-out"
132-
>
133-
<i class="fas fa-sign-in-alt mr-2"></i>
134-
Sign In
135-
</button>
136-
</div>
137-
</form>
138-
139-
@include('partials.passkey-authenticate')
167+
</form>
168+
</div>
140169
</div>
141170

142171
<!-- Card Footer -->

resources/views/partials/passkey-authenticate.blade.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
11
<div
22
x-data="passkeyLogin"
33
x-cloak
4-
x-show="supported"
54
data-options-url="{{ route('passkeys.authentication_options') }}"
65
data-server-passkey-error="{{ session('authenticatePasskey::reason') === 'invalid_passkey' ? '1' : '0' }}"
76
data-captcha-enabled="{{ \App\Support\CaptchaHelper::isEnabled() ? '1' : '0' }}"
87
data-captcha-field="{{ \App\Support\CaptchaHelper::isEnabled() ? \App\Support\CaptchaHelper::getResponseFieldName() : '' }}"
98
class="mt-6"
109
>
11-
<div class="relative flex items-center">
12-
<div class="flex-grow border-t border-gray-300 dark:border-gray-600"></div>
13-
<span class="mx-4 flex-shrink text-xs uppercase text-gray-500 dark:text-gray-400">or</span>
14-
<div class="flex-grow border-t border-gray-300 dark:border-gray-600"></div>
15-
</div>
1610
<p class="mt-3 text-xs text-gray-500 dark:text-gray-400">
1711
On managed/company devices, platform passkeys may be unavailable due to policy. Use a FIDO2 security key if prompted.
1812
</p>
@@ -58,7 +52,7 @@ class="mt-3 rounded-lg border border-blue-200 bg-blue-50 p-3 text-sm text-blue-8
5852
<div class="mt-2 flex flex-wrap gap-2">
5953
<button
6054
type="button"
61-
@click="focusPasswordLogin()"
55+
@click="$dispatch('use-password-login')"
6256
class="rounded-md border border-blue-300 px-3 py-1.5 text-xs font-medium text-blue-800 hover:bg-blue-100 dark:border-blue-600 dark:text-blue-200 dark:hover:bg-blue-900/40"
6357
>
6458
Use password login

0 commit comments

Comments
 (0)