Pārlūkot izejas kodu

open api auth call

master
Daniel pirms 1 gada
vecāks
revīzija
af9eb1798b
8 mainītis faili ar 267 papildinājumiem un 8 dzēšanām
  1. +8
    -6
      httpdocs/config/packages/security.yaml
  2. +1
    -1
      httpdocs/config/routes.yaml
  3. +8
    -0
      httpdocs/config/services.yaml
  4. +16
    -0
      httpdocs/src/Dto/LoginRequest.php
  5. +14
    -1
      httpdocs/src/Entity/MediaObject.php
  6. +111
    -0
      httpdocs/src/OpenApi/AuthDecorator.php
  7. +34
    -0
      httpdocs/src/OpenApi/OpenApiFactory.php
  8. +75
    -0
      httpdocs/src/Security/JwtAuthenticator.php

+ 8
- 6
httpdocs/config/packages/security.yaml Parādīt failu

@@ -16,12 +16,14 @@ security:
auth:
pattern: ^/api/auth
stateless: true
json_login:
check_path: /api/auth
username_path: email
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
custom_authenticators:
- App\Security\JwtAuthenticator
# json_login:
# check_path: /api/auth
# username_path: email
# password_path: password
# success_handler: lexik_jwt_authentication.handler.authentication_success
# failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/api/
stateless: true


+ 1
- 1
httpdocs/config/routes.yaml Parādīt failu

@@ -4,7 +4,7 @@ controllers:
namespace: App\Controller
type: attribute

# Bestehende Auth-Route
## Bestehende Auth-Route
auth:
path: /api/auth
methods: ['POST']


+ 8
- 0
httpdocs/config/services.yaml Parādīt failu

@@ -23,6 +23,14 @@ services:
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones

App\OpenApi\AuthDecorator:
decorates: 'api_platform.openapi.factory'
arguments: ['@.inner']

App\OpenApi\OpenApiFactory:
decorates: 'api_platform.openapi.factory'
arguments: [ '@.inner' ]

acme_api.event.authentication_success_listener:
class: App\EventListener\AuthenticationSuccessListener
tags:


+ 16
- 0
httpdocs/src/Dto/LoginRequest.php Parādīt failu

@@ -0,0 +1,16 @@
<?php
// src/Dto/LoginRequest.php
namespace App\Dto;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\ApiProperty;

#[ApiResource]
class LoginRequest
{
#[ApiProperty(example: "user@example.com")]
public string $email = '';

#[ApiProperty(example: "password123")]
public string $password = '';
}

+ 14
- 1
httpdocs/src/Entity/MediaObject.php Parādīt failu

@@ -27,7 +27,20 @@ use Vich\UploaderBundle\Mapping\Annotation as Vich;
shortName: 'MediaObject',
types: ['https://schema.org/MediaObject'],
operations: [
new Get(),
new Get(
openapi: new Model\Operation(
responses: [
'200' => [
'content' => [
'application/ld+json' => [
'schema' => ['$ref' => '#/components/schemas/MediaObject']
]
]
]
]
),
normalizationContext: ['groups' => ['media_object:read']]
),
new GetCollection(),
new Post(
controller: CreateMediaObjectAction::class,


+ 111
- 0
httpdocs/src/OpenApi/AuthDecorator.php Parādīt failu

@@ -0,0 +1,111 @@
<?php
// src/OpenApi/AuthDecorator.php
namespace App\OpenApi;

use ApiPlatform\OpenApi\Factory\OpenApiFactoryInterface;
use ApiPlatform\OpenApi\Model\PathItem;
use ApiPlatform\OpenApi\Model\Operation;
use ApiPlatform\OpenApi\Model\RequestBody;
use ApiPlatform\OpenApi\OpenApi;
use ArrayObject;

final class AuthDecorator implements OpenApiFactoryInterface
{
public function __construct(
private OpenApiFactoryInterface $decorated
) {}

public function __invoke(array $context = []): OpenApi
{
$openApi = ($this->decorated)($context);

$schemas = $openApi->getComponents()->getSchemas();

$schemas['Credentials'] = new \ArrayObject([
'type' => 'object',
'properties' => [
'email' => [
'type' => 'string',
'example' => 'user@example.com',
],
'password' => [
'type' => 'string',
'example' => 'password123',
],
],
]);

$schemas['AuthResponse'] = new \ArrayObject([
'type' => 'object',
'properties' => [
'token' => [
'type' => 'string',
'readOnly' => true,
],
'id' => [
'type' => 'string',
'readOnly' => true,
],
'email' => [
'type' => 'string',
'readOnly' => true,
],
'firstName' => [
'type' => 'string',
'readOnly' => true,
],
'lastName' => [
'type' => 'string',
'readOnly' => true,
],
'roles' => [
'type' => 'array',
'items' => [
'type' => 'string'
],
'readOnly' => true,
],
'user' => [
'$ref' => '#/components/schemas/User', // API Platform's automatisch generiertes Schema
'readOnly' => true,
],
],
]);

$pathItem = new PathItem(
ref: 'JWT Token',
post: new Operation(
operationId: 'postCredentialsItem',
tags: ['Auth'],
responses: [
'200' => [
'description' => 'Get JWT token',
'content' => [
'application/json' => [
'schema' => [
'$ref' => '#/components/schemas/AuthResponse',
],
],
],
],
],
summary: 'Get JWT token to login.',
requestBody: new RequestBody(
description: 'Generate new JWT Token',
content: new \ArrayObject([
'application/json' => [
'schema' => [
'$ref' => '#/components/schemas/Credentials',
],
],
]),
),
security: [],
),
);

$openApi->getPaths()->addPath('/api/auth', $pathItem);

return $openApi;
}
}

+ 34
- 0
httpdocs/src/OpenApi/OpenApiFactory.php Parādīt failu

@@ -0,0 +1,34 @@
<?php
namespace App\OpenApi;

use ApiPlatform\OpenApi\Factory\OpenApiFactoryInterface;
use ApiPlatform\OpenApi\OpenApi;
use ApiPlatform\OpenApi\Model;

class OpenApiFactory implements OpenApiFactoryInterface
{
public function __construct(
private OpenApiFactoryInterface $decorated
) {}

public function __invoke(array $context = []): OpenApi
{
$openApi = ($this->decorated)($context);

// Cleanup schema names
$schemas = $openApi->getComponents()->getSchemas();
$cleanedSchemas = new \ArrayObject();

foreach ($schemas as $key => $schema) {
// Remove the .jsonld-media_object.read suffix
$newKey = preg_replace('/\.jsonld-.*/', '', $key);
$cleanedSchemas[$newKey] = $schema;
}

$openApi = $openApi->withComponents(
$openApi->getComponents()->withSchemas($cleanedSchemas)
);

return $openApi;
}
}

+ 75
- 0
httpdocs/src/Security/JwtAuthenticator.php Parādīt failu

@@ -0,0 +1,75 @@
<?php
// src/Security/JwtAuthenticator.php
namespace App\Security;

use App\ApiResource\UserApi;
use App\Repository\UserRepository;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfonycasts\MicroMapper\MicroMapperInterface;

class JwtAuthenticator extends AbstractAuthenticator
{
public function __construct(
private JWTTokenManagerInterface $jwtManager,
private UserRepository $userRepository,
private MicroMapperInterface $microMapper
) {}

public function supports(Request $request): ?bool
{
return $request->getPathInfo() === '/api/auth' && $request->isMethod('POST');
}

public function authenticate(Request $request): Passport
{
$data = json_decode($request->getContent(), true);

if (!$data) {
throw new CustomUserMessageAuthenticationException('Invalid JSON.');
}

$email = $data['email'] ?? '';
$password = $data['password'] ?? '';

return new Passport(
new UserBadge($email),
new PasswordCredentials($password)
);
}

public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
$user = $token->getUser();

$jwtToken = $this->jwtManager->create($user);

return new JsonResponse([
'token' => $jwtToken,
'id' => $user->getId(),
'email' => $user->getEmail(),
'firstName' => $user->getFirstName(),
'lastName' => $user->getLastName(),
'roles' => $user->getRoles(),
'user' => $this->microMapper->map(
$user, UserApi::class
)
]);
}

public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
return new JsonResponse([
'message' => $exception->getMessage()
], Response::HTTP_UNAUTHORIZED);
}
}

Notiek ielāde…
Atcelt
Saglabāt