Skip to content

Commit 324b989

Browse files
committed
Get a working rough copy of the login page
1 parent 9471093 commit 324b989

File tree

13 files changed

+266
-69
lines changed

13 files changed

+266
-69
lines changed

app/Http/Controllers/Auth/LoginController.php

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
use Illuminate\Http\Request;
66
use Illuminate\Auth\AuthManager;
7+
use Illuminate\Http\JsonResponse;
78
use PragmaRX\Google2FA\Google2FA;
89
use Illuminate\Auth\Events\Failed;
9-
use Illuminate\Http\RedirectResponse;
10+
use Pterodactyl\Exceptions\DisplayException;
1011
use Pterodactyl\Http\Controllers\Controller;
1112
use Illuminate\Contracts\Auth\Authenticatable;
1213
use Illuminate\Contracts\Encryption\Encrypter;
@@ -106,11 +107,12 @@ public function __construct(
106107
* Handle a login request to the application.
107108
*
108109
* @param \Illuminate\Http\Request $request
109-
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response
110+
* @return \Illuminate\Http\JsonResponse
110111
*
112+
* @throws \Pterodactyl\Exceptions\DisplayException
111113
* @throws \Illuminate\Validation\ValidationException
112114
*/
113-
public function login(Request $request)
115+
public function login(Request $request): JsonResponse
114116
{
115117
$username = $request->input(self::USER_INPUT_FIELD);
116118
$useColumn = $this->getField($username);
@@ -128,37 +130,28 @@ public function login(Request $request)
128130

129131
$validCredentials = password_verify($request->input('password'), $user->password);
130132
if ($user->use_totp) {
131-
$token = str_random(64);
132-
$this->cache->put($token, ['user_id' => $user->id, 'valid_credentials' => $validCredentials], 5);
133-
134-
return redirect()->route('auth.totp')->with('authentication_token', $token);
133+
$token = str_random(128);
134+
$this->cache->put($token, [
135+
'user_id' => $user->id,
136+
'valid_credentials' => $validCredentials,
137+
'request_ip' => $request->ip(),
138+
], 5);
139+
140+
return response()->json([
141+
'complete' => false,
142+
'token' => $token,
143+
]);
135144
}
136145

137146
if ($validCredentials) {
138147
$this->auth->guard()->login($user, true);
139148

140-
return $this->sendLoginResponse($request);
149+
return response()->json(['complete' => true]);
141150
}
142151

143152
return $this->sendFailedLoginResponse($request, $user);
144153
}
145154

146-
/**
147-
* Handle a TOTP implementation page.
148-
*
149-
* @param \Illuminate\Http\Request $request
150-
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
151-
*/
152-
public function totp(Request $request)
153-
{
154-
$token = $request->session()->get('authentication_token');
155-
if (is_null($token) || $this->auth->guard()->user()) {
156-
return redirect()->route('auth.login');
157-
}
158-
159-
return view('auth.totp', ['verify_key' => $token]);
160-
}
161-
162155
/**
163156
* Handle a login where the user is required to provide a TOTP authentication
164157
* token. In order to add additional layers of security, users are not
@@ -167,27 +160,29 @@ public function totp(Request $request)
167160
*
168161
* @param \Illuminate\Http\Request $request
169162
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response
163+
*
164+
* @throws \Pterodactyl\Exceptions\DisplayException
170165
*/
171-
public function loginUsingTotp(Request $request)
166+
public function loginCheckpoint(Request $request)
172167
{
173-
if (is_null($request->input('verify_token'))) {
168+
if (is_null($request->input('confirmation_token')) || is_null($request->input('authentication_code'))) {
174169
return $this->sendFailedLoginResponse($request);
175170
}
176171

177172
try {
178-
$cache = $this->cache->pull($request->input('verify_token'), []);
173+
$cache = $this->cache->pull($request->input('confirmation_token'), []);
179174
$user = $this->repository->find(array_get($cache, 'user_id', 0));
180175
} catch (RecordNotFoundException $exception) {
181176
return $this->sendFailedLoginResponse($request);
182177
}
183178

184-
if (is_null($request->input('2fa_token')) || ! array_get($cache, 'valid_credentials')) {
179+
if (! array_get($cache, 'valid_credentials') || array_get($cache, 'request_ip') !== $request->ip()) {
185180
return $this->sendFailedLoginResponse($request, $user);
186181
}
187182

188183
if (! $this->google2FA->verifyKey(
189184
$this->encrypter->decrypt($user->totp_secret),
190-
$request->input('2fa_token'),
185+
$request->input('authentication_code'),
191186
$this->config->get('pterodactyl.auth.2fa.window')
192187
)) {
193188
return $this->sendFailedLoginResponse($request, $user);
@@ -203,24 +198,35 @@ public function loginUsingTotp(Request $request)
203198
*
204199
* @param \Illuminate\Http\Request $request
205200
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
206-
* @return \Illuminate\Http\RedirectResponse
201+
*
202+
* @throws \Pterodactyl\Exceptions\DisplayException
207203
*/
208-
protected function sendFailedLoginResponse(Request $request, Authenticatable $user = null): RedirectResponse
204+
protected function sendFailedLoginResponse(Request $request, Authenticatable $user = null)
209205
{
210206
$this->incrementLoginAttempts($request);
211207
$this->fireFailedLoginEvent($user, [
212208
$this->getField($request->input(self::USER_INPUT_FIELD)) => $request->input(self::USER_INPUT_FIELD),
213209
]);
214210

215-
$errors = [self::USER_INPUT_FIELD => trans('auth.failed')];
211+
throw new DisplayException(trans('auth.failed'));
212+
}
216213

217-
if ($request->expectsJson()) {
218-
return response()->json($errors, 422);
219-
}
214+
/**
215+
* Send the response after the user was authenticated.
216+
*
217+
* @param \Illuminate\Http\Request $request
218+
* @return \Illuminate\Http\Response
219+
*/
220+
protected function sendLoginResponse(Request $request)
221+
{
222+
$request->session()->regenerate();
223+
224+
$this->clearLoginAttempts($request);
220225

221-
return redirect()->route('auth.login')
222-
->withInput($request->only(self::USER_INPUT_FIELD))
223-
->withErrors($errors);
226+
return $this->authenticated($request, $this->guard()->user())
227+
?: response()->json([
228+
'intended' => $this->redirectPath(),
229+
]);
224230
}
225231

226232
/**

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"vue-template-compiler": "^2.5.16",
3939
"vueify-insert-css": "^1.0.0",
4040
"vuex": "^3.0.1",
41+
"vuex-flash": "^1.0.0",
4142
"vuex-i18n": "^1.10.5",
4243
"webpack": "^4.4.1",
4344
"webpack-stream": "^4.0.3",

resources/assets/pterodactyl/scripts/app.js

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import Vue from 'vue';
22
import Vuex from 'vuex';
33
import vuexI18n from 'vuex-i18n';
4+
import VuexFlash from 'vuex-flash';
5+
import { createFlashStore } from 'vuex-flash';
46
import VueRouter from 'vue-router';
57

68
// Helpers
@@ -15,7 +17,11 @@ window.Ziggy = Ziggy;
1517

1618
Vue.use(Vuex);
1719

18-
const store = new Vuex.Store();
20+
const store = new Vuex.Store({
21+
plugins: [
22+
createFlashStore(),
23+
],
24+
});
1925
const route = require('./../../../../vendor/tightenco/ziggy/src/js/route').default;
2026

2127
Vue.config.productionTip = false;
@@ -26,6 +32,10 @@ Vue.mixin({
2632
});
2733

2834
Vue.use(VueRouter);
35+
Vue.use(VuexFlash, {
36+
mixin: true,
37+
template: require('./components/errors/Flash.template')
38+
});
2939
Vue.use(vuexI18n.plugin, store);
3040

3141
Vue.i18n.add('en', Locales.en);
@@ -34,9 +44,20 @@ Vue.i18n.set('en');
3444
const router = new VueRouter({
3545
routes: [
3646
{
37-
path: '/:action?',
47+
name: 'login',
48+
path: '/',
49+
component: Login,
50+
},
51+
{
52+
name: 'forgot-password',
53+
path: '/forgot-password',
54+
component: Login,
55+
},
56+
{
57+
name: 'checkpoint',
58+
path: '/checkpoint',
3859
component: Login,
39-
}
60+
},
4061
]
4162
});
4263

resources/assets/pterodactyl/scripts/components/auth/ForgotPassword.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<form class="bg-white shadow-lg rounded-lg pt-10 px-8 pb-6 mb-4 animate fadein" method="post">
44
<div class="flex flex-wrap -mx-3 mb-6">
55
<div class="input-open">
6-
<input class="input" id="grid-email" type="email" aria-labelledby="grid-email" required
6+
<input class="input" id="grid-email" type="email" aria-labelledby="grid-email" ref="email" required
77
v-bind:value="email"
88
v-on:input="updateEmail($event)"
99
/>
@@ -27,14 +27,17 @@
2727
</template>
2828

2929
<script>
30-
import Csrf from "../shared/CSRF";
30+
import Csrf from "../forms/CSRF";
3131
3232
export default {
3333
components: {Csrf},
3434
name: 'forgot-password',
3535
props: {
3636
email: {type: String, required: true},
3737
},
38+
mounted: function () {
39+
this.$refs.email.focus();
40+
},
3841
data: function () {
3942
return {
4043
X_CSRF_TOKEN: window.X_CSRF_TOKEN,
Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,44 @@
11
<template>
22
<div>
33
<login-form
4-
v-if="this.$route.path === '/'"
5-
v-bind:email="email"
6-
v-on:update-email="onEmailUpdate"
4+
v-if="this.$route.name === 'login'"
5+
v-bind:user="user"
6+
v-on:update-email="onUpdateEmail"
77
/>
88
<forgot-password
9-
v-if="this.$route.path === '/forgot-password'"
10-
v-bind:email="email"
11-
v-on:update-email="onEmailUpdate"
9+
v-if="this.$route.name === 'forgot-password'"
10+
v-bind:email="user.email"
11+
v-on:update-email="onUpdateEmail"
12+
/>
13+
<two-factor-form
14+
v-if="this.$route.name === 'checkpoint'"
1215
/>
1316
</div>
1417
</template>
1518

1619
<script>
1720
import LoginForm from "./LoginForm";
1821
import ForgotPassword from "./ForgotPassword";
22+
import TwoFactorForm from "./TwoFactorForm";
1923
2024
export default {
2125
name: 'login',
2226
data: function () {
2327
return {
24-
email: '',
28+
user: {
29+
email: ''
30+
},
2531
};
2632
},
2733
methods: {
28-
onEmailUpdate: function (value) {
29-
this.$data.email = value;
34+
onUpdateEmail: function (value) {
35+
this.$data.user.email = value;
3036
},
3137
},
3238
components: {
39+
TwoFactorForm,
3340
ForgotPassword,
34-
LoginForm
41+
LoginForm,
3542
},
3643
}
3744
</script>

0 commit comments

Comments
 (0)