Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] [Feature] Implement custom auth guard for Shopify sessions and enable unit and feature testing. #469

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 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: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ jobs:
fail-fast: false
matrix:
php-version:
- "8.0"
- "8.1"
- "8.2"
defaults:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ Open the URL generated in your console. Once you grant permission to the app, yo

### Application Storage

This template uses [Laravel's Eloquent framework](https://laravel.com/docs/9.x/eloquent) to store Shopify session data.
This template uses [Laravel's Eloquent framework](https://laravel.com/docs/10.x/eloquent) to store Shopify session data.
It provides migrations to create the necessary tables in your database, and it stores and loads session data from them.

The database that works best for you depends on the data your app needs and how it is queried.
Expand Down
2 changes: 1 addition & 1 deletion web/app/Http/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Kernel extends HttpKernel
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\Illuminate\Http\Middleware\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
Expand Down
2 changes: 1 addition & 1 deletion web/app/Http/Middleware/TrustProxies.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace App\Http\Middleware;

use Fideloper\Proxy\TrustProxies as Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;

class TrustProxies extends Middleware
Expand Down
94 changes: 94 additions & 0 deletions web/app/Lib/ShopifyGuard.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

namespace App\Lib;

use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\UserProvider;
use Shopify\Auth\Session;
use Shopify\Utils;

class ShopifyGuard implements Guard
{
use GuardHelpers;

/**
* The Shopify session instance.
*
* @var \Shopify\Auth\Session
*/
protected Session $session;

/**
* The user provider fetches stored Shopify sessions.
*
* @var \Illuminate\Contracts\Auth\UserProvider
*/
protected UserProvider $provider;

/**
* Create a new authentication guard for Shopify sessions.
*
* @param \Illuminate\Contracts\Auth\UserProvider $provider
*/
public function __construct(UserProvider $provider)
{
$this->provider = $provider;
}

/**
* Get the current Shopify session.
*
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function user()
{
if (! is_null($this->session)) {
return $this->session;
}

$request = request();

if (! $request->hasHeader('Authorization')) {
return;
}

$shopifySession = Utils::loadCurrentSession(
$request->header(),
$request->cookie(),
false
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the isOnline flag. Any suggestions on how to dynamically populate this are welcome.

);

if (is_null($shopifySession)) {
return;
}

$this->session = $this->provider->retrieveById($shopifySession->getId());

return $this->session;
}

/**
* Validate a user's credentials.
*
* Credentials are validated through Shopify sessions so we can bypass the
* need to validate.
*
* @param array $credentials
* @return bool
*/
public function validate(array $credentials = [])
{
return true;
}

/**
* Via remember is disabled for Shopify sessions.
*
* @return bool
*/
public function viaRemember()
{
return false;
}
}
44 changes: 42 additions & 2 deletions web/app/Models/Session.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,49 @@
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User as Authenticatable;

class Session extends Model
class Session extends Authenticatable
{
use HasFactory;

/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = ['access_token'];

/**
* Disable "remember me" compatibility.
*
* @var null
*/
protected $rememberTokenName = null;

/**
* The non-primary key value that is used to identify a unique session.
*
* Normally, for a user User model, this would be something like "email",
* however, the equivalent for sessions is the session_id column.
*
* @return string
*/
public function getAuthIdentifierName()
{
return 'session_id';
}

/**
* Sessions do not have passwords, they have access tokens instead.
*
* This is the functional equivalent of a password in the context of
* Shopify sessions.
*
* @return string
*/
public function getAuthPassword()
{
return 'access_token';
}
}
7 changes: 6 additions & 1 deletion web/app/Providers/AuthServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

namespace App\Providers;

use App\Lib\ShopifyGuard;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;

class AuthServiceProvider extends ServiceProvider
Expand All @@ -25,6 +28,8 @@ public function boot()
{
$this->registerPolicies();

//
Auth::extend('shopify', function (Application $app, string $name, array $config) {
return new ShopifyGuard(Auth::createUserProvider($config['provider']));
});
}
}
37 changes: 19 additions & 18 deletions web/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,28 @@
"name": "laravel/laravel",
"type": "project",
"description": "The Laravel Framework.",
"keywords": [
"framework",
"laravel"
],
"keywords": ["framework", "laravel"],
"license": "UNLICENSED",
"require": {
"php": "~8.0.0 || ~8.1.0 || ~8.2.0",
"php": "~8.1.0 || ~8.2.0",
"ext-xml": "*",
"ext-zip": "*",
"doctrine/dbal": "^3.1",
"fideloper/proxy": "^4.4",
"fruitcake/laravel-cors": "^3.0",
"guzzlehttp/guzzle": "^7.0.1",
"laravel/framework": "^8.12",
"laravel/tinker": "^2.5",
"doctrine/dbal": "^3.0",
"guzzlehttp/guzzle": "^7.2",
"laravel/framework": "^10.0",
"laravel/sanctum": "^3.2",
"laravel/tinker": "^2.8",
"shopify/shopify-api": "^5.0",
"squizlabs/php_codesniffer": "^3.6"
},
"require-dev": {
"facade/ignition": "^2.5",
"fakerphp/faker": "^1.9.1",
"laravel/sail": "^1.0.1",
"mockery/mockery": "^1.4.2",
"nunomaduro/collision": "^5.0",
"phpunit/phpunit": "^9.3.3"
"laravel/pint": "^1.0",
"laravel/sail": "^1.18",
"mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^7.0",
"phpunit/phpunit": "^10.1",
"spatie/laravel-ignition": "^2.0"
},
"autoload": {
"psr-4": {
Expand Down Expand Up @@ -68,8 +65,12 @@
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true,
"php-http/discovery": true
}
},
"minimum-stability": "dev",
"minimum-stability": "stable",
"prefer-stable": true
}
64 changes: 10 additions & 54 deletions web/config/auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
*/

'defaults' => [
// 'guard' => 'web',
// 'passwords' => 'users',
'guard' => 'web',
],

/*
Expand All @@ -36,12 +35,11 @@
*/

'guards' => [
// 'web' => [
// 'driver' => 'session',
// 'provider' => 'users',
// ],

],
'web' => [
'driver' => 'shopify',
'provider' => 'sessions',
],
],

/*
|--------------------------------------------------------------------------
Expand All @@ -61,52 +59,10 @@
*/

'providers' => [
// 'users' => [
// 'driver' => 'eloquent',
// 'model' => App\Models\User::class,
// ],

// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
'sessions' => [
'driver' => 'eloquent',
'model' => \App\Models\Session::class,
],
],

/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| You may specify multiple password reset configurations if you have more
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expire time is the number of minutes that the reset token should be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
*/

// 'passwords' => [
// 'users' => [
// 'provider' => 'users',
// 'table' => 'password_resets',
// 'expire' => 60,
// 'throttle' => 60,
// ],
// ],

/*
|--------------------------------------------------------------------------
| Password Confirmation Timeout
|--------------------------------------------------------------------------
|
| Here you may define the amount of seconds before a password confirmation
| times out and the user is prompted to re-enter their password via the
| confirmation screen. By default, the timeout lasts for three hours.
|
*/

// 'password_timeout' => 10800,

];
5 changes: 5 additions & 0 deletions web/config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,9 @@
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
],

'shopify' => [
'key' => env('SHOPIFY_API_KEY'),
'secret' => env('SHOPIFY_API_SECRET'),
]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes more sense for this to be in web/config/shopify.php


];
22 changes: 22 additions & 0 deletions web/tests/CreatesApplication.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Tests;

use Illuminate\Contracts\Console\Kernel;

trait CreatesApplication
{
/**
* Creates the application.
*
* @return \Illuminate\Foundation\Application
*/
public function createApplication()
{
$app = require __DIR__.'/../bootstrap/app.php';

$app->make(Kernel::class)->bootstrap();

return $app;
}
}
Loading