Skip to content

Commit

Permalink
Change AuthUserProvider API to support api token authentication.
Browse files Browse the repository at this point in the history
  • Loading branch information
kitar committed Mar 24, 2020
1 parent 2d2a063 commit b9e2c45
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 21 deletions.
42 changes: 41 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,11 +286,39 @@ public function boot()
$this->registerPolicies();

Auth::provider('dynamodb', function ($app, array $config) {
return new AuthUserProvider(new $config['model']);
return new AuthUserProvider(
$app['hash'],
$config['model'],
$config['api_token_name'] ?? null,
$config['api_token_index'] ?? null
);
});
}
```

### Modify LoginController

The default authentication uses the `email` and `password` column to validate, which is not the primary key of the table. However, DynamoDB needs to use the primary key to identify, so we need to tweak the LoginController a bit.

```php
namespace App\Http\Controllers\Auth;

class LoginController extends Controller
{
...

/**
* Get the login username to be used by the controller.
*
* @return string
*/
public function username()
{
return 'your-primary-key';
}
}
```

### Change auth config

Then specify driver and model name for authentication in `config/auth.php`.
Expand All @@ -307,10 +335,14 @@ Then specify driver and model name for authentication in `config/auth.php`.
'users' => [
'driver' => 'dynamodb',
'model' => App\User::class,
'api_token_name' => 'api_token',
'api_token_index' => 'api_token-index'
],
],
```

`api_token_name` and `api_token_index` are optional, but we need them if we use api token authentication.

## Query Builder

We can use Query Builder without model.
Expand Down Expand Up @@ -566,6 +598,14 @@ $response = DB::table('ProductCatalog')
->scan();
```

If you are using Query Builder through model, you can access to `exclusiveStartKey` by:

```php
$products = ProductCatalog::limit(5)->scan();

$products->first()->meta()['LastEvaluatedKey']; // array
```

### Using Global Secondary Indexes

Some applications might need to perform many kinds of queries, using a variety of different attributes as query criteria. To support these requirements, you can create one or more global secondary indexes and issue `query` requests against these indexes in Amazon DynamoDB.
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"illuminate/support": "^6.0|^7.0",
"illuminate/container": "^6.0|^7.0",
"illuminate/database": "^6.0|^7.0",
"illuminate/hashing": "^6.0|^7.0",
"aws/aws-sdk-php": "^3.0"
},
"require-dev": {
Expand Down
96 changes: 89 additions & 7 deletions src/Kitar/Dynamodb/Model/AuthUserProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,44 @@

use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider as BaseUserProvider;
use Illuminate\Support\Facades\Hash;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;

class AuthUserProvider implements BaseUserProvider
{
/**
* The hasher implementation.
*
* @var \Illuminate\Contracts\Hashing\Hasher
*/
protected $hasher;

/**
* The Eloquent user model.
*
* @var string
*/
protected $model;

public function __construct(Authenticatable $model)
/**
* The column name of the api token.
*
* @var string
*/
protected $apiTokenName;

/**
* The index name to use when querying by api token.
*
* @var string
*/
protected $apiTokenIndex;

public function __construct(HasherContract $hasher, $model, $apiTokenName = null, $apiTokenIndex = null)
{
$this->model = $model;
$this->hasher = $hasher;
$this->apiTokenName = $apiTokenName;
$this->apiTokenIndex = $apiTokenIndex;
}

/**
Expand All @@ -23,7 +52,9 @@ public function __construct(Authenticatable $model)
*/
public function retrieveById($identifier)
{
return $this->model->find($identifier);
$model = $this->createModel();

return $model->find($identifier);
}

/**
Expand All @@ -37,9 +68,14 @@ public function retrieveByToken($identifier, $token)
{
$user = $this->retrieveById($identifier);

if ($user && $user->getRememberToken() == $token) {
return $user;
if (! $user) {
return;
}

$rememberToken = $user->getRememberToken();

return $rememberToken && hash_equals($rememberToken, $token)
? $user : null;
}

/**
Expand All @@ -52,17 +88,49 @@ public function retrieveByToken($identifier, $token)
public function updateRememberToken(Authenticatable $user, $token)
{
$user->setRememberToken($token);

$timestamps = $user->timestamps;

$user->timestamps = false;

$user->save();

$user->timestamps = $timestamps;
}

/**
* Retrieve a user by the given credentials.
* Identifier or API Token are supported.
*
* @param array $credentials
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByCredentials(array $credentials)
{
return $this->retrieveById($credentials['email']);
if (isset($credentials['password'])) {
unset($credentials['password']);
}

if (count($credentials) !== 1) {
return;
}

$model = $this->createModel();

$id = $credentials[$model->getAuthIdentifierName()] ?? null;

if ($id) {
return $this->retrieveById($id);
}

$apiToken = $this->apiTokenName ? $credentials[$this->apiTokenName] ?? null : null;

if ($apiToken && $this->apiTokenIndex) {
return $model->index($this->apiTokenIndex)
->keyCondition($this->apiTokenName, '=', $apiToken)
->query()
->first();
}
}

/**
Expand All @@ -74,6 +142,20 @@ public function retrieveByCredentials(array $credentials)
*/
public function validateCredentials(Authenticatable $user, array $credentials)
{
return Hash::check($credentials['password'], $user->password);
$plain = $credentials['password'];

return $this->hasher->check($plain, $user->getAuthPassword());
}

/**
* Create a new instance of the model.
*
* @return \Kitar\Dynamodb\Model\Model
*/
public function createModel()
{
$class = '\\'.ltrim($this->model, '\\');

return new $class;
}
}
Loading

0 comments on commit b9e2c45

Please sign in to comment.