Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 5 additions & 30 deletions app/Http/Controllers/Auth/LoginController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Services\SocialUserLoginService;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Laravel\Socialite\Facades\Socialite;

class LoginController extends Controller
Expand Down Expand Up @@ -40,8 +39,9 @@ class LoginController extends Controller
*
* @return void
*/
public function __construct()
{
public function __construct(
private readonly SocialUserLoginService $socialUserLoginService,
) {
$this->middleware('guest')->except('logout');
}

Expand Down Expand Up @@ -83,41 +83,16 @@ public function handleProviderCallback($provider): RedirectResponse
*/
public function loginUser($provider, $socialUser)
{
$user = \App\User::where(['email' => $socialUser->getEmail()])->first();

if (is_null($socialUser->getEmail())) {
// if ($socialUser->getEmail() == 'alainvd@gmail.com'){
Log::info('Null email detected');
Log::info(print_r($socialUser, true));
$admin = config('codeweek.administrator');
Mail::to($admin)->send(new \App\Mail\WarningEmail(print_r($socialUser, true)));
abort(500);
}

if ($user == null) {
//Create user
$user = \App\User::create(
[
'email' => $socialUser->getEmail(),
//'avatar' => $socialUser->getAvatar(),
'password' => bcrypt(Str::random()),
'firstname' => ($socialUser->getName()) ? $socialUser->getName() : $socialUser->getNickName(),
'lastname' => '',
'username' => ($socialUser->getNickName()) ? $socialUser->getNickName() : '',
'provider' => $provider,
'magic_key' => random_int(1000000, 2000000) * random_int(1000, 2000),
'email_verified_at' => Carbon::now()
]);

} else {
//update user
$user->provider = $provider;
$user->updated_at = Carbon::now();
$user->email_verified_at = Carbon::now();
$user->save();
}
$user = $this->socialUserLoginService->resolveOrCreateUser($provider, $socialUser);

//login with user
Auth::login($user, true);
}
}
69 changes: 69 additions & 0 deletions app/Http/Controllers/UserEmailChangeController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

namespace App\Http\Controllers;

use App\Services\UserEmailChangeService;
use App\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;

class UserEmailChangeController extends Controller
{
public function __construct(
private readonly UserEmailChangeService $emailChangeService,
) {
}

public function request(Request $request): RedirectResponse
{
$user = $request->user();

$validated = $request->validate([
'new_email' => 'required|email|max:255',
'current_password' => $this->emailChangeService->requiresPassword($user)
? 'required|string|max:72'
: 'nullable|string|max:72',
]);

$this->emailChangeService->requestChange(
$user,
$validated['new_email'],
$validated['current_password'] ?? null,
);

return back()->with(
'email_change_status',
'We sent a confirmation link to '.$user->fresh()->pending_email.'. Click it to finish updating your login email.',
);
}

public function confirm(Request $request, User $user, string $token): RedirectResponse
{
try {
$updatedUser = $this->emailChangeService->confirmChange((int) $user->id, $token);
} catch (ValidationException $exception) {
return redirect()
->route('login')
->withErrors($exception->errors());
}

if (Auth::check() && Auth::id() === $updatedUser->id) {
return redirect()
->route('profile')
->with('flash', 'Your login email has been updated to '.$updatedUser->email.'.');
}

return redirect()
->route('login')
->with('flash', 'Your login email has been updated. Please sign in with '.$updatedUser->email.'.');
}

public function cancel(Request $request): RedirectResponse
{
$this->emailChangeService->cancelPending($request->user());

return back()->with('email_change_status', 'Your pending email change has been cancelled.');
}
}
40 changes: 40 additions & 0 deletions app/Mail/PendingEmailChangeConfirmation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace App\Mail;

use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class PendingEmailChangeConfirmation extends Mailable
{
use Queueable, SerializesModels;

public function __construct(
public User $user,
public string $confirmUrl,
) {
}

public function envelope(): Envelope
{
return new Envelope(
subject: 'Confirm your new CodeWeek login email',
);
}

public function content(): Content
{
return new Content(
markdown: 'emails.en.pending-email-change-confirmation',
);
}

public function attachments(): array
{
return [];
}
}
40 changes: 40 additions & 0 deletions app/Mail/PendingEmailChangeNotification.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace App\Mail;

use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class PendingEmailChangeNotification extends Mailable
{
use Queueable, SerializesModels;

public function __construct(
public User $user,
public string $newEmail,
) {
}

public function envelope(): Envelope
{
return new Envelope(
subject: 'CodeWeek login email change requested',
);
}

public function content(): Content
{
return new Content(
markdown: 'emails.en.pending-email-change-notification',
);
}

public function attachments(): array
{
return [];
}
}
56 changes: 56 additions & 0 deletions app/Services/SocialUserLoginService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace App\Services;

use App\Services\Support\SupportEmailAddress;
use App\User;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
use Laravel\Socialite\Contracts\User as SocialiteUser;

class SocialUserLoginService
{
/**
* Find or create a local user for an OAuth login.
*
* Looks up by stable provider ID first so a changed login email does not
* cause a duplicate account when the provider still returns the old address.
*/
public function resolveOrCreateUser(string $provider, SocialiteUser $socialUser): User
{
$providerId = (string) $socialUser->getId();
$oauthEmail = SupportEmailAddress::normalize((string) $socialUser->getEmail());

$user = User::query()
->where('provider', $provider)
->where('provider_id', $providerId)
->first();

if ($user === null && $oauthEmail !== null) {
$user = User::query()
->whereRaw('LOWER(email) = ?', [$oauthEmail])
->first();
}

if ($user === null) {
return User::create([
'email' => $oauthEmail,
'password' => bcrypt(Str::random()),
'firstname' => $socialUser->getName() ?: $socialUser->getNickname(),
'lastname' => '',
'username' => $socialUser->getNickname() ?: '',
'provider' => $provider,
'provider_id' => $providerId,
'magic_key' => random_int(1000000, 2000000) * random_int(1000, 2000),
'email_verified_at' => Carbon::now(),
]);
}

$user->provider = $provider;
$user->provider_id = $providerId;
$user->email_verified_at = Carbon::now();
$user->save();

return $user;
}
}
Loading
Loading