diff --git a/framework/core/js/forum/src/components/LogInButton.js b/framework/core/js/forum/src/components/LogInButton.js
index 3de450589..f936204fb 100644
--- a/framework/core/js/forum/src/components/LogInButton.js
+++ b/framework/core/js/forum/src/components/LogInButton.js
@@ -13,8 +13,8 @@ export default class LogInButton extends Button {
props.className = (props.className || '') + ' LogInButton';
props.onclick = function() {
- const width = 620;
- const height = 400;
+ const width = 1000;
+ const height = 500;
const $window = $(window);
window.open(app.forum.attribute('baseUrl') + props.path, 'logInPopup',
diff --git a/framework/core/js/forum/src/components/SignUpModal.js b/framework/core/js/forum/src/components/SignUpModal.js
index b66e35f3d..19899560e 100644
--- a/framework/core/js/forum/src/components/SignUpModal.js
+++ b/framework/core/js/forum/src/components/SignUpModal.js
@@ -82,7 +82,7 @@ export default class SignUpModal extends Modal {
+ disabled={this.loading || (this.props.token && this.props.email)} />
{this.props.token ? '' : (
@@ -121,7 +121,8 @@ export default class SignUpModal extends Modal {
{avatar(user)}
{app.trans('core.welcome_user', {user})}
- {app.trans('core.confirmation_email_sent', {email: {user.email()}})}
,
+ {app.trans('core.confirmation_email_sent', {email: {user.email()}})}
+
{app.trans('core.go_to', {location: emailProviderName})}
@@ -161,7 +162,7 @@ export default class SignUpModal extends Modal {
}
onready() {
- if (this.props.username && !this.props.token) {
+ if (this.props.username && !this.props.email) {
this.$('[name=email]').select();
} else {
this.$('[name=username]').select();
diff --git a/framework/core/migrations/2015_09_15_000000_make_email_tokens_user_id_column_nullable.php b/framework/core/migrations/2015_09_15_000000_create_auth_tokens_table.php
similarity index 61%
rename from framework/core/migrations/2015_09_15_000000_make_email_tokens_user_id_column_nullable.php
rename to framework/core/migrations/2015_09_15_000000_create_auth_tokens_table.php
index 5e8ced5e1..e5e1d55f8 100644
--- a/framework/core/migrations/2015_09_15_000000_make_email_tokens_user_id_column_nullable.php
+++ b/framework/core/migrations/2015_09_15_000000_create_auth_tokens_table.php
@@ -12,7 +12,7 @@
use Flarum\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
-class MakeEmailTokensUserIdColumnNullable extends Migration
+class CreateAuthTokensTable extends Migration
{
/**
* Run the migrations.
@@ -21,8 +21,10 @@ class MakeEmailTokensUserIdColumnNullable extends Migration
*/
public function up()
{
- $this->schema->table('email_tokens', function (Blueprint $table) {
- $table->integer('user_id')->unsigned()->nullable()->change();
+ $this->schema->create('auth_tokens', function (Blueprint $table) {
+ $table->string('id', 100)->primary();
+ $table->string('payload', 150);
+ $table->timestamp('created_at');
});
}
@@ -33,8 +35,6 @@ class MakeEmailTokensUserIdColumnNullable extends Migration
*/
public function down()
{
- $this->schema->table('email_tokens', function (Blueprint $table) {
- $table->integer('user_id')->unsigned()->change();
- });
+ $this->schema->drop('auth_tokens');
}
}
diff --git a/framework/core/src/Core/Users/AuthToken.php b/framework/core/src/Core/Users/AuthToken.php
new file mode 100644
index 000000000..04c9f7174
--- /dev/null
+++ b/framework/core/src/Core/Users/AuthToken.php
@@ -0,0 +1,98 @@
+
+ *
+ * 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;
+ }
+}
diff --git a/framework/core/src/Core/Users/Commands/RegisterUserHandler.php b/framework/core/src/Core/Users/Commands/RegisterUserHandler.php
index 132a400e0..9d064c748 100644
--- a/framework/core/src/Core/Users/Commands/RegisterUserHandler.php
+++ b/framework/core/src/Core/Users/Commands/RegisterUserHandler.php
@@ -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));
diff --git a/framework/core/src/Core/Users/EmailToken.php b/framework/core/src/Core/Users/EmailToken.php
index eb61b0cd0..a5d03d4bb 100644
--- a/framework/core/src/Core/Users/EmailToken.php
+++ b/framework/core/src/Core/Users/EmailToken.php
@@ -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;
diff --git a/framework/core/src/Forum/Actions/ExternalAuthenticatorTrait.php b/framework/core/src/Forum/Actions/ExternalAuthenticatorTrait.php
index 0ed0337ef..601a8c566 100644
--- a/framework/core/src/Forum/Actions/ExternalAuthenticatorTrait.php
+++ b/framework/core/src/Forum/Actions/ExternalAuthenticatorTrait.php
@@ -13,7 +13,7 @@ namespace Flarum\Forum\Actions;
use Flarum\Core\Users\User;
use Zend\Diactoros\Response\HtmlResponse;
use Flarum\Api\Commands\GenerateAccessToken;
-use Flarum\Core\Users\EmailToken;
+use Flarum\Core\Users\AuthToken;
trait ExternalAuthenticatorTrait
{
@@ -25,33 +25,42 @@ trait ExternalAuthenticatorTrait
protected $bus;
/**
- * Respond with JavaScript to tell the Flarum app that the user has been
- * authenticated, or with information about their sign up status.
+ * Respond with JavaScript to inform the Flarum app about the user's
+ * authentication status.
*
- * @param string $email The email of the user's account.
- * @param string $username A suggested username for the user's account.
+ * An array of identification attributes must be passed as the first
+ * argument. These are checked against existing user accounts; if a match is
+ * found, then the user is authenticated and logged into that account via
+ * cookie. The Flarum app will then simply refresh the page.
+ *
+ * If no matching account is found, then an AuthToken will be generated to
+ * store the identification attributes. This token, along with an optional
+ * array of suggestions, will be passed into the Flarum app's sign up modal.
+ * This results in the user not having to choose a password. When they
+ * complete their registration, the identification attributes will be
+ * set on their new user account.
+ *
+ * @param array $identification
+ * @param array $suggestions
* @return HtmlResponse
*/
- protected function authenticated($email, $username)
+ protected function authenticated(array $identification, array $suggestions = [])
{
- $user = User::where('email', $email)->first();
+ $user = User::where($identification)->first();
- // If a user with this email address doesn't already exist, then we will
- // generate a unique confirmation token for this email address and add
- // it to the response, along with the email address and a suggested
- // username. Otherwise, we will log in the existing user by generating
- // an access token.
- if (! $user) {
- $token = EmailToken::generate($email);
- $token->save();
-
- $payload = compact('email', 'username');
-
- $payload['token'] = $token->id;
- } else {
+ // If a user with these attributes already exists, then we will log them
+ // in by generating an access token. Otherwise, we will generate a
+ // unique token for these attributes and add it to the response, along
+ // with the suggested account information.
+ if ($user) {
$accessToken = $this->bus->dispatch(new GenerateAccessToken($user->id));
$payload = ['authenticated' => true];
+ } else {
+ $token = AuthToken::generate($identification);
+ $token->save();
+
+ $payload = array_merge($identification, $suggestions, ['token' => $token->id]);
}
$content = sprintf('