mirror of
https://github.com/flarum/core.git
synced 2025-08-02 22:47:33 +02:00
Improve external authentication API
Some providers (e.g. Twitter) don't expose user email addresses, so it turns out we can't use that as the sole form of identification/account matching. This commit introduces a new `auth_tokens` table which stores arbitrary attributes during the sign up process. For example, when Twitter is authenticated, a new auth token containing the user's Twitter ID will be created. When sign up is completed with this token, that Twitter ID will be set as an attribute on the user's account.
This commit is contained in:
98
src/Core/Users/AuthToken.php
Normal file
98
src/Core/Users/AuthToken.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Core\Users;
|
||||
|
||||
use Flarum\Core\Model;
|
||||
use Flarum\Core\Exceptions\InvalidConfirmationTokenException;
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* @todo document database columns with @property
|
||||
*/
|
||||
class AuthToken extends Model
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $table = 'auth_tokens';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $dates = ['created_at'];
|
||||
|
||||
/**
|
||||
* Use a custom primary key for this model.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $incrementing = false;
|
||||
|
||||
/**
|
||||
* Generate an email token for the specified user.
|
||||
*
|
||||
* @param string $email
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function generate($payload)
|
||||
{
|
||||
$token = new static;
|
||||
|
||||
$token->id = str_random(40);
|
||||
$token->payload = $payload;
|
||||
$token->created_at = time();
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unserialize the payload attribute from the database's JSON value.
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public function getPayloadAttribute($value)
|
||||
{
|
||||
return json_decode($value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the payload attribute to be stored in the database as JSON.
|
||||
*
|
||||
* @param string $value
|
||||
*/
|
||||
public function setPayloadAttribute($value)
|
||||
{
|
||||
$this->attributes['payload'] = json_encode($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the token with the given ID, and assert that it has not expired.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param string $id
|
||||
*
|
||||
* @throws InvalidConfirmationTokenException
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function scopeValidOrFail($query, $id)
|
||||
{
|
||||
$token = $query->find($id);
|
||||
|
||||
if (! $token || $token->created_at < new DateTime('-1 day')) {
|
||||
throw new InvalidConfirmationTokenException;
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
}
|
@@ -11,7 +11,7 @@
|
||||
namespace Flarum\Core\Users\Commands;
|
||||
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Core\Users\EmailToken;
|
||||
use Flarum\Core\Users\AuthToken;
|
||||
use Flarum\Events\UserWillBeSaved;
|
||||
use Flarum\Core\Support\DispatchesEvents;
|
||||
use Flarum\Core\Settings\SettingsRepository;
|
||||
@@ -54,30 +54,36 @@ class RegisterUserHandler
|
||||
throw new PermissionDeniedException;
|
||||
}
|
||||
|
||||
// If a valid email confirmation token was provided as an attribute,
|
||||
// then we can create a random password for this user and consider their
|
||||
// email address confirmed.
|
||||
if (isset($data['attributes']['token'])) {
|
||||
$token = EmailToken::whereNull('user_id')->validOrFail($data['attributes']['token']);
|
||||
$username = array_get($data, 'attributes.username');
|
||||
$email = array_get($data, 'attributes.email');
|
||||
$password = array_get($data, 'attributes.password');
|
||||
|
||||
$email = $token->email;
|
||||
$password = array_get($data, 'attributes.password', str_random(20));
|
||||
} else {
|
||||
$email = array_get($data, 'attributes.email');
|
||||
$password = array_get($data, 'attributes.password');
|
||||
// If a valid authentication token was provided as an attribute,
|
||||
// then we won't require the user to choose a password.
|
||||
if (isset($data['attributes']['token'])) {
|
||||
$token = AuthToken::validOrFail($data['attributes']['token']);
|
||||
|
||||
$password = $password ?: str_random(20);
|
||||
}
|
||||
|
||||
// Create the user's new account. If their email was set via token, then
|
||||
// we can activate their account from the get-go, and they won't need
|
||||
// to confirm their email address.
|
||||
$user = User::register(
|
||||
array_get($data, 'attributes.username'),
|
||||
$username,
|
||||
$email,
|
||||
$password
|
||||
);
|
||||
|
||||
// If a valid authentication token was provided, then we will assign
|
||||
// the attributes associated with it to the user's account. If this
|
||||
// includes an email address, then we will activate the user's account
|
||||
// from the get-go.
|
||||
if (isset($token)) {
|
||||
$user->activate();
|
||||
foreach ($token->payload as $k => $v) {
|
||||
$user->$k = $v;
|
||||
}
|
||||
|
||||
if (isset($token->payload['email'])) {
|
||||
$user->activate();
|
||||
}
|
||||
}
|
||||
|
||||
event(new UserWillBeSaved($user, $actor, $data));
|
||||
|
@@ -44,7 +44,7 @@ class EmailToken extends Model
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function generate($email, $userId = null)
|
||||
public static function generate($email, $userId)
|
||||
{
|
||||
$token = new static;
|
||||
|
||||
|
Reference in New Issue
Block a user