Laravel Passport and Heroku where to store encryption keys? - laravel

I am trying to deploy a Laravel application, which uses Laravel Passport, with Heroku.
Everytime the app is deployed the slug is rebuilt causing the Laravel Passport encryption keys to be erased. It means everytime the app is deployed:
the storage folder permissions should be changed
new encryption keys should be generated
all existing tokens would be undecryptable
For now, the solution I ended up with is to store the OAuth private and public keys in two different Heroku config variables (a.k.a. environment variables). And I extended the PassportServiceProvider class so it looks for the encryption keys in environment variables if the environment is set to "production":
I created a new PassportServiceProvider class in app/Providers:
<?php
namespace App\Providers;
use Laravel\Passport\PassportServiceProvider as LaravelPassportServiceProvider;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\CryptKey;
use League\OAuth2\Server\ResourceServer;
class PassportServiceProvider extends LaravelPassportServiceProvider
{
protected function registerResourceServer()
{
$publicKeyPath = config('app.env') === 'production' ? env('OAUTH_PUBLIC_KEY') : 'oauth-public.key';
$this->app->singleton(ResourceServer::class, function () use ($publicKeyPath) {
return new ResourceServer(
$this->app->make(\Laravel\Passport\Bridge\AccessTokenRepository::class),
$this->makeCryptKey($publicKeyPath)
);
});
}
public function makeAuthorizationServer()
{
$privateKeyPath = config('app.env') === 'production' ? env('OAUTH_PRIVATE_KEY') : 'oauth-private.key';
return new AuthorizationServer(
$this->app->make(\Laravel\Passport\Bridge\ClientRepository::class),
$this->app->make(\Laravel\Passport\Bridge\AccessTokenRepository::class),
$this->app->make(\Laravel\Passport\Bridge\ScopeRepository::class),
$this->makeCryptKey($privateKeyPath),
app('encrypter')->getKey()
);
}
protected function makeCryptKey($key)
{
if (config('app.env') === 'production') {
return new CryptKey(str_replace('\n', "\n", $key), null, false);
}
return new CryptKey(file://'.Passport::keyPath($key), null, false);
}
}
Append the following line to the providers array in config/app.php:
Api\Providers\PassportServiceProvider::class,
Tell Laravel to not auto-discover the Laravel Passport package. in composer.json:
"extra": {
"laravel": {
"dont-discover": [
"laravel/passport"
]
}
}
This solution is not fully functional on its own because of this issue.
But I am not convinced it is the right way to do it. I wonder if someone already faced the problem. If so how did you solve it?
Thank you in advance.

Related

Can not get access to Telescope dashboard

I want to add telescope into my laravel 8 app, but having in .env
APP_ENV=local
TELESCOPE_ENABLED=true
and reading at site :
https://laravel.com/docs/8.x/telescope
The Telescope dashboard may be accessed at the /telescope route. By default, you will only be able to access this dashboard in the local environment.
on url
http://local-tads.com/telescope
I got 404 error, where http://local-tads.com - is local hosting of my app
In app/Providers/AppServiceProvider.php file I added lines :
<?php
namespace App\Providers;
class AppServiceProvider extends ServiceProvider
...
if ($this->app->environment('local')) {
$this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class);
\Event::listen(
[
TransactionBeginning::class,
],
function ($event) {
...
I have unmodified vendor/laravel/telescope/config/telescope.php file.
Have I to add route in routes/web.php and in which way ?
How to get access to telescope dashboard ?
UPDATED BLOCK :
I run both commands :
php artisan telescope:install
php artisan migrate
But I did not find config/telescope.php, so I copied it from /vendor/ subdirectory
Running command
php artisan route:list
has no any “telescope” entry.
In file app/Providers/AppServiceProvider.php I added lines with telescope :
<?php
namespace App\Providers;
use App\Library\Services\AdminCategoryCrud;
//use App\Providers\TelescopeServiceProvider;
use Illuminate\Database\Events\TransactionBeginning;
use Illuminate\Database\Events\TransactionCommitted;
use Illuminate\Database\Events\TransactionRolledBack;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Blade;
use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;
use Laravel\Telescope\TelescopeServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
if ($this->app->environment('local')) {
$this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class);
$this->app->register(TelescopeServiceProvider::class);
}
Not sure if all is correct?
in env I have :
APP_ENV=local
TELESCOPE_ENABLED=true
and in composer.json I added line :
"extra": {
"laravel": {
"dont-discover": [
"laravel/telescope"
]
}
},
and updated composer
But http://local-tads.com/telescope - still raise 404 error...
Thanks in advance!
I have this problem but I attention Laravel Documentation I watch this code and run them:
telescope:install
, you should remove the
TelescopeServiceProvider
service provider registration from your application's
config/app.php
configuration file. Instead, manually register Telescope's service providers in the
register
method of your
App\Providers\AppServiceProvider
class.
A potential answer to your problem could be removing the telescope package from the dont-discover array. So your new extra section would be like this one:
"extra": {
"laravel": {
"dont-discover": []
}
}
Also, don't forget to dump your autoload by running composer dump-autoload in your project folder. I hope this helps you :D
For more information please check this issue on the github repository.

Validator is not working on Resource routes in AdonisJS

I'm having problems with validator on Route.resource(). The validator is not been applied and when I run the command "adonis route:list" the av:TrainingPlan and Workout validators are not being listed. You can see that my other validators are working on single endpoint verb like "Route.post" on /users, /sessions/ and /passwords. On app.js under start folder I checked that the register was made too and all validator was generated by adonis cli.
My routes file was made based on documentation:
Route.group(() => {
Route.post('files', 'FileController.store')
Route
.resource('/training-plans', 'TrainingPlanController')
.apiOnly()
.validator(new Map([
[['training-plans.store'], ['TrainingPlan']]
]))
Route
.resource('/workouts', 'WorkoutController')
.apiOnly()
.validator(new Map([
[['workouts.store'], ['Workout']]
]))
}).middleware(['auth'])
TrainingPlan Validator:
'use strict'
class TrainingPlan {
get validateAll () {
return true
}
get rules () {
return {
title: 'required',
description: 'required',
start_date: `date|before:${new Date()}`,
end_date: `date|before:${new Date()}`
}
}
}
module.exports = TrainingPlan
What I'm missing out?
adonis route:list command result
The problem was resolved removing / before /workouts and /training-plans route.
I did not tested putting / before workouts.store and training-plans.store inside Map validator.

Override aliases defined in package's composer.json with Laravel app config defined ones

I am using 3 packages in my Laravel 5.8 application:
Backpack CRUD (which uses Backpack Base) https://github.com/Laravel-Backpack/CRUD
Styde\HTML https://github.com/StydeNet/html/
Prologue\Alerts https://github.com/prologuephp/alerts
These are clashing because Backpack Base relies on the Global Alias for "Alert" being set to use PrologueAlert. See an example of how it uses \Alert here:
private function checkLicenseCodeExists()
{
if ($this->app->environment() != 'local' && !config('backpack.base.license_code')) {
\Alert::add('warning', "<strong>You're using unlicensed software.</strong> Please ask your web developer to <a target='_blank' href='http://backpackforlaravel.com'>purchase a license code</a> to hide this message.");
}
}
Source: https://github.com/Laravel-Backpack/Base/blob/1.1.4/src/BaseServiceProvider.php#L264
Because I haven't bought that license yet I started seeing an error caused because that above snippet was trying to pass a string to Alert::add() but it was calling the add() method on Styde\Html\Alert\Container::add() which expects the parameter to be an instance of Styde\Html\Alert\Message instead of calling it on Prologue's version of Alert which accepts a string. It's calling the wrong "Alert"!
Even though my application is specifically set to use PrologueAlert for Alert
// config/app.php
'aliases' => [
...
'Alert' => Prologue\Alerts\Facades\Alert::class
]
I have discovered the reason is that in version 1.7 Styde moved the Aliases for his package from the protected $globalAliases variable on HTMLServiceProvider.php to the composer.json auto-discover section
"extra": {
"laravel": {
"providers": [
],
"aliases": {
"Field": "Styde\\Html\\Facades\\Field",
"Alert": "Styde\\Html\\Facades\\Alert",
"Menu": "Styde\\Html\\Facades\\Menu",
"Form": "Collective\\Html\\FormFacade",
"Html": "Collective\\Html\\HtmlFacade"
},
"dont-discover": [
"laravelcollective/html"
]
}
}
Source: https://github.com/StydeNet/html/commit/f51138fb42bef458f3f0e101b98344162b7327ba#diff-b5d0ee8c97c7abd7e3fa29b9a27d1780
Now, it seems my application is prioritising Styde's alias of "Alert" over my own application-set value!
Other than rolling back to use version 1.6 of Styde, how can I force Laravel to prioritise my own defined aliases over ones discovered via the composer.json?
I found the solution! It was actually inspired by a snippet in my original post.
You can add an extra section to your application's composer.json that get read by the Laravel application and used to decide which packages to ignore during auto-discover like this:
// composer.json
{
...
"extra": {
"laravel": {
"dont-discover": [
"styde/html"
]
}
}
}
This then allows you to pick and choose aliases from the problematic package and define as many or as few of the them as you like in your config/app.php (for my application I was only using the Field alias from Styde/Html so that was the only one I had to add to my App config).
I think as more and more package maintainers start leveraging the auto-discover feature this is going to become a more widely used feature.
Afterthought: This is a shift in the relationship between Composer and Laravel. Whereas the composer.json file was traditionally just a package manager that would be run at installation and then not used when the application is running, it is now a config file that is read by the application. I learned this the hard way as our pipeline that packages-up and deploys the code used to tidy up files that weren't required in the production environment. It was deleting composer.json which started the error happening again in our QA environment.

how to access vue.js api key in laravel application

hello there i am trying to access my youtube api key located in the .env file from within this code:
<template>
<div class="YoutubeDash__wrapper">
<video-group :videos="videos"></video-group>
</div>
</template>
<script>
import VideoGroup from './VideoGroup.vue';
import Search from './Search';
export default {
components: {
VideoGroup
},
created(){
Search({
apiKey: process.env.VUE_APP_SECRET,
term: 'laravel repo'
}, response => this.videos = response);
},
data(){
return {
videos: null
}
}
}
</script>
According to the documentation using env variables with vue.js. Everything seems to be correct. In my .env file i say: VUE_APP_SECRET=xxxxxxxxxxxxxxx, what am i missing here ?
I get this error message:
app.js:37809 Error: YouTube search would require a key
Any tips are welcome! Thanks a lot!
We need to work with a small amount of information here so I am going to make a few assumptions (based on the tags) mostly that you are using laravel and laravel-mix to compile your resources.
For laravel(-mix) to make your .env variables accessible by JS you need to prefix them with MIX_ i.e. MIX_VUE_APP_SECRET. This will make your variable accessible as process.env.MIX_VUE_APP_SECRET.
I prefer excluding laravel-mix from this process.
Usually, in my blade entry-point I use meta tags:
<meta name="myVal" content="{{ config('<any-config-key>') }}">
<any-config-key> can be any laravel configuration key including those taken from .env.
Then, in my javascript I do something like:
const setVueGlobal = (metaHeaderName, vueGlobalName) => {
let value = document.head.querySelector('meta[name="' + metaHeaderName + '"]').content;
if (!value) {
console.error('some error msg');
return null;
}
Vue.prototype[vueGlobalName] = value;
return value;
};
setVueGlobal('myVal', '$myVal');
Finally, accessing using this.$myVal

Private channel not working with Laravel echo server

I'm getting this JS error on the console:
app.js:167 Uncaught ReferenceError: receiverId is not defined
Here is my complete code:
PrivateChatController:
event(new PrivateMessageEvent($chat, $receiverId));
PrivateMessageEvent:
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use App\User;
use App\PrivateChat;
class PrivateMessageEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $privateChat, $receiverId;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(PrivateChat $privateChat, $receiverId)
{
$this->privateChat = $privateChat;
$this->receiverId = $receiverId;
}
/**
* Get the channels the event should broadcast on.
*
* #return Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('private-chat.' . $this->receiverId);
}
}
Bootstrap.js
import Echo from "laravel-echo"
window.Echo = new Echo({
broadcaster: 'socket.io',
host: window.location.hostname + ':6001'
});
window.Echo.private(`private-chat.${receiverId}`)
.listen('PrivateMessageEvent', (e) => {
console.log(e);
});
channels.php
Broadcast::channel('private-chat.{receiverId}', function ($user, $receiverId) {
return true; // this is just for debugging to allow anyone to listen on this channel
//return $user->id === $receiverId;
});
laravel-echo-server.json
{
"authHost": "http://localhost",
"authEndpoint": "/broadcasting/auth",
"clients": [],
"database": "redis",
"databaseConfig": {
"redis": {},
"sqlite": {
"databasePath": "/database/laravel-echo-server.sqlite"
}
},
"devMode": true,
"host": null,
"port": "6001",
"protocol": "http",
"socketio": {},
"sslCertPath": "",
"sslKeyPath": ""
}
In background queue:work and laravel-echo-server are already running
Upon firing that event, I'm getting this message on the laravel-echo-server console:
Channel: private-private-chat.
Event: App\Events\PrivateMessageEvent
CHANNEL private-private-chat.
Notes:
I'm successfully able to listen to the public channel. The only issue with the private channel.
Using latest Laravel version i.e 5.4
I have done all the things as per the official docs:
https://laravel.com/docs/master/broadcasting
https://github.com/tlaverdure/laravel-echo-server
I have also raised issue on github repo:
https://github.com/tlaverdure/laravel-echo-server/issues/158
I have spent more than 10 hours and tried everything I could, but no luck.
Thanks
You can set REDIS_PREFIX to NULL to remove the prefix else if it has a value then you must set keyPrefix in the echo server config.
If REDIS_PREFIX = NULL then do not add keyPrefix.
Important Notice
When using broadcastAs(), the call to listen('') call must start with a DOT
At this moment the behavior when keyPrefix is used is unknown, if you use the prefix settings, please comment on the outcome of the DOT requirement.
https://laravel.com/docs/6.x/broadcasting#broadcast-name
public function broadcastAs()
{
return 'server.created';
}
.listen('.server.created', function (e) {
....
});
I would check the DOT + PREFIX combo my self but I feel Laravel Echo is going to give me a heart attack if I work on it any longer.
If you do not use broadcastAs() then the naming will fallback to the event class name, in this case there is no DOT prefix injected, see the setup below:
laravel-echo-server.json
{
"host": "127.0.0.1",
"port": "6001",
"protocol": "http",
"database": "redis",
"databaseConfig": {
"redis": {
"host": "127.0.0.1",
"port": 6379,
"db": 0,
"keyPrefix": "VALUE OF REDIS_PREFIX"
}
}
/app/Events/MyExample.php
<?php
namespace App\Events;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
class MyExample implements ShouldBroadcastNow
{
private $id;
public $payload;
public function __construct($id, $payload)
{
$this->id = $id;
$this->payload = $payload;
}
public function broadcastOn()
{
return new PrivateChannel('example.' . $this->id);
}
}
Trigger an event (PHP)
use App\Events\MyExample
$payload = [
'duck' => 'sauce',
'Bass' => 'Epic'
];
event(new MyExample($id, $payload))
Listening for event (JavaScript)
Echo.private(`example.${id}`).listen('MyExample', event => {
console.log('payload', event.payload)
})
Try to change this line:
$this->$receiverId = $receiverId;
To this line:
$this->receiverId = $receiverId;
In your PrivateMessageEvent __construct()
Update:
Try to use use a fixed channel id like this:
window.Echo.private('private-chat.1')
I suggest you also to use presence channel, are private too but with more features, i.e.:
Echo.join('private-chat.1')
.listen('PrivateMessageEvent', (e) => {
console.log(e);
});
If you use a dinamic channel number like you use, i.e.:
window.Echo.private(`private-chat.${receiverId}`)
You have to give receiverId a value in javascript, this declaration is a generic room listener but receiverId should be defined, ${receiverId} is a string interpolation.
You can define receiverId in the template before inluding app.js, for example, using blade syntax:
<script>
receiverId = {{ $receiverId }};
</script>
Another think: I want to be clear that, in all the code above, receiverId represent the id of the chat/room a client want join to not the ID of the receiving user.
Try instead of event(new PrivateMessageEvent($chat, $receiverId));
this
App\Events\PrivateMessageEvent::dispatch($chat, $receiverId);
There are a few things you need to do.
1) Remove all 'private-' from the beginning of your channel names. These get handled behind the scenes by the various libraries (eg socket.io/laravel-echo). If you look at any debug output you will see 'private-' prepended to the front of the channel names but you DO NOT need to add these.
2) If you're using redis then make sure the REDIS_PREFIX is set to an empty string in your .env file. By default it's set to something like 'laravel_database' and this messes up the channel names.
REDIS_PREFIX=
3) Make sure you've got your laravel-echo server running (and redis/pusher). Also you need to make sure you have the queue worker running. In my case, as i;m running redis, i had to run:
php artisan queue:work redis

Resources