Skip to content

Commit

Permalink
chore: use OIDC Discovery with local cache
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentchalamon committed Mar 13, 2024
1 parent d4d0104 commit 0eaca8e
Show file tree
Hide file tree
Showing 57 changed files with 497 additions and 213 deletions.
3 changes: 2 additions & 1 deletion api/.env
Expand Up @@ -18,10 +18,11 @@
TRUSTED_PROXIES=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
TRUSTED_HOSTS=^(localhost|php)$
OIDC_SERVER_URL=https://localhost/oidc/realms/demo
OIDC_SERVER_URL_INTERNAL=http://php/oidc/realms/demo
OIDC_SERVER_URL_INTERNAL=http://keycloak:8080/oidc/realms/demo
OIDC_SWAGGER_CLIENT_ID=api-platform-swagger
OIDC_API_CLIENT_ID=api-platform-api
OIDC_API_CLIENT_SECRET=sEocbxCy7iFS8NzYzWyQ71QgxTDZ9fnU
OIDC_AUD=api-platform

###> symfony/framework-bundle ###
APP_ENV=dev
Expand Down
1 change: 1 addition & 0 deletions api/.env.test
Expand Up @@ -4,6 +4,7 @@ APP_SECRET='$ecretf0rt3st'
SYMFONY_DEPRECATIONS_HELPER=999999
PANTHER_APP_ENV=panther
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots
OIDC_JWK='{"kty": "EC","d": "cT3_vKHaGOAhhmzR0Jbi1ko40dNtpjtaiWzm_7VNwLA","use": "sig","crv": "P-256","x": "n6PnJPqNK5nP-ymwwsOIqZvjiCKFNzRyqWA8KNyBsDo","y": "bQSmMlDXOmtgyS1rhsKUmqlxq-8Kw0Iw9t50cSloTMM","alg": "ES256"}'

# API Platform distribution
TRUSTED_HOSTS=^example\.com|localhost$
2 changes: 2 additions & 0 deletions api/Dockerfile
Expand Up @@ -27,12 +27,14 @@ RUN apt-get update; \
file \
gettext \
git \
zip \
; \
rm -rf /var/lib/apt/lists/*

RUN set -eux; \
install-php-extensions \
apcu \
bcmath \
intl \
opcache \
zip \
Expand Down
7 changes: 6 additions & 1 deletion api/composer.json
Expand Up @@ -33,6 +33,7 @@
"symfony/uid": "7.0.*",
"symfony/validator": "7.0.*",
"symfony/yaml": "7.0.*",
"web-token/jwt-bundle": "^3.3",
"web-token/jwt-library": "^3.3",
"webonyx/graphql-php": "^15.8",
"zenstruck/foundry": "^1.36"
Expand Down Expand Up @@ -108,7 +109,11 @@
"symfony": {
"allow-contrib": false,
"require": "7.0.*",
"docker": false
"docker": false,
"endpoint": [
"https://api.github.com/repos/Spomky-Labs/recipes/contents/index.json?ref=main",
"flex://defaults"
]
}
}
}
83 changes: 82 additions & 1 deletion api/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions api/config/bundles.php
Expand Up @@ -18,4 +18,5 @@
DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true],
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['all' => true],
Zenstruck\Foundry\ZenstruckFoundryBundle::class => ['all' => true],
Jose\Bundle\JoseFramework\JoseFrameworkBundle::class => ['all' => true],
];
6 changes: 0 additions & 6 deletions api/config/packages/framework.yaml
Expand Up @@ -34,9 +34,3 @@ when@test:
test: true
#session:
# storage_factory_id: session.storage.factory.mock_file

services:
App\Tests\Api\Mock\:
resource: '../../tests/Api/Mock/'
autowire: true
autoconfigure: true
44 changes: 44 additions & 0 deletions api/config/packages/jose.yaml
@@ -0,0 +1,44 @@
jose:
jws:
serializers:
oidc:
serializers: ['jws_compact']
is_public: true
loaders:
oidc:
serializers: ['jws_compact']
signature_algorithms: ['HS256', 'RS256', 'ES256']
header_checkers: ['alg', 'iat', 'nbf', 'exp', 'aud', 'iss']
is_public: true

services:
_defaults:
autowire: true
autoconfigure: true

Jose\Component\Checker\AlgorithmChecker:
arguments:
$supportedAlgorithms: ['HS256', 'RS256', 'ES256']
tags:
- name: 'jose.checker.header'
alias: 'alg'
Jose\Component\Checker\AudienceChecker:
arguments:
$audience: '%env(OIDC_AUD)%'
tags:
- name: 'jose.checker.header'
alias: 'aud'
Jose\Component\Checker\IssuerChecker:
arguments:
$issuers: ['%env(OIDC_SERVER_URL)%']
tags:
- name: 'jose.checker.header'
alias: 'iss'

when@test:
jose:
jws:
builders:
oidc:
signature_algorithms: ['HS256', 'RS256', 'ES256']
is_public: true
29 changes: 10 additions & 19 deletions api/config/packages/security.yaml
@@ -1,7 +1,3 @@
parameters:
app.oidc.jwk: '{"kty": "EC","d": "cT3_vKHaGOAhhmzR0Jbi1ko40dNtpjtaiWzm_7VNwLA","use": "sig","crv": "P-256","x": "n6PnJPqNK5nP-ymwwsOIqZvjiCKFNzRyqWA8KNyBsDo","y": "bQSmMlDXOmtgyS1rhsKUmqlxq-8Kw0Iw9t50cSloTMM","alg": "ES256"}'
app.oidc.aud: 'api-platform'

security:
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers:
Expand Down Expand Up @@ -32,10 +28,14 @@ when@prod: &prod
firewalls:
main:
access_token:
token_handler:
oidc_user_info:
claim: email
base_uri: '%env(OIDC_SERVER_URL_INTERNAL)%/protocol/openid-connect/userinfo'
token_handler: App\Security\Http\AccessToken\Oidc\OidcDiscoveryTokenHandler
# todo support Discovery in Symfony
# oidc:
# claim: 'email'
# base_uri: '%env(OIDC_SERVER_URL)%'
# audience: '%env(OIDC_AUD)%'
# cache: '@cache.app' # default
# cache_ttl: 3600 # default

when@dev: *prod

Expand All @@ -47,16 +47,7 @@ when@test:
token_handler:
oidc:
claim: email
audience: '%app.oidc.aud%'
audience: '%env(OIDC_AUD)%'
issuers: [ '%env(OIDC_SERVER_URL)%' ]
algorithm: 'ES256'
key: '%app.oidc.jwk%'
# required by App\Tests\Api\Trait\SecurityTrait
parameters:
app.oidc.issuer: '%env(OIDC_SERVER_URL)%'
services:
app.security.jwk:
parent: 'security.access_token_handler.oidc.jwk'
public: true
arguments:
$json: '%app.oidc.jwk%'
key: '%env(OIDC_JWK)%'
8 changes: 8 additions & 0 deletions api/config/services.yaml
Expand Up @@ -22,3 +22,11 @@ services:

# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones

when@test:
services:
App\Tests\Api\Security\:
resource: '../tests/Api/Security/'
autowire: true
autoconfigure: true
public: true
2 changes: 1 addition & 1 deletion api/src/Command/BooksImportCommand.php
Expand Up @@ -84,7 +84,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$io->progressFinish();

$output->write($this->serializer->serialize($data, 'json', [JsonEncode::OPTIONS => JSON_PRETTY_PRINT]));
$output->write($this->serializer->serialize($data, 'json', [JsonEncode::OPTIONS => \JSON_PRETTY_PRINT]));

return Command::SUCCESS;
}
Expand Down
4 changes: 3 additions & 1 deletion api/src/DataFixtures/Story/DefaultStory.php
Expand Up @@ -14,7 +14,9 @@

final class DefaultStory extends Story
{
public function __construct(private readonly DecoderInterface $decoder) {}
public function __construct(private readonly DecoderInterface $decoder)
{
}

public function build(): void
{
Expand Down
Expand Up @@ -16,9 +16,11 @@
*/
final readonly class BookmarkQueryCollectionExtension implements QueryCollectionExtensionInterface
{
public function __construct(private Security $security) {}
public function __construct(private Security $security)
{
}

public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, Operation $operation = null, array $context = []): void
public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?Operation $operation = null, array $context = []): void
{
if (
Bookmark::class !== $resourceClass
Expand Down
2 changes: 1 addition & 1 deletion api/src/Doctrine/Orm/Filter/NameFilter.php
Expand Up @@ -31,7 +31,7 @@ public function getDescription(string $resourceClass): array
/**
* @param string|null $value
*/
protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, Operation $operation = null, array $context = []): void
protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?Operation $operation = null, array $context = []): void
{
if ('name' !== $property) {
return;
Expand Down
1 change: 0 additions & 1 deletion api/src/Entity/Review.php
Expand Up @@ -14,7 +14,6 @@
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use ApiPlatform\Metadata\UrlGeneratorInterface;
use ApiPlatform\State\CreateProvider;
use App\Repository\ReviewRepository;
use App\Serializer\IriTransformerNormalizer;
Expand Down
4 changes: 3 additions & 1 deletion api/src/Entity/User.php
Expand Up @@ -101,7 +101,9 @@ public function getId(): ?Uuid
return $this->id;
}

public function eraseCredentials(): void {}
public function eraseCredentials(): void
{
}

/**
* @return array<int, string>
Expand Down
4 changes: 3 additions & 1 deletion api/src/Security/Core/UserProvider.php
Expand Up @@ -17,7 +17,9 @@
*/
final readonly class UserProvider implements AttributesBasedUserProviderInterface
{
public function __construct(private ManagerRegistry $registry, private UserRepository $repository) {}
public function __construct(private ManagerRegistry $registry, private UserRepository $repository)
{
}

public function refreshUser(UserInterface $user): UserInterface
{
Expand Down

0 comments on commit 0eaca8e

Please sign in to comment.