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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ prepros-6.config
.ddev

# Test Caching / Logging
tests/.env
tests/_craft/config/project/*
tests/_craft/storage/logs/*
tests/_output/*
Expand Down
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,36 @@ We are also aware with this plugin that sometimes you may want to inject the exi

`{% include 'magic-login/_login-form' %}`

### Redirecting after a successful Magic Login

When generating a magic login link, you can control where the user is redirected after they click the emailed link and authentication completes.

To do this, include a hidden field named `magicLoginRedirectUrl` in the POST that requests the magic link.

```twig
<form method="post">
{{ actionInput('magic-login/magic-login/login') }}
{{ csrfInput() }}

{{ hiddenInput('magicLoginRedirectUrl', url('account')) }}

<input type="email" name="email">
<button type="submit">Send link</button>
</form>
```

If `magicLoginRedirectUrl` is not provided (or isn’t considered valid), the plugin will fall back to Craft’s `postLoginRedirect` general config setting.

**Valid redirect URLs**

The `magicLoginRedirectUrl` value must be a “full” URL. That means one of:

- Absolute URL (includes a scheme), e.g. `https://example.com/account`
- Root-relative URL (starts with `/`), e.g. `/account`
- Protocol-relative URL (starts with `//`), e.g. `//example.com/account`

Values like `account` (no leading `/`) will be treated as invalid and the plugin will fall back to `postLoginRedirect`.

## Technical Features / Under the Hood

### Password Generation
Expand Down
10 changes: 7 additions & 3 deletions src/services/MagicLoginAuthService.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Craft;

use craft\base\Component;
use craft\helpers\UrlHelper;
use creode\magiclogin\MagicLogin;
use creode\magiclogin\models\AuthModel;
use creode\magiclogin\records\AuthRecord;
Expand Down Expand Up @@ -92,9 +93,12 @@ public function createMagicLogin(string $userNameOrEmail)
$record->userId = $user->id;
$record->publicKey = $publicKey;
$record->privateKey = $privateKey;
$record->redirectUrl = Craft::$app
->getRequest()
->getValidatedBodyParam('magicLoginRedirectUrl') ?? $generalConfig->postLoginRedirect;
$redirectUrl = Craft::$app->getRequest()->getBodyParam('magicLoginRedirectUrl');
if (is_string($redirectUrl) && UrlHelper::isFullUrl($redirectUrl)) {
$record->redirectUrl = $redirectUrl;
} else {
$record->redirectUrl = $generalConfig->postLoginRedirect;
}
$record->save();

// Generate Datetime for current dateCreated and use it's timestamp.
Expand Down
2 changes: 1 addition & 1 deletion test-config/project/project.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ system:
edition: pro
live: true
name: 'Magic login testing'
schemaVersion: 5.6.0.2
schemaVersion: 5.9.0.8
timeZone: America/Los_Angeles
45 changes: 45 additions & 0 deletions tests/functional/LoginFormTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<?php

/**
* Functional tests for Magic Login login form.
*/

namespace creode\magiclogintests\acceptance;

use Craft;
Expand All @@ -8,9 +12,14 @@
use craft\elements\User;
use creode\magiclogin\records\AuthRecord;

/**
* Tests the Magic Login frontend login form workflow.
*/
class LoginFormTest extends BaseFunctionalTest
{
/**
* Functional test actor.
*
* @var \FunctionalTester
*/
protected $tester;
Expand Down Expand Up @@ -200,8 +209,44 @@ public function testSuccessfulLogin()
$this->assertEquals($emailSubject, $magicLoginEmail->getSubject());
}

/**
* Tests that the magicLoginRedirectUrl POST param is persisted on the auth record,
* and will be used after the user clicks their magic link.
*
* @return void
*/
public function testSuccessfulLoginPersistsProvidedRedirectUrl()
{
$validUser = User::findOne();

AuthRecord::deleteAll();

$authRecords = AuthRecord::find()->all();

$targetRedirect = '/after-magic-login';

$this->tester->amOnPage('/magic-login/login');
$this->tester->submitForm(
'#magic-login-form',
[
'email' => $validUser->email,
'magicLoginRedirectUrl' => $targetRedirect,
],
'submitButton'
);

$updatedAuthRecords = AuthRecord::find()->all();
$this->assertEquals(count($authRecords) + 1, count($updatedAuthRecords));

$authRecord = AuthRecord::find()->one();
$this->assertNotNull($authRecord, 'AuthRecord should be created for a valid login request.');
$this->assertEquals($targetRedirect, $authRecord->redirectUrl);
}

/**
* Tests that we have a rate limit setup for Magic Login.
*
* @return void
*/
public function testMagicLoginEmailRateLimit()
{
Expand Down
Loading