Skip to content

Commit

Permalink
Ability to customize credential keys e.g: email/username
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel de Wit committed Jun 20, 2022
1 parent cdbd4b3 commit 1c9001e
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 5 deletions.
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Add [Laravel Sanctum](https://github.com/laravel/sanctum) support to [Lighthouse
- [Email Verification](#email-verification)
- [Forgot Password](#forgot-password)
- [Reset Password](#reset-password)
- [Custom Identification](#custom-identification)

## Requirements

Expand Down Expand Up @@ -134,6 +135,8 @@ Apply the Authorization header on subsequent calls using the token
"Authorization": "Bearer 1|lJo1cMhrW9tIUuGwlV1EPjKnvfZKzvgpGgplbwX9"
```

(Using something other than email/password? See [Custom Identification](#custom-identification))

### Logout

Revoke the current token.
Expand Down Expand Up @@ -295,6 +298,37 @@ mutation {
}
```

### Custom Identification

You can customize which fields are used for authenticating users.

For example, using `username` instead of the default `email`.
```php
/*
|--------------------------------------------------------------------------
| Identification
|--------------------------------------------------------------------------
|
| Configure the credential fields by which the user will be identified.
|
*/

'identification' => [
'username' => 'username', // default: 'email'
'password' => 'password',
],
```

Update the GraphQL schema accordingly

```graphql
input LoginInput {
username: String! @rules(apply: ["required"])
password: String! @rules(apply: ["required"])
}
```


## Testing

```bash
Expand Down
11 changes: 11 additions & 0 deletions config/lighthouse-sanctum.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,15 @@
|
*/
'use_signed_email_verification_url' => false,

/*
|--------------------------------------------------------------------------
| Identification
|--------------------------------------------------------------------------
|
| Configure the credential fields by which the user will be identified.
| Default: email
*/

'user_identifier_field_name' => 'email',
];
5 changes: 4 additions & 1 deletion src/GraphQL/Mutations/Login.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@ public function __invoke($_, array $args): array
{
$userProvider = $this->createUserProvider();

$identificationKey = $this->getConfig()
->get('lighthouse-sanctum.identification.user_identifier_field_name', 'email');

$user = $userProvider->retrieveByCredentials([
'email' => $args['email'],
$identificationKey => $args[$identificationKey],
'password' => $args['password'],
]);

Expand Down
3 changes: 2 additions & 1 deletion src/Services/ResetPasswordService.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public function transformUrl(CanResetPassword $notifiable, string $token, string

public function setResetPasswordUrl(string $url): void
{
ResetPasswordNotification::createUrlUsing(function (CanResetPassword $notifiable, string $token) use ($url) {
/** @phpstan-ignore-next-line */
ResetPasswordNotification::createUrlUsing(function (CanResetPassword $notifiable, string $token) use ($url): string {
return $this->transformUrl($notifiable, $token, $url);
});
}
Expand Down
1 change: 1 addition & 0 deletions tests/Integration/GraphQL/Mutations/ForgotPasswordTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public function it_sends_a_reset_password_notification(): void
]);

Notification::assertSentTo($user, function (ResetPassword $notification) use ($user) {
/** @phpstan-ignore-next-line */
$url = call_user_func($notification::$createUrlCallback, $user, $notification->token);

return $url === "https://my-front-end.com/reset-password?email=john.doe@gmail.com&token={$notification->token}";
Expand Down
1 change: 1 addition & 0 deletions tests/Integration/Services/ResetPasswordServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public function it_sets_the_reset_password_url(): void

$this->service->setResetPasswordUrl('https://mysite.com/reset-password/__EMAIL__/__TOKEN__');

/** @phpstan-ignore-next-line */
$url = call_user_func(ResetPassword::$createUrlCallback, $user, $token);

static::assertSame('https://mysite.com/reset-password/user@example.com/token123', $url);
Expand Down
7 changes: 6 additions & 1 deletion tests/Traits/MocksUserProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ protected function mockConfig()
->shouldReceive('get')
->with('lighthouse-sanctum.provider')
->andReturn('sanctum-provider')
->getMock();
->getMock()
->shouldReceive('get')
->with('lighthouse-sanctum.identification.user_identifier_field_name', 'email')
->andReturn('email')
->getMock()
;

return $config;
}
Expand Down
59 changes: 59 additions & 0 deletions tests/Unit/GraphQL/Mutations/LoginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use DanielDeWit\LighthouseSanctum\Tests\Traits\MocksUserProvider;
use DanielDeWit\LighthouseSanctum\Tests\Unit\AbstractUnitTest;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Config\Repository as Config;
use Illuminate\Foundation\Auth\User;
use Laravel\Sanctum\NewAccessToken;
use Mockery;
Expand Down Expand Up @@ -62,6 +63,64 @@ public function it_logs_a_user_in(): void
static::assertSame('1234567890', $result['token']);
}

/**
* @test
*/
public function it_logs_a_user_in_using_custom_identification(): void
{
/** @var Config|MockInterface $config */
$config = Mockery::mock(Config::class)
->shouldReceive('get')
->with('lighthouse-sanctum.provider')
->andReturn('sanctum-provider')
->getMock()
->shouldReceive('get')
->with('lighthouse-sanctum.identification.user_identifier_field_name', 'email')
->andReturn('custom_key')
->getMock();

$token = Mockery::mock(NewAccessToken::class);
$token->plainTextToken = '1234567890';

/** @var UserHasApiTokens|MockInterface $user */
$user = Mockery::mock(UserHasApiTokens::class)
->shouldReceive('createToken')
->with('default')
->andReturn($token)
->getMock();

/** @var UserProvider|MockInterface $userProvider */
$userProvider = Mockery::mock(UserProvider::class)
->shouldReceive('retrieveByCredentials')
->with([
'custom_key' => 'foo@bar.com',
'password' => 'supersecret',
])
->andReturn($user)
->getMock()
->shouldReceive('validateCredentials')
->with($user, [
'custom_key' => 'foo@bar.com',
'password' => 'supersecret',
])
->andReturnTrue()
->getMock();

$mutation = new Login(
$this->mockAuthManager($userProvider),
$config,
);

$result = $mutation(null, [
'custom_key' => 'foo@bar.com',
'password' => 'supersecret',
]);

static::assertIsArray($result);
static::assertCount(1, $result);
static::assertSame('1234567890', $result['token']);
}

/**
* @test
*/
Expand Down
7 changes: 5 additions & 2 deletions tests/stubs/Users/UserHasApiTokens.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ class UserHasApiTokens extends User implements HasApiTokensContract
use HasFactory;
use Notifiable;

/**
* @var string
*/
protected $table = 'users';

/**
* @var string[]
* @var array<int, string>
*/
protected $fillable = [
'name',
Expand All @@ -30,7 +33,7 @@ class UserHasApiTokens extends User implements HasApiTokensContract
];

/**
* @var string[]
* @var array<int, string>
*/
protected $hidden = [
'password',
Expand Down

0 comments on commit 1c9001e

Please sign in to comment.