kdefives/oauth2-riot

为PHP League的OAuth 2.0客户端提供Riot (RSO) OAuth 2.0支持

v1.0.0 2021-02-12 20:37 UTC

This package is auto-updated.

Last update: 2024-09-22 21:47:19 UTC


README

为PHP League的OAuth 2.0客户端提供Riot (RSO) OAuth 2.0支持

version License: MIT

Riot OAuth 2.0客户端提供商

感谢使用以下(Google提供商)作为示例构建此提供商。

此包为PHP League的OAuth 2.0客户端提供了Riot OAuth 2.0支持。

此包与以下包进行了测试:https://github.com/knpuniversity/oauth2-client-bundle
以下是一个使用此包的用法示例。

以下版本的PHP受到支持。

  • PHP 7.3
  • PHP 7.4

使用RSO (Riot Sign On)通过Riot ID账户验证用户的包。

注意:要实现Riot RSO,将需要RSO账户客户端ID和客户端密钥。这些可以在RSO文档中找到: https://www.riotgames.com/en/DevRel/rso

要求

在您的knpu_oauth2_client.yaml中声明以下参数

使用https://github.com/knpuniversity/oauth2-client-bundle的knpu_oauth2_client声明示例

# file : app\config\packages\knpu_oauth2_client.yaml

knpu_oauth2_client:
    clients:
        # configure your clients as described here: https://github.com/knpuniversity/oauth2-client-bundle#configuration
        # will create service: "knpu.oauth2.client.foo_bar_oauth"
        # an instance of: KnpU\OAuth2ClientBundle\Client\OAuth2Client
        riot_oauth:
            type: generic
            provider_class: League\OAuth2\Client\Provider\Riot

            # optional: a class that extends OAuth2Client
            # client_class: Some\Custom\Client

            # optional: if your provider has custom constructor options
            provider_options:
                url_authorization: '%env(URL_AUTHORIZATION)%'
                url_token: '%env(URL_TOKEN)%'
                url_user_info: '%env(URL_USERINFO)%'

            # now, all the normal options!
            client_id: '%env(riot_client_id)%'
            client_secret: '%env(riot_client_secret)%'
            redirect_route: connect_riot_check
            redirect_params: {}

安装

要安装,请使用composer

composer require kdefives/oauth2-riot

用法

以下是一个用法示例。请阅读knpuniversity/oauth2-client-bundle的README文档了解如何集成提供者: https://github.com/knpuniversity/oauth2-client-bundle

使用Symfony 5.2.2的授权代码流示例(与PHP-7.4进行测试)

认证器声明

<?php
// app/src/Security/RiotAuthenticator.php

namespace App\Security;

use App\Entity\Player;
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Client\OAuth2ClientInterface;
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use League\OAuth2\Client\Provider\RiotUser;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;

/**
 * Class RiotAuthenticator used for Riot RSO authentication using knpuniversity/oauth2-client-bundle
 * @package App\Security
 */
class RiotAuthenticator extends SocialAuthenticator
{
    use TargetPathTrait;

    private $clientRegistry;
    private $em;
    private $router;

    public function __construct(ClientRegistry $clientRegistry,
                                EntityManagerInterface $em,
                                RouterInterface $router)
    {
        $this->clientRegistry = $clientRegistry;
        $this->em = $em;
        $this->router = $router;
    }

    public function supports(Request $request)
    {
        // continue ONLY if the current ROUTE matches the check ROUTE
        return $request->attributes->get('_route') === 'connect_riot_check';
    }

    public function getCredentials(Request $request)
    {
        // this method is only called if supports() returns true

        // For Symfony lower than 3.4 the supports method need to be called manually here:
        // if (!$this->supports($request)) {
        //     return null;
        // }

        return $this->fetchAccessToken($this->getRiotClient());
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
         /** @var RiotUser $riotUser */
        $riotUser = $this->getRiotClient()
            ->fetchUserFromToken($credentials);

        // We check we have a matching user in our database, then return the user
        return $this->em->getRepository(Player::class)->findOneBy(
            ['puuid' => $riotUser->getPuuid()]
        );

        // If return null, login should fail
        //return null;
    }

    /**
     * @return OAuth2ClientInterface
     */
    private function getRiotClient()
    {
        return $this->clientRegistry
            // "facebook_main" is the key used in config/packages/knpu_oauth2_client.yaml
            ->getClient('riot_oauth');
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        // Get initial target URI before redirection to RSO (login page or refresh token)
        $targetPath = $this->getTargetPath($request->getSession(), $providerKey);

        // Redirect to homepage by default
        if (!$targetPath) {
            $targetPath = $this->router->generate('homepage.display');
        }

        return new RedirectResponse($targetPath);

        // or, on success, let the request continue to be handled by the controller
        //return null;
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        //$message = strtr($exception->getMessageKey(), $exception->getMessageData());
        //return new Response($message, Response::HTTP_FORBIDDEN);

        // Redirect to homepage
        return new RedirectResponse(
            '/', // might be the site, where users choose their oauth provider
        );
    }

    /**
     * Called when authentication is needed, but it's not sent.
     * This redirects to the 'login'.
     * @param Request $request
     * @param AuthenticationException|null $authException
     * @return RedirectResponse
     */
    public function start(Request $request, AuthenticationException $authException = null)
    {
        return new RedirectResponse(
            '/connect/', // might be the site, where users choose their oauth provider
            Response::HTTP_TEMPORARY_REDIRECT
        );
    }
}

控制器声明

<?php
// app/src/Controller/RiotRsoController.php

namespace App\Controller;


use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

/**
 * Class RiotRsoController
 * @package App\Controller
 */
class RiotRsoController extends AbstractController
{
    /**
     * Link to this controller to start the "connect" process
     *
     * @Route("/connect", name="connect_riot_start")
     *
     * @param ClientRegistry $clientRegistry
     * @return RedirectResponse
     */
    public function connectAction(ClientRegistry $clientRegistry)
    {
        // on Symfony 3.3 or lower, $clientRegistry = $this->get('knpu.oauth2.registry');

        // will redirect to Riot RSO
        return $clientRegistry
            ->getClient('riot_oauth') // key used in config/packages/knpu_oauth2_client.yaml
            ->redirect(
                ['openid'], // the scopes you want to access
                []
            );
    }

    /**
     * After going to Riot RSO, you're redirected back here
     * because this is the "redirect_route" you configured
     * in config/packages/knpu_oauth2_client.yaml
     *
     * @Route("/riot-oauth/callback", name="connect_riot_check")
     *
     * @param Request $request
     * @param ClientRegistry $clientRegistry
     */
    public function connectCheckAction(Request $request, ClientRegistry $clientRegistry)
    {
        // ** if you want to *authenticate* the user, then
        // leave this method blank and create a Guard authenticator
        // (read below)

        //return new Response("<html>Hello World</html>");
    }
}

Guard认证器声明(security.yaml)

# app/config/packages/security.yaml

security:
    # https://symfony.com.cn/doc/current/security.html#where-do-users-come-from-user-providers
    providers:
        # used to reload user from session & other features (e.g. switch_user)
        #in_memory: { memory: ~ }
        app_user_provider:
            entity:
                class: App\Entity\Player
                property: id
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            #anonymous: ~
            anonymous: true
            lazy: true
            provider: app_user_provider

            # activate different ways to authenticate
            # https://symfony.com.cn/doc/current/security.html#firewalls-authentication

            # https://symfony.com.cn/doc/current/security/impersonating_user.html
           # switch_user: true

            guard:
                authenticators:
                    - App\Security\RiotAuthenticator

    # Easy way to control access for large sections of your site
    # Note: Only the *first* access control that matches will be used
    access_control:
        - { path: ^/something, role: IS_AUTHENTICATED_REMEMBERED }
        - { path: ^/something-else, role: IS_AUTHENTICATED_REMEMBERED }
        - { path: ^/*, role: IS_AUTHENTICATED_ANONYMOUSLY }

用于认证的实体

<?php
// app/src/Entity/Player.php

namespace App\Entity;

use App\Repository\PlayerRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * @ORM\Entity(repositoryClass=PlayerRepository::class)
 */
class Player implements UserInterface
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255, unique=true)
     *
     * A PUUID should never change (even if region transfers become a thing for val too)
     */
    private $puuid;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getPuuid(): ?string
    {
        return $this->puuid;
    }

    public function setPuuid(string $puuid): self
    {
        $this->puuid = $puuid;

        return $this;
    }

    /**
     * @inheritDoc
     */
    public function getRoles(): array
    {
        //$roles = $this->roles;
        // guarantee every user at least has ROLE_USER
        $roles[] = 'ROLE_USER';

        return array_unique($roles);
    }

    /**
     * @inheritDoc
     */
    public function getPassword(): ?string
    {
        return null;
    }

    /**
     * @inheritDoc
     */
    public function getSalt(): ?string
    {
        return null;
    }

    /**
     * @inheritDoc
     */
    public function getUsername(): string
    {
        return $this->puuid;
    }

    /**
     * @inheritDoc
     */
    public function eraseCredentials()
    {
        // If you store any temporary, sensitive data on the user, clear it here
        // $this->plainPassword = null;
    }
}

测试

可以使用以下命令运行测试

composer test

可以使用以下命令运行风格检查

composer check

如何贡献

如果您想快速在本地主机克隆、开发和测试,可以按照以下步骤操作。确实,您必须具有git和docker(例如Windows上的Docker Desktop)才能进行操作。

git clone ...
cd oauth2/riot
docker-compose build
docker-compose up -d php-fpm-riot-oauth2
docker-compose exec php-fpm-riot-oauth2 bash
composer test