Browse Source

Resolve all issues identified by phan

master
Adam Pippin 3 years ago
parent
commit
51e05643fd
  1. 5
      .phan/config.php
  2. 6
      .phan/stubs/Auth.php
  3. 3
      .phan/stubs/Cache.php
  4. 3
      .phan/stubs/Route.php
  5. 3
      .phan/stubs/Session.php
  6. 3
      .phan/stubs/Storage.php
  7. 4
      composer.json
  8. 10
      examples/custom/composer.lock
  9. 73
      src/Authkit2.php
  10. 35
      src/Http/Controllers/AuthenticationController.php
  11. 20
      src/Models/Token.php
  12. 62
      src/Observers/UserObserver.php
  13. 19
      src/Oidc/Authentication/Authentication.php
  14. 6
      src/Oidc/Authentication/ClientAuthentication.php
  15. 15
      src/Oidc/Authentication/TokenAuthentication.php
  16. 66
      src/Oidc/Client.php
  17. 1
      src/Oidc/Flows/ServiceAccountFlow.php
  18. 3
      src/Oidc/Flows/UserFlow.php
  19. 46
      src/Oidc/Token.php
  20. 3
      src/Providers/Authkit2ServiceProvider.php
  21. 9
      src/Providers/AuthnServiceProvider.php
  22. 10
      src/Providers/AuthzServiceProvider.php

5
.phan/config.php

@ -10,7 +10,10 @@ return [
'src/', 'src/',
'vendor/laravel/', 'vendor/laravel/',
'vendor/composer/', 'vendor/composer/',
'vendor/symfony/' 'vendor/symfony/',
'vendor/guzzlehttp/',
'vendor/psr/',
'vendor/firebase/'
], ],
'exclude_analysis_directory_list' => [ 'exclude_analysis_directory_list' => [

6
.phan/stubs/Auth.php

@ -1,7 +1,3 @@
<?php <?php
/** class Auth extends \Illuminate\Support\Facades\App {}
* @method static mixed loginUsingId(mixed $id)
* @method static void login(mixed $user)
*/
class Auth {}

3
.phan/stubs/Cache.php

@ -0,0 +1,3 @@
<?php
class Cache extends \Illuminate\Support\Facades\Cache {}

3
.phan/stubs/Route.php

@ -0,0 +1,3 @@
<?php
class Route extends \Illuminate\Support\Facades\Route {}

3
.phan/stubs/Session.php

@ -0,0 +1,3 @@
<?php
class Session extends \Illuminate\Support\Facades\Session {}

3
.phan/stubs/Storage.php

@ -0,0 +1,3 @@
<?php
class Storage extends \Illuminate\Support\Facades\Storage {}

4
composer.json

@ -31,5 +31,9 @@
"friendsofphp/php-cs-fixer": "^2.18", "friendsofphp/php-cs-fixer": "^2.18",
"phan/phan": "^4.0", "phan/phan": "^4.0",
"laravel/framework": "^8.31" "laravel/framework": "^8.31"
},
"scripts": {
"analyze": "phan",
"format": "php-cs-fixer fix --allow-risky=yes"
} }
} }

10
examples/custom/composer.lock

@ -12,7 +12,7 @@
"dist": { "dist": {
"type": "path", "type": "path",
"url": "../..", "url": "../..",
"reference": "aea8352e39283f5c1a8d4a3f9089ee101f3efcbe" "reference": "2745fdc84ef4fa8d1ed834ba8bec761740b2f1d7"
}, },
"require": { "require": {
"firebase/php-jwt": "^5.2", "firebase/php-jwt": "^5.2",
@ -37,6 +37,14 @@
"authkit2\\": "src" "authkit2\\": "src"
} }
}, },
"scripts": {
"analyze": [
"phan"
],
"format": [
"php-cs-fixer fix --allow-risky=yes"
]
},
"license": [ "license": [
"proprietary" "proprietary"
], ],

73
src/Authkit2.php

@ -3,8 +3,8 @@
namespace authkit2; namespace authkit2;
/** /**
* Internal class for providing a single interface integrating with native PHP, * Helper class to abstract away the framework authkit2 is running on as well
* Laravel, and any other environment we need to support. * as provide simple helper methods for using it outside of a framework.
* *
* @method static mixed cache(string $key, callable $generator) * @method static mixed cache(string $key, callable $generator)
* @method static mixed cache_get(string $key, mixed $default = null) * @method static mixed cache_get(string $key, mixed $default = null)
@ -179,10 +179,36 @@ class Authkit2
protected function getLaravelCallbacks(): array protected function getLaravelCallbacks(): array
{ {
return [ return [
'session_get' => function(string $key) { return \Session::get($key); }, 'session_get' =>
'session_set' => function(string $key, $value) { \Session::put($key, $value); }, /**
'cache_get' => function(string $key) { return \Cache::get($key); }, * Fetch a variable from the session
'cache_set' => function(string $key, $value) { \Cache::set($key, $value); } * @param string $key
* @return mixed
*/
function(string $key) { return \Session::get($key); },
'session_set' =>
/**
* Set a variable in the session
* @param string $key
* @param mixed $value
* @return void
*/
function(string $key, $value): void { \Session::put($key, $value); },
'cache_get' =>
/**
* Fetch a value from cache
* @param string $key
* @return mixed
*/
function(string $key) { return \Cache::get($key); },
'cache_set' =>
/**
* Set a value in cache
* @param string $key
* @param mixed $value
* @return void
*/
function(string $key, $value): void { \Cache::set($key, $value); }
]; ];
} }
@ -250,22 +276,55 @@ class Authkit2
{ {
} }
/**
* Configure the authkit2 library
*
* @param string $client_id
* @param string $client_secret
* @param string $endpoint
* @return void
*/
protected function ak2_configure(string $client_id, string $client_secret, string $endpoint): void protected function ak2_configure(string $client_id, string $client_secret, string $endpoint): void
{ {
$this->client = new \authkit2\Oidc\Client($endpoint, $client_id, $client_secret); $this->client = new \authkit2\Oidc\Client($endpoint, $client_id, $client_secret);
} }
/**
* Fetch a OIDC client authenticated as this application
*
* @return Oidc\Client
*/
protected function ak2_get_client(): Oidc\Client protected function ak2_get_client(): Oidc\Client
{ {
return $this->client; return $this->client;
} }
/**
* Given the essential values from a token (access token, refresh token),
* convert that into a Token object that can be used to make and authenticate
* requests.
*
* The refresh token is not strictly required, however if the token is
* expired then requests will simply fail. This use case is intended for
* authenticating requests using tokens other applications have sent to us.
*
* @param string $access_token
* @param ?string $refresh_token
* @return Oidc\Token
*/
protected function ak2_get_token(string $access_token, ?string $refresh_token = null): Oidc\Token protected function ak2_get_token(string $access_token, ?string $refresh_token = null): Oidc\Token
{ {
return Oidc\Token::fromString($this->client, $access_token, $refresh_token); return Oidc\Token::fromString($this->client, $access_token, $refresh_token);
} }
protected function ak2_refresh_token(Oidc\Token $token): Token /**
* Refresh a token object -- generate a new access token from its
* refresh_token.
*
* @param Oidc\Token $token
* @return Oidc\Token a newly generated token
*/
protected function ak2_refresh_token(Oidc\Token $token): Oidc\Token
{ {
return $this->client->refreshToken($token); return $this->client->refreshToken($token);
} }

35
src/Http/Controllers/AuthenticationController.php

@ -11,6 +11,10 @@ use authkit2\Models\Token;
*/ */
class AuthenticationController extends Controller class AuthenticationController extends Controller
{ {
/**
* OIDC flow to mediate the actual login process and exchanges
* @var \authkit2\Oidc\Flows\UserFlow
*/
protected $user_flow; protected $user_flow;
public function __construct(\authkit2\Oidc\Flows\UserFlow $user_flow) public function __construct(\authkit2\Oidc\Flows\UserFlow $user_flow)
@ -44,7 +48,7 @@ class AuthenticationController extends Controller
*/ */
public function callback(Request $request) public function callback(Request $request)
{ {
$user_class = config('auth.providers.users.model'); $user_class = (string)config('auth.providers.users.model');
// Verify the passed in state value // Verify the passed in state value
$this->user_flow->validateState($request->state); $this->user_flow->validateState($request->state);
@ -86,17 +90,36 @@ class AuthenticationController extends Controller
} }
} }
// TODO: Log a useful error message in these cases
if (!isset($user)) if (!isset($user))
{ {
// TODO: Log a useful error message
abort(500); abort(500);
die();
}
else if (!method_exists($user, 'save'))
{
abort(500);
die();
}
else if (!method_exists($user, 'getAuthIdentifierName'))
{
abort(500);
die();
} }
$token = new Token(); $token = new Token();
$token->id = $oidc_token->getUserId(); $token->id = $oidc_token->getUserId();
$token->access_token = $oidc_token->getAccessToken(); $token->access_token = $oidc_token->getAccessToken();
$token->refresh_token = $oidc_token->getRefreshToken(); $refresh_token = $oidc_token->getRefreshToken();
if (!isset($refresh_token))
{
abort(500);
die();
}
$token->refresh_token = $refresh_token;
// @phan-suppress-next-line PhanNonClassMethodCall
$token->user_id = $user->{$user->getAuthIdentifierName()}; $token->user_id = $user->{$user->getAuthIdentifierName()};
// @phan-suppress-next-line PhanNonClassMethodCall
$token->save(); $token->save();
} }
else else
@ -109,10 +132,16 @@ class AuthenticationController extends Controller
$login_event_result = event(new \authkit2\Events\UserLogin($user, $user_info)); $login_event_result = event(new \authkit2\Events\UserLogin($user, $user_info));
if (!sizeof($login_event_result)) if (!sizeof($login_event_result))
{ {
if (!isset($user) || !method_exists($user, 'save'))
{
// TODO: Log a useful error message
abort(500);
}
// If nothing handled the login event, assume we're using laravel default // If nothing handled the login event, assume we're using laravel default
// everything and just go ahead and update the name/email // everything and just go ahead and update the name/email
$user->name = $user_info['name']; $user->name = $user_info['name'];
$user->email = $user_info['email']; $user->email = $user_info['email'];
// @phan-suppress-next-line PhanNonClassMethodCall
$user->save(); $user->save();
} }

20
src/Models/Token.php

@ -7,14 +7,26 @@ use \Illuminate\Database\Eloquent\Model;
/** /**
* User's OIDC token * User's OIDC token
* *
* @property string id * @property string $id
* @property int user_id * @property int $user_id
* @property string access_token * @property string $access_token
* @property string refresh_token * @property string $refresh_token
*/ */
class Token extends Model class Token extends Model
{ {
/**
* Override the default generated table name so we can namespace this and
* avoid conflicting with anything the app might already be using.
*
* @var string
*/
protected $table = 'authkit2_token'; protected $table = 'authkit2_token';
/**
* Disable Eloquent's default created/updated fields
*
* @var bool
*/
public $timestamps = false; public $timestamps = false;
} }

62
src/Observers/UserObserver.php

@ -4,15 +4,30 @@ namespace authkit2\Observers;
use authkit2\Authkit2; use authkit2\Authkit2;
use authkit2\Models\Token; use authkit2\Models\Token;
/**
* Watch for user models being loaded or saved and handle installing an
* `authkit` object on them to allow the main application to easily interact
* with other services as the user or fetch data from the OIDC provider.
*/
class UserObserver class UserObserver
{ {
/*'retrieved', 'creating', 'created', 'updating', 'updated', /**
'saving', 'saved', 'restoring', 'restored', 'replicating', * When a user model is saved we have to remove the authkit property from it
'deleting', 'deleted', 'forceDeleted',*/ * to avoid Eloquent or another ORM from trying to save it. When we do so,
* we cache it here so we can restore it when the save is complete.
*
* @var array<mixed,string>
*/
protected static $token_cache = []; protected static $token_cache = [];
public function retrieved($user) /**
* Run in response to a user model being loaded, locating their token and
* creating a client to place on the model
*
* @param mixed $user
* @return void
*/
public function retrieved($user): void
{ {
// Find the token + refresh token for the user // Find the token + refresh token for the user
$token = Token::where('user_id', $user->{$user->getAuthIdentifierName()})->first(); $token = Token::where('user_id', $user->{$user->getAuthIdentifierName()})->first();
@ -22,25 +37,48 @@ class UserObserver
// Set a refresh callback on the token -- when it's been refreshed, // Set a refresh callback on the token -- when it's been refreshed,
// save the new tokens. // save the new tokens.
$user->authkit->setRefreshCallback(function($oidc_token) use ($token) { $user->authkit->setRefreshCallback(
$token->access_token = $oidc_token->getAccessToken(); /**
$token->refresh_token = $oidc_token->getRefreshToken(); * When a token has been refreshed, save the updated values
$token->save(); * to the token model
}); * @param \authkit2\Oidc\Token $oidc_token
* @return void
*/
function(\authkit2\Oidc\Token $oidc_token) use ($token) : void {
$token->access_token = $oidc_token->getAccessToken();
$token->refresh_token = $oidc_token->getRefreshToken();
$token->save();
});
} }
public function saving($user) /**
* Before we save a user model, remove the token/client so the ORM doesn't
* try and write those out.
*
* @param mixed $user
* @return void
*/
public function saving($user): void
{ {
if (isset($user->authkit)) if (isset($user->authkit))
static::$token_cache[$user->{$user->getAuthIdentifierName()}] = $user->authkit; static::$token_cache[$user->{$user->getAuthIdentifierName()}] = $user->authkit;
unset($user->authkit); unset($user->authkit);
} }
public function saved($user) /**
* After a user model is saved, restore the client and token
*
* @param mixed $user
* @return void
*/
public function saved($user): void
{ {
$user_id = $user->{$user->getAuthIdentifierName()}; $user_id = $user->{$user->getAuthIdentifierName()};
if (isset(static::$token_cache[$user_id])) if (isset(static::$token_cache[$user_id]))
{
$user->authkit = static::$token_cache[$user_id]; $user->authkit = static::$token_cache[$user_id];
unset(static::$token_cache[$user_id]);
}
} }
} }

19
src/Oidc/Authentication/Authentication.php

@ -10,10 +10,10 @@ abstract class Authentication
/** /**
* Authenticate the passed in HTTP request * Authenticate the passed in HTTP request
* *
* @param \GuzzleHttp\Psr7\Request $request request to authenticate * @param \Psr\Http\Message\RequestInterface $request request to authenticate
* @return \GuzzleHttp\Psr7\Request authenticated request * @return \Psr\Http\Message\RequestInterface authenticated request
*/ */
public abstract function authenticate(\GuzzleHttp\Psr7\Request $request): \GuzzleHttp\Psr7\Request; public abstract function authenticate(\Psr\Http\Message\RequestInterface $request): \Psr\Http\Message\RequestInterface;
/** /**
* Wrap this provider up as a middleware appropriate for use directly with * Wrap this provider up as a middleware appropriate for use directly with
@ -24,7 +24,12 @@ abstract class Authentication
public function getMiddleware(): callable public function getMiddleware(): callable
{ {
$auth = $this; $auth = $this;
return function(callable $handler) use ($auth) { return function(callable $handler) use ($auth) : callable {
/**
* @param \Psr\Http\Message\RequestInterface $request
* @param mixed[] $options
* @return mixed
*/
return function(\Psr\Http\Message\RequestInterface $request, array $options) use ($handler, $auth) { return function(\Psr\Http\Message\RequestInterface $request, array $options) use ($handler, $auth) {
return $handler( return $handler(
$auth->authenticate($request), $auth->authenticate($request),
@ -34,6 +39,12 @@ abstract class Authentication
}; };
} }
/**
* Fetch a guzzle client with the authentication middleware included
*
* @param mixed[] $options options to pass through to the guzzle client
* @return \GuzzleHttp\Client
*/
public function getClient(array $options = []): \GuzzleHttp\Client public function getClient(array $options = []): \GuzzleHttp\Client
{ {
$stack = new \GuzzleHttp\HandlerStack(); $stack = new \GuzzleHttp\HandlerStack();

6
src/Oidc/Authentication/ClientAuthentication.php

@ -37,10 +37,10 @@ class ClientAuthentication extends Authentication
* Client authentication uses HTTP basic authentication with the client id * Client authentication uses HTTP basic authentication with the client id
* as the username and the client secret as the password. * as the username and the client secret as the password.
* *
* @param \GuzzleHttp\Psr7\Request $request request to authenticate * @param \Psr\Http\Message\RequestInterface $request request to authenticate
* @return \GuzzleHttp\Psr7\Request authenticated request * @return \Psr\Http\Message\RequestInterface authenticated request
*/ */
public function authenticate(\GuzzleHttp\Psr7\Request $request): \GuzzleHttp\Psr7\Request public function authenticate(\Psr\Http\Message\RequestInterface $request): \Psr\Http\Message\RequestInterface
{ {
return $request->withHeader('Authorization', 'Basic '.base64_encode($this->client_id.':'.$this->client_secret)); return $request->withHeader('Authorization', 'Basic '.base64_encode($this->client_id.':'.$this->client_secret));
} }

15
src/Oidc/Authentication/TokenAuthentication.php

@ -17,7 +17,7 @@ class TokenAuthentication extends Authentication
/** /**
* Who to call if the token is expired * Who to call if the token is expired
* @var callable * @var ?callable
*/ */
protected $refresh_callback; protected $refresh_callback;
@ -25,6 +25,7 @@ class TokenAuthentication extends Authentication
* Create a new token authentication provider * Create a new token authentication provider
* *
* @param Token $token token to authenticate requests with * @param Token $token token to authenticate requests with
* @param ?callable $refreshCallback callback to generate us a new token when our existing one expires
*/ */
public function __construct(Token $token, callable $refreshCallback = null) public function __construct(Token $token, callable $refreshCallback = null)
{ {
@ -32,6 +33,12 @@ class TokenAuthentication extends Authentication
$this->refresh_callback = $refreshCallback; $this->refresh_callback = $refreshCallback;
} }
/**
* Set the callback to be called when the underlying token expires
*
* @param callable $refreshCallback
* @return void
*/
public function setRefreshCallback(callable $refreshCallback): void public function setRefreshCallback(callable $refreshCallback): void
{ {
$this->refresh_callback = $refreshCallback; $this->refresh_callback = $refreshCallback;
@ -42,10 +49,10 @@ class TokenAuthentication extends Authentication
* *
* Token authentication uses the token as a bearer token. * Token authentication uses the token as a bearer token.
* *
* @param \GuzzleHttp\Psr7\Request $request request to authenticate * @param \Psr\Http\Message\RequestInterface $request request to authenticate
* @return \GuzzleHttp\Psr7\Request authenticated request * @return \Psr\Http\Message\RequestInterface authenticated request
*/ */
public function authenticate(\GuzzleHttp\Psr7\Request $request): \GuzzleHttp\Psr7\Request public function authenticate(\Psr\Http\Message\RequestInterface $request): \Psr\Http\Message\RequestInterface
{ {
if ($this->token->isExpired() && isset($this->refresh_callback)) if ($this->token->isExpired() && isset($this->refresh_callback))
{ {

66
src/Oidc/Client.php

@ -35,7 +35,7 @@ class Client
/** /**
* OIDC config fetched from the server or restored from cache * OIDC config fetched from the server or restored from cache
* @var array<string,mixed> * @var ?array<string,mixed>
*/ */
protected $oidc_config; protected $oidc_config;
@ -76,7 +76,7 @@ class Client
* *
* @return ?string * @return ?string
*/ */
public function getUrl(): string public function getUrl(): ?string
{ {
return $this->oidc_url; return $this->oidc_url;
} }
@ -84,17 +84,22 @@ class Client
/** /**
* Get the OpenId Connect configuration * Get the OpenId Connect configuration
* *
* @return array * @return array<string,string|array|bool>
*/ */
public function getConfiguration(): array public function getConfiguration(): array
{ {
if (!isset($this->oidc_config)) if (!isset($this->oidc_config))
{ {
$url = $this->oidc_url; $url = $this->oidc_url;
$this->oidc_config = Authkit2::cache('oidc.config.'.md5($this->oidc_url), function() use ($url) { $this->oidc_config = Authkit2::cache('oidc.config.'.md5($this->oidc_url),
$response = (new \GuzzleHttp\Client())->get($url.'/.well-known/openid-configuration'); /**
return json_decode($response->getBody(), true); * @return array<string,string|array|bool>
}); */
function() use ($url) {
$response = (new \GuzzleHttp\Client())->get($url.'/.well-known/openid-configuration');
return json_decode($response->getBody(), true);
}
);
} }
return $this->oidc_config; return $this->oidc_config;
@ -110,10 +115,15 @@ class Client
if (!isset($this->oidc_jwks)) if (!isset($this->oidc_jwks))
{ {
$client = $this; $client = $this;
$this->oidc_jwks = Authkit2::cache('oidc.config.'.md5($this->oidc_url).'.jwks', function() use ($client) { $this->oidc_jwks = Authkit2::cache('oidc.config.'.md5($this->oidc_url).'.jwks',
$response = $client->get($client->getConfiguration()['jwks_uri']); /**
return json_decode(json_encode($response), true); * @return array<string,array>
}); */
function() use ($client) {
$response = $client->get($client->getConfiguration()['jwks_uri']);
return json_decode(json_encode($response), true);
}
);
} }
return $this->oidc_jwks; return $this->oidc_jwks;
} }
@ -219,6 +229,15 @@ class Client
return Token::fromResponse($this, $response); return Token::fromResponse($this, $response);
} }
/**
* Generate the URL to redirect to in order to initiate the three-legged
* oauth flow
*
* @param string $redirect_uri url to redirect the user to after authentication
* @param string[] $scopes scopes to request from the openid provider
* @param string $state nonce
* @return string fully formed url
*/
public function createAuthorizationRedirectUrl(string $redirect_uri, array $scopes, string $state): string public function createAuthorizationRedirectUrl(string $redirect_uri, array $scopes, string $state): string
{ {
return $this->getEndpointUrl('authorization').'?'.http_build_query([ return $this->getEndpointUrl('authorization').'?'.http_build_query([
@ -230,6 +249,13 @@ class Client
]); ]);
} }
/**
* Generate the URL to redirect to in order to initiate a signout from the
* OIDC provider
*
* @param string $redirect_uri url to redirect the user to after logout
* @return string fully formed url
*/
public function createLogoutUrl(string $redirect_uri): string public function createLogoutUrl(string $redirect_uri): string
{ {
return $this->getEndpointUrl('end_session').'?'.http_build_query([ return $this->getEndpointUrl('end_session').'?'.http_build_query([
@ -237,11 +263,26 @@ class Client
]); ]);
} }
/**
* Refresh a token using a refresh token
*
* @param Token $token expired token that includes a refresh token
* @return Token newly generated token
*/
public function refreshToken(Token $token): Token public function refreshToken(Token $token): Token
{ {
return $this->createTokenFromRefreshToken($token->getRefreshToken()); $refresh_token = $token->getRefreshToken();
if (!isset($refresh_token))
throw new \Exception("Cannot refresh token initialized without refresh token");
return $this->createTokenFromRefreshToken($refresh_token);
} }
/**
* Fetch the available information on the user from the OIDC provider
*
* @param Token $token token representing the user
* @return array<string,mixed>
*/
public function getTokenUserInfo(Token $token): array public function getTokenUserInfo(Token $token): array
{ {
return json_decode($token->getClient()->get($this->getEndpointUrl('userinfo'))->getBody(), true); return json_decode($token->getClient()->get($this->getEndpointUrl('userinfo'))->getBody(), true);
@ -249,5 +290,4 @@ class Client
// todo: introspect, etc // todo: introspect, etc
} }

1
src/Oidc/Flows/ServiceAccountFlow.php

@ -2,7 +2,6 @@
namespace authkit2\Oidc\Flows; namespace authkit2\Oidc\Flows;
use authkit2\Oidc\Client; use authkit2\Oidc\Client;
use authkit2\Oidc\Authentication\ClientAuthentication;
use authkit2\Oidc\Token; use authkit2\Oidc\Token;
/** /**

3
src/Oidc/Flows/UserFlow.php

@ -3,7 +3,6 @@
namespace authkit2\Oidc\Flows; namespace authkit2\Oidc\Flows;
use authkit2\Authkit2; use authkit2\Authkit2;
use authkit2\Oidc\Client; use authkit2\Oidc\Client;
use authkit2\Oidc\Authentication\ClientAuthentication;
use authkit2\Oidc\Token; use authkit2\Oidc\Token;
/** /**
@ -74,8 +73,8 @@ class UserFlow
* *
* THIS DOES NOT VALIDATE THE STATE. Call validateState first. * THIS DOES NOT VALIDATE THE STATE. Call validateState first.
* *
* @param string $redirect_uri url the oidc endpoint redirects back to; must match one given in call to getRedirectUrl
* @param string $code code returned by the authorization flow * @param string $code code returned by the authorization flow
* @param string $redirect_uri url the oidc endpoint redirects back to; must match one given in call to getRedirectUrl
* @return Token * @return Token
*/ */
public function exchangeCodeForToken(string $code, string $redirect_uri): Token public function exchangeCodeForToken(string $code, string $redirect_uri): Token

46
src/Oidc/Token.php

@ -1,7 +1,6 @@
<?php <?php
namespace authkit2\Oidc; namespace authkit2\Oidc;
use authkit2\Authkit2;
use authkit2\Oidc\Client; use authkit2\Oidc\Client;
use Firebase\JWT\JWT; use Firebase\JWT\JWT;
use Firebase\JWT\JWK; use Firebase\JWT\JWK;
@ -25,7 +24,7 @@ class Token
/** /**
* OIDC JWT refresh token * OIDC JWT refresh token
* @var string * @var ?string
*/ */
protected $refresh_token; protected $refresh_token;
@ -81,7 +80,7 @@ class Token
/** /**
* Get a HTTP client that's authenticated with this token's credentials * Get a HTTP client that's authenticated with this token's credentials
* *
* @param array $options * @param array<string,mixed> $options
* @return \GuzzleHttp\Client * @return \GuzzleHttp\Client
*/ */
public function getClient(array $options = []): \GuzzleHttp\Client public function getClient(array $options = []): \GuzzleHttp\Client
@ -90,26 +89,29 @@ class Token
// to ask it to refresh itself // to ask it to refresh itself
$state = new \stdClass(); $state = new \stdClass();
$state->refresher = function($token) use ($state) { $state->refresher =
$client = $this->client; function(Token $token) use ($state) : Token {
$refresh_callback = $this->refresh_callback; $client = $this->client;
$refresh_callback = $this->refresh_callback;
// Refresh the token // Refresh the token
$new_token = $client->createTokenFromRefreshToken($this->refresh_token); if (!isset($this->refresh_token))
throw new \Exception("Token expired");
$new_token = $client->createTokenFromRefreshToken($this->refresh_token);
// Rebind this callback to the new token // Rebind this callback to the new token
$state->refresher->bindTo($new_token); $state->refresher->bindTo($new_token);
// Call the refresh callback // Call the refresh callback
if (isset($refresh_callback)) if (isset($refresh_callback))
{ {
// Copy over the token-level refresh callback // Copy over the token-level refresh callback
$new_token->setRefreshCallback($refresh_callback); $new_token->setRefreshCallback($refresh_callback);
$refresh_callback($new_token); $refresh_callback($new_token);
} }
return $new_token; return $new_token;
}; };
$auth = new Authentication\TokenAuthentication($this, $state->refresher); $auth = new Authentication\TokenAuthentication($this, $state->refresher);
return $auth->getClient($options); return $auth->getClient($options);
@ -171,7 +173,7 @@ class Token
} }
$this->refresh_token_data = json_decode(json_encode($this->decode($this->refresh_token)), true); $this->refresh_token_data = json_decode(json_encode($this->decode($this->refresh_token)), true);
} }
return $this->token_data; return $this->refresh_token_data;
} }
/** /**
@ -266,9 +268,9 @@ class Token
/** /**
* Fetch the user's refresh token * Fetch the user's refresh token
* *
* @return string * @return ?string
*/ */
public function getRefreshToken(): string public function getRefreshToken(): ?string
{ {
return $this->refresh_token; return $this->refresh_token;
} }

3
src/Providers/Authkit2ServiceProvider.php

@ -4,6 +4,9 @@ namespace authkit2\Providers;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
/**
* Core Authkit2 provider
*/
class Authkit2ServiceProvider extends ServiceProvider class Authkit2ServiceProvider extends ServiceProvider
{ {

9
src/Providers/AuthnServiceProvider.php

@ -19,10 +19,10 @@ class AuthnServiceProvider extends ServiceProvider
*/ */
public function register(): void public function register(): void
{ {
$this->app->singleton(\authkit2\Oidc\Flows\ServiceAccountFlow::class, function($app) { $this->app->singleton(\authkit2\Oidc\Flows\ServiceAccountFlow::class, function() : \authkit2\Oidc\Flows\ServiceAccountFlow {
return new \authkit2\Oidc\Flows\ServiceAccountFlow(Authkit2::get_client()); return new \authkit2\Oidc\Flows\ServiceAccountFlow(Authkit2::get_client());
}); });
$this->app->singleton(\authkit2\Oidc\Flows\UserFlow::class, function($app) { $this->app->singleton(\authkit2\Oidc\Flows\UserFlow::class, function() : \authkit2\Oidc\Flows\UserFlow {
return new \authkit2\Oidc\Flows\UserFlow(Authkit2::get_client()); return new \authkit2\Oidc\Flows\UserFlow(Authkit2::get_client());
}); });
} }
@ -36,7 +36,7 @@ class AuthnServiceProvider extends ServiceProvider
{ {
// register our observer on the user model so we can dynamically add/remove // register our observer on the user model so we can dynamically add/remove
// the token object + client // the token object + client
$user_model = config('auth.providers.users.model'); $user_model = (string)config('auth.providers.users.model');
$user_model::observe(\authkit2\Observers\UserObserver::class); $user_model::observe(\authkit2\Observers\UserObserver::class);
// Register all authentication routes // Register all authentication routes
@ -66,6 +66,7 @@ class AuthnServiceProvider extends ServiceProvider
// TODO: For some reason route($name) isn't working here. We're // TODO: For some reason route($name) isn't working here. We're
// just looking through the routes manually for now... // just looking through the routes manually for now...
// @phan-suppress-next-line PhanTypeNoPropertiesForeach
foreach (\Route::getRoutes() as $route) foreach (\Route::getRoutes() as $route)
{ {
if ($route->getName() === 'login.callback') if ($route->getName() === 'login.callback')
@ -124,7 +125,7 @@ class AuthnServiceProvider extends ServiceProvider
// running a composer require/composer install without credentials present. // running a composer require/composer install without credentials present.
if (config('authkit.authn.openid.client_id') != null && config('authkit.authn.openid.client_secret') != null && config('authkit.authn.openid.endpoint') != null) if (config('authkit.authn.openid.client_id') != null && config('authkit.authn.openid.client_secret') != null && config('authkit.authn.openid.endpoint') != null)
{ {
$this->app->booted(function($app) { $this->app->booted(function() {
Authkit2::configure(config('authkit.authn.openid.client_id'), config('authkit.authn.openid.client_secret'), config('authkit.authn.openid.endpoint')); Authkit2::configure(config('authkit.authn.openid.client_id'), config('authkit.authn.openid.client_secret'), config('authkit.authn.openid.endpoint'));
}); });
} }

10
src/Providers/AuthzServiceProvider.php

@ -12,10 +12,20 @@ use Illuminate\Support\ServiceProvider;
*/ */
class AuthzServiceProvider extends ServiceProvider class AuthzServiceProvider extends ServiceProvider
{ {
/**
* Register the additional service providers the authorization process depends on
*
* @return void
*/
public function register() public function register()
{ {
} }
/**
* Initialize and register all authorization resources
*
* @return void
*/
public function boot() public function boot()
{ {
} }

Loading…
Cancel
Save