I've done a URL shortener app for my server side programming subject.
For fun I decided to deploy it on a web server, in this case the oracle free tier with an ubuntu server with apache, and I also adquired a public domain.
In my local environment all works fine using the symfony local server:
symfony server:start
But once I deployed the app the redirections doesn't work anymore.
All occur on the home page, the user types the URL to shortener( and the new 'endpoint'), Symfony validates the URL and if it's the case the new 'endpoint', then if all it's valid store the data and also in the home shows the new URL (ex. '/hjas8ab') that the user needs to paste it behind my domain (ex. mydomain.xyz/hjas8ab) and then Symfony checks that the 'endpoint' is associated to an URL in the database and redirects the user to that URL. And is in this step that the app deployed fails, even if I hardcode the URL to redirect.
So I think it's the Apache virtual host configuration, but I'm not sure.
Here is the code:
ShortenerController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class ShortenerController extends AbstractController
{
#[Route('/{url?}', name: 'shortener')]
public function index(?String $created, ?String $endpoint,?bool $autogenerated, ?String $url, Request $req): Response
{
if($_SERVER["REQUEST_METHOD"] === "GET" && !is_null($url) ){
return $this->forward('App\Controller\UrlAddressController::redirection', ['finalRedirection' => $url]);
}
if(!is_null($endpoint)){
return $this->render('shortener/index.html.twig', [
'created' => $created, 'endpoint' => $endpoint, 'autogenerated' => $autogenerated
]);
}
return $this->render('shortener/index.html.twig');
}
#[Route('/create/autogenerated', name: 'autogenerated')]
public function autogenerated(Request $req): Response
{
define('URL_PATTERN', "/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+#)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+#)[A-Za-z0-9.-]+)(:[0-9]{0,5})?(#[\w]*)?((?:\/[\+~%\/.\w\-_]*)?\??(?:[-\+=&;%#.\w_]*)#?(?:[.\!\/\\w]*))?)/i");
$urlOriginal = $req->request->get('url');
$validUrl = preg_match(URL_PATTERN, $urlOriginal);
if($validUrl !== 1){
return $this->render('shortener/index.html.twig', ['invalidAutogenerated'=> 'La URL introducida no es valida.']);
}
$autogeneratedURL = bin2hex(random_bytes(6));
return $this->forward('App\Controller\UrlAddressController::createUrlAddress', ['urlEndpoint' => $autogeneratedURL, 'realUrl' => $urlOriginal, 'autogenerated' => true]);
}
#[Route('/create/custom', name: 'custom')]
public function custom(Request $req): Response
{
define('URL_PATTERN', "/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+#)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+#)[A-Za-z0-9.-]+)(:[0-9]{0,5})?(#[\w]*)?((?:\/[\+~%\/.\w\-_]*)?\??(?:[-\+=&;%#.\w_]*)#?(?:[.\!\/\\w]*))?)/i");
define('ENDPOINT_PATTERN', "/^[A-Za-z0-9]{7,15}$/i");
$urlOriginal = $req->request->get('urlCustom');
$nuevaUrl = $req->request->get('endpoint');
$validUrl = preg_match(URL_PATTERN, $urlOriginal);
$validEndpoint = preg_match(ENDPOINT_PATTERN, $nuevaUrl);
if($validUrl !== 1){
return $this->render('shortener/index.html.twig', ['invalidCustom'=> 'La URL introducida no es valida.']);
}
if($validEndpoint !== 1){
return $this->render('shortener/index.html.twig', ['invalidEndPoint'=> 'La URL introducida no es valida. Solo caracteres alfanuméricos.']);
}
return $this->forward('App\Controller\UrlAddressController::createUrlAddress', ['urlEndpoint' => $nuevaUrl, 'realUrl' => $urlOriginal, 'autogenerated' => 0]);
}
}
UrlAddressController.php
<?php
namespace App\Controller;
use App\Entity\UrlAddress;
use App\Repository\UrlAddressRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Annotation\Route;
class UrlAddressController extends AbstractController
{
#[Route('/urlAddress', name: 'url_address')]
public function createUrlAddress(ManagerRegistry $doctrine, String $urlEndpoint, String $realUrl, bool $autogenerated): Response
{
$created = '';
$entityManager = $doctrine->getManager();
$newUrl = new UrlAddress();
$newUrl->setUrlEndpoint($urlEndpoint);
$newUrl->setRealUrl($realUrl);
$newUrl->setAutogenerated($autogenerated);
$entityManager->persist($newUrl);
$entityManager->flush();
if($autogenerated){
$created = "Hemos generado tu URL automáticamente con exito.";
}else{
$created = "Tu custom URL se ha creado con exito.";
}
return $this->forward('App\Controller\ShortenerController::index', [
'created' => $created,
'endpoint' => "/$urlEndpoint",
'autogenerated' => $autogenerated
]);
}
#[Route('/redirection', name: 'redirection')]
public function redirection(String $finalRedirection, ManagerRegistry $doctrine, Request $request): Response
{
$repository = $doctrine->getRepository(UrlAddress::class);
$url = $repository->findOneBy(['urlEndpoint'=> $finalRedirection]);
if(!$url){
// return new Response('No encontramos esa URL en nuestras bases de datos');
// return $this->forward('App\Controller\ShortenerController::index', [
// 'error' => 'No encontramos esa URL en nuestras bases de datos'
// ]);
$this->addFlash('error', 'No encontramos esa URL en nuestras bases de datos');
return $this->redirectToRoute('shortener');
}
$finalUrl = $url->getRealUrl();
if(str_split($finalUrl)[0] === 'h'){
return $this->redirect($finalUrl);
}else{
return $this->redirect('http://'.$finalUrl);
}
}
}
UrlAddres.php (Entity)
<?php
namespace App\Entity;
use App\Repository\UrlAddressRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: UrlAddressRepository::class)]
class UrlAddress
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 255)]
private $urlEndpoint;
#[ORM\Column(type: 'string', length: 255)]
private $realUrl;
#[ORM\Column(type: 'boolean')]
private $autogenerated;
public function getId(): ?int
{
return $this->id;
}
public function getUrlEndpoint(): ?string
{
return $this->urlEndpoint;
}
public function setUrlEndpoint(string $urlEndpoint): self
{
$this->urlEndpoint = $urlEndpoint;
return $this;
}
public function getRealUrl(): ?string
{
return $this->realUrl;
}
public function setRealUrl(string $realUrl): self
{
$this->realUrl = $realUrl;
return $this;
}
The Apache virtual host configuration:
<VirtualHost *:80>
ServerName mydomain.xyz
ServerAlias www.mydomain.xyz
ServerAdmin webmaster#localhost
DocumentRoot /var/www/mydomain.xyz/public
<Directory /var/www/mydomain.xyz/public>
# enable the .htaccess rewrites
AllowOverride All
Order Allow,Deny
Allow from All
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Related
i have a problem with my production environnement.
In local i'm using php-cli and in production php-fpm and in production when $Response->send() is excecuted, i pass in the first if(\function_exists('fastcgi_finish_request'))) and this show me a white page instead of my render.
In my PublicController.php i have this :
#[Route('/user/validate/account', name: 'user_validate_account', methods: ['GET'])]
public function validateEmail(Request $request, SecurityService $securityService): Response
{
if($this->getUser() && $this->getUser()->getIsEmailVerified() && !$tokenRequested = $request->get('token')) return $this->redirectToRoute('public_home');
$securityService->forceLogout();
$session = $request->getSession();
$session->remove('_user_validate_account_security_token');
if($tokenRequested = $request->get('token')) {
$securityToken = $this->securityTokenRepository->findOneByToken($tokenRequested);
if(!$securityToken) {
$this->addFlash('globalInfo', "Le token de sécurité est invalide !");
return $this->redirectToRoute('public_user_validate_account');
} elseif($securityToken->getTokenType() === SecurityToken::_VALIDATION_EMAIL) {
if ($securityToken->getExpiresAt() < DateUtility::now()) {
$this->addFlash('globalInfo', "Votre token de sécurité à expiré, merci renouveler votre demande !");
$this->securityTokenRepository->remove($securityToken, true);
return $this->redirectToRoute('public_user_validate_account');
}
$securityToken->getUser()->setIsEmailVerified(true);
$this->securityTokenRepository->flush();
if ($securityToken->getUser()->getIsPasswordNeedToChange()) {
$session->set('_user_validate_account_security_token', $securityToken);
return $this->redirectToRoute('public_user_validate_password');
} else {
$this->addFlash('globalInfo', "Votre compte est vérifié, bonne gestion :)");
return $this->redirectToRoute('public_home');
}
}
}
return $this->render('security/user_validate/account.html.twig');
}
In local, with php cli when
$securityService->forceLogout();
is executed, script will continue and show my render. But in production i'm using php-fpm and i have a white page.
My $securityService->forceLogout(); use Response from HttpRequest of Symfony:
<?php
namespace App\Service;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Http\Event\LogoutEvent;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
class SecurityService
{
public function __construct(
private readonly RequestStack $requestStack,
private readonly EventDispatcherInterface $eventDispatcher,
private readonly TokenStorageInterface $tokenStorage
)
{}
public function forceLogout() : void
{
$logoutEvent = new LogoutEvent($this->requestStack->getCurrentRequest(), $this->tokenStorage->getToken());
$this->eventDispatcher->dispatch($logoutEvent);
$this->tokenStorage->setToken(null);
$response = new Response();
$response->headers->clearCookie('REMEMBERME');
$response->send();
}
}
When $response-send(); :
/**
* Sends HTTP headers and content.
*
* #return $this
*/
public function send(): static
{
$this->sendHeaders();
$this->sendContent();
if (\function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
} elseif (\function_exists('litespeed_finish_request')) {
litespeed_finish_request();
} elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
static::closeOutputBuffers(0, true);
flush();
}
return $this;
}
In production, i go to the 2nd elsif, and this show my page with my render, but in production i enter in the first if (\function_exists('fastcgi_finish_request')) and this show me a white page 200.
Someone can help me to fixe this please ?
I'm doing a login and registration screen, the registration screen is perfect, but the login screen is giving me a headache to authenticate.
the registration is done, but as soon as I log in it gives this error...
"Undefined property: Illuminate\Support\Facades\Request::$email"
I don't know what else to do to make it work.
CONTROLLER:
<?php
namespace App\Http\Controllers;
use App\Models\Usuario;
use Illuminate\Support\Facades\Auth;
use Request;
class Usuarios extends Controller
{
public function cadastrar()
{
$usuario = new Usuario(Request::all());
$usuario->save();
return redirect('/')->with('mensagem_sucesso', 'Cadastro efetuado com sucesso!');
}
public function index()
{
return view('layout/cadastrousuario');
}
public function indexlogin()
{
return view('layout/login');
}
public function logar(Request $request)
{
if (Auth::attempt(['email' => $request->email, 'password' => $request-> password])) {
dd('voce esta logado');
} else {
dd('voce nao esta logado');
}
}
}
MODEL:
<?php
namespace App\Models;
use App\Models\Model\Request;
use DB;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Support\Facades\Hash;
class Usuario extends Authenticatable
{
protected $table = 'usuario';
public $timestamps = false;
protected $fillable =
array(
"codigo",
"nome",
"email",
"apelido",
"senha",
"bloqueado",
"saldo",
"saldo_atual",
"admin"
);
use HasFactory;
}
ROUTE:
<?php
use App\Http\Controllers\Lancamentos;
use App\Http\Controllers\LancamentosSimplificado;
use App\Http\Controllers\Usuarios;
use Illuminate\Support\Facades\Route;
// Route = (rota)::get ou post = (method) ( '/home' = (link) , [Lancamentos = (controller) :: class, 'logar' = ( function) ;
Route::get('/', [Lancamentos::class, 'index']);
Route::get('/salvar', [Lancamentos::class, 'salvar']);
Route::get('/maisdetalhes/{codigo}', [Lancamentos::class, 'maisdetalhes']);
Route::get('/criarchat', [Lancamentos::class, 'criarchat']);
Route::post('/cadastrar', [Lancamentos::class, 'cadastrar']);
Route::post('/cadastrar-simplificado', [LancamentosSimplificado::class, 'cadastrar']);
Route::get('/criarchat', [LancamentosSimplificado::class, 'listar']);
Route::get('/chat/{codigo}', [Lancamentos::class, 'chat']);
Route::get('/chatcriado/{codigo}', [LancamentosSimplificado::class, 'chatcriado']);
Route::get('/cadastrar-usuario', [Usuarios::class, 'index']);
Route::post('/cadastrar-usuario', [Usuarios::class, 'cadastrar']);
Route::get('/login', [Usuarios::class, 'indexlogin']);
Route::post('/login', [Usuarios::class, 'logar']);
page image as soon as I click login
to start you have to make validations in the register function to be sure that the email address arrives well and is registered. i would start by modifying this function
public function cadastrar(Request $r)
{
$r->validate([
'name' => 'required|string',
'email' => 'required|email|unique:users',
'password' => 'min:6',
'password_confirmation' => 'required_with:password|same:password|min:6',
'custom_field' => 'custom validation'
]);
$input = $r->all();
$input['password'] = Hash::make($r->password);
$utilisateur = Model::create($input); //the Model == Usuario;
return redirect('/')->with([
'message' => "Cadastro efetuado com sucesso!",
'alert-type' => 'success',
]);
}
this is just a code snippet, I don't pretend to say that it's exactly what you need.the next way is the login function
if (Auth::attempt(['email' => $r->email, 'password' => $r->password])) {
// The user is active, not suspended, and exists.
$user = Auth::user();
if($user->fcm_token != Null){
$token = $user->createToken('AUTH')->accessToken;
$user->remember_token = $token;
$user->device_token = $user->fcm_token;
$user->save();
$response = [
"data"=> [
'user'=> $user,
'token'=> $token,
],
'message_fr' => 'Utilisateur connecté avec succès',
'message_en' => 'User logged successfully',
];
return response()->json($response, 200);
}else{
$response = [
'message_fr' => 'Vous êtes peut-être un robot',
'message_en' => 'You may be a robot',
];
return response()->json($response, 422);
}
} else {
$response = [
'message_fr' => 'Veuillez vérifier vos informations de connexion',
'message_en' => 'Please check your login information',
];
return response()->json($response, 422);
}
since you put a validation on the register, you are sure that the email is not only present, but also conforms to the nomenclature of an email
these two methods presented are examples taken from my source code of an available project, Good luck to you
You are using the wrong Request class. Request (Illuminate\Support\Facades\Request) that is aliased in config/app.php is the Facade, static proxy, for the bound Request class instance, Illuminate\Http\Request. If you want an instance of a Request you need to be using Illuminate\Http\Request.
use Illuminate\Http\Request;
Now via dependency injection you will have an instance of the Request class (which has magic methods to access inputs via dynamic properties). If you keep what you have then you would not be asking for an instance via dependency injection and would have to use the Facade as a Facade:
public function logar()
{
...
$something = Request::input(...); // static call to Facade
...
}
When I run PHPUnit tests, there are some errors displayed in my console, even though these errors do not affect my tests (they all pass). And it is very annoying to see all these errors
displayed errors
I tried in phpunit.xml file add the following lines:
convertErrorsToExceptions="false"
convertNoticesToExceptions="false"
convertWarningsToExceptions="false"
but it didn't help. I am using:
PHPUNIT: 9.5
Symfony: 6.0
PHP: 8.1
My tests look like this:
/**
* #test
* #dataProvider editItemInvalidDataProvider
*/
public function should_not_edit_item_and_not_redirect_when_failure(
?string $itemName,
?string $itemSellIn,
?string $itemQuality
): void {
$this->loginUser();
$crawler = $this->client->request('POST', '/item/1/edit');
$button = $crawler->selectButton('edit-item');
$form = $button->form();
$form['item[name]']->setValue($itemName);
$form['item[sellIn]']->setValue($itemSellIn);
$form['item[quality]']->setValue($itemQuality);
$this->client->submit($form);
$this->assertResponseNotHasHeader('location');
}
/** #test */
public function should_return_403_when_not_logged_in_and_reaching_edit_page(): void
{
$this->client->request('GET', '/item/1/edit');
$this->assertResponseStatusCodeSame(403);
}
And the controller:
#[Route('/item/{id}/edit', name: 'item_edit')]
#[ParamConverter('item', class: Item::class)]
#[IsGranted('ROLE_ADMIN', statusCode: 403)]
public function edit(
?Item $item,
Request $request,
): Response {
if (!$item) {
$this->addFlash('error', 'No item found');
return $this->redirectToRoute('item_list');
}
$form = $this->createForm(ItemType::class, $item);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->entityManager->persist($item);
$this->entityManager->flush();
$this->addFlash('success', 'Item successfully edited!');
return $this->redirectToRoute('item_list');
}
return $this->render('item/edit.html.twig', [
'form' => $form->createView(),
]);
}
Any ideas on how to suppress those errors?
You can tell the client to not follow any redirects with the following config:
$this->client->request('POST', '/item/1/edit', [
'max_redirects' => 0,
]);
This should prevent some (maybe all) of your errors.
one question...If I want to disable email verification upon user registration ( I would like for users to be logged in automatically after registration) how should I do that? Should I change it in the configuration somewhere or should I override controllers and manually enable users and add verification for them? I saw that in previous sylius versions there were a configuration for verification in sylius_user (SyliusUserBundle) but in new version, there is no configuration for that.
Thank you.
//edit//
I have overridden controller for registration(code below) and just got User and enabled it plus logged him in with service provided with sylius.
<?php
namespace AppBundle\Controller;
use Blameable\Fixture\Document\User;
use FOS\RestBundle\View\View;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Sylius\Bundle\ResourceBundle\Controller\ResourceController as BaseCustomerController;
use Sylius\Component\Resource\ResourceActions;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Sylius\Bundle\UserBundle\Security\UserLogin as UserLogin;
class CustomerController extends BaseCustomerController
{
/**
* #param Request $request
*
* #return Response
*/
public function createAction(Request $request)
{
$configuration = $this->requestConfigurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($configuration, ResourceActions::CREATE);
$newResource = $this->newResourceFactory->create($configuration, $this->factory);
$form = $this->resourceFormFactory->create($configuration, $newResource);
if ($request->isMethod('POST') && $form->handleRequest($request)->isValid()) {
$newResource = $form->getData();
$event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::CREATE, $configuration, $newResource);
if ($event->isStopped() && !$configuration->isHtmlRequest()) {
throw new HttpException($event->getErrorCode(), $event->getMessage());
}
if ($event->isStopped()) {
$this->flashHelper->addFlashFromEvent($configuration, $event);
return $this->redirectHandler->redirectToIndex($configuration, $newResource);
}
if ($configuration->hasStateMachine()) {
$this->stateMachine->apply($configuration, $newResource);
}
$newResource->getUser()->enable();
$this->repository->add($newResource);
$this->get('sylius.security.user_login')->login($newResource->getUser());
$this->eventDispatcher->dispatchPostEvent(ResourceActions::CREATE, $configuration, $newResource);
if (!$configuration->isHtmlRequest()) {
return $this->viewHandler->handle($configuration, View::create($newResource, Response::HTTP_CREATED));
}
$this->flashHelper->addSuccessFlash($configuration, ResourceActions::CREATE, $newResource);
return $this->redirectHandler->redirectToResource($configuration, $newResource);
}
if (!$configuration->isHtmlRequest()) {
return $this->viewHandler->handle($configuration, View::create($form, Response::HTTP_BAD_REQUEST));
}
$view = View::create()
->setData([
'configuration' => $configuration,
'metadata' => $this->metadata,
'resource' => $newResource,
$this->metadata->getName() => $newResource,
'form' => $form->createView(),
])
->setTemplate($configuration->getTemplate(ResourceActions::CREATE . '.html'))
;
return $this->viewHandler->handle($configuration, $view);
}
}
You can simply do that by bringing back two classes from this PR:
UserAutoLoginListener
UserRegistrationFormSubscriber
EDIT: SOLVED
Apparently this plugin, was having some problem missing the request headers. The solution was adding
SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0
To the .htaccess file to make Authorizaion variable available as this issue report says:
https://github.com/yiisoft/yii2/issues/6631
I'm currently working with Yii2 and using this OAuth2 plugin (Filsh/yii2-oauth2-server) for login and to work with tokens from a mobile HTML5 app.
I've configured everything, and it's retrieving the token, but when I try to send the token via POST it throws an error.
I start by calling send() to retrieve the token.
function send(){
var url = "http://www.server.org/app/api/oauth2/rest/token";
var data = {
'grant_type':'password',
'username':'user',
'password':'pass',
'client_id':'clientid',
'client_secret':'clientsecret',
};
$.ajax({
type: "POST",
url: url,
data: data,
success:function(data){
console.log(data);
token = data.access_token;
},
})
};
Then when I perform this a call to createuser().
function createuser(){
var url = "http://www.server.org/app/api/v1/users/create";
var data = {
'callback':'asdf',
'username': 'user',
'password':'pass',
'first_name':'name',
'last_name':'lastname'
};
$.ajax({
type: "POST",
url: url,
data: data,
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
},
success:function(r){
console.log(r);
},
});
}
It returns
Unauthorized: You are requesting with an invalid credential
When I change to GET instead, it works fine.
This is my controller, I'm already using:
['class' => HttpBearerAuth::className()],
['class' => QueryParamAuth::className(), 'tokenParam' => 'accessToken'],
As authentication method.
<?php
namespace app\api\modules\v1\controllers;
use Yii;
use app\models\OauthUsers;
use yii\rest\ActiveController;
use yii\web\Response;
use yii\helpers\ArrayHelper;
use yii\filters\auth\HttpBearerAuth;
use yii\filters\auth\QueryParamAuth;
use filsh\yii2\oauth2server\filters\ErrorToExceptionFilter;
use filsh\yii2\oauth2server\filters\auth\CompositeAuth;
class UsersController extends \yii\web\Controller
{
public function behaviors()
{
return ArrayHelper::merge(parent::behaviors(), [
'authenticator' => [
'class' => CompositeAuth::className(),
'authMethods' => [
['class' => HttpBearerAuth::className()],
['class' => QueryParamAuth::className(), 'tokenParam' => 'accessToken'],
]
],
'exceptionFilter' => [
'class' => ErrorToExceptionFilter::className()
],
'class' => \yii\filters\ContentNegotiator::className(),
]);
}
/**
* Creates a new model.
* If creation is successful, the browser will be redirected to the 'view' page.
* #return mixed
*/
public function actionCreate()
{
$model = new OauthUsers;
try {
if ($model->load($_POST) && $model->save()) {
return $this->redirect(Url::previous());
} elseif (!\Yii::$app->request->isPost) {
$model->load($_GET);
}
} catch (\Exception $e) {
$msg = (isset($e->errorInfo[2]))?$e->errorInfo[2]:$e->getMessage();
$model->addError('_exception', $msg);
}
return "true";
}
}
This is my configuration object
'oauth2' => [
'class' => 'filsh\yii2\oauth2server\Module',
'tokenParamName' => 'accessToken',
'tokenAccessLifetime' => 3600 * 24,
'storageMap' => [
'user_credentials' => 'app\models\OauthUsers',
],
'grantTypes' => [
'user_credentials' => [
'class' => 'OAuth2\GrantType\UserCredentials',
],
'refresh_token' => [
'class' => 'OAuth2\GrantType\RefreshToken',
'always_issue_new_refresh_token' => true
]
]
]
This is class OauthUsers
<?php
namespace app\models;
use Yii;
use \app\models\base\OauthUsers as BaseOauthUsers;
/**
* This is the model class for table "oauth_users".
*/
class OauthUsers extends BaseOauthUsers
implements \yii\web\IdentityInterface,\OAuth2\Storage\UserCredentialsInterface
{
/**
* #inheritdoc
*/
public static function findIdentity($id) {
$dbUser = OauthUsers::find()
->where([
"id" => $id
])
->one();
if (!count($dbUser)) {
return null;
}
return new static($dbUser);
}
/**
* #inheritdoc
*/
public static function findIdentityByAccessToken($token, $userType = null) {
$at = OauthAccessTokens::find()
->where(["access_token" => $token])
->one();
$dbUser = OauthUsers::find()
->where(["id" => $at->user_id])
->one();
if (!count($dbUser)) {
return null;
}
return new static($dbUser);
}
/**
* Implemented for Oauth2 Interface
*/
public function checkUserCredentials($username, $password)
{
$user = static::findByUsername($username);
if (empty($user)) {
return false;
}
return $user->validatePassword($password);
}
/**
* Implemented for Oauth2 Interface
*/
public function getUserDetails($username)
{
$user = static::findByUsername($username);
return ['user_id' => $user->getId()];
}
/**
* Finds user by username
*
* #param string $username
* #return static|null
*/
public static function findByUsername($username) {
$dbUser = OauthUsers::find()
->where([
"username" => $username
])
->one();
if (!count($dbUser)) {
return null;
}
return new static($dbUser);
}
/**
* #inheritdoc
*/
public function getId()
{
return $this->id;
}
/**
* #inheritdoc
*/
public function getAuthKey()
{
return $this->authKey;
}
/**
* #inheritdoc
*/
public function validateAuthKey($authKey)
{
return $this->authKey === $authKey;
}
/**
* Validates password
*
* #param string $password password to validate
* #return boolean if password provided is valid for current user
*/
public function validatePassword($password)
{
return $this->password === $password;
}
}
I've changed createuser too, but still receiving a 401. I'm not sure it is passing through findIdentityByAccessToken (access_token is in a different table than oauth users, thats why I'm querying it first).
Any thoughts?
I don't know the plugin you are using but what I know is that you can use the Yii2 HttpBearerAuth filter when implementing OAuth 2.0 which means using the HTTP Bearer token. And that token is typically passed with the Authorization header of the HTTP request instead of the body request and it usually looks like :
Authorization: Bearer y-9hFW-NhrI1PK7VAXYdYukwWVrNTkQ1
The idea is about saving the token you received from the server somewhere (should be a safe place) and include it in the headers of the requests that requires server authorization (maybe better explained here, here or here) So the JS code you are using to send a POST request should look more like this :
function createuser(){
var url = "http://www.server.org/app/api/v1/users/create";
var data = {
'callback':'cb',
'username': 'user',
'password':'pass',
'first_name':'name',
'last_name':'lastname'
};
$.ajax({
type: "POST",
url: url,
data: data,
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
},
success:function(r){
console.log(r);
},
});
}
Also check in the User class (the one defined in your config files and responsible of authenticating a user) check the implementation of the findIdentityByAccessToken() method. it should usually look like this (see Yii docs for more details) :
public static function findIdentityByAccessToken($token, $type = null)
{
return static::findOne(['auth_key' => $token]);
}
This is the function that will receive the token and it should return null when authentication should fail (by default findOne() returns null when no record is found) or returns a User instance to register it and make it accessible within Yii::$app->user->identity or Yii::$app->user->id anywhere inside your app so you can implement whatever logic you need inside it like checking the validity of an access token or its existence in a different table.
Apparently this plugin (Filsh/yii2-oauth2-server), was having some problem missing the request headers. The solution was adding
SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0
To the .htaccess file to make Authorizaion variable available as this says:
https://github.com/yiisoft/yii2/issues/6631